I believe that this cloud function is not idempotent
https://github.com/firebase/snippets-node/blob/9ae3a00985b53025fdc82716046882af71b6009d/firestore/solution-aggregation/functions/index.js#L11-L37
If it's retried, I think it's possible to get a wrong value for newNumRatings.
exports.aggregateRatings = functions.firestore .document('restaurants/{restId}/ratings/{ratingId}') .onWrite((change, context) => { // Get value of the newly added rating var ratingVal = change.after.data().rating;
// Get a reference to the restaurant
var restRef = db.collection('restaurants').doc(context.params.restId);
// Update aggregations in a transaction
return db.runTransaction(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
});
});
});
});
exports.aggregateRatings = functions.firestore .document('restaurants/{restId}/ratings/{ratingId}') .onWrite((change, context) => { // Get value of the newly added rating var ratingVal = change.after.data().rating;
// Get a reference to the restaurant
var restRef = db.collection('restaurants').doc(context.params.restId);
// Update aggregations in a transaction
return db.runTransaction(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
});
});
});
});
exports.aggregateRatings = functions.firestore .document('restaurants/{restId}/ratings/{ratingId}') .onWrite((change, context) => { // Get value of the newly added rating var ratingVal = change.after.data().rating;
// Get a reference to the restaurant
var restRef = db.collection('restaurants').doc(context.params.restId);
// Update aggregations in a transaction
return db.runTransaction(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
});
});
});
});