使用聚合查询汇总数据
聚合查询会处理多个索引条目中的数据,然后返回单个摘要值。
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);
count_aggregate_collection.js
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)
}
ViewController.swift
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);
}
}];
ViewController.m
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());
}
}
});
DocSnippets.java
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())
}
}
DocSnippets.kt
Dart
// Returns number of documents in users collection
db.collection("cities").count().get().then(
(res) => print(res.count),
onError: (e) => print("Error completing: $e"),
);
firestore.dart
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
}
aggregate_query_count.go
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}")
aggregate_query_count.py
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);
count_aggregate_query.js
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)
}
ViewController.swift
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);
}
}];
ViewController.m
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());
}
}
});
DocSnippets.java
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())
}
}
DocSnippets.kt
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"),
);
firestore.dart
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
}
aggregate_query_count.go
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}")
aggregate_query_count.py
使用 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)
}
ViewController.swift
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"]]);
}
}];
ViewController.m
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());
}
}
});
DocSnippets.java
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())
}
}
DocSnippets.kt
Dart
db.collection("cities").aggregate(sum("population")).get().then(
(res) => print(res.getAverage("population")),
onError: (e) => print("Error completing: $e"),
);
firestore.dart
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)
}
ViewController.swift
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"]]);
}
}];
ViewController.m
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());
}
}
});
DocSnippets.java
Kotlin
``` val query = db.collection("cities").whereEqualTo("capital", true) val aggregateQuery = query.aggregate(AggregateField.sum("population")) aggregateQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
