Single & Compound Indexes
// Single field index
db.users.createIndex({ email: 1 }); // ascending
db.users.createIndex({ createdAt: -1 }); // descending
// Unique index
db.users.createIndex({ email: 1 }, { unique: true });
// Compound index (field order matters for queries)
db.orders.createIndex({ userId: 1, createdAt: -1 });
// Supports: { userId } and { userId, createdAt }
// Does NOT support: { createdAt } alone
// Covered index: include all projected fields
db.products.createIndex({ category: 1, price: 1, name: 1 });
// Query: db.products.find({ category: "tech" }, { price: 1, name: 1, _id: 0 })
// Result: IXSCAN only — no FETCH stage needed
// List indexes
db.users.getIndexes();
// Drop index
db.users.dropIndex({ email: 1 });
db.users.dropIndex("email_1");
Text Indexes
// Create text index on one or more fields
db.articles.createIndex({ title: "text", body: "text" });
// Weighted text index
db.articles.createIndex(
{ title: "text", body: "text" },
{ weights: { title: 10, body: 1 }, name: "article_text_idx" }
);
// Full-text search query
db.articles.find(
{ $text: { $search: "mongodb performance" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } });
// Phrase search (wrap in quotes)
db.articles.find({ $text: { $search: "\"query planner\"" } });
// Negate a word
db.articles.find({ $text: { $search: "mongodb -slow" } });
// Only one text index per collection allowed
TTL & Partial Indexes
// TTL index: auto-delete documents after N seconds
db.sessions.createIndex(
{ expiresAt: 1 },
{ expireAfterSeconds: 0 } // delete when expiresAt is reached
);
// Or set a fixed TTL from document creation
db.logs.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 86400 } // 24 hours
);
// Partial index: only index documents matching a filter
db.orders.createIndex(
{ userId: 1 },
{ partialFilterExpression: { status: "active" } }
);
// Sparse index: skip documents where field is null/missing
db.users.createIndex({ phone: 1 }, { sparse: true });
// Partial index saves space and speeds writes when only
// a subset of documents needs indexing
2dsphere & Geospatial
// 2dsphere index for GeoJSON geometry
db.places.createIndex({ location: "2dsphere" });
// Insert GeoJSON point
db.places.insertOne({
name: "Coffee Shop",
location: { type: "Point", coordinates: [116.4, 39.9] }
});
// Find places within 1km radius
db.places.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [116.4, 39.9] },
$maxDistance: 1000 // meters
}
}
});
// $geoWithin a polygon
db.places.find({
location: {
$geoWithin: {
$geometry: {
type: "Polygon",
coordinates: [[[116.3,39.8],[116.5,39.8],[116.5,40.0],[116.3,40.0],[116.3,39.8]]]
}
}
}
});
explain() Analysis
// Run explain to inspect query plan
db.orders.find({ userId: 42 }).explain("executionStats");
// Key fields to check:
// winningPlan.stage:
// COLLSCAN = full collection scan (bad without index)
// IXSCAN = index scan (good)
// FETCH = fetch document from disk after IXSCAN
// COVERED = served entirely from index (best)
// executionStats:
// nReturned: documents returned
// totalKeysExamined: index keys scanned
// totalDocsExamined: documents fetched from disk
// Ideal: nReturned ≈ totalKeysExamined, totalDocsExamined ≈ 0
// Force an index hint
db.orders.find({ userId: 42 }).hint({ userId: 1 }).explain();
// Background index build (non-blocking)
db.large_collection.createIndex({ field: 1 }, { background: true });