Episode 12 of 17

Indexing in MongoDB

Master MongoDB indexing — the single most important performance optimization. Learn single-field, compound, multikey, text, and TTL indexes. Understand index strategies, explain() for query analysis, and when to use (and avoid) indexes.

Indexes are the single most important factor in MongoDB performance. Without indexes, MongoDB performs a collection scan — reading every document to find matches. With the right indexes, queries go from scanning millions of documents to near-instant lookups.

Why Indexes Matter

// Without index: scans ALL documents (slow on large collections)
db.users.find({ email: "alice@example.com" })
// With 1 million users, this reads ALL 1 million documents

// With index: direct lookup (fast)
db.users.createIndex({ email: 1 })
db.users.find({ email: "alice@example.com" })
// Now this reads only 1 document — indexed lookup

Creating Indexes

Single-Field Index

// Ascending index on email
db.users.createIndex({ email: 1 })

// Descending index on createdAt
db.posts.createIndex({ createdAt: -1 })

// Unique index (prevents duplicates)
db.users.createIndex({ email: 1 }, { unique: true })

Compound Index

// Index on multiple fields
db.products.createIndex({ category: 1, price: -1 })

// This index supports queries on:
// 1. { category: "electronics" }                    — uses index
// 2. { category: "electronics", price: { $gt: 500 }} — uses index
// 3. { price: { $gt: 500 } }                        — does NOT use this index
// The order of fields in a compound index matters!

Multikey Index (Arrays)

// MongoDB automatically creates multikey indexes for array fields
db.posts.createIndex({ tags: 1 })

// Now you can efficiently query:
db.posts.find({ tags: "mongodb" })
db.posts.find({ tags: { $in: ["mongodb", "nosql"] } })

Text Index

// Create a text index for full-text search
db.posts.createIndex({ title: "text", content: "text" })

// Search for text
db.posts.find({ $text: { $search: "mongodb tutorial" } })

// Search with relevance score
db.posts.find(
    { $text: { $search: "mongodb tutorial" } },
    { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })

TTL Index

// Automatically delete documents after a specified time
db.sessions.createIndex(
    { createdAt: 1 },
    { expireAfterSeconds: 3600 }  // Delete after 1 hour
)

Sparse Index

// Only index documents that HAVE the indexed field
db.users.createIndex(
    { phone: 1 },
    { sparse: true }  // Documents without "phone" field are not indexed
)

Partial Index

// Index only documents matching a condition
db.orders.createIndex(
    { status: 1 },
    {
        partialFilterExpression: {
            status: { $in: ["processing", "shipped"] }
        }
    }
)
// Only indexes active orders, not completed/cancelled ones
// Smaller index = faster queries and less storage

Viewing Indexes

// List all indexes on a collection
db.users.getIndexes()

// Output:
// [
//   { v: 2, key: { _id: 1 }, name: '_id_' },           // Default
//   { v: 2, key: { email: 1 }, name: 'email_1', unique: true },
//   { v: 2, key: { role: 1, name: 1 }, name: 'role_1_name_1' }
// ]

Dropping Indexes

// Drop a specific index by name
db.users.dropIndex("email_1")

// Drop all indexes (except _id)
db.users.dropIndexes()

// Drop a specific index by key pattern
db.users.dropIndex({ email: 1 })

explain() — Analyzing Queries

The explain() method shows how MongoDB executes a query:

// Basic execution stats
db.users.find({ email: "alice@example.com" }).explain("executionStats")

Key things to look for in explain output:

  • COLLSCAN — Collection scan (bad — no index used)
  • IXSCAN — Index scan (good — using an index)
  • totalDocsExamined — Documents read (lower is better)
  • totalKeysExamined — Index entries read
  • executionTimeMillis — Query time in milliseconds
// Example: checking if a query uses an index
db.users.find({ role: "developer", age: { $gt: 25 } })
    .explain("executionStats")

// If winningPlan.stage is "COLLSCAN", you need an index
// If winningPlan.stage is "IXSCAN", the index is being used

Index Strategy Best Practices

The ESR Rule for compound indexes:

  1. Equality fields first — Fields with exact match (status: "active")
  2. Sort fields next — Fields used in sort()
  3. Range fields last — Fields with range queries ($gt, $lt)
// Query: active users, sorted by name, age > 25
db.users.find({ status: "active", age: { $gt: 25 } }).sort({ name: 1 })

// Optimal index (ESR):
db.users.createIndex({ status: 1, name: 1, age: 1 })
// Equality(status) → Sort(name) → Range(age)

When NOT to Index

  • Small collections (under 1000 documents) — scans are fast enough
  • Fields with very low cardinality (e.g., boolean fields with 50/50 distribution)
  • Write-heavy collections where reads are rare — each index slows down writes
  • Too many indexes — each index uses memory and slows inserts/updates

What's Next

Indexes are the foundation of MongoDB performance. In the next episode, we'll explore the aggregation framework — MongoDB's powerful data processing pipeline for grouping, transforming, and analyzing data.

TutorialMongoDBDatabaseNoSQL