Skip to main content

MongoDB Index Properties

MongoDB indexes are essential for improving query efficiency. But beyond just creating standard indexes, MongoDB offers various index properties that let you customize how indexes work and what data they include. Understanding these properties can help you create more efficient indexes tailored to your application's needs.

Introduction to Index Properties

In MongoDB, an index doesn't have to be just a simple ordered reference to your document fields. You can configure indexes with special behaviors and constraints to match your specific requirements. These properties provide fine-grained control over your indexes and can help optimize both performance and data integrity.

This guide covers the most important index properties in MongoDB:

  • Unique Indexes
  • Sparse Indexes
  • Partial Indexes
  • TTL Indexes
  • Hidden Indexes
  • Collation

Let's explore each of these properties and how they can benefit your application.

Unique Indexes

Unique indexes ensure that the indexed fields don't store duplicate values. This property is essential when you need to enforce uniqueness in your data, such as for usernames or email addresses.

Creating a Unique Index

To create a unique index, use the createIndex() method with the unique property set to true:

javascript
db.users.createIndex({ email: 1 }, { unique: true })

This command creates an ascending index on the email field and ensures that all email values in the collection are unique.

Example: Handling Duplicate Key Errors

If you try to insert a document with a duplicate value in a unique index, MongoDB will return a duplicate key error:

javascript
// First insert
db.users.insertOne({ name: "John", email: "[email protected]" })
// Result: Successfully inserted

// Second insert with the same email
db.users.insertOne({ name: "Jane", email: "[email protected]" })
// Result: MongoError: E11000 duplicate key error collection: test.users index: email_1 dup key: { email: "[email protected]" }

Unique Compound Indexes

You can also create unique indexes on multiple fields, which ensures that the combination of values is unique:

javascript
db.products.createIndex({ category: 1, sku: 1 }, { unique: true })

This allows the same sku to appear in different categories, but prevents duplicate SKUs within the same category.

Sparse Indexes

Sparse indexes only include documents that have the indexed field. Documents that don't contain the indexed field are skipped.

Creating a Sparse Index

javascript
db.customers.createIndex({ "premium_account_id": 1 }, { sparse: true })

In this example, only customers with a premium_account_id field will be included in the index.

Benefits of Sparse Indexes

Sparse indexes are smaller than non-sparse indexes when the indexed field is missing from many documents. This can save storage space and improve write performance.

javascript
// These documents would be included in the sparse index
db.customers.insertMany([
{ name: "Alice", premium_account_id: "P123" },
{ name: "Bob", premium_account_id: "P456" }
])

// This document would NOT be included in the sparse index
db.customers.insertOne({ name: "Charlie" })

Partial Indexes

Partial indexes represent an evolution of sparse indexes with more flexibility. They only index the documents that meet a specified filter expression, allowing for even more granular control over which documents are indexed.

Creating a Partial Index

javascript
db.orders.createIndex(
{ orderDate: 1 },
{
partialFilterExpression: {
status: "active",
total: { $gt: 100 }
}
}
)

This index only includes documents where the status field equals "active" and the total field is greater than 100.

Partial vs. Sparse Indexes

Partial indexes offer more flexibility than sparse indexes:

javascript
// Sparse index only checks if the field exists
db.collection.createIndex({ field: 1 }, { sparse: true })

// Partial index can use any query criteria
db.collection.createIndex(
{ field: 1 },
{ partialFilterExpression: { field: { $exists: true, $gt: 10 } } }
)

TTL Indexes (Time-To-Live)

TTL indexes automatically remove documents from a collection after a specified amount of time. They're perfect for session data, temporary logs, or any data that should expire.

Creating a TTL Index

javascript
db.sessions.createIndex(
{ lastModified: 1 },
{ expireAfterSeconds: 3600 } // 1 hour
)

This index will cause documents to be deleted approximately 1 hour after the time specified in their lastModified field.

Important TTL Constraints

  • TTL indexes can only be created on fields containing date values.
  • The TTL monitor runs every 60 seconds by default, so expirations may not be immediate.
  • You can only specify the TTL property when creating the index.
