firestoreでtimestampカラム(createdAt,updatedAt)を自動付与する
Gakuです。
最近はもっぱら、flutterとfirebaseで開発を行っています。
firestoreはNoSQLということもあって癖がありますが、最近ようやく慣れてきて爆速開発環境でニヤニヤしてます。
(個人開発ならこれくらいの環境でいいんだよ!
そんなfirestoreですが、ソートを実施するためや、リストの制限のために、documentの生成時・変更時にcreatedAtとupdatedAtを必ず付与する設計にしている人も多いのではないでしょうか?
createdAtとupdatedAtカラムをdocumentの生成時に必ず付与するようにしてもいいのですが、firestore ruleでcreatedAtとupdatedAtのルールを毎回付与するのも面倒ですし、
createdAtとupdatedAtがあることによって、upsertなメソッドを記載する時に嫌な書き方になることもあったので、firebase functionで一律で付与する設定にしてみました。
実際のコード
// 一階層目のcreatedAtの自動付与 export const createdCommonProcessNested1 = functions.firestore .document("{colId}/{colDocId}") .onCreate(async (snap) => { await fireStore.doc(snap.ref.path).set({ createdAt: admin.firestore.FieldValue.serverTimestamp(), updatedAt: admin.firestore.FieldValue.serverTimestamp(), }, {merge: true}); }); // 一階層目のupdatedAtの自動付与 export const updatedCommonProcessNested1 = functions.firestore .document("{colId}/{colDocId}") .onUpdate(async (snap) => { if (snap.before.data().updatedAt?._seconds !== snap.after.data().updatedAt._seconds) { return; } await fireStore.doc(snap.before.ref.path).set({ updatedAt: admin.firestore.FieldValue.serverTimestamp(), }, {merge: true}); }); // 二階層目のupdatedAtの自動付与 export const createdCommonProcessNested2 = functions.firestore .document("{colId}/{colDocId}/{subColId}/{subColDocId}") .onCreate(async (snap) => { await fireStore.doc(snap.ref.path).set({ createdAt: admin.firestore.FieldValue.serverTimestamp(), updatedAt: admin.firestore.FieldValue.serverTimestamp(), }, {merge: true}); }); // 二階層目のupdatedAtの自動付与 export const updatedCommonProcessNested2 = functions.firestore .document("{colId}/{colDocId}/{subColId}/{subColDocId}") .onUpdate(async (snap) => { if (snap.before.data().updatedAt?._seconds !== snap.after.data().updatedAt._seconds) { return; } await fireStore.doc(snap.before.ref.path).set({ updatedAt: admin.firestore.FieldValue.serverTimestamp(), }, {merge: true}); });
こんな感じでfirebase functionsに記載しておくことでcreatedAtとupdatedAtを自動で付与・更新してくれます。 ポイントとしては
export const createdCommonProcessNested1 = functions.firestore .document("{colId}/{colDocId}")
.document("{colId}/{colDocId}")の部分でwildcardを指定することができないため、階層毎にメソッドを定義してあげる必要があります。
(今回の場合1階層目のdocumentと2階層目のdocumentを生成・更新した際にcreatedAtとupdatedAtを更新するようになります。
なので3階層目などで自動付与を実施する場合、別途メソッドを定義してあげる必要があります。
また、update時
documentのupdateを実施する→updatedAtの更新を実施するメソッドを実行→updatedAtの更新を実施するメソッドを実行→・・・∞
というようにupdateを検知してupdatedAtの更新を実施しているので、何もしないと∞ループに陥ってしまいます。 なので
if (snap.before.data().updatedAt?._seconds !== snap.after.data().updatedAt._seconds) { return; }
の部分で、更新時にupdatedAtを更新したか検知し、updatedAtを更新した(上記で定義したupdatedAtの更新メソッドで更新)場合、処理を終了するようにしています。
おわり
以上です。この設定しておけば、createdAtとupdatedAtの概念を考えずに実装できるようになるので、とりあえず設定しておけば幸せになります。