Index types in Cloud Firestore

Indexes are an important factor in the performance of a database. Much like theindex of a book which maps topics in a book to page numbers, a database indexmaps the items in a database to their locations in the database. When you querya database, the database can use an index to quickly identify the locations ofthe items that you requested.

This page describes the two types of indexes thatCloud Firestore uses,single-field indexes andcompositeindexes.

Index definition and structure

An index is defined on a list of fields of a given document, with acorrespondingindex mode for each field.

An index contains an entry for every field named in the index definition. Theindex includes all documents that are the potential results for queries based onthe index. A document is included in the index only if it has an indexed valueset for every field used in the index. If the index definition refers to a fieldfor which the document has no value set, that document won't appear in the index.In this case, the document will never be returned as a result for any query based on the index.

The composite index is sorted by field values, in the order specified in theindex definition.

An index behind every query

If no index exists for a query, most databases crawl through their contents itemby item, a slow process that slows down even more as the database grows.Cloud Firestore guarantees high query performance by using indexes forall queries. As a result, query performance depends on the sizeof the result set and not on the number of items in the database.

Less index management, more app development

Cloud Firestore includes features that reduce the amount of time that youneed to spend on index management. The indexes required for the most basicqueries are automatically created for you. As you use and test your app,Cloud Firestore helps you identify andcreate additional indexes that your app requires.

Index types

Cloud Firestore uses two types of indexes:single-field andcomposite. Besides the number of fields indexed, single-field andcomposite indexes differ in how you manage them.

Single-field indexes

A single-field index stores a sorted mapping of all the documents in acollection that contain a specific field. Each entry in a single-field indexrecords a document's value for a specific field and the location of the documentin the database.Cloud Firestore uses these indexes to perform manybasic queries. You manage single-field indexes by configuring your database'sautomatic indexing settings and index exemptions.

Automatic indexing

By default,Cloud Firestore automatically maintains single-field indexesfor each field in a document and each subfield in a map.Cloud Firestoreuses the following default settings for single-field indexes:

  • For each non-array and non-map field,Cloud Firestore defines twocollection-scope single-field indexes, one in ascending modeand one in descending mode.

  • For each map field,Cloud Firestore creates the following:

    • One collection-scope ascending index for each non-array, non-map subfield.
    • One collection-scope descending index for each non-array, non-map subfield.
    • One collection-scope ascending index for the whole map value
    • One collection-scope descending index for the whole map value
    • One collection-scope array-contains index for each array subfield.
    • Cloud Firestore recursively indexes each map subfield.
  • For each array field in a document,Cloud Firestore creates the following:

    • One collection-scope ascending index for the whole array value
    • One collection-scope descending index for the whole array value
    • One collection-scope array-contains index.
  • Single-field indexes with collection group scope are not maintained bydefault.

Single-field index exemptions

You can exempt a field from yourautomatic indexingsettings by creating a single-field index exemption.An indexing exemption overrides the database-wide automatic index settings. Anexemption can enable a single-field index that your automatic indexing settingswould otherwise disable or disable a single-field index that automatic indexingwould otherwise enable. For cases where exemptions can be useful, see theindexing best practices.

Use the* field path value to add collection-level index exemptions on allfields in a collection group. For example, for collection groupcomments, setthe field path to* to match all fields in thecomments collection group anddisable indexing of all the fields under the collection group. You can then addexemptions to index only the fields required for your queries. Reducing thenumber of indexed fields reduces storage costs and can improve writeperformance.

If you create a single-field index exemption for a map field, the map'ssubfields inherit those settings. You can, however, define single-field indexexemptions for specific subfields. If you delete an exemption for a subfield,the subfield will inherit its parent's exemption settings, if they exist, or thedatabase-wide settings if no parent exemptions exist.

Note: An exemption only applies to automatic index settings. A field exemptedfrom single-field indexing can still be indexed as part of a composite index.

To create and manage single-field index exemptions, seeManage indexes.

Composite indexes

A composite index stores a sorted mapping of all the documents in a collection,based on an ordered list of fields to index.

