MongoDB from Basics to Production · MongoDB Basics
Indexes and explain()
Create single-field and compound indexes, order compound keys with the equality-sort-range rule, and verify query plans with explain.
What you'll build
A collection of 5,000 orders queried by customer and date: the query plan without an index (a collection scan), with a compound index in the right order, and as a covered query that never touches the documents.
The idea
- Without an index, MongoDB scans every document (
COLLSCAN). An index (IXSCAN) jumps to matching entries. - Compound indexes follow the ESR rule: Equality fields first, then Sort fields, then Range fields.
explain("executionStats")shows the winning plan,totalDocsExaminedandnReturned; examined should be close to returned.- A covered query only uses indexed fields (projection excludes
_idunless indexed), so no documents are read.
The code
const docs = [];
for (let i = 0; i < 5000; i++) {
docs.push({ customerId: "c" + (i % 100), createdAt: new Date(Date.UTC(2026, 0, 1 + (i % 60))), total: (i % 50) + 5 });
}
db.orders.insertMany(docs);
const query = { customerId: "c42", createdAt: { $gte: new Date("2026-02-01") } };
const stats = (cursor) => {
const e = cursor.explain("executionStats");
const plan = JSON.stringify(e.queryPlanner.winningPlan).match(/"stage":"(COLLSCAN|IXSCAN)"/)[1];
const s = e.executionStats;
return `${plan}, examined ${s.totalDocsExamined} docs / ${s.totalKeysExamined} keys, returned ${s.nReturned}`;
};
print("no index: ", stats(db.orders.find(query).sort({ createdAt: -1 })));
db.orders.createIndex({ customerId: 1, createdAt: -1, total: 1 }); // equality, sort/range, then covered field
print("ESR index: ", stats(db.orders.find(query).sort({ createdAt: -1 })));
print("covered query:", stats(db.orders.find(query, { _id: 0, createdAt: 1, total: 1 }).sort({ createdAt: -1 })));
print("indexes:", db.orders.getIndexes().map(i => i.name).join(", "));
Output
no index: COLLSCAN, examined 5000 docs / 0 keys, returned 17 ESR index: IXSCAN, examined 17 docs / 17 keys, returned 17 covered query: IXSCAN, examined 0 docs / 17 keys, returned 17 indexes: _id_, customerId_1_createdAt_-1_total_1
Run this on your computer — see “Get Started” for setup.
Common mistakes
- Indexing every field "just in case": indexes cost memory and slow writes.
- Compound indexes in the wrong order (range field first), forcing in-memory sorts.
- Never checking
explainand finding the collection scan in production. - Creating indexes on large production collections without considering the build load; schedule and monitor them.
Key points
- Check plans with explain("executionStats"): examined vs returned.
- Order compound indexes by equality, sort, range.
- Covered queries read only the index.
Further reading: Indexes · The ESR rule · Explain results
Practice questions
What is the ESR rule?
Answer
In compound indexes, put fields used for Equality first, then fields used for Sort, then fields used for Range conditions.
How do you tell from explain output that a query is inefficient?
Answer
A COLLSCAN stage, or totalDocsExamined much larger than nReturned.
What is a covered query?
Answer
A query whose filter and projected fields are all in one index (with _id excluded unless indexed), so MongoDB never reads the documents.
Open in the app to mark lessons complete, take notes and track your progress.