代码之家  ›  专栏  ›  技术社区  ›  methuselah

检索所有记录的计数[重复]

  •  0
  • methuselah  · 技术社区  · 7 年前

    是否可以使用新的FireBase数据库FireStore计算一个集合有多少项?

    如果是,我该怎么做?

    0 回复  |  直到 7 年前
        1
  •  105
  •   Trevor    7 年前

    更新(2019年4月)-fieldvalue.increment(参见大型收集解决方案)


    和许多问题一样,答案是- 这要看情况而定 .

    在前端处理大量数据时应该非常小心。除了让你的前端感觉迟钝, 消防仓库也 charges you $0.60 per million reads 你做到了。


    小收藏 (少于100份文件)

    小心使用-前端用户体验可能会受到影响

    在前端处理这个问题应该是可以的,只要您没有对这个返回的数组做太多的逻辑操作。

    db.collection('...').get().then(snap => {
       size = snap.size // will return the collection size
    });
    

    中等收藏 (100至1000份文件)

    小心使用-FireStore读取调用可能会花费很多

    在前端处理这个问题是不可行的,因为它有太多的潜力来减慢用户系统的速度。我们应该处理这个逻辑服务器端,只返回大小。

    这种方法的缺点是,您仍然在调用FireStore读取(等于集合的大小),从长远来看,这可能会使您付出比预期更多的代价。

    云功能:

    ...
    db.collection('...').get().then(snap => {
        res.status(200).send({length: snap.size});
    });
    

    前端:

    yourHttpClient.post(yourCloudFunctionUrl).toPromise().then(snap => {
         size = snap.length // will return the collection size
    })
    

    大型收藏 (1000多份文件)

    最可扩展的解决方案


    字段值。增量()

    As of April 2019 Firestore now allows incrementing counters, completely atomically, and without reading the data prior . 这样可以确保即使同时从多个源更新(以前使用事务解决),我们也有正确的计数器值,同时还可以减少我们执行的数据库读取次数。


    通过监听任何文档的删除或创建,我们可以在数据库中的计数字段中添加或删除。

    查看FireStore文档- Distributed Counters 或者看看 Data Aggregation 杰夫·德莱尼。他的指导对于任何使用AngularFire的人来说都是非常棒的,但是他的课程也应该延续到其他框架中。

    云功能:

    export const documentWriteListener = 
        functions.firestore.document('collection/{documentUid}')
        .onWrite((change, context) => {
    
        if (!change.before.exists) {
            // New document Created : add one to count
    
            db.doc(docRef).update({numberOfDocs: FieldValue.increment(1)});
    
        } else if (change.before.exists && change.after.exists) {
            // Updating existing document : Do nothing
    
        } else if (!change.after.exists) {
            // Deleting document : subtract one from count
    
            db.doc(docRef).update({numberOfDocs: FieldValue.increment(-1)});
    
        }
    
    return;
    });
    

    现在在前端,您只需查询这个numberOfDocs字段就可以获得集合的大小。

        2
  •  18
  •   Ompel    8 年前

    最简单的方法是读取“querysnapshot”的大小。

    db.collection("cities").get().then(function(querySnapshot) {      
        console.log(querySnapshot.size); 
    });
    

    您还可以读取“querysnapshot”中docs数组的长度。

    querySnapshot.docs.length;
    

    或者如果“querysnapshot”通过读取空值为空,则返回布尔值。

    querySnapshot.empty;
    
        3
  •  12
  •   jbb    8 年前

    据我所知,这方面没有内置解决方案,目前只能在node sdk中实现。 如果你有

    db.collection(“somecollection”)。

    你可以用

    。选择([字段])

    定义要选择的字段。如果您执行空的select(),您将得到一个文档引用数组。

    例子:

    db.collection('someCollection').select().get().then( (snapshot) => console.log(snapshot.docs.length) );

    此解决方案只是对下载所有文档的最坏情况的优化,不能扩展到大型集合中!

    还可以看看这个:
    How to get a count of number of documents in a collection with Cloud Firestore

        4
  •  7
  •   Ferran Verdés    7 年前

    仔细计算文件数量 大型收藏品 . 如果您想要为每个集合都有一个预先计算好的计数器,那么使用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'));
    

    如您所见,防止多次执行的关键是 事件ID 在上下文对象中。如果对同一事件多次处理该函数,则在所有情况下,事件ID都是相同的。不幸的是,您的数据库中必须有“事件”集合。

        5
  •  4
  •   Sam Stern    8 年前

    不,目前没有对聚合查询的内置支持。不过,您可以做一些事情。

    第一个是 documented here . 您可以使用事务或云功能来维护聚合信息:

    此示例演示如何使用函数跟踪子集合中的评级数量以及平均评级。

    exports.aggregateRatings = firestore
      .document('restaurants/{restId}/ratings/{ratingId}')
      .onWrite(event => {
        // Get value of the newly added rating
        var ratingVal = event.data.get('rating');
    
        // Get a reference to the restaurant
        var restRef = db.collection('restaurants').document(event.params.restId);
    
        // Update aggregations in a transaction
        return db.transaction(transaction => {
          return transaction.get(restRef).then(restDoc => {
            // Compute new number of ratings
            var newNumRatings = restDoc.data('numRatings') + 1;
    
            // Compute new average rating
            var oldRatingTotal = restDoc.data('avgRating') * restDoc.data('numRatings');
            var newAvgRating = (oldRatingTotal + ratingVal) / newNumRatings;
    
            // Update restaurant info
            return transaction.update(restRef, {
              avgRating: newAvgRating,
              numRatings: newNumRatings
            });
          });
        });
    });
    

    如果您只想不经常地对文档进行计数,那么JBB提到的解决方案也很有用。确保使用 select() 语句以避免下载所有文档(当您只需要计数时,这会占用大量带宽)。 选择() 目前仅在服务器SDK中可用,因此解决方案无法在移动应用程序中工作。

        6
  •  4
  •   Angus    7 年前

    我同意@matthew,会的 花了很多钱 如果您执行这样的查询。

    [在启动项目之前为开发人员提供建议]

    因为我们在一开始就已经预见到了这种情况,所以实际上我们可以创建一个集合,即带有文档的计数器,将所有计数器存储在具有类型的字段中。 number .

    例如:

    对于集合上的每个CRUD操作,更新计数器文档:

    1. 当你 创造 新集合/子集合: (柜台+1) [1写入操作]
    2. 当你 删除 集合/子集合: (柜台-1) [1写入操作]
    3. 当你 更新 现有集合/子集合,对计数器文档不执行任何操作: (0)
    4. 当你 阅读 现有集合/子集合,对计数器文档不执行任何操作: (0)

    下次,当您想要获取集合的数量时,只需要查询/指向文档字段。[1读取操作]

    此外,您可以将集合名称存储在一个数组中,但这很困难,FireBase中的数组条件如下所示:

    // we send this
    ['a', 'b', 'c', 'd', 'e']
    // Firebase stores this
    {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
    
    // since the keys are numeric and sequential,
    // if we query the data, we get this
    ['a', 'b', 'c', 'd', 'e']
    
    // however, if we then delete a, b, and d,
    // they are no longer mostly sequential, so
    // we do not get back an array
    {2: 'c', 4: 'e'}
    

    因此,如果您不打算删除集合,那么实际上可以使用数组存储集合名称列表,而不是每次查询所有集合。

    希望有帮助!

        7
  •  2
  •   nanobar    7 年前

    递增计数器使用 admin.firestore.FieldValue.increment :

    exports.onInstanceCreate = functions.firestore.document('projects/{projectId}/instances/{instanceId}')
      .onCreate((snap, context) =>
        db.collection('projects').doc(context.params.projectId).update({
          instanceCount: admin.firestore.FieldValue.increment(1),
        })
      );
    
    exports.onInstanceDelete = functions.firestore.document('projects/{projectId}/instances/{instanceId}')
      .onDelete((snap, context) =>
        db.collection('projects').doc(context.params.projectId).update({
          instanceCount: admin.firestore.FieldValue.increment(-1),
        })
      );
    

    在这个例子中,我们增加一个 instanceCount 每次将文档添加到 instances 子集合。如果字段尚不存在,则将创建该字段并将其递增到1。

    增量在内部是事务性的,但是您应该使用 distributed counter 如果你需要增加的频率超过每1秒一次。

    通常最好实施 onCreate onDelete 而不是 onWrite 如你所说 写上 对于更新,这意味着您在不必要的函数调用上花费了更多的钱(如果您更新了集合中的文档)。

        8
  •  0
  •   Nipun Madan    7 年前

    没有直接选项可用。你不能这样做 db.collection("CollectionName").count() . 下面是两种查找集合中文档数的方法。

    1:-获取集合中的所有文档,然后获取其大小。(不是最佳解决方案)

    db.collection("CollectionName").get().subscribe(doc=>{
    console.log(doc.size)
    })
    

    通过使用上面的代码,您的文档读取量将等于集合中文档的大小,因此必须避免使用上面的解决方案。

    2:-在集合中创建一个单独的文档,该文档将存储集合中的文档数。(最佳解决方案)

    db.collection("CollectionName").doc("counts")get().subscribe(doc=>{
    console.log(doc.count)
    })
    

    上面我们创建了一个名为counts的文档来存储所有的count信息。您可以按以下方式更新count文档:

    • 在文档计数上创建FireStore触发器
    • 创建新文档时,增加counts文档的count属性。
    • 删除文档时,递减Counts文档的Count属性。

    W.R.T价格(文件读取=1)和快速数据检索上述解决方案是好的。

        9
  •  0
  •   Rob Phillips    7 年前

    我花了一段时间根据上面的一些答案来完成这项工作,所以我想我会把它分享给其他人使用。希望它有用。

    'use strict';
    
    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    admin.initializeApp();
    const db = admin.firestore();
    
    exports.countDocumentsChange = functions.firestore.document('library/{categoryId}/documents/{documentId}').onWrite((change, context) => {
    
        const categoryId = context.params.categoryId;
        const categoryRef = db.collection('library').doc(categoryId)
        let FieldValue = require('firebase-admin').firestore.FieldValue;
    
        if (!change.before.exists) {
    
            // new document created : add one to count
            categoryRef.update({numberOfDocs: FieldValue.increment(1)});
            console.log("%s numberOfDocs incremented by 1", categoryId);
    
        } else if (change.before.exists && change.after.exists) {
    
            // updating existing document : Do nothing
    
        } else if (!change.after.exists) {
    
            // deleting document : subtract one from count
            categoryRef.update({numberOfDocs: FieldValue.increment(-1)});
            console.log("%s numberOfDocs decremented by 1", categoryId);
    
        }
    
        return 0;
    });
    
        10
  •  -1
  •   Sampath Patro    8 年前
    firebaseFirestore.collection("...").addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
    
                int Counter = documentSnapshots.size();
    
            }
        });
    
    推荐文章