javascript
// Insert a document with a date field
db.sessions.insertOne({
userId: "12345",
lastModified: new Date(),
data: { isLoggedIn: true }
})
// Document will be automatically removed after 1 hour

Hidden Indexes

Hidden indexes are maintained by MongoDB but not used by the query planner. This feature is useful for testing the impact of removing an index without actually dropping it.

Creating a Hidden Index

javascript
db.products.createIndex({ category: 1 }, { hidden: true })

Unhiding an Index

javascript
db.products.unhideIndex({ category: 1 })

Hidden indexes are particularly useful when you want to evaluate if an index is actually being used or if it's a good candidate for removal:

javascript
// Hide an index you suspect isn't needed
db.collection.collMod({
index: {
keyPattern: { field: 1 },
hidden: true
}
})

// Run your application and check if performance degrades
// If no issues, you can drop the index
// If problems occur, unhide it without rebuilding
db.collection.collMod({
index: {
keyPattern: { field: 1 },
hidden: false
}
})

Collation

Collation allows you to specify language-specific rules for string comparison, such as case insensitivity and accent sensitivity.

Creating an Index with Collation

javascript
db.users.createIndex(
{ lastName: 1 },
{
collation: {
locale: "en",
strength: 2 // Case-insensitive
}
}
)

Example Query Using Collation

For a query to use a collation-based index, it must specify the same collation:

javascript
db.users.find({ lastName: "smith" }).collation({ 
locale: "en",
strength: 2
})
// This will match "Smith", "SMITH", "smith", etc.

Combining Index Properties

Many index properties can be combined to create highly specialized indexes:

javascript
db.products.createIndex(
{ sku: 1 },
{
unique: true,
partialFilterExpression: { status: "active" },
collation: { locale: "en", strength: 2 }
}
)

This creates an index that:

  • Enforces uniqueness on the sku field
  • Only indexes documents where status is "active"
  • Performs case-insensitive comparison of sku values

Practical Example: User Management System

Let's see how different index properties might be used in a user management system:

javascript
// 1. Unique index for user emails
db.users.createIndex(
{ email: 1 },
{ unique: true }
)

// 2. TTL index for password reset tokens
db.passwordResetTokens.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 3600 } // Expires after 1 hour
)

// 3. Partial index for premium users
db.users.createIndex(
{ lastLogin: 1 },
{ partialFilterExpression: { accountType: "premium" } }
)

// 4. Case-insensitive search index
db.users.createIndex(
{ username: 1 },
{ collation: { locale: "en", strength: 2 } }
)

This system efficiently handles email uniqueness, automatically expires password reset tokens, optimizes queries specifically for premium users, and enables case-insensitive username searches.

Monitoring Index Properties

You can view all indexes and their properties using the getIndexes() method:

javascript
db.collection.getIndexes()

This returns an array of documents describing each index, including all configured properties:

javascript
[
{
"v": 2,
"key": { "_id": 1 },
"name": "_id_",
"ns": "test.collection"
},
{
"v": 2,
"key": { "email": 1 },
"name": "email_1",
"unique": true,
"ns": "test.collection"
},
{
"v": 2,
"key": { "createdAt": 1 },
"name": "createdAt_1",
"expireAfterSeconds": 3600,
"ns": "test.collection"
}
]

Summary

MongoDB index properties provide powerful ways to customize how your indexes work:

  • Unique indexes prevent duplicate values
  • Sparse indexes only index documents containing the indexed field
  • Partial indexes only index documents matching specific criteria
  • TTL indexes automatically remove documents after a specified time
  • Hidden indexes are maintained but not used by the query planner
  • Collation allows language-specific string comparisons

By choosing the right index properties, you can optimize performance, enforce data integrity, and implement advanced features like automatic document expiration.

Additional Resources

Exercises

  1. Create a unique index on a productCode field in a products collection.
  2. Create a TTL index that expires documents in a sessions collection after 30 minutes.
  3. Create a partial index that only indexes customers with a premiumMember field set to true.
  4. Create a case-insensitive index on a tags field using collation.
  5. Create a compound index with mixed properties: make it unique on {email: 1, organizationId: 1} but only for documents where status is "active".

Understanding and correctly applying these index properties will help you build more efficient and robust MongoDB applications.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)