Section 6: Intermediate MongoDB

🚀 MongoDB Indexing Mastery

Transform your queries from slow to lightning-fast! Master indexes with visual animations, real-world scenarios, and hands-on examples

📖 The Million-User Speed Crisis

Meet Alex, a backend developer at TechStart, a rapidly growing social media app. Everything was great when they had 10,000 users. Their MongoDB queries returned results in milliseconds. Life was good! 😊

❌ The Crisis Hits - 1 Million Users!

One Monday morning, Alex's phone explodes with alerts. The app is CRAWLING! 🐌

25 sec
Login Time
(was 50ms)
40 sec
Search Time
(was 100ms)
60 sec
Profile Load
(was 30ms)

The Problem: MongoDB was scanning through 1 MILLION documents for every query! Like reading every single book in a library to find one specific book! 📚💀

Then Alex discovered MongoDB Indexes... 🎉

✅ After Adding Indexes

Alex added just 3 strategic indexes. The results were MAGICAL! ✨

15 ms
Login Time
1,666x FASTER! 🚀
20 ms
Search Time
2,000x FASTER! 🚀
10 ms
Profile Load
6,000x FASTER! 🚀

The Magic: Instead of scanning 1 million documents, MongoDB now uses indexes to jump DIRECTLY to the exact documents needed! Like using a book's index to find page numbers instantly! 🎯

Let's learn how Alex did it, and how YOU can speed up your queries by 1000x! 🚀

🎯 What Are Indexes?

An index is a special data structure that stores a small portion of your data in an easy-to-traverse form. Think of it like a book's index - instead of reading every page to find "MongoDB", you check the index and jump directly to page 247! 📖

💡 Real-World Analogy

❌ WITHOUT Index (Collection Scan)

Scenario: Finding "John Smith" in a phone book with NO alphabetical order.

  • Read EVERY SINGLE PAGE
  • Check EVERY SINGLE NAME
  • 1 million names = 1 million checks!
  • Time: Hours!

MongoDB scans EVERY document 😰

✅ WITH Index (Index Scan)

Scenario: Finding "John Smith" in a phone book WITH alphabetical order.

  • Open to section "S"
  • Find "Smith"
  • Find "John Smith"
  • Time: Seconds!

MongoDB jumps DIRECTLY to data! 🚀

📊 Key Benefits of Indexes

⚡

Lightning-Fast Queries

Transform 30-second queries into 10ms responses. 1000x+ speed improvements are common!

🎯

Efficient Sorting

Pre-sorted index data makes ORDER BY operations instant instead of expensive in-memory sorts.

💾

Reduced RAM Usage

Only load needed documents into memory instead of scanning entire collections.

📈

Better Scalability

Performance stays consistent as your data grows from thousands to millions of documents.

🔬 How Indexes Work: B-Tree Structure

MongoDB uses B-Tree (Balanced Tree) data structures for indexes. Let's visualize how they work!

📊 B-Tree Index Structure (Age Field)
ROOT 50 < 50 25 >= 50 75 LEAF age: 18 → doc1 age: 22 → doc2 age: 24 → doc3 LEAF age: 28 → doc4 age: 35 → doc5 age: 42 → doc6 LEAF age: 52 → doc7 age: 58 → doc8 age: 63 → doc9 LEAF age: 78 → doc10 age: 82 → doc11 age: 89 → doc12 🔍 Query: age = 28 1. Start at ROOT: 28 < 50 → go LEFT 2. At node 25: 28 >= 25 → go RIGHT 3. Found in LEAF! Return doc4 ✓
🎯 Key Points About B-Trees:
  • Balanced: All leaf nodes are at the same depth - ensures consistent performance
  • Sorted: Data is stored in sorted order - enables binary search (fast!)
  • Logarithmic Time: Finding data takes O(log n) time instead of O(n)
  • Example: 1 million documents = only ~20 comparisons needed! vs 1 million without index!
  • Leaf Nodes: Contain pointers to actual documents in collection

⚡ Query Performance: With vs Without Index

Let's see the DRAMATIC difference indexes make! 🚀

