使用聚合查询汇总数据

聚合查询会处理多个索引条目中的数据,然后返回单个摘要值。

Cloud Firestore 支持以下聚合查询:

  • count()
  • sum()
  • average()

Cloud Firestore 会计算聚合结果,并仅将结果传回您的应用。与在应用中执行完整查询并计算聚合相比,聚合查询可以节省计费的文档读取和传输的字节数所产生的费用。

聚合查询依赖于您的查询已在使用的现有索引配置,并且会根据扫描到的索引条目数量按比例进行扩缩。随着聚合中的项数增加,延迟时间也会变长。

使用 count() 聚合

通过 count() 聚合查询,您可以确定集合或查询中的文档数量。

如需详细了解示例数据,请参阅获取数据

以下 count() 聚合会返回 cities 集合中的城市总数。

Web

const coll = collection(db, "cities"); const snapshot = await getCountFromServer(coll); console.log('count: ', snapshot.data().count);
Swift
注意:此产品不适用于 watchOS 和 App Clip 目标。
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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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

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("cities").count().get().then(  (res) => print(res.count),  onError: (e) => print("Error completing: $e"),  );
Go
package firestore import ( "context" "errors" "fmt" "io" "cloud.google.com/go/firestore" firestorepb "cloud.google.com/go/firestore/apiv1/firestorepb" ) func createCountQuery(w io.Writer, projectID string) error { // Instantiate the client 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) // `alias` argument--"all"--provides a key for accessing the aggregate query // results. The alias value must be unique across all aggregation aliases in // an aggregation query and must conform to allowed Document field names. // // See https://cloud.google.com/firestore/docs/reference/rpc/google.firestore.v1#document for details. aggregationQuery := query.NewAggregationQuery().WithCount("all") results, err := aggregationQuery.Get(ctx) if err != nil { return err } count, ok := results["all"] if !ok { return errors.New("firestore: couldn't get alias for COUNT from results") } countValue := count.(*firestorepb.Value) fmt.Fprintf(w, "Number of results from query: %d\n", countValue.GetIntegerValue()) return nil } 
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);  
Python
from google.cloud import firestore from google.cloud.firestore_v1 import aggregation from google.cloud.firestore_v1.base_query import FieldFilter def create_count_query(project_id: str) -> None:  """Builds an aggregate query that returns the number of results in the query.  Arguments:  project_id: your Google Cloud Project ID  """ client = firestore.Client(project=project_id) collection_ref = client.collection("users") query = collection_ref.where(filter=FieldFilter("born", ">", 1800)) aggregate_query = aggregation.AggregationQuery(query) # `alias` to provides a key for accessing the aggregate query results aggregate_query.count(alias="all") results = aggregate_query.get() for result in results: print(f"Alias of results from query: {result[0].alias}") print(f"Number of results from query: {result[0].value}") 

count() 聚合会考虑查询的所有过滤条件以及所有 limit 子句。

Web

const coll = collection(db, "cities"); const q = query(coll, where("state", "==", "CA")); const snapshot = await getCountFromServer(q); console.log('count: ', snapshot.data().count);
Swift
注意:此产品不适用于 watchOS 和 App Clip 目标。
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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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

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 collection queries. db.collection("cities").where("capital", isEqualTo: 10).count().get().then(  (res) => print(res.count),  onError: (e) => print("Error completing: $e"),  );
Go
package firestore import ( "context" "errors" "fmt" "io" "cloud.google.com/go/firestore" firestorepb "cloud.google.com/go/firestore/apiv1/firestorepb" ) func createCountQuery(w io.Writer, projectID string) error { // Instantiate the client 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) // `alias` argument--"all"--provides a key for accessing the aggregate query // results. The alias value must be unique across all aggregation aliases in // an aggregation query and must conform to allowed Document field names. // // See https://cloud.google.com/firestore/docs/reference/rpc/google.firestore.v1#document for details. aggregationQuery := query.NewAggregationQuery().WithCount("all") results, err := aggregationQuery.Get(ctx) if err != nil { return err } count, ok := results["all"] if !ok { return errors.New("firestore: couldn't get alias for COUNT from results") } countValue := count.(*firestorepb.Value) fmt.Fprintf(w, "Number of results from query: %d\n", countValue.GetIntegerValue()) return nil } 
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);  
Python
from google.cloud import firestore from google.cloud.firestore_v1 import aggregation from google.cloud.firestore_v1.base_query import FieldFilter def create_count_query(project_id: str) -> None:  """Builds an aggregate query that returns the number of results in the query.  Arguments:  project_id: your Google Cloud Project ID  """ client = firestore.Client(project=project_id) collection_ref = client.collection("users") query = collection_ref.where(filter=FieldFilter("born", ">", 1800)) aggregate_query = aggregation.AggregationQuery(query) # `alias` to provides a key for accessing the aggregate query results aggregate_query.count(alias="all") results = aggregate_query.get() for result in results: print(f"Alias of results from query: {result[0].alias}") print(f"Number of results from query: {result[0].value}") 

