0
votes

I have a firestore database that stores some documents by their ID in multiple locations. I want to write a function that can access all instances of this document by ID and update/delete them without knowing exactly where they are.

For instance, in a shopping example with multiple users each having a subcollection with a cart, wishlist, and owned. The document ID may or may not exist in each one of the user doc's subcollections. I'm trying to write a function that iterates through these lists and performs the action upon matching the docID, yet have been unsuccessful in finding an example of this.

I understand how to add all references to a batch but not how to find them without a crazy amount of read/write operations over the entire user collection. Is this possible?

My initial thought is to:

    let batch = admin.firestore().batch();
    let allUsers = db.collection('users').get()
      .then(snapshot => {
        snapshot.forEach(doc => {
          batch.delete(db.collection('users').doc(doc.id).collection('cart').doc(ID_TO_MATCH));
          batch.delete(db.collection('users').doc(doc.id).collection('wishlist').doc(ID_TO_MATCH));
          batch.delete(db.collection('users').doc(doc.id).collection('owned').doc(ID_TO_MATCH));
        });
      })
      .catch(err => {
        console.log('Error getting documents', err);
      });
      return batch.commit().then(function () {
          // should delete all the direct referenes
      });

This would work, as firestore returns a success for deletion of documents that don't exits. The obvious downside is A LOT of document reads as the number of users grow. Is there anyway to use .where() queries to make this more efficient?

1

1 Answers

1
votes

You could use a where() query based on the documentId sentinel, as follows:

...collection('cart').where(firebase.firestore.FieldPath.documentId(), '==', ID_TO_MATCH)

but I don't think it will change something in terms of pricing or performance. As a matter of fact, "there is a minimum charge of one document read for each query that you perform, even if the query returns no results", as explained in the doc. So reading the doc by doing

collection('cart').doc(ID_TO_MATCH)

or by doing

collection('cart').where(firebase.firestore.FieldPath.documentId(), '==', ID_TO_MATCH)

does not make any difference IMO.


Another possibility would be to use a collectionGroup query. The problem, in this specific case, is that when you query a collection group by FieldPath.documentId(), the value provided must result in a valid document path. See this SO post for more details. So this will not work with ID_TO_MATCH...

But, there is a workaround: you could save in your documents a field which has the same value than the document ID and then a collectionGroup query would work.

So, with a field docId in your document, that holds the value of the Document ID you can write the following query:

const cartsQuery = db.collectionGroup('cart').where('docId', '==', ID_TO_MATCH);