📊 Query: db.users.find({ age: 28 })
❌ WITHOUT Index (COLLSCAN) ✅ WITH Index (IXSCAN) age: 18 age: 22 age: 24 age: 28 ✓ age: 35 ... age: 89 ❌ Collection Scan (COLLSCAN) Documents Examined: 1,000,000 Time: 25,000 ms (25 sec) RAM Used: 500 MB Scans EVERY document! 😰 Linear search O(n) Index Node Jump! Index Node Jump! FOUND! ✓ age: 28 ⚡ ✅ Index Scan (IXSCAN) Documents Examined: 1 Time: 15 ms RAM Used: 0.5 MB Direct jump to data! 🚀 Binary search O(log n) ⚡ SPEEDUP 1,666x 25 sec → 15 ms RAM: 1000x less! 500 MB → 0.5 MB

💡 The Mathematics of Speed

Without Index

Linear Search O(n):
• 1,000 docs = 1,000 checks
• 1,000,000 docs = 1,000,000 checks
• 10x data = 10x slower! 😰

With Index

Binary Search O(log n):
• 1,000 docs = ~10 checks
• 1,000,000 docs = ~20 checks
• 10x data = +3 checks! 🚀

🗂️ Types of Indexes in MongoDB

1️⃣

Single Field Index

Index on one field. Most common and simple.

db.users.createIndex({ age: 1 })

Use: Queries on single field

2️⃣

Compound Index

Index on multiple fields. Field order matters!

db.users.createIndex({
  city: 1, age: 1
})

Use: Queries on multiple fields

3️⃣

Multikey Index

Index on array fields. Creates index entry per array element.

db.posts.createIndex({ tags: 1 })

Use: Queries on array elements

4️⃣

Text Index

Full-text search. Tokenizes and stems words.

db.articles.createIndex({
  content: "text"
})

Use: Text search queries

5️⃣

Geospatial Index

Index for geographic data. 2d or 2dsphere.

db.places.createIndex({
  location: "2dsphere"
})

Use: Location-based queries

6️⃣

Hashed Index

Hashes field value. For sharding and equality.

db.users.createIndex({
  userId: "hashed"
})

Use: Shard keys, equality only

🖥️ Live Console Demo

Experience the MASSIVE speed difference! Click the buttons to simulate real MongoDB operations.

Interactive MongoDB Console
MongoDB Shell v6.0.0
Connected to: localhost:27017

💡 Try these commands to see index magic in action!

📊 What You'll See

Watch how the same query goes from 25 seconds (COLLSCAN) without an index to 15 milliseconds (IXSCAN) with an index! This demonstrates a 1,666x speedup - the real power of indexing!

🛠️ Creating and Managing Indexes

📝 Basic Index Creation

Create Single Field Index
// Create ascending index on age field
db.users.createIndex({ age: 1 })

// Create descending index
db.users.createIndex({ age: -1 })

// 1 = ascending order (18, 22, 25, 28...)
// -1 = descending order (89, 82, 78, 75...)

// MongoDB automatically creates an index on _id field!

⚡ Index Options

Unique Index
// Ensure email is unique across all documents
db.users.createIndex(
  { email: 1 },
  { unique: true }
)

// Prevents duplicate emails!
// Insert with duplicate email will fail with error
Sparse Index
// Only index documents that have the field
db.users.createIndex(
  { phoneNumber: 1 },
  { sparse: true }
)

// Documents without phoneNumber are NOT in index
// Saves space if field is optional!
TTL Index (Time To Live)
// Auto-delete documents after expiration
db.sessions.createIndex(
  { createdAt: 1 },
  { expireAfterSeconds: 3600 }  // Delete after 1 hour
)

// Perfect for sessions, logs, temporary data!
// MongoDB deletes expired documents automatically
Partial Index
// Index only documents matching a condition
db.orders.createIndex(
  { customerId: 1, orderDate: 1 },
  { 
    partialFilterExpression: { 
      status: "active" 
    }
  }
)

// Only indexes active orders!
// Saves space, faster for common queries

🔍 Viewing and Managing Indexes

List and Manage Indexes
// List all indexes on collection
db.users.getIndexes()