使用 sum() 聚合

使用 sum() 聚合可返回与给定查询匹配的数值的总和,例如:

Web

const coll = collection(firestore, 'cities'); const snapshot = await getAggregateFromServer(coll, {  totalPopulation: sum('population') }); console.log('totalPopulation: ', snapshot.data().totalPopulation);  
Swift
注意:此产品不适用于 watchOS 和 App Clip 目标。
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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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

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())  } }

Dart

db.collection("cities").aggregate(sum("population")).get().then(  (res) => print(res.getAverage("population")),  onError: (e) => print("Error completing: $e"),  );
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 }  

sum() 聚合会考虑查询的所有过滤条件以及所有限制子句,例如:

Web

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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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

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())  } }

Dart

db  .collection("cities")  .where("capital", isEqualTo: true)  .aggregate(sum("population"))  .get()  .then(  (res) => print(res.getAverage("population")),  onError: (e) => print("Error completing: $e"),  );
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 }  

使用 average() 聚合

使用 average() 聚合可返回与给定查询匹配的数值的平均值,例如:

Web

const coll = collection(firestore, 'cities'); const snapshot = await getAggregateFromServer(coll, {  averagePopulation: average('population') }); console.log('averagePopulation: ', snapshot.data().averagePopulation);  
Swift
注意:此产品不适用于 watchOS 和 App Clip 目标。
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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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

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())  } }

Dart

db.collection("cities").aggregate(average("population")).get().then(  (res) => print(res.getAverage("population")),  onError: (e) => print("Error completing: $e"),  );
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 }  

average() 聚合会考虑查询的所有过滤条件以及所有限制子句,例如:

Web

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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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

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())  } }

Dart

db  .collection("cities")  .where("capital", isEqualTo: true)  .aggregate(average("population"))  .get()  .then(  (res) => print(res.getAverage("population")),  onError: (e) => print("Error completing: $e"),  );
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 }  

在查询中计算多项聚合

您可以将多项聚合合并到一个聚合流水线中。这样可以减少所需的索引读取次数。如果查询包含多个字段的聚合,则查询可能需要复合索引。在这种情况下,Cloud Firestore 会建议一个索引。

以下示例在单个聚合查询中执行多次聚合:

Web

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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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
注意:此产品不适用于 watchOS 和 App Clip 目标。
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

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())  } }

Dart

db  .collection("cities")  .aggregate(  count(),  sum("population"),  average("population"),  )  .get()  .then(  (res) {  print(res.count);  print(res.getSum("population"));  print(res.getAverage("population"));  },  onError: (e) => print("Error completing: $e"), );
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  } }  

具有多项聚合的查询仅包含此类文档,即包含每个聚合中所有字段的文档。这可能会导致单独执行每次聚合时得到不同的结果。

聚合查询的安全规则

Cloud Firestore Security Rules针对聚合查询的工作方式与针对返回文档的查询的工作方式相同。换句话说,当且仅当您的规则允许客户端执行某些集合或集合组查询时,客户端也可以对这些查询执行聚合操作。详细了解 Cloud Firestore Security Rules与查询的相互作用

行为和限制

在使用聚合查询时,请注意以下行为和限制:

  • 您不能将聚合查询与实时监听器和离线查询搭配使用。聚合查询只能通过直接服务器响应进行处理。查询只会通过 Cloud Firestore 后端进行处理,并且会跳过本地缓存和任何已缓冲的更新。此行为与 Cloud Firestore 事务内执行的操作相同。

  • 如果聚合无法在 60 秒内完成解析,则会返回 DEADLINE_EXCEEDED 错误。聚合性能取决于您的索引配置以及数据集的规模。

    如果操作无法在 60 秒时限内完成,作为一种可能的解决方法,您可以针对大型数据集使用计数器

  • 聚合查询会从索引条目中进行读取,并且仅包含编入索引的字段。

  • 如果向聚合查询添加 OrderBy 子句,则查询只会对存在排序字段的文档进行计数。

  • 对于 sum()average() 聚合,系统会忽略非数字值。sum()average() 聚合仅考虑整数值和浮点值。

  • 在单个查询中组合多项聚合时,请注意,sum()average() 会忽略非数字值,而 count() 会包含非数字值。

  • 如果您合并针对不同字段的聚合,则计算将仅包含带所有这些字段的文档。

价格

聚合查询的价格取决于查询匹配的索引条目的数量。您需要为大量匹配的条目支付少量读取费用。 对于查询所读取的每批不超过 1,000 个索引条目,系统都会收取相当于一次读取操作的费用。

如需详细了解聚合查询的价格,请参阅聚合查询