Create Indexes to Speed Up Queries
Learn how to create indexes in MongoDB to dramatically speed up your queries. This lesson covers basic index creation, choosing the right fields, and troubleshooting common pitfalls.
Focus: create indexes to speed up queries
Your MongoDB queries are getting slower and slower as your collections grow. You've tried adding more RAM, upgrading your machine, and even rewriting your queries — but nothing seems to help. The real culprit is often a missing index: MongoDB is scanning every document in your collection just to find a few matching records. In this lesson, you'll learn how to create indexes to speed up queries, turning those painful full collection scans into lightning-fast index lookups. By the end, you'll be able to identify slow queries, create the right indexes, and understand when (and when not) to index.
The problem this lesson solves
Imagine you run an e-commerce site with a products collection. Your users often search for products by category and price range:
db.products.find({ category: "electronics", price: { $lt: 500 } })
Initially, this query returns in milliseconds. But as your catalog grows to hundreds of thousands of products, response times balloon to several seconds. What happened?
Without an index, MongoDB must perform a collection scan — it reads every document in the collection, checks the category and price fields, and only then returns the matching ones. This is inefficient and doesn't scale. Indexes solve this by creating a sorted data structure (a B-tree) that allows MongoDB to jump directly to the relevant documents, just like the index at the back of a book helps you find pages without reading the whole book.
Pro tip: The
.explain("executionStats")method is your best friend. It reveals whether MongoDB is using an index or scanning the entire collection — the first step to spotting a missing index.
Core concept / mental model
Think of indexes as a phone book at a library. Without an index, MongoDB is like a librarian who must walk through every aisle, open every book, and check each page to find what you asked for. With an index, it's like having a card catalog: you look up the topic, get the exact shelf location, and walk straight to the book. The cost? Every time you add, update, or delete a document, MongoDB must also update the index — just like a librarian must add a new card when a book arrives.
In MongoDB, an index is a special data structure that stores a small portion of the collection's data set in an easy-to-traverse form. It stores the value of a specific field or set of fields, ordered by the value of the field. This allows MongoDB to search efficiently, reducing the number of documents it must examine.
Key terms
- Index key(s): The field(s) on which the index is built.
- Collation: Rules for string comparison (e.g., case-insensitive).
- Selectivity: How uniquely an index value identifies a document. High selectivity means fewer documents match.
- Compound index: An index on multiple fields, e.g.,
{ category: 1, price: 1 }. - Covered query: When all fields in a query are part of an index, MongoDB can return results without fetching documents.
How it works step by step
Let's walk through the process of identifying a slow query, creating an index, and verifying the improvement.
1. Detect a slow query
Use the explain() method to see the query execution plan:
db.products.find({ category: "electronics", price: { $lt: 500 } }).explain("executionStats")
Look for "stage": "COLLSCAN" — this indicates a full collection scan. Also note totalDocsExamined compared to nReturned. If huge numbers are examined but few are returned, an index is needed.
2. Create the index
Decide which fields to index. For this query, a compound index on category and price is ideal:
db.products.createIndex({ category: 1, price: 1 })
Here, 1 means ascending order; -1 would mean descending. Order matters for compound indexes — the most selective field should come first.
3. Verify the improvement
Run the same explain() again. You should see "stage": "IXSCAN" and a dramatic drop in totalDocsExamined. The query should now be much faster.
4. Keep an eye on index usage
MongoDB has a built-in query optimizer that picks the best index for each query. You don't have to hint which index to use — it will automatically choose the most efficient plan. But you can force an index with .hint() if needed (rare).
Hands-on walkthrough
Let's practice with a real example. Assume you have a products collection with 100,000 documents.
Step 1: Insert sample data (simplified)
for (let i = 0; i < 100000; i++) {
db.products.insertOne({
name: "Product" + i,
category: ["electronics", "books", "clothing"][i % 3],
price: Math.floor(Math.random() * 1000)
});
}
Step 2: Check the query performance without an index
db.products.find({ category: "electronics", price: { $lt: 500 } }).explain("executionStats")
Look at executionStats — you'll likely see totalDocsExamined: 100000 and nReturned: ~16666, with stage: "COLLSCAN". This is slow.
Step 3: Create the index
db.products.createIndex({ category: 1, price: 1 })
Step 4: Re-run the query with explain
db.products.find({ category: "electronics", price: { $lt: 500 } }).explain("executionStats")
Now you should see stage: "IXSCAN", totalDocsExamined: 0 (because the index alone is enough to determine matches), and nReturned: ~16666. The query is faster.
Step 5: Compare read vs. write performance
Indexes speed up reads but slow down writes. Test with a quick insert loop:
console.time("insert with index");
for (let i = 0; i < 1000; i++) {
db.products.insertOne({ name: "New", category: "test", price: 1 });
}
console.timeEnd("insert with index");
Compare this to a collection without indexes. You'll see slightly slower inserts, but the read gain usually outweighs this cost.
Compare options / when to choose what
| Index type | Use case | When to choose it |
|---|---|---|
Single-field index ({ field: 1 }) |
Simple equality queries | When you filter on one field only |
Compound index ({ field1: 1, field2: -1 }) |
Queries on multiple fields | When queries filter/sort by multiple fields — put the most selective field first |
| Multikey index (e.g., on arrays) | Fields that contain arrays | When querying array elements |
| Text index | Full-text search | When you need $text queries for natural language |
| Hashed index | Equality queries on large fields (e.g., long strings) | When you only need equality and want even distribution |
General rule: Create indexes for the queries you actually run, not for every field. Too many indexes slow down writes and consume storage.
Pro tip: Check slow query logs (
db.getProfilingLevel()) or use MongoDB Atlas's Performance Advisor to discover missing indexes.
Troubleshooting & edge cases
Slow queries still after creating an index
- Wrong field order in a compound index: if your query filters on
priceandcategory, but your index is{ price: 1, category: 1 }, MongoDB may not use it efficiently. Ensure the first field matches the most selective filter. - Unused index: use
.explain()to see if the index is actually used. If not, remove it (db.collection.dropIndex()) to avoid write overhead. - Low selectivity: indexing a field with only a few distinct values (e.g.,
is_active: true/false) won't speed up queries much — MongoDB may still scan many documents.
Index creation takes too long
- Background creation: use the
background: trueoption (though it's deprecated in MongoDB 4.2+ as all builds are background). Usedb.collection.createIndex(..., { background: true })if needed for older versions. - Build on a replica set: index builds are rolling by default — they won't block reads/writes.
Index size vs. performance
- Large indexes consume memory. If your working set doesn't fit in RAM, you'll see disk reads. Keep indexes lean.
Common mistakes
- Creating an index on every field — bloats storage and slows writes unnecessarily.
- Using a single-field index when a compound index would serve multiple queries.
- Forgetting that index order matters for compound indexes —
{ category: 1, price: 1 }is different from{ price: 1, category: 1 }. - Ignoring
.explain()— don't guess; analyze before and after.
What you learned & what's next
You've learned how to create indexes to speed up queries: you can now identify slow queries with explain(), choose the right index type, create indexes with createIndex(), and verify performance improvements. You understand the trade-off between read speed and write overhead, and you can recognize common index pitfalls.
In the next lesson, you'll build on this foundation by exploring advanced indexing techniques, such as partial indexes, TTL indexes, and geospatial indexes, to handle even more specialized query patterns.
Keep practicing: run explain() on your own collections, spot collection scans, and create indexes that make your queries fly!
Practice recap
Open your mongosh and run db.collection.createIndex({ field: 1 }) on a collection with several thousand documents that you query frequently. Use .explain('executionStats') before and after to compare totalDocsExamined and execution time. Then experiment with a compound index to see how field order affects performance.
Common mistakes
- Creating an index on every field, which bloats storage and slows writes unnecessarily.
- Using a single-field index when a compound index would better serve multi-field queries.
- Forgetting that index key order matters for compound indexes —
{ category: 1, price: 1 }is not the same as{ price: 1, category: 1 }. - Not checking
.explain()before and after index creation, so you miss inefficiencies.
Variations
- Use MongoDB Atlas's Performance Advisor to automatically suggest indexes based on real query patterns.
- For read-heavy applications, consider covering indexes to avoid fetching documents entirely.
- Use partial indexes to index only a subset of documents matching a filter, reducing index size and write overhead.
Real-world use cases
- E-commerce platform: index
categoryandpriceto speed up product filtering and price ranges. - Log analytics: index
timestampto quickly query logs within a time window. - User search: create a text index on
namefields to enable fast$textsearches.
Key takeaways
- Indexes transform collection scans into efficient lookups, dramatically speeding up queries.
- Use compound indexes on fields you filter or sort together, with the most selective field first.
- Always verify the impact with
.explain('executionStats')— look forIXSCANinstead ofCOLLSCAN. - Indexes speed up reads but add write overhead, so create them only for actual query patterns.
- Be mindful of index size and memory usage; too many indexes can hurt performance.
- MongoDB's optimizer automatically picks the best index — you don't need to hint in most cases.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.