// Drop specific index
db.users.dropIndex("age_1")

// Drop index by specification
db.users.dropIndex({ age: 1 })

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

// Get index stats
db.users.stats()

// Check if query uses index
db.users.find({ age: 28 }).explain("executionStats")

🔗 Compound Indexes: Multiple Fields

Compound Indexes contain references to multiple fields. They're MORE POWERFUL than multiple single-field indexes! The order of fields matters!

Creating Compound Index
// Create compound index: city THEN age
db.users.createIndex({ city: 1, age: 1 })

// This index can efficiently support:
// ✅ { city: "New York" }
// ✅ { city: "New York", age: 28 }
// ✅ { city: "New York", age: { $gt: 25 } }

// But NOT efficiently:
// ❌ { age: 28 } // Doesn't use city (first field)

// Field order matters! Think "phone book": 
// Last name THEN first name works
// But finding by first name alone is slow!
📊 Compound Index Structure: { city: 1, age: 1 }
Compound Index: Sorted by City THEN Age Los Angeles age: 22 → doc1 age: 28 → doc2 New York age: 18 → doc3 age: 25 → doc4 age: 35 → doc5 age: 42 → doc6 San Francisco age: 30 → doc7 age: 45 → doc8 ✅ Efficient Queries (Uses Index) 1. { city: "New York" } 2. { city: "New York", age: 25 } 3. { city: "New York", age: { $gt: 30 } } City is first field → Index is used! Finds city section, then scans ages within it ❌ Inefficient Queries (Can't Use Index) 1. { age: 25 } 2. { age: { $gt: 30 } } 3. { age: 25, city: "New York" } City not specified → Full scan needed! Can't skip cities, must check all!
🎯 Compound Index Rules:
  • Left-to-Right Rule: Index can be used if query starts with the FIRST field(s) in index
  • Index { a: 1, b: 1, c: 1 } supports: {a}, {a,b}, {a,b,c} - but NOT {b}, {c}, {b,c}
  • Order matters for sorting: Sort order must match index order for optimal performance
  • ESR Rule: Equality fields first, Sort fields next, Range fields last
  • Max 32 fields: But keep it practical - usually 2-4 fields max!

💡 Compound Index Best Practices

ESR Rule Example
// Query: Find active users in NYC, sorted by age
db.users.find({
  city: "New York",    // Equality
  status: "active",    // Equality
  age: { $gt: 25 }     // Range
}).sort({ age: 1 })    // Sort

// BEST index follows ESR rule:
// E (Equality) → S (Sort) → R (Range)
db.users.createIndex({
  city: 1,      // E: Equality first
  status: 1,    // E: Equality second
  age: 1        // S & R: Sort/Range last
})

// This index handles ALL parts of the query efficiently!

🎮 Interactive: Test Query Coverage

Index: { city: 1, age: 1, status: 1 }

Query Examples:
Result:
Click a query to test if it can use the index!

🔄 Query Execution Flow: Step-by-Step

Let's visualize how MongoDB executes a query WITH and WITHOUT an index!

Query: { age: 28 } ❌ WITHOUT Index Step 1: Query Parser Analyzes query structure Step 2: Check Indexes ❌ No index found! Step 3: COLLSCAN Scan ALL documents 1,000,000 documents! Step 4: Return Results Found: 5 documents Time: 25,000 ms 😰 ✅ WITH Index Step 1: Query Parser Analyzes query structure Step 2: Check Indexes ✅ Index found: age_1 Step 3: IXSCAN Navigate B-Tree index Only ~20 checks! ⚡ Step 4: Return Results Found: 5 documents Time: 15 ms 🚀 1,666x FASTER!

🎯 Index Selection Decision Flow

How does MongoDB choose which index to use? Follow the decision tree!

START Does collection have indexes? NO COLLSCAN Full collection scan O(n) - Slow! 🐌 YES Query fields match an index? NO COLLSCAN Can't use index Still slow! ⚠️ YES Multiple indexes match query? NO (1 index) IXSCAN Use the index! O(log n) - Fast! 🚀 YES (multiple) Query Planner Evaluate & choose best IXSCAN (Best Index) Most selective index Optimal performance! ⚡
🧠 How Query Planner Chooses:
  • Selectivity: Index that narrows down results most (fewest docs examined)
  • Index size: Smaller indexes are faster to traverse
  • Sort compatibility: Index that matches sort order avoids in-memory sort
  • Covered query: Index that contains all fields needed (no doc fetch)
  • Historical performance: MongoDB learns from past query execution!

📊 Real-Time Performance Comparison

❌ WITHOUT Index

25s Response Time
Documents Scanned: 1,000,000
Index Used: None
Execution Stage: COLLSCAN

✅ WITH Index

15ms Response Time
Documents Scanned: 5
Index Used: age_1
Execution Stage: IXSCAN
⚡ PERFORMANCE IMPROVEMENT
1,666x
From 25,000ms to 15ms - Same query, MASSIVE difference!

🔢 Multikey Indexes: Indexing Arrays

Multikey Indexes automatically index array elements. MongoDB creates an index entry for EACH array element! Perfect for tags, categories, skills arrays.

Creating Multikey Index
// Sample document with array
{
  "_id": 1,
  "name": "John",
  "tags": ["mongodb", "database", "nosql"],
  "skills": ["javascript", "python", "react"]
}

// Create index on array field
db.users.createIndex({ tags: 1 })

// MongoDB automatically detects array and creates multikey index!
// Creates index entries:
// - "mongodb" → doc 1
// - "database" → doc 1
// - "nosql" → doc 1

// Now queries are FAST:
db.users.find({ tags: "mongodb" })  // Uses index!
db.users.find({ tags: { $in: ["mongodb", "database"] } })  // Uses index!
📊 How Multikey Index Works
Original Documents Document 1 name: "Alice" tags: ["mongodb", "database"] Document 2 name: "Bob" tags: ["nosql", "mongodb"] Document 3 name: "Charlie" tags: ["database", "sql"] Index Multikey Index on "tags" "database" → doc1, doc3 "mongodb" → doc1, doc2 "nosql" → doc2 "sql" → doc3 Each array element gets its own index entry! Query: { tags: "mongodb" } Index lookup: "mongodb" → Returns doc1 & doc2 instantly! 🚀
⚠️ Multikey Index Limitations:
  • One multikey field per compound index: Can't have { arr1: 1, arr2: 1 } if both are arrays
  • Larger index size: More array elements = more index entries = more storage
  • Slower writes: Updating array updates multiple index entries
  • Best for: Tags, categories, skills - arrays with reasonable size (< 100 elements)

🔤 Text Indexes: Full-Text Search

Text Indexes enable powerful full-text search! They tokenize words, remove stop words, stem words, and support multiple languages. Think "Google search" for your MongoDB data!

Creating Text Index
// Create text index on single field
db.articles.createIndex({ content: "text" })

// Create text index on multiple fields
db.articles.createIndex({
  title: "text",
  content: "text",
  tags: "text"
})

// Search using text index
db.articles.find({ $text: { $search: "mongodb database" } })

// Search with phrases
db.articles.find({ $text: { $search: "\"full text search\"" } })

// Exclude words with minus
db.articles.find({ $text: { $search: "mongodb -sql" } })

// Sort by text score (relevance)
db.articles.find(
  { $text: { $search: "mongodb" } },
  { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })
✨ Text Index Features:
  • Tokenization: "Hello World" → ["hello", "world"]
  • Stop Words: Removes "the", "is", "at", etc.
  • Stemming: "running", "runs" → "run"
  • Case-insensitive: "MongoDB" = "mongodb" = "MONGODB"
  • Relevance Scoring: Returns most relevant results first
  • Multi-language: Supports 15+ languages!
  • Only one per collection: Can have only ONE text index

🎯 Index Strategy & Best Practices

✅ DO's - Follow These

  • Index query filters: Fields in WHERE clauses
  • Index sort fields: Fields in ORDER BY
  • Use compound indexes: Better than multiple single indexes
  • Follow ESR rule: Equality, Sort, Range
  • Monitor with explain(): Check if index is used
  • Index selectivity: High-cardinality fields first
  • Analyze slow queries: Profile and optimize

❌ DON'Ts - Avoid These

  • Don't over-index: Too many = slow writes
  • Don't index low-cardinality: e.g., gender (M/F)
  • Don't ignore RAM: Indexes must fit in RAM
  • Don't create duplicate: Remove unused indexes
  • Don't index everything: Balance reads vs writes
  • Don't forget maintenance: Rebuild fragmented indexes
  • Don't ignore order: Wrong field order = wasted index

📊 Query Analysis with explain()

Analyzing Query Performance
// Check if query uses index
db.users.find({ age: 28 }).explain("executionStats")

// Key fields to check in output:
// {
//   "executionStats": {
//     "executionSuccess": true,
//     "nReturned": 5,              // Documents returned
//     "executionTimeMillis": 2,    // Query time
//     "totalDocsExamined": 5,      // Docs scanned
//     "totalKeysExamined": 5,      // Index entries scanned
//     "executionStages": {
//       "stage": "FETCH",
//       "inputStage": {
//         "stage": "IXSCAN",        // ✅ Using index!
//         "indexName": "age_1"
//       }
//     }
//   }
// }

// BAD signs:
// - "stage": "COLLSCAN" → Full collection scan!
// - totalDocsExamined >> nReturned → Scanning too many docs
// - High executionTimeMillis → Slow query

// GOOD signs:
// - "stage": "IXSCAN" → Using index!
// - totalDocsExamined ≈ nReturned → Efficient
// - Low executionTimeMillis → Fast query

⚡ Performance Optimization Tips

💾

Keep Indexes in RAM

Indexes should fit entirely in RAM for best performance. Monitor index size with db.collection.stats().

Formula: RAM ≥ Index Size + Working Set
🎯

Index Selectivity

Index high-cardinality fields (many unique values). Low cardinality (e.g., boolean) = inefficient.

Good: email, userId
Bad: gender, isActive
⚖️

Read/Write Balance

More indexes = faster reads but slower writes. Find the right balance for your workload.

Read-heavy: More indexes OK
Write-heavy: Fewer indexes
🔄

Covered Queries

Query returns ONLY indexed fields = ultra-fast! No document fetch needed.

find({age: 28}, {age: 1, _id: 0})
📊

Index Intersection

MongoDB can use multiple indexes for one query (v2.6+), but compound index is usually faster.

Prefer: Single compound index
Over: Multiple single indexes
🧹

Remove Unused Indexes

Regularly audit indexes. Remove those never used - they waste RAM and slow writes.

db.collection.aggregate([{$indexStats:{}}])

🎯 Golden Rules of Indexing

1. Index your queries: Every field in WHERE, ORDER BY, JOIN should be considered for indexing

2. Compound > Multiple: One compound index beats multiple single-field indexes

3. Monitor & Analyze: Use explain() religiously. Measure, don't guess!

4. Balance is Key: Too few = slow reads. Too many = slow writes. Find your sweet spot!

🔢 Multikey Indexes: Indexing Arrays

Multikey Indexes automatically index array elements. MongoDB creates an index entry for EACH array element! Perfect for tags, categories, skills arrays.

Creating Multikey Index
// Sample document with array
{
  "_id": 1,
  "name": "John",
  "tags": ["mongodb", "database", "nosql"],
  "skills": ["javascript", "python", "react"]
}

// Create index on array field
db.users.createIndex({ tags: 1 })

// MongoDB automatically detects array and creates multikey index!
// Creates index entries:
// - "mongodb" → doc 1
// - "database" → doc 1
// - "nosql" → doc 1

// Now queries are FAST:
db.users.find({ tags: "mongodb" })  // Uses index!
db.users.find({ tags: { $in: ["mongodb", "database"] } })  // Uses index!
📊 How Multikey Index Works
Original Documents Document 1 name: "Alice" tags: ["mongodb", "database"] Document 2 name: "Bob" tags: ["nosql", "mongodb"] Document 3 name: "Charlie" tags: ["database", "sql"] Index Multikey Index on "tags" "database" → doc1, doc3 "mongodb" → doc1, doc2 "nosql" → doc2 "sql" → doc3 Each array element gets its own index entry! Query: { tags: "mongodb" } Index lookup: "mongodb" → Returns doc1 & doc2 instantly! 🚀
⚠️ Multikey Index Limitations:
  • One multikey field per compound index: Can't have { arr1: 1, arr2: 1 } if both are arrays
  • Larger index size: More array elements = more index entries = more storage
  • Slower writes: Updating array updates multiple index entries
  • Best for: Tags, categories, skills - arrays with reasonable size (< 100 elements)

🔤 Text Indexes: Full-Text Search

Text Indexes enable powerful full-text search! They tokenize words, remove stop words, stem words, and support multiple languages. Think "Google search" for your MongoDB data!

Creating Text Index
// Create text index on single field
db.articles.createIndex({ content: "text" })

// Create text index on multiple fields
db.articles.createIndex({
  title: "text",
  content: "text",
  tags: "text"
})

// Search using text index
db.articles.find({ $text: { $search: "mongodb database" } })

// Search with phrases
db.articles.find({ $text: { $search: "\"full text search\"" } })

// Exclude words with minus
db.articles.find({ $text: { $search: "mongodb -sql" } })

// Sort by text score (relevance)
db.articles.find(
  { $text: { $search: "mongodb" } },
  { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })
✨ Text Index Features:
  • Tokenization: "Hello World" → ["hello", "world"]
  • Stop Words: Removes "the", "is", "at", etc.
  • Stemming: "running", "runs" → "run"
  • Case-insensitive: "MongoDB" = "mongodb" = "MONGODB"
  • Relevance Scoring: Returns most relevant results first
  • Multi-language: Supports 15+ languages!
  • Only one per collection: Can have only ONE text index

🎯 Index Strategy & Best Practices

✅ DO's - Follow These

  • Index query filters: Fields in WHERE clauses
  • Index sort fields: Fields in ORDER BY
  • Use compound indexes: Better than multiple single indexes
  • Follow ESR rule: Equality, Sort, Range
  • Monitor with explain(): Check if index is used
  • Index selectivity: High-cardinality fields first
  • Analyze slow queries: Profile and optimize

❌ DON'Ts - Avoid These

  • Don't over-index: Too many = slow writes
  • Don't index low-cardinality: e.g., gender (M/F)
  • Don't ignore RAM: Indexes must fit in RAM
  • Don't create duplicate: Remove unused indexes
  • Don't index everything: Balance reads vs writes
  • Don't forget maintenance: Rebuild fragmented indexes
  • Don't ignore order: Wrong field order = wasted index

📊 Query Analysis with explain()

Analyzing Query Performance
// Check if query uses index
db.users.find({ age: 28 }).explain("executionStats")

// Key fields to check in output:
// {
//   "executionStats": {
//     "executionSuccess": true,
//     "nReturned": 5,              // Documents returned
//     "executionTimeMillis": 2,    // Query time
//     "totalDocsExamined": 5,      // Docs scanned
//     "totalKeysExamined": 5,      // Index entries scanned
//     "executionStages": {
//       "stage": "FETCH",
//       "inputStage": {
//         "stage": "IXSCAN",        // ✅ Using index!
//         "indexName": "age_1"
//       }
//     }
//   }
// }

// BAD signs:
// - "stage": "COLLSCAN" → Full collection scan!
// - totalDocsExamined >> nReturned → Scanning too many docs
// - High executionTimeMillis → Slow query

// GOOD signs:
// - "stage": "IXSCAN" → Using index!
// - totalDocsExamined ≈ nReturned → Efficient
// - Low executionTimeMillis → Fast query

⚡ Performance Optimization Tips

💾

Keep Indexes in RAM

Indexes should fit entirely in RAM for best performance. Monitor index size with db.collection.stats().

Formula: RAM ≥ Index Size + Working Set
🎯

Index Selectivity

Index high-cardinality fields (many unique values). Low cardinality (e.g., boolean) = inefficient.

Good: email, userId
Bad: gender, isActive
⚖️

Read/Write Balance

More indexes = faster reads but slower writes. Find the right balance for your workload.

Read-heavy: More indexes OK
Write-heavy: Fewer indexes
🔄

Covered Queries

Query returns ONLY indexed fields = ultra-fast! No document fetch needed.

find({age: 28}, {age: 1, _id: 0})
📊

Index Intersection

MongoDB can use multiple indexes for one query (v2.6+), but compound index is usually faster.

Prefer: Single compound index
Over: Multiple single indexes
🧹

Remove Unused Indexes

Regularly audit indexes. Remove those never used - they waste RAM and slow writes.

db.collection.aggregate([{$indexStats:{}}])

🎯 Golden Rules of Indexing

1. Index your queries: Every field in WHERE, ORDER BY, JOIN should be considered for indexing

2. Compound > Multiple: One compound index beats multiple single-field indexes

3. Monitor & Analyze: Use explain() religiously. Measure, don't guess!

4. Balance is Key: Too few = slow reads. Too many = slow writes. Find your sweet spot!

❓ Interview Questions & Answers

Q1 What is an index in MongoDB? How does it improve query performance? ▼

Answer:

What is an Index:

An index is a special data structure (B-Tree) that stores a small portion of your collection's data in an easy-to-traverse form. It's like a book's index - instead of reading every page to find "MongoDB", you check the index and jump to page 247!

How it Improves Performance:

  • Reduces documents scanned: Without index = scan ALL documents (O(n)). With index = binary search (O(log n))
  • Example: 1 million documents
    • Without index: 1,000,000 checks → 25 seconds
    • With index: ~20 checks → 15 milliseconds
    • Result: 1,666x FASTER! 🚀
  • Efficient sorting: Data already sorted in index = instant ORDER BY
  • Reduced RAM usage: Only load needed documents, not entire collection

How it Works: MongoDB uses B-Tree structure. Each non-leaf node contains keys that divide the tree, leaf nodes contain pointers to actual documents. Searching involves traversing from root to leaf (logarithmic time).

Q2 What is a compound index? When should you use it? ▼

Answer:

Compound Index: An index on multiple fields. Order of fields matters!

Example:

db.users.createIndex({ city: 1, age: 1 })

// This index can efficiently support:
// ✅ { city: "NYC" }
// ✅ { city: "NYC", age: 28 }
// ✅ { city: "NYC", age: { $gt: 25 } }

// But NOT:
// ❌ { age: 28 } // city not specified

When to Use:

  • Queries filter on multiple fields: WHERE city AND age
  • Queries filter AND sort: WHERE city ORDER BY age
  • Better than multiple single indexes: One compound index is more efficient

Field Order Rules:

  • Left-to-Right Rule: Index can be used if query starts with first field(s)
  • ESR Rule: Equality fields first, Sort fields next, Range fields last
  • Example: Index {a:1, b:1, c:1} supports {a}, {a,b}, {a,b,c} but NOT {b}, {c}, {b,c}
Q3 Explain the difference between COLLSCAN and IXSCAN in MongoDB. ▼

Answer:

These are execution stages you see in explain() output:

COLLSCAN (Collection Scan):

  • What: Full collection scan - examines EVERY document
  • Performance: O(n) - linear time
  • Example: 1M documents = 1M checks
  • RAM: High usage - loads many documents
  • When: No suitable index, or small collections where index isn't worth it
  • Sign: ❌ BAD for large collections!

IXSCAN (Index Scan):

  • What: Uses index to find documents directly
  • Performance: O(log n) - logarithmic time
  • Example: 1M documents = ~20 checks
  • RAM: Low usage - only loads matching documents
  • When: Suitable index exists for query
  • Sign: ✅ GOOD - this is what you want!

How to Check:

db.users.find({ age: 28 }).explain("executionStats")

// Look for "stage" field:
// "stage": "COLLSCAN" → ❌ Full scan
// "stage": "IXSCAN" → ✅ Using index
Q4 What is a multikey index? How does it work? ▼

Answer:

Multikey Index: Automatically created when you index an array field. MongoDB creates one index entry for EACH array element.

Example:

// Document
{
  name: "Alice",
  tags: ["mongodb", "database", "nosql"]
}

// Create index
db.users.createIndex({ tags: 1 })

// MongoDB creates index entries:
// "mongodb" → Alice
// "database" → Alice
// "nosql" → Alice

// Query is fast!
db.users.find({ tags: "mongodb" }) // Uses index!

How it Works:

  • For each document, MongoDB creates multiple index entries
  • One entry per array element
  • Array with 10 elements = 10 index entries for that document

Limitations:

  • One multikey field per compound index: Can't index {arr1: 1, arr2: 1} if both are arrays
  • Larger index size: More elements = more entries = more storage
  • Slower writes: Updating array updates multiple index entries

Best For: Tags, categories, skills - arrays with reasonable size (< 100 elements per document)

Q5 What is the ESR rule in compound indexes? ▼

Answer:

ESR Rule: Best practice for compound index field order.

  • E = Equality fields first
  • S = Sort fields next
  • R = Range fields last

Why: This order maximizes index efficiency because:

  • Equality narrows down fastest: city = "NYC" eliminates most documents
  • Sort uses pre-sorted index: Data already sorted by sort fields
  • Range scans remaining: Only scan range within narrowed dataset

Example:

// Query
db.users.find({
  city: "NYC",        // E: Equality
  status: "active",   // E: Equality
  age: { $gt: 25 }    // R: Range
}).sort({ name: 1 })  // S: Sort

// BEST index following ESR:
db.users.createIndex({
  city: 1,      // E: Equality
  status: 1,    // E: Equality
  name: 1,      // S: Sort
  age: 1        // R: Range
})
Q6 What are covered queries? Why are they fast? ▼

Answer:

Covered Query: A query where ALL returned fields are in the index. MongoDB can answer the query entirely from the index without fetching actual documents!

Example:

// Create index
db.users.createIndex({ age: 1, name: 1 })

// ✅ COVERED query - Returns only indexed fields
db.users.find(
  { age: 28 },
  { age: 1, name: 1, _id: 0 }  // Only age & name
)

// ❌ NOT COVERED - Returns non-indexed field
db.users.find(
  { age: 28 },
  { age: 1, name: 1, email: 1 }  // email not in index
)

Why Ultra-Fast:

  • No document fetch: Doesn't load documents from disk
  • Index-only operation: All data in index (which is in RAM)
  • Minimal I/O: No disk reads for documents
  • Example speed: 2-10x faster than regular index scan!
Q7 What happens when you have too many indexes? What are the trade-offs? ▼

Answer:

Negative Impacts of Too Many Indexes:

1. Slower Writes:

  • Every INSERT/UPDATE/DELETE must update ALL indexes
  • 10 indexes = 10x write overhead per operation
  • Example: Insert with 10 indexes can be 5-10x slower than no indexes

2. Increased Storage:

  • Each index consumes disk space
  • Indexes can be 10-50% of collection size
  • 10 indexes = potentially 100-500% storage overhead!

3. RAM Pressure:

  • All indexes should fit in RAM for optimal performance
  • Too many indexes = not all fit in RAM = disk I/O = slow

The Trade-off:

Indexes trade write performance for read performance.

  • Read-heavy app: More indexes OK (90% reads, 10% writes)
  • Write-heavy app: Fewer indexes better (30% reads, 70% writes)
  • Typical sweet spot: 5-10 indexes per collection
Q8 How do you identify and fix slow queries in MongoDB? ▼

Answer:

Step-by-Step Process:

1. Enable Profiling:

// Enable profiler for queries > 100ms
db.setProfilingLevel(1, { slowms: 100 })

// View slow queries
db.system.profile.find().sort({ ts: -1 }).limit(10)

2. Use explain() to Analyze:

db.users.find({ age: 28 }).explain("executionStats")

// Red flags:
// ❌ "stage": "COLLSCAN"
// ❌ totalDocsExamined >> nReturned
// ❌ executionTimeMillis > 1000

3. Common Solutions:

  • COLLSCAN: Create index on query fields
  • Wrong Index: Create better compound index following ESR rule
  • Excessive Scanning: Improve selectivity with compound index