Queries in Cloud Firestore let you find documents in large collections. To gain insight into properties of the collection as a whole, you can aggregate data over a collection.
You can aggregate data either at read-time or at write time:
-
Read-time aggregationscalculate a result at the time of the request. Cloud Firestore supports the
count(),sum(), andaverage()aggregation queries at read-time. Read-time aggregation queries are are easier to add to your app than write-time aggregations. For more on aggregation queries, see Summarize data with aggregation queries . -
Write-time aggregationscalculate a result each time the app performs a relevant write operation. Write-time aggregations are more work to implement, but you might use them instead of read-time aggregations for one of the following reasons:
- You want to listen to the aggregation result for real-time updates.
The
count(),sum(), andaverage()aggregation queries do not support real-time updates. - You want to store the aggregation result in a client-side cache.
The
count(),sum(), andaverage()aggregation queries do not support caching. - You are aggregating data from tens of thousands of documents for each of your users and consider costs. At a lower number of documents, read-time aggregations cost less. For a large number of documents in an aggregations, write-time aggregations might cost less.
- You want to listen to the aggregation result for real-time updates.
The
You can implement a write-time aggregation using either a client-side transaction or with Cloud Functions . The following sections describe how to implement write-time aggregations.
Solution: Write-time aggregation with a client-side transaction
Consider a local recommendations app that helps users find great restaurants. The following query retrieves all the ratings for a given restaurant:
Web
db . collection ( "restaurants" ) . doc ( "arinell-pizza" ) . collection ( "ratings" ) . get ();
Swift
do { let snapshot = try await db . collection ( "restaurants" ) . document ( "arinell-pizza" ) . collection ( "ratings" ) . getDocuments () print ( snapshot ) } catch { print ( error ) }
Objective-C
FIRQuery * query = [[[ self . db collectionWithPath : @"restaurants" ] documentWithPath : @"arinell-pizza" ] collectionWithPath : @"ratings" ]; [ query getDocumentsWithCompletion :^ ( FIRQuerySnapshot * _Nullable snapshot , NSError * _Nullable error ) { // ... }];
Kotlin
db . collection ( "restaurants" ) . document ( "arinell-pizza" ) . collection ( "ratings" ) . get ()
Java
db . collection ( "restaurants" ) . document ( "arinell-pizza" ) . collection ( "ratings" ) . get ();
Rather than fetching all ratings and then computing aggregate information, we can store this information on the restaurant document itself:
Web
var arinellDoc = { name : 'Arinell Pizza' , avgRating : 4.65 , numRatings : 683 };
Swift
struct Restaurant { let name : String let avgRating : Float let numRatings : Int } let arinell = Restaurant ( name : "Arinell Pizza" , avgRating : 4.65 , numRatings : 683 )
Objective-C
@interface FIRRestaurant : NSObject @property ( nonatomic , readonly ) NSString * name ; @property ( nonatomic , readonly ) float averageRating ; @property ( nonatomic , readonly ) NSInteger ratingCount ; - ( instancetype ) initWithName: ( NSString * ) name averageRating :( float ) averageRating ratingCount :( NSInteger ) ratingCount ; @end @implementation FIRRestaurant - ( instancetype ) initWithName: ( NSString * ) name averageRating :( float ) averageRating ratingCount :( NSInteger ) ratingCount { self = [ super init ]; if ( self != nil ) { _name = name ; _averageRating = averageRating ; _ratingCount = ratingCount ; } return self ; } @ end . m

