Cloud Firestore collection count

后端 未结 17 1298
庸人自扰
庸人自扰 2020-11-22 09:25

Is it possible to count how many items a collection has using the new Firebase database, Cloud Firestore?

If so, how do I do that?

相关标签:
17条回答
  • 2020-11-22 09:54

    As far as I know there is no build-in solution for this and it is only possible in the node sdk right now. If you have a

    db.collection('someCollection')
    

    you can use

    .select([fields])
    

    to define which field you want to select. If you do an empty select() you will just get an array of document references.

    example:

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

    This solution is only a optimization for the worst case of downloading all documents and does not scale on large collections!

    Also have a look at this:
    How to get a count of number of documents in a collection with Cloud Firestore

    0 讨论(0)
  • 2020-11-22 09:54

    In 2020 this is still not available in the Firebase SDK however it is available in Firebase Extensions (Beta) however it's pretty complex to setup and use...

    A reasonable approach

    Helpers... (create/delete seems redundant but is cheaper than onUpdate)

    export const onCreateCounter = () => async (
      change,
      context
    ) => {
      const collectionPath = change.ref.parent.path;
      const statsDoc = db.doc("counters/" + collectionPath);
      const countDoc = {};
      countDoc["count"] = admin.firestore.FieldValue.increment(1);
      await statsDoc.set(countDoc, { merge: true });
    };
    
    export const onDeleteCounter = () => async (
      change,
      context
    ) => {
      const collectionPath = change.ref.parent.path;
      const statsDoc = db.doc("counters/" + collectionPath);
      const countDoc = {};
      countDoc["count"] = admin.firestore.FieldValue.increment(-1);
      await statsDoc.set(countDoc, { merge: true });
    };
    
    export interface CounterPath {
      watch: string;
      name: string;
    }
    
    

    Exported Firestore hooks

    
    export const Counters: CounterPath[] = [
      {
        name: "count_buildings",
        watch: "buildings/{id2}"
      },
      {
        name: "count_buildings_subcollections",
        watch: "buildings/{id2}/{id3}/{id4}"
      }
    ];
    
    
    Counters.forEach(item => {
      exports[item.name + '_create'] = functions.firestore
        .document(item.watch)
        .onCreate(onCreateCounter());
    
      exports[item.name + '_delete'] = functions.firestore
        .document(item.watch)
        .onDelete(onDeleteCounter());
    });
    
    

    In action

    The building root collection and all sub collections will be tracked.

    Here under the /counters/ root path

    Now collection counts will update automatically and eventually! If you need a count, just use the collection path and prefix it with counters.

    const collectionPath = 'buildings/138faicnjasjoa89/buildingContacts';
    const collectionCount = await db
      .doc('counters/' + collectionPath)
      .get()
      .then(snap => snap.get('count'));
    

    Limitations

    As this approach uses a single database and document, it is limited to the Firestore constraint of 1 Update per Second for each counter. It will be eventually consistent, but in cases where large amounts of documents are added/removed the counter will lag behind the actual collection count.

    0 讨论(0)
  • 2020-11-22 09:54

    I agree with @Matthew, it will cost a lot if you perform such query.

    [ADVICE FOR DEVELOPERS BEFORE STARTING THEIR PROJECTS]

    Since we have foreseen this situation at the beginning, we can actually make a collection namely counters with a document to store all the counters in a field with type number.

    For example:

    For each CRUD operation on the collection, update the counter document:

    1. When you create a new collection/subcollection: (+1 in the counter) [1 write operation]
    2. When you delete a collection/subcollection: (-1 in the counter) [1 write operation]
    3. When you update an existing collection/subcollection, do nothing on the counter document: (0)
    4. When you read an existing collection/subcollection, do nothing on the counter document: (0)

    Next time, when you want to get the number of collection, you just need to query/point to the document field. [1 read operation]

    In addition, you can store the collections name in an array, but this will be tricky, the condition of array in firebase is shown as below:

    // 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'}
    

    So, if you are not going to delete the collection , you can actually use array to store list of collections name instead of querying all the collection every time.

    Hope it helps!

    0 讨论(0)
  • 2020-11-22 09:54

    A workaround is to:

    write a counter in a firebase doc, which you increment within a transaction everytime you create a new entry

    You store the count in a field of your new entry (i.e: position: 4).

    Then you create an index on that field (position DESC).

    You can do a skip+limit with a query.Where("position", "<" x).OrderBy("position", DESC)

    Hope this helps!

    0 讨论(0)
  • 2020-11-22 09:58

    This uses counting to create numeric unique ID. In my use, I will not be decrementing ever, even when the document that the ID is needed for is deleted.

    Upon a collection creation that needs unique numeric value

    1. Designate a collection appData with one document, set with .doc id only
    2. Set uniqueNumericIDAmount to 0 in the firebase firestore console
    3. Use doc.data().uniqueNumericIDAmount + 1 as the unique numeric id
    4. Update appData collection uniqueNumericIDAmount with firebase.firestore.FieldValue.increment(1)
    firebase
        .firestore()
        .collection("appData")
        .doc("only")
        .get()
        .then(doc => {
            var foo = doc.data();
            foo.id = doc.id;
    
            // your collection that needs a unique ID
            firebase
                .firestore()
                .collection("uniqueNumericIDs")
                .doc(user.uid)// user id in my case
                .set({// I use this in login, so this document doesn't
                      // exist yet, otherwise use update instead of set
                    phone: this.state.phone,// whatever else you need
                    uniqueNumericID: foo.uniqueNumericIDAmount + 1
                })
                .then(() => {
    
                    // upon success of new ID, increment uniqueNumericIDAmount
                    firebase
                        .firestore()
                        .collection("appData")
                        .doc("only")
                        .update({
                            uniqueNumericIDAmount: firebase.firestore.FieldValue.increment(
                                1
                            )
                        })
                        .catch(err => {
                            console.log(err);
                        });
                })
                .catch(err => {
                    console.log(err);
                });
        });
    
    0 讨论(0)
提交回复
热议问题