是否可以使用新的Firebase数据库Cloud Firestore来计算一个集合有多少项?

如果是,我该怎么做?


当前回答

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

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

你也可以在querySnapshot中读取文档数组的长度。

querySnapshot.docs.length;

或者如果“querySnapshot”为空,则读取空值,将返回一个布尔值。

querySnapshot.empty;

其他回答

和许多问题一样,答案是——视情况而定。

在前端处理大量数据时应该非常小心。除了让你的前端感觉迟钝之外,Firestore还会向你收取每百万次读取60美元的费用。


小型收藏(少于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+文档)

最具可扩展性的解决方案


FieldValue.increment ()

截至2019年4月,Firestore现在允许增量计数器,完全原子,无需事先读取数据。这确保了即使同时从多个源进行更新(以前使用事务解决)也能获得正确的计数器值,同时还减少了执行的数据库读取次数。


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

参见firestore文档-分布式计数器 或者看看杰夫·德莱尼的《数据聚合》。他的指南对于任何使用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字段来获得集合的大小。

使用admin. keystore . 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),
    })
  );

在本例中,每次将文档添加到instances子集合时,我们都会增加项目中的instanceCount字段。如果该字段还不存在,它将被创建并增加到1。

增量在内部是事务性的,但如果需要更频繁地递增,则应该使用分布式计数器。

通常最好实现onCreate和onDelete而不是onWrite,因为你将调用onWrite进行更新,这意味着你在不必要的函数调用上花费了更多的钱(如果你更新了你的集合中的文档)。

使用带有偏移量和限制的分页的解决方案:

public int collectionCount(String collection) {
        Integer page = 0;
        List<QueryDocumentSnapshot> snaps = new ArrayList<>();
        findDocsByPage(collection, page, snaps);
        return snaps.size();
    }

public void findDocsByPage(String collection, Integer page, 
                           List<QueryDocumentSnapshot> snaps) {
    try {
        Integer limit = 26000;
        FieldPath[] selectedFields = new FieldPath[] { FieldPath.of("id") };
        List<QueryDocumentSnapshot> snapshotPage;
        snapshotPage = fireStore()
                        .collection(collection)
                        .select(selectedFields)
                        .offset(page * limit)
                        .limit(limit)
                        .get().get().getDocuments();    
        if (snapshotPage.size() > 0) {
            snaps.addAll(snapshotPage);
            page++;
            findDocsByPage(collection, page, snaps);
        }
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

findDocsPage是一个递归方法,用于查找集合的所有页面 selectedFields用于优化查询,只获得id字段而不是整个文档 限制每个查询页面的最大大小 页面定义用于分页的初始页面

从我做的测试来看,它可以很好地收集大约120k条记录!

自9.11.0版本以来,有一个新的内置函数getCountFromServer(),它在不实际下载文档的情况下获取结果集中的文档数量。

https://firebase.google.com/docs/reference/js/firestore_#getcountfromserver

firebaseFirestore.collection("...").addSnapshotListener(new EventListener<QuerySnapshot>() {
        @Override
        public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {

            int Counter = documentSnapshots.size();

        }
    });