Reputation: 641
I am storing scores of a user in google cloud firestore as each score a new document in the collection named as "points".
collection name: points
I want to sum all values in value field.
I have google many times but found nothing. Is there any alternative for sum() or any other way to implement recording scores of a user?
Upvotes: 19
Views: 12872
Reputation: 598765
Since late 2023 Firestore has built-in support for calculating the sum of a certain field across documents efficiently, as well as calculating an average and counting the number of results. These operations are vastly cheaper than the naïve solution I described below, but when you'll still want to consider write-time calculations for aggregating across really large (think 10s of thousands) of documents across large number of clients.
For more on this, see the Firebase documentation on summarizing data with aggregation queries and the link to the write-time aggregation solution I included below. Also see Hugo's answer for more on this, and code samples of how to apply it to your data structure.
Previous answer 👇
There are no built-in aggregation operators in Cloud Firestore.
The naïve solution is to load the data in the client and sum it there, but that means that you (and your users) incur the cost of loading all documents for each time they need to show the sum.
A better way is to keep a so-called running total in your database, which you update whenever a document is written to (added, modified, removed) to the "points" collection. For this you have two options: do it from the client, or do it from a trusted environment (such as Cloud Functions). The Firestore documentation on aggregation queries describes both options and shows sample code.
Upvotes: 23
Reputation: 101
With Firebase JS SDK 10.5.0 released on October 12th 2023, Cloud Firestore now supports sum
and average
aggregations queries.
I recommend you check the aggregation query documentation for more information.
sum()
function with the Web v9 SDKAssuming you want to sum all the scores in the points
collection:
const coll = collection(firestore, 'points');
const snapshot = await getAggregateFromServer(coll, {
totalScores: sum('value')
});
console.log('totalScores: ', snapshot.data().totalScores);
Assuming you want to sum the scores of one particular user
:
const coll = collection(firestore, 'points');
const q = query(coll, where('userid', '==', user.id));
const snapshot = await getAggregateFromServer(q, {
totalUserScore: sum('value')
});
console.log('totalUserScore: ', snapshot.data().totalUserScore);
Note: Don't forget to import from firestore the functions used in the above examples. That includes collection
, getAggregateFromServer
, sum
, query
and where
.
Be aware of the following limitations when using the sum()
function (included here in the documentation)
For
sum()
andaverage()
aggregations, non-numeric values are ignored.sum()
andaverage()
aggregations take into account only integer values and floating-point number values.When combining multiple aggregations in a single query, note that
sum()
andaverage()
ignore non-numeric values whilecount()
includes non-numeric values.
Upvotes: 3
Reputation: 7405
Use a cloud function which will make a url for you.
Example:
import { Request, Response } from 'express'
import * as admin from 'firebase-admin'
import * as functions from 'firebase-functions'
export const getUsersCount = functions.runWith({ memory: '2GB', timeoutSeconds: 60 }).https.onRequest(async (req: Request, res: Response) => {
const allUsers = await admin
.firestore()
.collection('users')
.get()
const numberOfUsers = allUsers.size;
res.status(200).json({
allTimeUsers: numberOfUsers,
})
return true;
})
Then just do Firebase deploy --only functions:getUsersCount The logs will print out the url for you. The url might take awhile to load if it's a big app.
Upvotes: 1
Reputation: 1277
You can either use forEach or iterate in a for loop. This answer on stack overflow could help. Here's an example from the same:
for (var i in querySnapshot.docs) {
const doc = querySnapshot.docs[i]
//do what you want to here
}
---OR---
you can use forEach like this
const collRef = firebase.firestore().collection('todos');
const query = collRef.orderBy('position');
const items = query.get()
.then((snapshot) => {
let newCount = 0;
snapshot.forEach((doc) => {
const docRef = collRef.doc(doc.id);
docRef.update({ position: newCount });
newCount += 1;
});
});
Upvotes: 0