Indexes
Indexes improve query performance by allowing MongoDB to find documents without scanning the entire collection.
Defining Indexes
const Users = collection({
name: 'users',
schema: {
_id: field.id(),
email: field.email(),
name: field.string(),
role: field.enum(['user', 'admin']),
status: field.enum(['active', 'inactive']),
createdAt: field.datetime(),
},
indexes: [
// Simple index
{ key: { email: 1 } },
// Unique index
{ key: { email: 1 }, unique: true },
// Compound index
{ key: { role: 1, createdAt: -1 } },
// Descending index
{ key: { createdAt: -1 } },
],
});Index Types
Single Field Index
{ key: { email: 1 } } // Ascending
{ key: { createdAt: -1 } } // DescendingCompound Index
Index on multiple fields (order matters):
{ key: { status: 1, createdAt: -1 } }This index supports queries like:
{ status: 'active' }{ status: 'active', createdAt: { $gte: date } }- Sorting by
{ status: 1, createdAt: -1 }
Unique Index
Enforce unique values:
{ key: { email: 1 }, unique: true }Attempting to insert a duplicate throws an error.
Sparse Index
Only index documents that have the field:
{ key: { optionalField: 1 }, sparse: true }Useful for optional fields where you want unique constraint only on documents that have the field.
Partial Index
Index only documents matching a filter:
{
key: { email: 1 },
partialFilterExpression: { status: 'active' }
}Only indexes documents where status === 'active'.
Text Index
Enable full-text search:
{ key: { title: 'text', content: 'text' } }Query with:
Users.find({ $text: { $search: 'mongodb tutorial' } });TTL Index
Auto-delete documents after a time period:
{
key: { expiresAt: 1 },
expireAfterSeconds: 0 // Delete when expiresAt is reached
}
// Or delete after fixed duration
{
key: { createdAt: 1 },
expireAfterSeconds: 86400 // Delete 24 hours after creation
}Wildcard Index
Index all fields (use sparingly):
{ key: { '$**': 1 } }
// Or specific path
{ key: { 'metadata.$**': 1 } }Index Options
| Option | Type | Description |
|---|---|---|
unique | boolean | Enforce unique values |
sparse | boolean | Only index documents with the field |
background | boolean | Build index in background (deprecated in 4.2+) |
expireAfterSeconds | number | TTL - auto-delete after seconds |
partialFilterExpression | object | Only index matching documents |
name | string | Custom index name |
collation | object | Locale-specific string comparison |
Auto-Creation
By default, Monch creates indexes on first connection:
const Users = collection({
name: 'users',
schema: { /* ... */ },
indexes: [
{ key: { email: 1 }, unique: true },
],
createIndexes: true, // Default: true
});Disable auto-creation:
const Users = collection({
name: 'users',
schema: { /* ... */ },
indexes: [ /* ... */ ],
createIndexes: false, // Don't auto-create
});Manual Index Creation
Create indexes explicitly:
// Create all configured indexes
const indexNames = await Users.ensureIndexes();
console.log('Created indexes:', indexNames);Common Index Patterns
Email Lookup
indexes: [
{ key: { email: 1 }, unique: true },
]Status + Date Queries
// For queries like: { status: 'active' } sorted by createdAt
indexes: [
{ key: { status: 1, createdAt: -1 } },
]User Activity
indexes: [
{ key: { userId: 1, createdAt: -1 } }, // User's activity timeline
{ key: { createdAt: -1 } }, // Global activity feed
]Search with Filters
indexes: [
{ key: { title: 'text', content: 'text' } }, // Text search
{ key: { category: 1, createdAt: -1 } }, // Category browse
]Geo-Spatial
// For location-based queries
indexes: [
{ key: { location: '2dsphere' } },
]
// Usage
Users.find({
location: {
$near: {
$geometry: { type: 'Point', coordinates: [-73.9667, 40.78] },
$maxDistance: 1000, // meters
},
},
});Best Practices
1. Index Fields Used in Queries
// If you query like this:
Users.find({ role: 'admin', status: 'active' });
// Create this index:
{ key: { role: 1, status: 1 } }2. Consider Query Patterns
Order compound index fields by:
- Equality conditions first
- Sort fields second
- Range conditions last
// Query: { status: 'active', role: 'admin' } sorted by createdAt
// Index: status (equality) + role (equality) + createdAt (sort)
{ key: { status: 1, role: 1, createdAt: -1 } }3. Don't Over-Index
- Each index uses memory and disk space
- Indexes slow down writes (must update indexes)
- Remove unused indexes
4. Use Covered Queries
If an index contains all fields needed by a query, MongoDB can return results from the index alone:
// Index
{ key: { email: 1, name: 1 } }
// This query is "covered" - only needs the index
Users.find({ email: 'alice@example.com' })
.project({ email: 1, name: 1, _id: 0 });5. Monitor Index Usage
Use MongoDB tools to analyze index usage:
// In MongoDB shell
db.users.aggregate([{ $indexStats: {} }]);Viewing Existing Indexes
Access the raw collection to view indexes:
const raw = await Users.collection;
const indexes = await raw.indexes();
console.log(indexes);