A query that takes milliseconds on ten thousand documents can take seconds on ten million, unless an index is there to help. Indexes are the single biggest lever you have over MongoDB performance. In this CodeYourCraft lesson you will create single-field and compound indexes, read execution plans with explain(), and learn the rules that decide whether an index actually gets used.
Without an index, MongoDB answers a query with a collection scan (COLLSCAN), reading every document and testing it against the filter. An index is a sorted B-tree of field values that lets the server jump straight to matching documents (IXSCAN), turning linear work into logarithmic work. Every collection automatically has one index on _id, which is why lookups by _id are always fast.
use shop
// Single-field index, ascending
db.products.createIndex({ price: 1 })
// Unique index: enforce one account per email
db.customers.createIndex({ email: 1 }, { unique: true })
// Compound index: filter by category, sort by price within it
db.products.createIndex({ category: 1, price: -1 })
// Multikey happens automatically when the field is an array
db.products.createIndex({ tags: 1 })
db.products.getIndexes() // list all indexes on the collectionDirection (1 or -1) barely matters for single-field indexes, but it matters for compound indexes that support sorts on multiple fields in mixed directions.
explain() is your performance microscope:
db.products.find({ category: 'audio', price: { $lt: 5000 } })
.explain('executionStats')In the output, focus on four numbers: the winning plan stage (IXSCAN good, COLLSCAN bad for selective queries), totalKeysExamined, totalDocsExamined, and nReturned. A healthy query examines roughly as many keys and documents as it returns. If totalDocsExamined is 500000 and nReturned is 20, your index is missing or wrong.
Order the fields of a compound index as Equality, Sort, Range. Fields you match exactly come first, then the field you sort by, then range conditions:
// Query: category equals X, sort by rating, price within a range
db.products.createIndex({ category: 1, rating: -1, price: 1 })A compound index also serves any prefix of itself: the index above can answer queries on category alone, or category plus rating, but not queries on price alone.
If every field a query filters on and returns lives inside one index, MongoDB can answer from the index alone without touching documents. Project only indexed fields and exclude _id (unless it is in the index):
db.products.find(
{ category: 'audio' },
{ category: 1, price: 1, _id: 0 }
)Covered queries show totalDocsExamined: 0 in explain output and are as fast as MongoDB gets.
Indexes are not free. Every insert, update, and delete must also update each index, and each index consumes RAM and disk. A write-heavy collection with ten indexes will feel it. Review db.products.aggregate([{ $indexStats: {} }]) periodically and drop indexes that no queries use. As a rule of thumb, design indexes from your actual query patterns, not speculatively.
With fast reads in hand, the next lesson tackles the aggregation pipeline, MongoDB's framework for analytics and data transformation.