Note: You can have at most one array field per composite index.

Cloud Firestore uses composite indexes to supportqueries not already supported by single-field indexes.

Cloud Firestore doesn't automatically create composite indexes like itdoes for single-field indexes because of the large number of possible fieldcombinations. Instead,Cloud Firestore helps youidentify and createrequired composite indexes as you build your app.

Any time you attempt a query that isn't supported by an index,Cloud Firestorereturns an error message with a link that you can follow to create the missingindex.

You can also define and manage composite indexes manuallyby using the console or by using theFirebaseCLI. For more on creating and managingcomposite indexes, seeManage indexes.

Index modes and query scopes

You configure single-field and composite indexes differently, but both requirethat you configure index modes and query scopes for your indexes.

Index modes

When you define an index, you select an index mode for each indexed field. Eachfield's index mode supports specific query clauses on that field. Youcan select from the following index modes:

Index modeDescription
AscendingSupports<,<=,==,>=,>,!=,in, andnot-in, query clauses on the field and supports sorting results in ascending order based on this field value.
DescendingSupports<,<=,==,>=,>,!=,in, andnot-in query clauses on the field and supports sorting results in descending order based on this field value.
Array‑containsSupportsarray-contains andarray-contains-any query clauses on the field.
VectorSupportsFindNearest query clauses on the field.

Query scopes

Each index is scoped to either a collection or a collection group. This is knownas the index's query scope:

Collection scope
Cloud Firestore creates indexes with collection scope by default.These indexes support queries that return results from a single collection.

Collection group scope
A collection group includes all collections with the same collection ID. Torun acollection group query that returns filteredor ordered results from a collection group, you must create a correspondingindex with collection group scope.

Default ordering and the__name__ field

In addition to sorting documents by the index modesspecified for each field (ascending or descending) , indexes apply a finalsorting by the__name__ field of each document. The value of the__name__field is set to the full document path. This means that documentsin the result set with the same field values are sorted by document path.

By default, the__name__ field is sorted in the same direction of the lastsorted field in the index definition. For example:

CollectionFields indexedQuery scope
cities name,__name__Collection
cities state,__name__Collection
cities country, population,__name__Collection

To sort results by the non-default__name__ direction, you need tocreate that index.

Index properties

