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 .