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

如果是,我该怎么做?


当前回答

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

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条记录!

其他回答

根据本文档,Cloud Firestore支持count()聚合查询,并在预览版中可用。

颤振/飞镖代码丢失(在写这篇文章的时候),所以我玩了一下,下面的函数似乎可以工作:

  Future<int> getCount(String path) async {
    var collection = _fireStore.collection(path);
    var countQuery = collection.count();
    var snapShot = await countQuery.get(source: AggregateSource.server);
    return snapShot.count;
  }

不,目前还没有内置的聚合查询支持。然而,有几件事你可以做。

这里记录了第一个。您可以使用事务或云函数来维护聚合信息:

这个例子展示了如何使用一个函数来跟踪子集合中的评级数量,以及平均评级。

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()语句来避免下载所有文档(当您只需要一个计数时,这是很大的带宽)。select()目前仅在服务器sdk中可用,因此该解决方案不适用于移动应用程序。

目前,firebase只允许服务器端计数,如下所示

const collectionRef = db.collection('cities');
const snapshot = await collectionRef.count().get();
console.log(snapshot.data().count);

请不要,这是为nodeJS

据我所知,目前还没有内置的解决方案,只能在节点sdk中实现。 如果你有

db.collection('someCollection')

你可以使用

.select([fields])

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

例子:

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

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

再看看这个: 如何获得在一个集合与云Firestore的文件的数量计数

解决办法是:

在firebase文档中编写一个计数器,每次创建新条目时都在事务中增加计数器

您将计数存储在新条目的字段中(即:position: 4)。

然后在该字段上创建一个索引(position DESC)。

您可以对查询执行跳过+限制操作。Where("position", "<" x).OrderBy("position", DESC)

希望这能有所帮助!