I have a food app with a star system. Every time a user selects a rating/star I send their rating to the database and then run a firebase transaction
to update the total amount of users who left a rating.
let theirRating: Double = // whatEverStarRatingTheySelected
let dict = [currentUserId: theirRating]
let reviewsRef = Database.database().reference().child("reviews").child(postId)
reviewsRef.updateChildValues(dict) { (error, ref) in
let postsRef = Database.database().reference().child("posts").child(postId).child("totalUsersCount")
postsRef.runTransactionBlock({ (mutableData: MutableData) -> TransactionResult in
// ...
})
}
When displaying the actual rating I pull the the post which has the totalUsersCount as a property, I pull all of the users actual ratings, add them all together, and then feed both numbers into an algo to spit out the actual rating. I do all of this on the client. How can I do the same thing "I pull all of the users actual rating, add them all together" with a Cloud Function, that is similar to this answer?
Database.database().reference().child("posts").child(postId).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dict = snapshot.value as? [String:Any] else { return }
let post = Post(dict: dict)
let totalUsers = post.totalUsersCount
Database.database().reference().child("reviews").child(postId).observeSingleEvent(of: .value, with: { (snapshot) in
var ratingSum = 0.0
// *** how can I do this part using a Cloud Function so that way I can just read this number as a property on the post instead of doing all of this on the client ***
for review in (snapshot.children.allObjects as! [DataSnapshot]) {
guard let rating = review.value as? Double else { continue }
ratingSum += rating
}
let starRatingToDisplay = self.algoToComputeStarRating(totalUsersCount: totalUsersCount, ratingsSum: ratingSum)
})
})
This isn't a question about the algoToComputeStarRating, that I can do on the client. I want to add all of the user's ratings together using a Cloud Function, then just add that result as a property to the post. That way when I pull the post all I have to do is:
Database.database().reference().child("posts").child(postId).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dict = snapshot.value as? [String:Any] else { return }
let post = Post(dict: dict)
let totalUsersCount = post.totalUsersCount
let ratingsSum = post.ratingsSum
let starRatingToDisplay = self.algoToComputeStarRating(totalUsersCount: totalUsersCount, ratingsSum: ratingSum)
})
Database structure:
@posts
@postId
-postId: "..."
-userId: "..."
-totalUsersCount: 22
-ratingsSum: 75 // this should be the result from the cloud function
@reviews
@postId
-uid_1: theirRating
-uid_2: theirRating
// ...
-uid_22: theirRating
This is what I tried so far:
exports.calculateTotalRating = functions.https.onRequest((data, response) => {
const postId = data.postId;
const totalUsersCtRef = admin.database().ref('/posts/' + postId + '/' + 'totalUsersCt');
const postsRef = admin.database().ref('/posts/' + postId);
admin.database().ref('reviews').child(postId).once('value', snapshot => {
if (snapshot.exists()) {
var ratingsSum = 0.0;
snapshot.forEach(function(child) {
ratingsSum += child().val()
})
.then(() => {
return postsRef.set({ "ratingsSum": ratingsSum})
})
.then(() => {
return totalUsersCtRef.set(admin.database.ServerValue.increment(1));
})
.catch((error) => {
console.log('ERROR - calculateTotalRating() Failed: ', error);
});
}
});
});