An index that allows the query to be executed most efficiently is defined by the following properties:

  • Fields used in equality filters
  • Fields used in sort orders
  • Fields used in range and inequality filters (that are not already included in sort orders)
  • Fields used in aggregations (that aren't already included in sort orders and range and inequality filters)

Cloud Firestore computes the results for queries as follows:

  1. Identifies the index corresponding to the query's collection, filter properties, filter operators, and sort orders.
  2. Identifies the index position from which the scanning starts. The start position is prefixed with the query's equality filters and ends with the range and inequality filters on the firstorderBy field.
  3. Starts scanning the index, returning each document that satisfies all the filters, until the scanning process does one of the following:
    • Encounters a document that doesn't meet the filter conditions and confirms that any subsequent document will never fully meet the filter conditions.
    • Reaches the end of the index.
    • Collects the maximum number of results requested by the query.

Indexing example

By automatically creating single-field indexes for you,Cloud Firestoreallows your application to quickly support the most basic database queries.Single-field indexes allow you to perform simple queries based on field valuesand the comparators<,<=,==,>=,>, andin. For array fields, they allowyou to performarray-contains andarray-contains-any queries.

To illustrate, examine the following examples from the point of view ofindex creation. The following snippet creates afewcity documents in acities collection and setsname,state,country,capital,population, andtags fields for each document:

Web
varcitiesRef=db.collection("cities");citiesRef.doc("SF").set({name:"San Francisco",state:"CA",country:"USA",capital:false,population:860000,regions:["west_coast","norcal"]});citiesRef.doc("LA").set({name:"Los Angeles",state:"CA",country:"USA",capital:false,population:3900000,regions:["west_coast","socal"]});citiesRef.doc("DC").set({name:"Washington, D.C.",state:null,country:"USA",capital:true,population:680000,regions:["east_coast"]});citiesRef.doc("TOK").set({name:"Tokyo",state:null,country:"Japan",capital:true,population:9000000,regions:["kanto","honshu"]});citiesRef.doc("BJ").set({name:"Beijing",state:null,country:"China",capital:true,population:21500000,regions:["jingjinji","hebei"]});

Assuming the default automatic indexing settings,Cloud Firestore updatesone ascending single-field index per field, one descending single-field index per field, and one array-contains single-field index forthe array field. Each row in the following table represents an entry in asingle-field index:

CollectionField indexedQuery scope
cities nameCollection
cities stateCollection
cities countryCollection
cities capitalCollection
cities populationCollection
cities regionsCollection
cities nameCollection
cities stateCollection
cities countryCollection
cities capitalCollection
cities populationCollection
cities regionsCollection
citiesarray-contains regionsCollection

Queries supported by single-field indexes

Using these automatically created single-field indexes, you can run simplequeries like the following:

Web
conststateQuery=citiesRef.where("state","==","CA");constpopulationQuery=citiesRef.where("population","<",100000);constnameQuery=citiesRef.where("name",">=","San Francisco");

You can also createin and compound equality (==) queries:

Web
citiesRef.where('country', 'in', ["USA", "Japan", "China"])// Compound equality queriescitiesRef.where("state", "==", "CO").where("name", "==", "Denver")citiesRef.where("country", "==", "USA")         .where("capital", "==", false)         .where("state", "==", "CA")         .where("population", "==", 860000)

If you need to run a compound query that uses a range comparison (<,<=,>, or>=) or if you need to sort by a different field, you must create acomposite index for that query.

Thearray-contains index allows you to query theregions array field:

Web
citiesRef.where("regions","array-contains","west_coast")// array-contains-any and array-contains use the same indexescitiesRef.where("regions","array-contains-any",["west_coast","east_coast"])

Queries supported by composite indexes

Cloud Firestore uses composite indexes to supportcompound queries not already supported by single-field indexes. For example, youwould need a composite index for the following queries:

Web
citiesRef.where("country", "==", "USA").orderBy("population", "asc")citiesRef.where("country", "==", "USA").where("population", "<", 3800000)citiesRef.where("country", "==", "USA").where("population", ">", 690000)// in and == clauses use the same indexcitiesRef.where("country", "in", ["USA", "Japan", "China"])         .where("population", ">", 690000)

These queries require the composite index below. Since the query usesan equality (== orin) for thecountry field, you can usean ascending or descending index mode for this field. By default,inequality clauses apply an ascending sort order based on the field in theinequality clause.

CollectionFields indexedQuery scope
cities (or) country, populationCollection

To run the same queries but with a descending sort order, youneed an additional composite index in the descending direction forpopulation:

Web
citiesRef.where("country", "==", "USA").orderBy("population", "desc")citiesRef.where("country", "==", "USA")         .where("population", "<", 3800000)         .orderBy("population", "desc")citiesRef.where("country", "==", "USA")         .where("population", ">", 690000)         .orderBy("population", "desc")citiesRef.where("country", "in", ["USA", "Japan", "China"])         .where("population", ">", 690000)         .orderBy("population", "desc")
CollectionFields indexedQuery scope
cities country, populationCollection
citiescountry,populationCollection

To avoid performance loss caused byindex merging, we recommend that you createa composite index to combine anarray-contains orarray-contains-any querywith additional clauses:

Web
citiesRef.where("regions","array-contains","east_coast").where("capital","==",true)// array-contains-any and array-contains use the same indexcitiesRef.where("regions","array-contains-any",["west_coast","east_coast"]).where("capital","==",true)
CollectionFields indexedQuery scope
citiesarray-contains tags, (or) capitalCollection

Queries supported by collection group indexes

To demonstrate an index with collection group scope, add alandmarks sub-collection to some of thecity documents:

Web
varcitiesRef=db.collection("cities");citiesRef.doc("SF").collection("landmarks").doc().set({name:"Golden Gate Bridge",category:"bridge"});citiesRef.doc("SF").collection("landmarks").doc().set({name:"Golden Gate Park",category:"park"});citiesRef.doc("DC").collection("landmarks").doc().set({name:"National Gallery of Art",category:"museum"});citiesRef.doc("DC").collection("landmarks").doc().set({name:"National Mall",category:"park"});

Using the following single-field index with collection scope, you can querya single city'slandmarks collection based on thecategory field:

CollectionFields indexedQuery scope
landmarks (or) categoryCollection
Web
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park")citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])

If you're interested in querying the landmarks across all cities, for example,you run this query on the collection group that consists of alllandmarkscollections. You must also enable alandmarks single-field index withcollection group scope:

CollectionFields indexedQuery scope
landmarks (or) categoryCollection group

With this index enabled, you can query thelandmarks collection group:

Web
varlandmarksGroupRef=db.collectionGroup("landmarks");landmarksGroupRef.where("category","==","park")landmarksGroupRef.where("category","in",["park","museum"])

To run a collection group query that returns filteredor ordered results, you must enable a corresponding single-field or compositeindex with collection group scope. Collection group queries that don't filteror order results, however, do not require any additional index definitions.

For example, you can run the following collection group query without enablingan additional index:

Web
db.collectionGroup("landmarks").get()

Index entries

Your project's configured indexes and the structure of a document determinethe number of index entries for a document. Index entries count towardstheindex entry count limit.

The following example demonstrates the index entries of a document.

Document

/cities/SF

city_name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]

