简体   繁体   中英

Update multiple doc, in multiple collection, in one transaction Firestore

This is the scenario: I need to store rankings in Firestore. A ranking is made by users and each user has a certain amount of points.

When I store a ranking (that are store in the "matches" collection) I then need to update a lot of data, counters and statistics for all the users belonging to the ranking. The users are stored in the user collection, and every user represent a single document.

I would like to do so in a single transaction, so that if something goes wrong, I don't have mixed data (something that has been updated and something not).

To do so I thought about looping over all the user in the ranking and, for each user, read the data (I need to read the data because I have also averages to compute) and update it accordingly. Unfortunately, when I read and write the 1st user, in the first loop, I can't read the second one, because in this case a new read comes after the write of the previous user, which is forbidden by transactions.

Do you have any clue on a possible solution?

@Dharmaraj I read about batched writes, but from what I understood they can only be used if you don't need to read anything from the docs you need to write to, right?

What I need, instead, is the possibility, for each user in the ranking (and therefore each user in my for loop), to read some of his data (for example the average points scored in all the matches or the amount of times he ranked first in a match) and update those data with the new data I am inserting.

Example:

Starting situation:

user1 = {
   played_matches: 3
   average_points: 12
   times_in_first_position: 3
}
user2 = {
   played_matches: 2
   average_points: 8
   times_in_first_position: 1
}

New ranking:

[
   {username: "user2", points: 2, position: 1}
   {username: "user1", points: 1, position: 2}
]

When I add a new ranking, like the one in the snippet above, I have to update the "matches" collection, simply adding this ranking AND then I need to update the data of the users, like this:

user1 = {
   played_matches: 4 (+1)
   average_points: 9,25 (the new average)
   times_in_first_position: 3 (+0)
}
user2 = {
   played_matches: 3 (+1)
   average_points: 6 (the new average)
   times_in_first_position: 2 (+1)
}

Those are only some of the data I need to update. The situation is way more complex, but this is a good approximation.

Diving into my current code, what I do is

for(let user of theSortedUsers){
    if(user.id){
        const userDocRef = doc(db, "users", user.id);
        const userDoc = await transaction.get(userDocRef);
        const newPlayedMatches = parseInt(userDoc.data().played_matches) + 1;
        const newAveragePoints = ((userDoc.data().average_points * userDoc.data().played_matches) + user.points)/ newPlayedMatches;
        transaction.update(userDocRef, { 
              average_points: newAveragePoints,
              played_matches: newPlayedMatches,
        });
    }
}

And, with this, code, after user1 is processed, user two fails, with a firestore error related to the fact that all reads must be done before writes.

To further explain, what I need to do is this what is described here: https://firebase.google.com/docs/firestore/solutions/aggregation But with multiple ratings and multiple restaurant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM