はじめに
- Firebase の Cloud Firestore をある程度触ってる方向けの内容なので、基礎的な説明は省きます。
- サンプルコードは JavaScript ですので、他言語の SDK をお使いの方は適宜読み替えてください。
公式ドキュメントにも載っておらず、ググってもなかなか出てこない情報だったのでどなたかの役に立てば幸いです
何を解決したいか
ref. https://firebase.google.com/docs/firestore/query-data/listen
Cloud Firestore でリアルタイムアップデートを入手する際、以下のように何も考えず今までのドキュメントを全件取得すると、保存済みのデータ 1 件ごとに added
イベントが発火します。
// チャットアプリなどで messages コレクションのドキュメントスナップショットを取得する例
firebase
.firestore()
.collection("messages")
.orderBy("createdAt", "desc")
.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
if (change.type === "added") {
// 保存済みのデータ n 件分出力される
console.log("New message:", change.doc.data());
}
});
});
ref. https://firebase.google.com/docs/firestore/query-data/listen#view_changes_between_snapshots
最初のクエリ スナップショットには、クエリに一致する既存のすべてのドキュメントの added イベントが含まれています。これは、クエリの初期状態に対してクエリ スナップショットが最新の状態になるように、一連の変更を取得しているためです。
これの何が問題かというと、 added
イベント 1 回につき 1 回の読み取りとしてカウントされてしまうので、ものすごく無駄なコストがかかってしまいます。
ref. https://firebase.google.com/docs/firestore/pricing#listens
クエリの結果をリッスンする場合、結果セット内のドキュメントを追加または更新するたびに、1 回の読み取りとして課金されます。
解決方法
上記のチャットアプリの例であれば、スナップショットとして以前の最新メッセージの保存時間より新しいメッセージだけ取得するようにすれば良いです。
※ Firestore の Timestamp 型と JavaScript の Date 型を比較するためには fromDate()
メソッドを使って型変換してあげる必要があります。
// チャットアプリなどで messages コレクションのドキュメントスナップショットを取得する例
firebase
.firestore()
.collection("messages")
.orderBy("createdAt", "desc")
.where(
"createdAt",
">",
// 変数 lastCreatedAt には Date 型の値を渡してあげてください
// See. https://firebase.google.com/docs/reference/js/firebase.firestore.Timestamp.html#fromdate
firebase.firestore.Timestamp.fromDate(lastCreatedAt)
)
.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
if (change.type === "added") {
// 新しく追加されたデータのみ出力される
console.log("New message:", change.doc.data());
}
});
});