32

I have a firestore collections named users, each users have a generated id with a field score :

users 
    0e8X3VFL56rHBxxgkYOW
        score : 4
    3SeDjrgAWMmh3ranh2u
        score : 5

I use redux-firestore and i want to reset all my users score at 0, something like

firestore.update({ collection: 'users' }, { score : 0 }

I can't achieve this because update method need a document id

Do you know how to do this ?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Lionel B
  • 1,172
  • 2
  • 9
  • 24

6 Answers6

54

You can get all the documents in the collection, get their id's and perform updates using those id's:

db.collection("cities").get().then(function(querySnapshot) {
    querySnapshot.forEach(function(doc) {
        doc.ref.update({
            capital: true
        });
    });
});
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
thehamzarocks
  • 733
  • 5
  • 10
  • 11
    I have around 1 million documents in a collection and i want to update one property in all of them. What is the best way to do it? 1 million documents with thousands of users using the app in realtime. I guess reading all of them and updating will cost a lot of reads and writes. – Robert Williams Sep 07 '20 at 17:56
  • 3
    @RobertWilliams so what did you do? I am in similar boat – Abdulrahman Abdelkader Feb 03 '21 at 03:26
  • 3
    @AbdulrahmanAbdelkader there is no other way other than what this answer suggests. I had to read all the documents and update using a loop. – Robert Williams Feb 03 '21 at 15:10
  • @RobertWilliams Thanks a lot! Didn't expect a reply since the comment is old but I think that's what will I do too since there is no thing else? However do you suggest doing it using cloud functions or you have another solution – Abdulrahman Abdelkader Feb 04 '21 at 03:17
  • 1
    It depends what your aim is. In my case I had millions of documents in one collection and i wanted a quick fix so instead of deploying firebase functions I wrote some code to get 500 documents per request and update and then get next 500 and update them. I didn't try firebase functions because i don't know much about them and also i was afraid it might increase my firebase bill. – Robert Williams Feb 07 '21 at 10:43
  • 1
    how does this work exactly? doesn't calling `get()` on a collection ref attempt to retrieve all the documents at once? how is this possible without pagination? Is this answer assuming a small set of data? – rosghub Jun 22 '21 at 19:04
  • In the past I used an approach where I would get the querySnapshot as described here, and then iterate through it in batches of 500 updates each. The problem I'm facing now is the collection grew and now even the get() call causes the JavaScript heap to run out of memory. My collection does not have millions of documents (hundreds of thousands). How did you solve this @RobertWilliams? – mariogarranz Nov 09 '21 at 10:10
  • For anyone having the same issue as described in my previous comment, I could not find a way to increase the heap size when running the Firebase shell. So I created a Node.js program that would connect to the database and I added the code for the update there. Then all I had to do was run this code through Node.js with an increased heap size (--max-old-space-size=4096 did it for me) – mariogarranz Nov 09 '21 at 11:05
  • how to use this code with firebase 9 ? – Nanda Z Jan 24 '22 at 12:59
15

For some strange reason the accepted answer ( thehamzarocks ) wasn't working for me, none of the documents were updated. Maybe there's a bug in AngularFire2. Anyway, I decided to loop over the docs array of the QuerySnapshot instead of using its forEach method, and add each update to a batch queue. Batching bulk operations is also more efficient than sending a new update request for each update operation.

resetScore(): Promise<void> {
  return this.usersCollectionRef.ref.get().then(resp => {
    console.log(resp.docs)
    let batch = this.afs.firestore.batch();

    resp.docs.forEach(userDocRef => {
      batch.update(userDocRef.ref, {'score': 0, 'leadsWithSalesWin': 0, 'leadsReported': 0});
    })
    batch.commit().catch(err => console.error(err));
  }).catch(error => console.error(error))
}
jsaddwater
  • 1,781
  • 2
  • 18
  • 28
4

Batch updates are nice but bare in mind that they are limited to 500 document updates per transaction. If this reset isn't done often maybe simplest approach is:

async function resetScores() {
  const collection = await db
    .collection("users")
    .get()
  collection.forEach(doc=> {
    doc.ref
      .update({
        score: 0
      })
  })
}
radulle
  • 1,437
  • 13
  • 19
3

I came across this post while searching for similar solutions. Firestore now has batched writes, which will update all documents in one go. This could be an ideal solution for fewer documents.

Updating @thehamzarocks's answer:

const batch = db.batch()

db.collection('cities').get().then(function(querySnapshot) {
    querySnapshot.forEach(function(doc) {
        const docRef = db.collection('cities').doc(doc.id)
        batch.update(docRef, { capital: true })
    });

    batch.commit();
});
raiser00
  • 452
  • 4
  • 15
2

Firestore doesn't have the ability to bulk update documents without knowing their IDs. You will have to somehow know the document ID of each document to update (perform a query, or do batches of queries), and update each one individually.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
-1

Sorry if the question is old but I thought providing a new answer to this question might be useful to someone else too. I managed to bulk update the entries of a list using the following command:

 this.db
  .list<User[]>('users')
  .set('/', users);

Edit: I'm using AngularFireDatabase.

mdx0111
  • 129
  • 1
  • 6