This chapter follows Mongoose with MongoDB. Indexes are not free: they speed reads, slow writes, and consume RAM. Create them to match filters, sort, and projection in your real queries.
Single-field and compound indexes
A single-field index on { email: 1 } supports equality and range on email. A compound index { status: 1, createdAt: -1 } supports queries that filter on status and sort on createdAt when the filter uses a prefix of the index keys.
The ESR rule
For compound indexes, a useful mnemonic is Equality → Sort → Range:
// Query: { status: 'open' }, sort { priority: -1 }, filter { createdAt: { $gte: date } }db.tickets.createIndex({ status: 1, priority: -1, createdAt: -1 })Put equality fields first, then sort field, then range field.
Multikey, unique, partial, TTL, wildcard
- Multikey: indexes on array fields—one index entry per array element.
- Unique:
{ email: 1 }, { unique: true }rejects duplicates (null counts as a value unless sparse). - Partial: index only documents matching a filter—smaller index for archived flags.
- TTL:
{ expiresAt: 1 }, { expireAfterSeconds: 0 }deletes documents when date passes. - Wildcard:
{ 'metrics.$**': 1 }for dynamic metric keys (use sparingly).
explain() and covered queries
db.orders.find({ customerId: id, status: 'shipped' }) .sort({ createdAt: -1 }) .explain('executionStats')Look at totalDocsExamined versus nReturned. A covered query satisfies the query entirely from the index (totalDocsExamined: 0 with projection of indexed fields only).
Create indexes from TypeScript
import { getDb } from '../db/client.js';
export async function ensureTicketIndexes() { const db = await getDb(); await db.collection('tickets').createIndexes([ { key: { status: 1, createdAt: -1 }, name: 'status_createdAt' }, { key: { assigneeId: 1 }, name: 'assigneeId', sparse: true }, ]);}Build indexes in production with { background: true } where supported, or during maintenance windows on large collections.
Capped and time series collections
Capped collections have fixed size and FIFO behavior—useful for bounded logs:
db.createCollection('logs', { capped: true, size: 10485760, max: 5000 })Time series collections optimize storage and queries for timestamped measurements:
db.createCollection('sensor', { timeseries: { timeField: 't', metaField: 'sensorId', granularity: 'minutes' },})Check your understanding
Question 1 of 5
0 of 5 checked