An aggregation query processes the data from multiple index entries to return a
single summary value.
Cloud Firestore supports the following
aggregation queries:
Cloud Firestore calculates the aggregation and transmits only the result
back to your app. Compared to executing a full query and calculating the
aggregation in your app, aggregation queries save on both billed document reads
and bytes transferred.
Aggregation queries rely on the existing index configuration that
your queries already use, and scale proportionally to the number of index
entries scanned. Latency increases with the number of items
in the aggregation.
Use the
count()
aggregation
The
count()
aggregation query lets you
determine the number of documents in a collection or query.
Refer to the example data we set up in
Getting data
.
The following
count()
aggregation returns the total number of cities in the
cities
collection.
Web modular API
const coll = collection(db, "cities");
const snapshot = await getCountFromServer(coll);
console.log('count: ', snapshot.data().count);
Swift
Note:
This product is not available on watchOS and App Clip targets.
let query = db.collection("cities")
let countQuery = query.count
do {
let snapshot = try await countQuery.getAggregation(source: .server)
print(snapshot.count)
} catch {
print(error)
}
Objective-C
Note:
This product is not available on watchOS and App Clip targets.
FIRCollectionReference *query = [self.db collectionWithPath:@"cities"];
[query.count aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot *snapshot,
NSError *error) {
if (error != nil) {
NSLog(@"Error fetching count: %@", error);
} else {
NSLog(@"Cities count: %@", snapshot.count);
}
}];
Java
Query query = db.collection("cities");
AggregateQuery countQuery = query.count();
countQuery.get(AggregateSource.SERVER).addOnCompleteListener(new OnCompleteListener<AggregateQuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<AggregateQuerySnapshot> task) {
if (task.isSuccessful()) {
// Count fetched successfully
AggregateQuerySnapshot snapshot = task.getResult();
Log.d(TAG, "Count: " + snapshot.getCount());
} else {
Log.d(TAG, "Count failed: ", task.getException());
}
}
});
Kotlin+KTX
val query = db.collection("cities")
val countQuery = query.count()
countQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
if (task.isSuccessful) {
// Count fetched successfully
val snapshot = task.result
Log.d(TAG, "Count: ${snapshot.count}")
} else {
Log.d(TAG, "Count failed: ", task.getException())
}
}
Dart
// Returns number of documents in users collection
db.collection("users").count().get().then(
(res) => print(res.count),
onError: (e) => print("Error completing: $e"),
);
Java
CollectionReference collection = db.collection("cities");
AggregateQuerySnapshot snapshot = collection.count().get().get();
System.out.println("Count: " + snapshot.getCount());
Node.js
const collectionRef = db.collection('cities');
const snapshot = await collectionRef.count().get();
console.log(snapshot.data().count);
The
count()
aggregation takes into account any filters on the query and any
limit
clauses.
Web modular API
const coll = collection(db, "cities");
const q = query(coll, where("state", "==", "CA"));
const snapshot = await getCountFromServer(q);
console.log('count: ', snapshot.data().count);
Swift
Note:
This product is not available on watchOS and App Clip targets.
let query = db.collection("cities").whereField("state", isEqualTo: "CA")
let countQuery = query.count
do {
let snapshot = try await countQuery.getAggregation(source: .server)
print(snapshot.count)
} catch {
print(error)
}
Objective-C
Note:
This product is not available on watchOS and App Clip targets.
FIRQuery *query =
[[self.db collectionWithPath:@"cities"]
queryWhereField:@"state"
isEqualTo:@"CA"];
[query.count aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot *snapshot,
NSError *error) {
if (error != nil) {
NSLog(@"Error fetching count: %@", error);
} else {
NSLog(@"Cities count: %@", snapshot.count);
}
}];
Java
Query query = db.collection("cities").whereEqualTo("state", "CA");
AggregateQuery countQuery = query.count();
countQuery.get(AggregateSource.SERVER).addOnCompleteListener(new OnCompleteListener<AggregateQuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<AggregateQuerySnapshot> task) {
if (task.isSuccessful()) {
// Count fetched successfully
AggregateQuerySnapshot snapshot = task.getResult();
Log.d(TAG, "Count: " + snapshot.getCount());
} else {
Log.d(TAG, "Count failed: ", task.getException());
}
}
});
Kotlin+KTX
val query = db.collection("cities").whereEqualTo("state", "CA")
val countQuery = query.count()
countQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
if (task.isSuccessful) {
// Count fetched successfully
val snapshot = task.result
Log.d(TAG, "Count: ${snapshot.count}")
} else {
Log.d(TAG, "Count failed: ", task.getException())
}
}
Dart
// This also works with collectionGroup queries.
db.collection("users").where("age", isGreaterThan: 10).count().get().then(
(res) => print(res.count),
onError: (e) => print("Error completing: $e"),
);
Java
CollectionReference collection = db.collection("cities");
Query query = collection.whereEqualTo("state", "CA");
AggregateQuerySnapshot snapshot = query.count().get().get();
System.out.println("Count: " + snapshot.getCount());
Node.js
const collectionRef = db.collection('cities');
const query = collectionRef.where('state', '==', 'CA');
const snapshot = await query.count().get();
console.log(snapshot.data().count);
Use the
sum()
aggregation
Use the
sum()
aggregation to return the total sum of numeric values that
match a given query?for example:
Web modular API
const coll = collection(firestore, 'cities');
const snapshot = await getAggregateFromServer(coll, {
totalPopulation: sum('population')
});
console.log('totalPopulation: ', snapshot.data().totalPopulation);
Swift
Note:
This product is not available on watchOS and App Clip targets.
let query = db.collection("cities")
let aggregateQuery = query.aggregate([AggregateField.sum("population")])
do {
let snapshot = try await aggregateQuery.getAggregation(source: .server)
print(snapshot.get(AggregateField.sum("population")))
} catch {
print(error)
}
Objective-C
Note:
This product is not available on watchOS and App Clip targets.
FIRQuery *query = [self.db collectionWithPath:@"cities"];
FIRAggregateQuery *aggregateQuery = [query aggregate:@[
[FIRAggregateField aggregateFieldForSumOfField:@"population"]]];
[aggregateQuery aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot *snapshot,
NSError *error) {
if (error != nil) {
NSLog(@"Error fetching aggregate: %@", error);
} else {
NSLog(@"Sum: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"population"]]);
}
}];
Java
Query query = db.collection("cities");
AggregateQuery aggregateQuery = query.aggregate(AggregateField.sum("population"));
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener(new OnCompleteListener<AggregateQuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<AggregateQuerySnapshot> task) {
if (task.isSuccessful()) {
// Aggregate fetched successfully
AggregateQuerySnapshot snapshot = task.getResult();
Log.d(TAG, "Sum: " + snapshot.get(AggregateField.sum("population")));
} else {
Log.d(TAG, "Aggregation failed: ", task.getException());
}
}
});
Kotlin+KTX
val query = db.collection("cities")
val aggregateQuery = query.aggregate(AggregateField.sum("population"))
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
if (task.isSuccessful) {
// Aggregate fetched successfully
val snapshot = task.result
Log.d(TAG, "Sum: ${snapshot.get(AggregateField.sum("population"))}")
} else {
Log.d(TAG, "Aggregate failed: ", task.getException())
}
}
Java
collection = db.collection("cities");
snapshot = collection.aggregate(sum("population")).get().get();
System.out.println("Sum: " + snapshot.get(sum("population")));
Node.js
const coll = firestore.collection('cities');
const sumAggregateQuery = coll.aggregate({
totalPopulation: AggregateField.sum('population'),
});
const snapshot = await sumAggregateQuery.get();
console.log('totalPopulation: ', snapshot.data().totalPopulation);
Python
collection_ref = client.collection("users")
aggregate_query = aggregation.AggregationQuery(collection_ref)
aggregate_query.sum("coins", alias="sum")
results = aggregate_query.get()
for result in results:
print(f"Alias of results from query: {result[0].alias}")
print(f"Sum of results from query: {result[0].value}")
Go
func createSumQuery(w io.Writer, projectID string) error {
ctx := context.Background()
client, err := firestore.NewClient(ctx, projectID)
if err != nil {
return err
}
defer client.Close()
collection := client.Collection("users")
query := collection.Where("born", ">", 1850)
aggregationQuery := query.NewAggregationQuery().WithSum("coins", "sum_coins")
results, err := aggregationQuery.Get(ctx)
if err != nil {
return err
}
sum, ok := results["sum_coins"]
if !ok {
return errors.New("firestore: couldn't get alias for SUM from results")
}
sumValue := sum.(*firestorepb.Value)
fmt.Fprintf(w, "Sum of results from query: %d\n", sumValue.GetIntegerValue())
return nil
}
The
sum()
aggregation takes into account any filters on the query and any
limit clauses?for example:
Web modular API
const coll = collection(firestore, 'cities');
const q = query(coll, where('capital', '==', true));
const snapshot = await getAggregateFromServer(q, {
totalPopulation: sum('population')
});
console.log('totalPopulation: ', snapshot.data().totalPopulation);
Swift
Note:
This product is not available on watchOS and App Clip targets.
let query = db.collection("cities").whereField("capital", isEqualTo: true)
let aggregateQuery = query.aggregate([AggregateField.sum("population")])
do {
let snapshot = try await aggregateQuery.getAggregation(source: .server)
print(snapshot.get(AggregateField.sum("population")))
} catch {
print(error)
}
Objective-C
Note:
This product is not available on watchOS and App Clip targets.
FIRQuery *query = [[self.db collectionWithPath:@"cities"]
queryWhereFilter:[FIRFilter filterWhereField:@"capital" isEqualTo:@YES]];
FIRAggregateQuery *aggregateQuery = [query aggregate:@[
[FIRAggregateField aggregateFieldForSumOfField:@"population"]]];
[aggregateQuery aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot *snapshot,
NSError *error) {
if (error != nil) {
NSLog(@"Error fetching aggregate: %@", error);
} else {
NSLog(@"Sum: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"population"]]);
}
}];
Java
Query query = db.collection("cities").whereEqualTo("capital", true);
AggregateQuery aggregateQuery = query.aggregate(AggregateField.sum("population"));
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener(new OnCompleteListener<AggregateQuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<AggregateQuerySnapshot> task) {
if (task.isSuccessful()) {
// Aggregate fetched successfully
AggregateQuerySnapshot snapshot = task.getResult();
Log.d(TAG, "Sum: " + snapshot.get(AggregateField.sum("population")));
} else {
Log.d(TAG, "Aggregation failed: ", task.getException());
}
}
});
Kotlin+KTX
val query = db.collection("cities").whereEqualTo("capital", true)
val aggregateQuery = query.aggregate(AggregateField.sum("population"))
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
if (task.isSuccessful) {
// Aggregate fetched successfully
val snapshot = task.result
Log.d(TAG, "Sum: ${snapshot.get(AggregateField.sum("population"))}")
} else {
Log.d(TAG, "Aggregate failed: ", task.getException())
}
}
Java
collection = db.collection("cities");
query = collection.whereEqualTo("state", "CA");
snapshot = query.aggregate(sum("population")).get().get();
System.out.println("Sum: " + snapshot.get(sum("population")));
Node.js
const coll = firestore.collection('cities');
const q = coll.where("capital", "==", true);
const sumAggregateQuery = q.aggregate({
totalPopulation: AggregateField.sum('population'),
});
const snapshot = await sumAggregateQuery.get();
console.log('totalPopulation: ', snapshot.data().totalPopulation);
Python
collection_ref = client.collection("users")
query = collection_ref.where(filter=FieldFilter("people", "==", "Matthew"))
aggregate_query = aggregation.AggregationQuery(query)
aggregate_query.sum("coins", alias="sum")
results = aggregate_query.get()
for result in results:
print(f"Alias of results from query: {result[0].alias}")
print(f"Sum of results from query: {result[0].value}")
Go
func createSumQuery(w io.Writer, projectID string) error {
ctx := context.Background()
client, err := firestore.NewClient(ctx, projectID)
if err != nil {
return err
}
defer client.Close()
collection := client.Collection("users")
query := collection.Where("born", ">", 1850).Limit(5)
aggregationQuery := query.NewAggregationQuery().WithSum("coins", "sum_coins")
results, err := aggregationQuery.Get(ctx)
if err != nil {
return err
}
sum, ok := results["sum_coins"]
if !ok {
return errors.New("firestore: couldn't get alias for SUM from results")
}
sumValue := sum.(*firestorepb.Value)
fmt.Fprintf(w, "Sum of results from query: %d\n", sumValue.GetIntegerValue())
return nil
}
Use the
average()
aggregation
Use the
average()
aggregation to return the average of numeric values that match a
given query?for example:
Web modular API
const coll = collection(firestore, 'cities');
const snapshot = await getAggregateFromServer(coll, {
averagePopulation: average('population')
});
console.log('averagePopulation: ', snapshot.data().averagePopulation);
Swift
Note:
This product is not available on watchOS and App Clip targets.
let query = db.collection("cities")
let aggregateQuery = query.aggregate([AggregateField.average("population")])
do {
let snapshot = try await aggregateQuery.getAggregation(source: .server)
print(snapshot.get(AggregateField.average("population")))
} catch {
print(error)
}
Objective-C
Note:
This product is not available on watchOS and App Clip targets.
FIRQuery *query = [self.db collectionWithPath:@"cities"];
FIRAggregateQuery *aggregateQuery = [query aggregate:@[
[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]];
[aggregateQuery aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot *snapshot,
NSError *error) {
if (error != nil) {
NSLog(@"Error fetching aggregate: %@", error);
} else {
NSLog(@"Avg: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]);
}
}];
Java
Query query = db.collection("cities");
AggregateQuery aggregateQuery = query.aggregate(AggregateField.average("population"));
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener(new OnCompleteListener<AggregateQuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<AggregateQuerySnapshot> task) {
if (task.isSuccessful()) {
// Aggregate fetched successfully
AggregateQuerySnapshot snapshot = task.getResult();
Log.d(TAG, "Average: " + snapshot.get(AggregateField.average("population")));
} else {
Log.d(TAG, "Aggregation failed: ", task.getException());
}
}
});
Kotlin+KTX
val query = db.collection("cities")
val aggregateQuery = query.aggregate(AggregateField.average("population"))
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
if (task.isSuccessful) {
// Aggregate fetched successfully
val snapshot = task.result
Log.d(TAG, "Average: ${snapshot.get(AggregateField.average("population"))}")
} else {
Log.d(TAG, "Aggregate failed: ", task.getException())
}
}
Java
collection = db.collection("cities");
snapshot = collection.aggregate(average("population")).get().get();
System.out.println("Average: " + snapshot.get(average("population")));
Node.js
const coll = firestore.collection('cities');
const averageAggregateQuery = coll.aggregate({
averagePopulation: AggregateField.average('population'),
});
const snapshot = await averageAggregateQuery.get();
console.log('averagePopulation: ', snapshot.data().averagePopulation);
Python
collection_ref = client.collection("users")
aggregate_query = aggregation.AggregationQuery(collection_ref)
aggregate_query.avg("coins", alias="avg")
results = aggregate_query.get()
for result in results:
print(f"Alias of results from query: {result[0].alias}")
print(f"Average of results from query: {result[0].value}")
Go
func createAvgQuery(w io.Writer, projectID string) error {
ctx := context.Background()
client, err := firestore.NewClient(ctx, projectID)
if err != nil {
return err
}
defer client.Close()
collection := client.Collection("users")
query := collection.Where("born", ">", 1850)
aggregationQuery := query.NewAggregationQuery().WithAvg("coins", "avg_coins")
results, err := aggregationQuery.Get(ctx)
if err != nil {
return err
}
avg, ok := results["avg_coins"]
if !ok {
return errors.New("firestore: couldn't get alias for AVG from results")
}
avgValue := avg.(*firestorepb.Value)
fmt.Fprintf(w, "Avg of results from query: %d\n", avgValue.GetDoubleValue())
return nil
}
The
average()
aggregation takes into account any filters on the query and any limit
clauses?for example:
Web modular API
const coll = collection(firestore, 'cities');
const q = query(coll, where('capital', '==', true));
const snapshot = await getAggregateFromServer(q, {
averagePopulation: average('population')
});
console.log('averagePopulation: ', snapshot.data().averagePopulation);
Swift
Note:
This product is not available on watchOS and App Clip targets.
let query = db.collection("cities").whereField("capital", isEqualTo: true)
let aggregateQuery = query.aggregate([AggregateField.average("population")])
do {
let snapshot = try await aggregateQuery.getAggregation(source: .server)
print(snapshot.get(AggregateField.average("population")))
} catch {
print(error)
}
Objective-C
Note:
This product is not available on watchOS and App Clip targets.
FIRQuery *query = [[self.db collectionWithPath:@"cities"]
queryWhereFilter:[FIRFilter filterWhereField:@"capital" isEqualTo:@YES]];
FIRAggregateQuery *aggregateQuery = [query aggregate:@[
[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]];
[aggregateQuery aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot *snapshot,
NSError *error) {
if (error != nil) {
NSLog(@"Error fetching aggregate: %@", error);
} else {
NSLog(@"Avg: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]);
}
}];
Java
Query query = db.collection("cities").whereEqualTo("capital", true);
AggregateQuery aggregateQuery = query.aggregate(AggregateField.average("population"));
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener(new OnCompleteListener<AggregateQuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<AggregateQuerySnapshot> task) {
if (task.isSuccessful()) {
// Aggregate fetched successfully
AggregateQuerySnapshot snapshot = task.getResult();
Log.d(TAG, "Average: " + snapshot.get(AggregateField.average("population")));
} else {
Log.d(TAG, "Aggregation failed: ", task.getException());
}
}
});
Kotlin+KTX
val query = db.collection("cities").whereEqualTo("capital", true)
val aggregateQuery = query.aggregate(AggregateField.average("population"))
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
if (task.isSuccessful) {
// Aggregate fetched successfully
val snapshot = task.result
Log.d(TAG, "Average: ${snapshot.get(AggregateField.average("population"))}")
} else {
Log.d(TAG, "Aggregate failed: ", task.getException())
}
}
Java
collection = db.collection("cities");
query = collection.whereEqualTo("state", "CA");
snapshot = query.aggregate(average("population")).get().get();
System.out.println("Average: " + snapshot.get(average("population")));
Node.js
const coll = firestore.collection('cities');
const q = coll.where("capital", "==", true);
const averageAggregateQuery = q.aggregate({
averagePopulation: AggregateField.average('population'),
});
const snapshot = await averageAggregateQuery.get();
console.log('averagePopulation: ', snapshot.data().averagePopulation);
Python
collection_ref = client.collection("users")
query = collection_ref.where(filter=FieldFilter("people", "==", "Matthew"))
aggregate_query = aggregation.AggregationQuery(query)
aggregate_query.avg("coins", alias="avg")
results = aggregate_query.get()
for result in results:
print(f"Alias of results from query: {result[0].alias}")
print(f"Average of results from query: {result[0].value}")
Go
func createAvgQuery(w io.Writer, projectID string) error {
ctx := context.Background()
client, err := firestore.NewClient(ctx, projectID)
if err != nil {
return err
}
defer client.Close()
collection := client.Collection("users")
query := collection.Where("born", ">", 1850).Limit(5)
aggregationQuery := query.NewAggregationQuery().WithAvg("coins", "avg_coins")
results, err := aggregationQuery.Get(ctx)
if err != nil {
return err
}
avg, ok := results["avg_coins"]
if !ok {
return errors.New("firestore: couldn't get alias for AVG from results")
}
avgValue := avg.(*firestorepb.Value)
fmt.Fprintf(w, "Avg of results from query: %d\n", avgValue.GetDoubleValue())
return nil
}
Calculate multiple aggregations in a query
You can combine multiple aggregations in a single aggregation pipeline. This can reduce the number of index reads required. If the query includes aggregations on multiple fields, the query might requires a composite index. In that case,
Cloud Firestore suggests an index.
The following example performs multiple aggregations in a single aggregation query:
Web modular API
const coll = collection(firestore, 'cities');
const snapshot = await getAggregateFromServer(coll, {
countOfDocs: count(),
totalPopulation: sum('population'),
averagePopulation: average('population')
});
console.log('countOfDocs: ', snapshot.data().countOfDocs);
console.log('totalPopulation: ', snapshot.data().totalPopulation);
console.log('averagePopulation: ', snapshot.data().averagePopulation);
Swift
Note:
This product is not available on watchOS and App Clip targets.
let query = db.collection("cities")
let aggregateQuery = query.aggregate([
AggregateField.count(),
AggregateField.sum("population"),
AggregateField.average("population")])
do {
let snapshot = try await aggregateQuery.getAggregation(source: .server)
print("Count: \(snapshot.get(AggregateField.count()))")
print("Sum: \(snapshot.get(AggregateField.sum("population")))")
print("Average: \(snapshot.get(AggregateField.average("population")))")
} catch {
print(error)
}
Objective-C
Note:
This product is not available on watchOS and App Clip targets.
FIRQuery *query = [self.db collectionWithPath:@"cities"];
FIRAggregateQuery *aggregateQuery = [query aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"population"],
[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]];
[aggregateQuery aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot *snapshot,
NSError *error) {
if (error != nil) {
NSLog(@"Error fetching aggregate: %@", error);
} else {
NSLog(@"Count: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]]);
NSLog(@"Sum: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"population"]]);
NSLog(@"Avg: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]);
}
}];
Java
Query query = db.collection("cities");
AggregateQuery aggregateQuery = query.aggregate(
AggregateField.count(),
AggregateField.sum("population"),
AggregateField.average("population"));
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener(new OnCompleteListener<AggregateQuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<AggregateQuerySnapshot> task) {
if (task.isSuccessful()) {
// Aggregate fetched successfully
AggregateQuerySnapshot snapshot = task.getResult();
Log.d(TAG, "Count: " + snapshot.get(AggregateField.count()));
Log.d(TAG, "Sum: " + snapshot.get(AggregateField.sum("population")));
Log.d(TAG, "Average: " + snapshot.get(AggregateField.average("population")));
} else {
Log.d(TAG, "Aggregation failed: ", task.getException());
}
}
});
Kotlin+KTX
val query = db.collection("cities")
val aggregateQuery = query.aggregate(
AggregateField.count(),
AggregateField.sum("population"),
AggregateField.average("population")
)
aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
if (task.isSuccessful) {
// Aggregate fetched successfully
val snapshot = task.result
Log.d(TAG, "Count: ${snapshot.get(AggregateField.count())}")
Log.d(TAG, "Sum: ${snapshot.get(AggregateField.sum("population"))}")
Log.d(TAG, "Average: ${snapshot.get(AggregateField.average("population"))}")
} else {
Log.d(TAG, "Aggregate failed: ", task.getException())
}
}
Java
collection = db.collection("cities");
query = collection.whereEqualTo("state", "CA");
AggregateQuery aggregateQuery = query.aggregate(count(), sum("population"), average("population"));
snapshot = aggregateQuery.get().get();
System.out.println("Count: " + snapshot.getCount());
System.out.println("Sum: " + snapshot.get(sum("population")));
System.out.println("Average: " + snapshot.get(average("population")));
Node.js
const coll = firestore.collection('cities');
const aggregateQuery = coll.aggregate({
countOfDocs: AggregateField.count(),
totalPopulation: AggregateField.sum('population'),
averagePopulation: AggregateField.average('population')
});
const snapshot = await aggregateQuery.get();
console.log('countOfDocs: ', snapshot.data().countOfDocs);
console.log('totalPopulation: ', snapshot.data().totalPopulation);
console.log('averagePopulation: ', snapshot.data().averagePopulation);
Python
collection_ref = client.collection("users")
query = collection_ref.where(filter=FieldFilter("people", "==", "Matthew"))
aggregate_query = aggregation.AggregationQuery(query)
aggregate_query.sum("coins", alias="sum").avg("coins", alias="avg")
results = aggregate_query.get()
for result in results:
print(f"Alias of results from query: {result[0].alias}")
print(f"Aggregation of results from query: {result[0].value}")
Go
func createMultiAggregationQuery(w io.Writer, projectID string) error {
ctx := context.Background()
client, err := firestore.NewClient(ctx, projectID)
if err != nil {
return err
}
defer client.Close()
collection := client.Collection("users")
query := collection.Where("born", ">", 1850)
aggregationQuery := query.NewAggregationQuery().WithCount("count").WithSum("coins", "sum_coins").WithAvg("coins", "avg_coins")
results, err := aggregationQuery.Get(ctx)
if err != nil {
return err
}
}
Queries with multiple aggregations include only the documents that contain all
the fields in each aggregation. This might lead to different results from
performing each aggregation separately.
Security rules for aggregation queries
Cloud Firestore Security Rules work the same on aggregation queries as on
queries that return documents. In other words, if and only if your rules allow
clients to execute certain collection or collection group queries, clients can
also perform the aggregation on those queries. Learn more about
how Cloud Firestore Security Rules interact with queries
.
Behavior and limitations
As you work with aggregation queries, note the following behavior and limitations:
You cannot use aggregation queries with real-time listeners and
offline queries. Aggregation queries are only supported via direct server
response. Queries are served only by the Cloud Firestore backend, skipping
the local cache and any buffered updates. This behavior is identical to
operations performed inside
Cloud Firestore transactions
.
If an aggregation cannot resolve within 60 seconds, it returns a
DEADLINE_EXCEEDED
error. Performance depends on your index configuration and
on the size of the dataset.
If the operation cannot be completed within the 60 second deadline, a
possible workaround is to use
counters
for large datasets.
Aggregation queries read from index entries and include only indexed
fields.
Adding an
OrderBy
clause to an aggregation query limits the aggregation to
the documents where the sorting field exists.
For
sum()
and
average()
aggregations, non-numeric values are ignored.
sum()
and
average()
aggregations take into account only integer values and
floating-point number values.
When combining multiple aggregations in a single query, note that
sum()
and
average()
ignore non-numeric values while
count()
includes non-numeric values.
If you combine aggregations that are on different fields, the calculation
includes only the documents that contain all those fields.
Pricing
Pricing for aggregations queries depends on the number of index entries matched
by the query. You are charged a small number of reads for a large number of
matched entries.
See more detailed
pricing information
.