Firestoreで、コレクション内のドキュメントの総数を取得するにはどうすればよいですか?
たとえば、私が持っている場合
/people
/123456
/name - 'John'
/456789
/name - 'Jane'
私が持っている人の数を問い合わせて、2を得ます。
/ peopleでクエリを実行し、返された結果の長さを取得することもできますが、特に大きなデータセットでこれを実行するため、それは無駄に思えます。
現在、次の3つのオプションがあります。
これは基本的にあなたが言及したアプローチです。コレクションからすべてを選択し、クライアント側でカウントします。これは小さなデータセットでは十分に機能しますが、データセットが大きい場合は明らかに機能しません。
このアプローチでは、Cloud Functionsを使用して、コレクションに追加および削除するたびにカウンターを更新できます。
これは、追加/削除が1秒あたり1以下のレートでのみ発生する限り、どのデータセットサイズでもうまく機能します。これにより、すぐにalmost currentカウントを取得するために、1つのドキュメントを読むことができます。
毎秒1を超える必要がある場合は、 ドキュメントごとの分散カウンター を実装する必要があります。
Cloud Functionsを使用する代わりに、クライアントでドキュメントを追加または削除すると同時にカウンターを更新できます。これは、カウンタも最新であることを意味しますが、ドキュメントを追加または削除する場所には必ずこのロジックを含める必要があります。
オプション2と同様に、毎秒を超えたい場合は、分散カウンターを実装する必要があります。
AngulareFire2を使用している場合は、次のことができます(private afs: AngularFirestore
がコンストラクターに挿入されていると仮定します):
this.afs.collection(myCollection).valueChanges().subscribe( values => console.log(values.length));
ここで、values
はmyCollection
内のすべてのアイテムの配列です。メタデータは必要ないので、valueChanges()
メソッドを直接使用できます。
集約は行く方法です(クライアント側が公開したくないかもしれないユーザーに情報を公開するので、firebase関数はこれらの集約を更新する推奨方法のように見えます) https://firebase.google.com/docs/firestore/ソリューション/集計
別の方法(推奨されません)は、大きなリストには適さず、リスト全体をダウンロードする必要があります:この例のようなres.size:
db.collection('products').get().then(res => console.log(res.size))
クラウド機能を使用して、大規模なコレクションのドキュメント数を慎重にカウントしてください。すべてのコレクションに対して事前に計算されたカウンターが必要な場合、firestoreデータベースでは少し複雑です。
このようなコードはこの場合は機能しません:
export const customerCounterListener =
functions.firestore.document('customers/{customerId}')
.onWrite((change, context) => {
// on create
if (!change.before.exists && change.after.exists) {
return firestore
.collection('metadatas')
.doc('customers')
.get()
.then(docSnap =>
docSnap.ref.set({
count: docSnap.data().count + 1
}))
// on delete
} else if (change.before.exists && !change.after.exists) {
return firestore
.collection('metadatas')
.doc('customers')
.get()
.then(docSnap =>
docSnap.ref.set({
count: docSnap.data().count - 1
}))
}
return null;
});
その理由は、Firestoreのドキュメントにあるように、すべてのクラウドFirestoreトリガーはべき等でなければならないためです。 https://firebase.google.com/docs/functions/firestore-events#limitations_and_guarantees
したがって、コードの複数の実行を防ぐために、イベントとトランザクションで管理する必要があります。これは、大規模なコレクションカウンタを処理する私の特定の方法です。
const executeOnce = (change, context, task) => {
const eventRef = firestore.collection('events').doc(context.eventId);
return firestore.runTransaction(t =>
t
.get(eventRef)
.then(docSnap => (docSnap.exists ? null : task(t)))
.then(() => t.set(eventRef, { processed: true }))
);
};
const documentCounter = collectionName => (change, context) =>
executeOnce(change, context, t => {
// on create
if (!change.before.exists && change.after.exists) {
return t
.get(firestore.collection('metadatas')
.doc(collectionName))
.then(docSnap =>
t.set(docSnap.ref, {
count: ((docSnap.data() && docSnap.data().count) || 0) + 1
}));
// on delete
} else if (change.before.exists && !change.after.exists) {
return t
.get(firestore.collection('metadatas')
.doc(collectionName))
.then(docSnap =>
t.set(docSnap.ref, {
count: docSnap.data().count - 1
}));
}
return null;
});
ここでの使用例:
/**
* Count documents in articles collection.
*/
exports.articlesCounter = functions.firestore
.document('articles/{id}')
.onWrite(documentCounter('articles'));
/**
* Count documents in customers collection.
*/
exports.customersCounter = functions.firestore
.document('customers/{id}')
.onWrite(documentCounter('customers'));
ご覧のとおり、複数の実行を防ぐためのキーは、コンテキストオブジェクトでeventIdと呼ばれるプロパティです。関数が同じイベントに対して何度も処理された場合、イベントIDはすべての場合で同じになります。残念ながら、データベースには「イベント」コレクションが必要です。
次のダンの回答:データベース内に個別のカウンターを作成し、Cloud Functionsを使用してメンテナンスできます。 (書き込み時のベストエフォート)
// Example of performing an increment when item is added
module.exports.incrementIncomesCounter = collectionRef.onCreate(event => {
const counterRef = event.data.ref.firestore.doc('counters/incomes')
counterRef.get()
.then(documentSnapshot => {
const currentCount = documentSnapshot.exists ? documentSnapshot.data().count : 0
counterRef.set({
count: Number(currentCount) + 1
})
.then(() => {
console.log('counter has increased!')
})
})
})
このコードは、それを行う方法の完全な例を示しています: https://Gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7