Indexes helps to improve query performance. Firestore Enterprise edition does not create any indexes by default. By default, Firestore Enterprise performs a full collection scan to find documents that match a query, which can be slow and expensive for large collections. To avoid this, you can create indexes to optimize your queries.
An index consists of the following:
- a collection ID.
- a list of fields in the given collection.
- an order, either ascending or descending, for each field.
The order and sort direction of each field uniquely defines the index. For example, the following indexes are two distinct indexes and not interchangeable:
- Field name
name(ascending) andpopulation(descending) - Field name
name(descending) andpopulation(ascending)
Dense indexes: By default, Firestore indexes store data from all documents in a collection. An index entry will be added for a document regardless of whether the document contains any of the fields specified in the index. Non-existent fields are treated as having a NULL value when generating index entries.
Sparse indexes: To change this behavior, you can define the index as a sparse index. A sparse index indexes only the documents in the collection that contain a value (including null) for at least one of the indexed fields. A sparse index reduces storage costs and can improve performance.
You can use unique index option to enforce unique values for the indexed fields. For indexes on multiple fields, each combination of values must be unique across the index. The database rejects any update and insert operations that attempt to create index entries with duplicate values.
| Query Type | Index Required |
|---|---|
Simple Equalitywhere("a", "==", 1) |
Single-Field Index on field a |
Simple Range/Sortwhere("a", ">", 1).orderBy("a") |
Single-Field Index on field a |
Multiple Equalitywhere("a", "==", 1).where("b", "==", 2) |
Single-Field Index on field a and b |
Equality + Range/Sortwhere("a", "==", 1).where("b", ">", 2) |
Composite Index on field a and b |
Multiple Rangeswhere("a", ">", 1).where("b", ">", 2) |
Composite Index on field a and b |
Array Contains + Equalitywhere("tags", "array-contains", "news").where("active", "==", true) |
Composite Index on field tags and active |
If no indexes is present, Firestore Enterprise will perform a full collection scan to find documents that match a query.
Your indexes should be defined in firestore.indexes.json (pointed to by firebase.json).
Define a dense index:
{
"indexes": [
{
"collectionGroup": "cities",
"queryScope": "COLLECTION",
"density": "DENSE",
"fields": [
{ "fieldPath": "country", "order": "ASCENDING" },
{ "fieldPath": "population", "order": "DESCENDING" }
]
}
],
"fieldOverrides": []
}Define a sparse-any index:
{
"indexes": [
{
"collectionGroup": "cities",
"queryScope": "COLLECTION",
"density": "SPARSE_ANY",
"fields": [
{ "fieldPath": "country", "order": "ASCENDING" },
{ "fieldPath": "population", "order": "DESCENDING" }
]
}
],
"fieldOverrides": []
}Define a unique index:
{
"indexes": [
{
"collectionGroup": "cities",
"queryScope": "COLLECTION",
"density": "SPARSE_ANY",
"unique": true,
"fields": [
{ "fieldPath": "country", "order": "ASCENDING" },
{ "fieldPath": "population", "order": "DESCENDING" }
]
}
],
"fieldOverrides": []
}Deploy indexes only:
npx firebase-tools@latest -y deploy --only firestore:indexes