MongoDB indexes.
Single-field
db.users.createIndex({ email: 1 }) // asc
db.users.createIndex({ created: -1 }) // desc
Compound
db.users.createIndex({ status: 1, created: -1 })
ESR rule: Equality, Sort, Range. Order fields accordingly.
Unique
db.users.createIndex({ email: 1 }, { unique: true })
Sparse
db.users.createIndex({ phone: 1 }, { sparse: true })
Skip docs missing the field.
Partial
db.users.createIndex(
{ email: 1 },
{ partialFilterExpression: { active: true } }
)
Index only matching docs.
TTL
db.sessions.createIndex({ expires: 1 }, { expireAfterSeconds: 0 })
Auto-delete based on date field.
Text
db.posts.createIndex({ title: "text", body: "text" })
db.posts.find({ $text: { $search: "redis tutorial" } })
db.posts.find({ $text: { $search: "\"exact phrase\"" } })
db.posts.find({ $text: { $search: "redis -php" } }) // exclude
db.posts.find({ $text: { $search: "..." } }, { score: { $meta: "textScore" } })
.sort({ score: { $meta: "textScore" } })
One text index per collection.
Geospatial
db.places.createIndex({ location: "2dsphere" })
db.places.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [-73.97, 40.77] },
$maxDistance: 1000, // meters
},
},
})
Wildcard
db.coll.createIndex({ "$**": 1 })
db.coll.createIndex({ "meta.$**": 1 })
Indexes all subfields. Useful for flexible schemas.
Hashed
db.coll.createIndex({ user_id: "hashed" })
For shard key on high-cardinality.
Hidden
db.coll.createIndex({ x: 1 }, { hidden: true })
db.coll.unhideIndex("x_1")
Test impact of dropping without removing.
Background / non-blocking
In modern Mongo, all index builds are non-blocking.
explain
db.users.find({ email: "..." }).explain("executionStats")
// look for IXSCAN (good) vs COLLSCAN (bad)
$hint
db.coll.find({...}).hint({ status: 1, created: -1 })
Force index.
Drop unused
db.coll.aggregate([{ $indexStats: {} }]) // usage
db.coll.dropIndex("name_1")
ESR pattern
For query { status: "x", created: { $gt: ... } } sorted by priority:
db.coll.createIndex({ status: 1, priority: 1, created: 1 })
Equality → Sort → Range.
Index intersection
Mongo can use multiple indexes per query but compound is usually faster.
Multikey (array)
{ tags: ["a", "b", "c"] }
db.coll.createIndex({ tags: 1 })
Each tag indexed. Be careful: multikey + compound limits.
Index size
db.coll.totalIndexSize()
db.coll.stats().indexSizes
Watch for >50% RAM.
Build progress
db.currentOp({ "command.createIndexes": { $exists: true } })
Common mistakes
- Indexing every field (writes slow).
- Forgetting compound order (ESR).
- COLLSCAN on big collection.
- Multiple text indexes (one allowed).
- Index too wide → ramp up RAM.
Read this next
If you want my Mongo perf checklist, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .