Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Count documents with aggregation queries

Stay organized with collections Save and categorize content based on your preferences.

An aggregation query processes the data from multiple index entries to return a single summary value.

Cloud Firestore supports the count() aggregation query. count() allows you to determine the number of documents in a collection or query. The server calculates the count, and transmits only the result, a single integer, back to your app, saving on both billed document reads and bytes transferred, compared to executing the full query.

Aggregation queries rely on the existing index configuration that your queries already use, and scale proportionally to the number of index entries scanned. This means that aggregations of small- to medium-sized data sets perform within 20-40 ms, though latency increases with the number of items counted.

Use the count() aggregation

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 version 9

const coll = collection(db, "cities");
const snapshot = await getCountFromServer(coll);
console.log('count: ', snapshot.data().count);
    
Swift
let collection = db.collection("cities")
let countQuery = collection.count
do {
  let snapshot = try await countQuery.getAggregation(source: .server)
  print(snapshot.count)
} catch {
  print(error);
}
      
Objective-C
FIRCollectionReference* collection = [db collectionWithPath:@"cities"];
[collection.count
  aggregationWithSource:FIRAggregateSourceServer
  completion:^(FIRAggregateQuerySnapshot *snapshot, NSError *error) {
    if (error == nil) {
        NSLog(@"Cities count: %@", snapshot.count);
    } else {
        NSLog(@"Error fetching count: %@", error);
    }
  }
];
      

Java

CollectionReference collection = db.collection("cities");
AggregateQuery countQuery = collection.count();
countQuery.get(AggregateSource.SERVER).addOnCompleteListener(task -> {
  if (task.isSuccessful()) {
    AggregateQuerySnapshot snapshot = task.getResult();
    Log.d(TAG, "Count: " + snapshot.getCount());
  } else {
    Log.d(TAG, "Count failed: ", task.getException());
  }
});
      

Kotlin+KTX

val collection = db.collection("cities")
val countQuery = collection.count()
countQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
  if (task.isSuccessful) {
    val snapshot = task.result
    Log.d(TAG, "Count: ${snapshot.count}")
  } else {
    Log.d(TAG, "Count failed: ", task.getException())
  }
}
      
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. For example, the following aggregation returns a count of the number of cities where state equals CA.

Web version 9

const coll = collection(db, "cities");
const query_ = query(coll, where('state', '==', 'CA'));
const snapshot = await getCountFromServer(query_);
console.log('count: ', snapshot.data().count);
    
Swift
let collection = db.collection("cities")
let query = collection.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
FIRCollectionReference* collection = [db collectionWithPath:@"cities"];
FIRQuery* query = [collection queryWhereField:@"state" isEqualTo:@"CA"];
[query.count
  aggregationWithSource:FIRAggregateSourceServer
  completion:^(FIRAggregateQuerySnapshot *snapshot, NSError *error) {
    if (error == nil) {
        NSLog(@"Cities count: %@", snapshot.count);
    } else {
        NSLog(@"Error fetching count: %@", error);
    }
  }
];
      

Java

CollectionReference collection = db.collection("cities");
Query query = collection.whereEqualTo("state", "CA");
AggregateQuery countQuery = query.count();
countQuery.get(AggregateSource.SERVER).addOnCompleteListener(task -> {
  if (task.isSuccessful()) {
    AggregateQuerySnapshot snapshot = task.getResult();
    Log.d(TAG, "Count: " + snapshot.getCount());
  } else {
    Log.d(TAG, "Count failed: ", task.getException());
  }
});
      

Kotlin+KTX

val collection = db.collection("cities")
val query = collection.whereEqualTo("state", "CA")
val countQuery = query.count()
countQuery.get(AggregateSource.SERVER).addOnCompleteListener { task ->
  if (task.isSuccessful) {
    val snapshot = task.result
    Log.d(TAG, "Count: ${snapshot.count}")
  } else {
    Log.d(TAG, "Count failed: ", task.getException())
  }
}
      
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);
      

Cloud Firestore Security Rules work the same on count() aggregation queries as on normal 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 count() aggregation on those queries. Learn more about how Cloud Firestore Security Rules interact with queries.

Limitations

Note the following limitations on the count() aggregation query:

  • count() aggregation queries are currently 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. You cannot currently use count() queries with real-time listeners and offline queries.

  • If a count() 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 data sets.

  • The count() aggregation reads from index entries and counts only indexed fields.

  • Adding an OrderBy clause to the query limits the count to the entities where the sorting property exists.

Pricing

Pricing for count() 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.