Firebase 如何获取集合中文档的计数?

Firebase 如何获取集合中文档的计数?,firebase,google-cloud-firestore,Firebase,Google Cloud Firestore,如果我在一个集合中有大量文档,我可以在不必加载整个集合的情况下获得文档数 db.collection('largecollection').get().then(snapshot => { length = snapshot.size; }) 没有内置API来获取集合中文档数的计数。因此,默认情况下,您必须下载所有文档才能对其进行计数 正如Renaud所评论的,常见的解决方法是保留一个单独的计数器,您可以在每次添加/删除文档时更新该计数器。每次添加/删除文档时,您都可以在应用程序中

如果我在一个集合中有大量文档,我可以在不必加载整个集合的情况下获得文档数

db.collection('largecollection').get().then(snapshot => {
   length = snapshot.size;
})

没有内置API来获取集合中文档数的计数。因此,默认情况下,您必须下载所有文档才能对其进行计数

正如Renaud所评论的,常见的解决方法是保留一个单独的计数器,您可以在每次添加/删除文档时更新该计数器。每次添加/删除文档时,您都可以在应用程序中执行此操作。但在服务器上这样做更可靠,比如Firebase的云功能。有关此方法的示例,请参阅。虽然该示例适用于Firebase实时数据库,但该方法同样适用于Firestore


另请参阅上的Firestore文档部分。

仔细计算大型收藏的文档数量。如果您希望为每个集合都有一个预先计算的计数器,那么firestore数据库有点复杂

db.collection('largecollection').get().then(snapshot => {
   length = snapshot.size;
})
这种代码在这种情况下不起作用:

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触发器都必须是幂等的:

解决方案 因此,为了防止代码多次执行,您需要使用事件和事务进行管理。这是我处理大型收集计数器的特殊方式:

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在所有情况下都是相同的。很遗憾,您的数据库中必须有“事件”集合。

您可以使用云函数在每次向集合中添加文档时(以及在必要时每次删除文档时)更新计数器