Single-Field indexes

  • city_name ASC
  • city_name DESC
  • neighborhoods ASC
  • neighborhoods DESC
  • temperatures ASC
  • temperatures DESC
  • temperatures.summer ASC
  • temperatures.summer DESC
  • temperatures.winter ASC
  • temperatures.winter DESC
  • neighborhoods Array Contains

Composite indexes

  • city_name ASC, neighborhoods ARRAY
  • city_name DESC, neighborhoods ARRAY

Index entries

This indexing configuration results in the following index entries for thedocument:

IndexIndexed data
Single-field index entries
city_name ASCcity_name: "San Francisco"
city_name DESCcity_name: "San Francisco"
neighborhoods ASCneighborhoods: ["Mission", "Downtown", "Marina"]
neighborhoods DESCneighborhoods: ["Mission", "Downtown", "Marina"]
temperatures ASCtemperatures: {summer: 67, winter: 55}
temperatures DESCtemperatures: {summer: 67, winter: 55}
temperatures.summer ASCtemperatures.summer: 67
temperatures.summer DESCtemperatures.summer: 67
temperatures.winter ASCtemperatures.winter: 55
temperatures.winter DESCtemperatures.winter: 55
neighborhoods Array Containsneighborhoods: "Mission"
neighborhoods Array Containsneighborhoods: "Downtown"
neighborhoods Array Containsneighborhoods: "Marina"
Composite index entries
city_name ASC, neighborhoods ARRAYcity_name: "San Francisco", neighborhoods: "Mission"
city_name ASC, neighborhoods ARRAYcity_name: "San Francisco", neighborhoods: "Downtown"
city_name ASC, neighborhoods ARRAYcity_name: "San Francisco", neighborhoods: "Marina"
city_name DESC, neighborhoods ARRAYcity_name: "San Francisco", neighborhoods: "Mission"
city_name DESC, neighborhoods ARRAYcity_name: "San Francisco", neighborhoods: "Downtown"
city_name DESC, neighborhoods ARRAYcity_name: "San Francisco", neighborhoods: "Marina"

Indexes and pricing

Indexes contribute to thestorage costs of your application.For more information about how to calculate storage size for indexes, seeIndex entry size.

Use index merging

AlthoughCloud Firestore uses an index for every query, it doesn'tnecessarily require one index per query. For queries with multiple equality(==) clauses and, optionally, anorderBy clause,Cloud Firestore canre-use existing indexes.Cloud Firestore can merge the indexes for simpleequality filters to build the composite indexes needed for larger equalityqueries.

