【问题标题】:How to get count of documents in a collection? [duplicate]如何获取集合中的文档数量? [复制]
【发布时间】:2018-10-03 10:02:29
【问题描述】:

如果我的集合中有大量文档,我是否可以在不加载整个集合的情况下获取文档数。

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

【问题讨论】:

标签: firebase google-cloud-firestore


【解决方案1】:

没有用于获取集合中文档数量的内置 API。因此,默认情况下,您必须下载所有文档才能计算它们。

正如 Renaud 所评论的,解决此问题的常见解决方法是保留一个单独的计数器,每次添加/删除文档时都会更新该计数器。您可以在您的应用程序中执行此操作,updating the counter with a transaction 每次添加/删除文档时。但在服务器中执行此操作更可靠,例如 Cloud Functions for Firebase。有关此方法的示例,请参阅the functions-samples repo。虽然该示例适用于 Firebase 实时数据库,但该方法同样适用于 Firestore。

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

【讨论】:

    【解决方案2】:

    小心计算大型集合的文档数量。如果您想为每个集合设置一个预先计算的计数器,那么使用 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;
    });
    

    原因是因为每个 Cloud 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 在所有情况下都相同。不幸的是,您的数据库中必须有“事件”集合。

    猜你喜欢
    • 2020-07-29
    • 2021-03-30
    • 2016-10-21
    • 2018-05-25
    • 2018-03-15
    • 1970-01-01
    • 2021-11-04
    • 2012-04-20
    相关资源
    最近更新 更多