How MongoDB indexes work and why queries slow down without them
collection scan vs index scan, B-tree index structure, index entry, query explain plan, COLLSCAN vs IXSCAN, nReturned vs totalDocsExamined
Without an index, MongoDB scans everything
Without an index, MongoDB performs a collection scan โ it reads every single document in the collection to find matches. On a 10 million document collection, a query for one user by email examines all 10 million documents. With an index, the same query reads 1 document via a B-tree lookup. This is the single biggest performance lever in MongoDB.
How B-tree indexes work
MongoDB's default indexes are B-trees. An index on the email field stores sorted email values alongside pointers back to their full documents. Lookups drop from O(n) to O(log n) โ roughly 23 comparisons for 10 million documents instead of 10 million reads. Every secondary index adds storage overhead and a maintenance cost on every write.
// Always check the query plan before adding indexes
const plan = await db.collection('users')
.find({ email: 'ada@example.com' })
.explain('executionStats')
// BEFORE index โ slow
// stage: 'COLLSCAN', totalDocsExamined: 1000000, executionTimeMillis: 320
// AFTER creating index on email:
await db.collection('users').createIndex({ email: 1 })
// stage: 'IXSCAN', totalDocsExamined: 1, executionTimeMillis: 1The write trade-off
Each index maintains a separate B-tree structure. Every insert, update, or delete must update all relevant indexes. A collection with 15 indexes pays a heavy write penalty per operation. Profile before adding indexes, and regularly remove unused ones using db.collection('users').dropIndex('email_1') to keep write throughput healthy.