You can reduce indexing costs by identifying situations where you can use indexmerging. For example, in arestaurants collection for a restaurant rating app:

  • restaurants

    • burgerthyme

      name : "Burger Thyme"
      category : "burgers"
      city : "San Francisco"
      editors_pick : true
      star_rating : 4

This app uses queries like the following. The app uses combinations of equalityclauses forcategory,city, andeditors_pick while always sorting byascendingstar_rating:

Web
db.collection("restaurants").where("category", "==", "burgers")                            .orderBy("star_rating")db.collection("restaurants").where("city", "==", "San Francisco")                            .orderBy("star_rating")db.collection("restaurants").where("category", "==", "burgers")                            .where("city", "==", "San Francisco")                            .orderBy("star_rating")db.collection("restaurants").where("category", "==", "burgers")                            .where("city", "==" "San Francisco")                            .where("editors_pick", "==", true )                            .orderBy("star_rating")

You could create an index for each query:

CollectionFields indexedQuery scope
restaurants category, star_ratingCollection
restaurants city, star_ratingCollection
restaurants category, city, star_ratingCollection
restaurants category, city, editors_pick, star_ratingCollection

As a better solution, you can reduce the numberof indexes by taking advantage ofCloud Firestore's ability to mergeindexes for equality clauses:

CollectionFields indexedQuery scope
restaurants category, star_ratingCollection
restaurants city, star_ratingCollection
restaurants editors_pick, star_ratingCollection

Not only is this set of indexes smaller, it also supports an additional query:

Web
db.collection("restaurants").where("editors_pick", "==", true)                            .orderBy("star_rating")

Indexing limits

The following limits apply to indexes. For more information about quotas and limits, seeQuotas and Limits.

LimitDetails
Maximum number of composite indexes for a database
Maximum number of single-field configurations for a database

One field level configuration can contain multiple configurations for the same field. For example, a single-field indexing exemption and a TTL policy on the same field count as one field configuration towards the limit.

Maximum number of index entries for each document

40,000

The number of index entries is the sum of the following for a document:

  • The number of single-field index entries
  • The number of composite index entries

To see howCloud Firestore turns a document and a set of indexes into index entries, seethis index entry count example.

Maximum number of fields in a composite index100
Maximum size of an index entry

7.5 KiB

To see howCloud Firestore calculates index entry size, seeindex entry size.

Maximum sum of the sizes of a document's index entries

8 MiB

The total size is the sum of the following for a document:

  • The sum of the size of a document's single-field index entries
  • The sum of the size of a document's composite index entries
  • Maximum size of an indexed field value

    1500 bytes

    Field values over 1500 bytes are truncated. Queries involving truncated field values may return inconsistent results.

    Indexing best practices

    For most apps, you can rely on automatic indexing and the error message links tomanage your indexes. However, you may want to add single-field exemptions in thefollowing cases:

    CaseDescription
    Large string fields

    If you have a string field that often holds long string values that you don't use for querying, you can cut storage costs by exempting the field from indexing.

    High write rates to a collection containing documents with sequential values

    If you index a field that increases or decreases sequentially between documents in a collection, like a timestamp, then the maximum write rate to the collection is 500 writes per second. If you don't query based on the field with sequential values, you can exempt the field from indexing to bypass this limit.

    In an IoT use case with a high write rate, for example, a collection containing documents with a timestamp field might approach the 500 writes per second limit.

    TTL fields

    If you useTTL (time-to-live) policies, note that the TTL field must be a timestamp. Indexing on TTL fields is enabled by default and can affect performance at higher traffic rates. As a best practice, add single-field exemptions for your TTL fields.

    Large array or map fields

    Large array or map fields can approach the limit of 40,000 index entries per document. If you are not querying based on a large array or map field, you should exempt it from indexing.

    If you are using queries with range and inequality operators on multiple fields, see theindexingconsiderations that you should consider to optimize theperformance and cost ofCloud Firestore queries

    For more information about how to resolve indexing issues (index fanout,INVALID_ARGUMENT errors) see thetroubleshooting page.

    Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

    Last updated 2026-02-18 UTC.