# Firestore Indexes Reference Indexes allow Firestore to ensure that query performance depends on the size of the result set, not the size of the database. ## Index Types ### Single-Field Indexes In Standard Edition, Firestore **automatically creates** a single-field index for every field in a document (and subfields in maps). * **Support**: Simple equality queries (`==`) and single-field range/sort queries (`<`, `<=`, `orderBy`). * **Behavior**: You generally don't need to manage these unless you want to *exempt* a field. ### Composite Indexes A composite index stores a sorted mapping of all documents based on an ordered list of fields. * **Support**: Complex queries that filter or sort by **multiple fields**. * **Creation**: These are **NOT** automatically created. You must define them manually or via the console/CLI. ## Automatic vs. Manual Management ### What is Automatic? - Indexes for simple queries. - Merging of single-field indexes for multiple equality filters (e.g., `where("state", "==", "CA").where("country", "==", "USA")`). ### When Do I Need to Act? If you attempt a query that requires a composite index, the SDK will throw an error containing a **direct link** to the Firebase Console to create that specific index. **Example Error:** > "The query requires an index. You can create it here: > https://console.firebase.google.com/project/..." ## Query Support Examples | Query Type | Index Required | | :-------------------------------------------------------- | :----------------------------------- | | **Simple Equality**
\`where("a", | Automatic (Single-Field) | | : "==", 1)\` : : | | | **Simple Range/Sort**
\`where("a", | Automatic (Single-Field) | | : ">", 1).orderBy("a")\` : : | | | **Multiple Equality**
\`where("a", | Automatic (Merged Single-Field) | | : "==", 1).where("b", "==", 2)\` : : | | | \*\*Equality + | **Composite Index** | | : Range/Sort\*\*
\`where("a", "==", : : | | | : 1).where("b", ">", 2)\` : : | | | **Multiple Ranges**
\`where("a", | **Composite Index** (and technically | | : ">", 1).where("b", ">", 2)\` : limited query support) : | | | \*\*Array Contains + | **Composite Index** | | : Equality\*\*
\`where("tags", : : | | | : "array-contains", : : | | | : "news").where("active", "==", true)\` : : | | ## Best Practices & Exemptions You can **exempt** fields from automatic indexing to save storage or strictly enforce write limits. ### 1. High Write Rates (Sequential Values) - **Problem**: Indexing fields that increase sequentially (like `timestamp`) limits the write rate to ~500 writes/second per collection. - **Solution**: If you don't query on this field, **exempt** it from simple indexing. ### 2. Large String/Map/Array Fields - **Problem**: Indexing limits (40k entries per doc). Indexing large blobs wastes storage. - **Solution**: Exempt large text blobs or huge arrays if they aren't used for filtering. ### 3. TTL Fields - **Problem**: TTL (Time-To-Live) deletion can cause index churn. - **Solution**: Exempt the TTL timestamp field from indexing if you don't query it. ## Management ### Config files Your indexes should be defined in `firestore.indexes.json` (pointed to by `firebase.json`). ```json { "indexes": [ { "collectionGroup": "cities", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "country", "order": "ASCENDING" }, { "fieldPath": "population", "order": "DESCENDING" } ] } ], "fieldOverrides": [] } ``` ### CLI Commands Deploy indexes only: `bash npx -y firebase-tools@latest deploy --only firestore:indexes`