Skip to content

Firestore Indexes Deep Dive — Composite Indexes, Query Performance, and Optimization

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Firestore Indexes Deep Dive. We cover key concepts, practical examples, and best practices to help you master this topic.

Firestore indexes are data structures that enable efficient query execution. Single-field indexes are automatic, while Composite indexes must be explicitly created for compound queries involving multiple fields.

What You'll Learn

  • How indexes enable fast Firestore queries
  • Creating and managing composite indexes
  • Optimizing index strategies for performance

Why It Matters

Proper index design is critical for Firestore query performance and cost. Missing indexes cause query failures; excessive indexes slow writes. DodaTech's Firestore databases are optimized with carefully selected composite indexes.

flowchart TD
    subgraph "Index Types"
        SI["Single-Field Indexes"]
        CI["Composite Indexes"]
    end
    SI -->|"Automatic"| Q1["Simple queries"]
    CI -->|"Manual"| Q2["Compound queries"]
    subgraph "Cost"
        R["Reads: Faster queries"]
        W["Writes: Slower per write"]
    end
    CI --> R
    CI --> W
    style SI fill:#dbeafe,stroke:#2563eb
    style CI fill:#fef08a,stroke:#ca8a04

Code Examples

// Understanding when indexes are needed
// Single-field index: Automatic
const q1 = query(
  collection(db, 'users'),
  where('role', '==', 'admin')
);

// Composite index needed: (role, createdAt)
const q2 = query(
  collection(db, 'users'),
  where('role', '==', 'admin'),
  orderBy('createdAt', 'desc')
);

// Composite index needed: (isActive, score)
const q3 = query(
  collection(db, 'users'),
  where('isActive', '==', true),
  where('score', '>=', 100),
  orderBy('score', 'desc')
);
// firestore.indexes.json - Full configuration
{
  "indexes": [
    {
      "collectionGroup": "users",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "role", "order": "ASCENDING" },
        { "fieldPath": "createdAt", "order": "DESCENDING" }
      ]
    },
    {
      "collectionGroup": "orders",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "status", "order": "ASCENDING" },
        { "fieldPath": "total", "order": "ASCENDING" },
        { "fieldPath": "createdAt", "order": "DESCENDING" }
      ]
    },
    {
      "collectionGroup": "products",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "order": "ASCENDING" },
        { "fieldPath": "price", "order": "ASCENDING" }
      ]
    }
  ],
  "fieldOverrides": [
    {
      "collectionGroup": "logs",
      "fieldPath": "message",
      "indexes": [
        { "order": "ASCENDING", "queryScope": "COLLECTION" },
        { "order": "DESCENDING", "queryScope": "COLLECTION" }
      ]
    }
  ]
}
# Manage indexes via CLI
# List current indexes
firebase firestore:indexes

# Export indexes to JSON
firebase firestore:indexes > firestore.indexes.json

# Deploy indexes
firebase deploy --only firestore:indexes

# Delete an index (via config file and redeploy)
# Remove from firestore.indexes.json and redeploy
# Monitoring index usage
from google.cloud import firestore_admin_v1

client = firestore_admin_v1.FirestoreAdminClient()

# List indexes for a database
parent = client.database_path('project-id', '(default)')
indexes = client.list_indexes(parent=parent)

for index in indexes:
    fields = [
        f'{f.field_path}:{f.order.name}'
        for f in index.fields
    ]
    print(f'{index.name}: {", ".join(fields)}')

Common Mistakes

1. Creating Composite Indexes for Every Possible Query

More indexes = slower writes. Index only queries your application actually uses.

2. Not Deleting Unused Indexes

Review and remove indexes that are no longer used by any query.

3. Overlooking Collection Group Indexes

Queries on subcollections need collection group indexes, not regular indexes.

4. Using the Wrong Field Order in Composite Indexes

The field order must match the query: equality fields first, range fields second, orderBy last.

5. Ignoring Index Size Limits

Index entries contribute to document size. Large indexed fields increase storage costs.

Practice Questions

  1. What indexes are created automatically in Firestore?
  2. How many fields can a composite index contain?
  3. What happens when you run a query without a required index?
  4. How do you create a composite index for a collection group query?
  5. How do you delete an unused index?

Answers:

  1. Single-field indexes for every field in every document.
  2. Up to 8 fields per composite index.
  3. The query fails with an error and provides a link to create the required index.
  4. Set queryScope to COLLECTION_GROUP in the index definition.
  5. Remove it from firestore.indexes.json and redeploy, or delete it in the Console.

Challenge: Analyze your Firestore application's query patterns. Identify all compound queries, create the required composite indexes, remove unused indexes, and measure the performance improvement. Generate an index optimization report.

FAQ

What is the difference between ASCENDING and DESCENDING indexes?

ASCENDING stores values low to high; DESCENDING stores high to low. Firestore can reverse ASCENDING indexes for DESCENDING queries, but performance may suffer.

How many composite indexes can I create?

The default limit is 200 composite indexes per database. You can request an increase via Google Cloud support.

Do composite indexes increase storage costs?

Yes. Each composite index entry takes storage space. The cost is proportional to the number of documents and the number of indexed fields.

How do index exemptions work?

Use fieldOverrides in firestore.indexes.json to exclude specific fields from indexing, reducing write latency for large fields that are never queried.

Can I create indexes through the Firebase Console?

Yes. Go to Console > Firestore > Indexes to view, create, and delete indexes manually.

Mini Project

Build a Firestore index optimizer: analyze your application's query patterns by scanning source code, generate the optimal set of composite indexes, compare against existing indexes, produce a diff report, and automatically update firestore.indexes.json.

What's Next

Learn about real-time listeners with onSnapshot for live data updates, then explore batch writes for bulk operations.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro