Skip to content
ExponaBLOG
MONGODB, NOSQL & DATA MODELING · DAY 014 / 100

Special index types: a practical MongoDB & NoSQL guide

Indexes for specific needs.

Expona Admin6 min read

Introduction

By day 14 of any data modeling journey, you've likely built compound indexes, debugged slow queries, and watched explain() output more times than you'd like to admit. But general-purpose indexes only get you so far. When you need to search text content, geofence user locations, automatically clean up expired data, or index only a slice of your collection, you need special index types. This guide covers the practical MongoDB tools that solve specific problems without the overhead of indexing everything.

Text indexes unlock full-text search capabilities in MongoDB, letting you query natural language across one or more fields. Unlike simple prefix matching or regex patterns, text indexes tokenize content, handle stemming (in supported languages), and rank results by relevance.

Consider a product catalog where you need to search titles and descriptions. Without a text index, every search query triggers a collection scan. With one, you get instant results:

javascript

db.products.createIndex({
  title: "text",
  description: "text"
});

// Query: find products matching "ergonomic laptop stand"
db.products.find({
  $text: { $search: "ergonomic laptop stand" }
}).projection({
  score: { $meta: "textScore" }
}).sort({
  score: { $meta: "textScore" }
});

This returns results ranked by relevance, with higher scores appearing first. The tokenization means "laptops" will match "laptop", and "ergonomically" will match "ergonomic"—standard stemming behavior across most languages.

Text indexes shine in e-commerce search, content management systems, and knowledge bases. One collection can have only one text index by default, though you can work around this with separate indexes on individual fields if needed. Also note that text search is case-insensitive and doesn't support phrase queries natively—if you need exact phrase matching or complex boolean logic, consider a search engine like Elasticsearch.

2dsphere for geospatial queries

Location-based features are central to ride-hailing, delivery, and mapping applications. The 2dsphere index handles geographic coordinates and answers questions like "what restaurants are within 2km of my location?" or "which drivers are on this route?"

First, store coordinates in GeoJSON format. MongoDB expects longitude before latitude (opposite of typical lat/lng notation):

javascript

db.restaurants.insertOne({
  name: "The Pasta House",
  location: {
    type: "Point",
    coordinates: [-73.97, 40.77]  // [longitude, latitude]
  },
  cuisine: "Italian"
});

db.restaurants.createIndex({ location: "2dsphere" });

// Find restaurants within 5km (5000 meters)
db.restaurants.find({
  location: {
    $near: {
      $geometry: {
        type: "Point",
        coordinates: [-73.95, 40.78]
      },
      $maxDistance: 5000
    }
  }
});

The $near operator returns results sorted by distance, closest first. For simpler "within this box" queries, use $geoWithin:

javascript

db.restaurants.find({
  location: {
    $geoWithin: {
      $centerSphere: [[-73.95, 40.78], 5000 / 6371000]
    }
  }
});

The sphere radius is in radians; divide your meters by Earth's mean radius (6,371,000m) to convert. In production ride-hailing and delivery systems, 2dsphere indexes are essential performance tools—without them, proximity queries degrade rapidly as your location dataset grows.

TTL indexes to expire documents

Many applications need automatic data cleanup: session tokens that expire after 24 hours, temporary OTP records, analytics events older than 90 days, or draft data that shouldn't persist. TTL (Time To Live) indexes handle this elegantly by automatically deleting documents when a timestamp field reaches a specified age.

Create a TTL index on a date field, specifying expiration time in seconds:

javascript

db.sessions.createIndex(
  { createdAt: 1 },
  { expireAfterSeconds: 86400 }  // 24 hours
);

// Insert a session
db.sessions.insertOne({
  userId: "user123",
  token: "abc123xyz",
  createdAt: new Date()
});

// After 24 hours, MongoDB automatically deletes this document

TTL cleanup runs roughly every 60 seconds, so there's no guarantee a document vanishes instantly at the expiration moment. If you need precise deletion timing, TTL indexes aren't suitable; instead, use scheduled jobs or application-level cleanup.

In CRM systems, use TTL indexes for temporary contact records waiting email confirmation. In trading platforms, clean up temporary price alerts. In SaaS applications, auto-expire debug logs. The pattern applies anywhere you have time-bound data:

javascript

db.emailVerifications.createIndex(
  { verificationSentAt: 1 },
  { expireAfterSeconds: 3600 }  // 1 hour to verify
);

db.debugLogs.createIndex(
  { timestamp: 1 },
  { expireAfterSeconds: 604800 }  // 7 days retention
);

One limitation: a collection can have only one TTL index. If you need multiple expiration schedules, maintain separate collections or handle deletion via application logic.

Partial indexes for subsets

Not every document in a collection needs indexing. If you frequently filter on a subset—say, only active users, or orders still pending—a partial index indexes just those documents, saving memory and index maintenance overhead.

Define a filter expression using the same query syntax as find():

javascript

db.orders.createIndex(
  { customerId: 1, createdAt: -1 },
  {
    partialFilterExpression: {
      status: { $in: ["pending", "processing"] }
    }
  }
);

// This query uses the partial index efficiently
db.orders.find({
  status: "pending",
  customerId: "cust456"
}).sort({ createdAt: -1 });

// This query does NOT use the partial index
// because it includes completed orders
db.orders.find({
  customerId: "cust456"
});

Partial indexes are particularly valuable for:

  • Soft deletes: Only index non-deleted documents with { deletedAt: null }
  • State machines: Only index active or in-flight states
  • Multi-tenant applications: Combine tenant IDs with status filters
  • Large collections with sparse active data: Reduce index size significantly

In an e-commerce platform, index only orders with status pending or processing. In a CRM, index only leads marked active or qualified. The partial index stays lean while still providing fast lookups for the queries that matter.

Common mistakes

Text index confusion with regex: Teams often reach for regex search first, not realizing it can't use indexes effectively. Regex queries do collection scans unless they're anchored at the start of a field. Always consider text indexes for natural language search.

GeoJSON coordinate order: Swapping longitude and latitude is one of the most common geospatial bugs. Remember: MongoDB expects [longitude, latitude], not [lat, lng]. Many coordinate systems default to lat/lng, so double-check your data source.

TTL index field is not a date: TTL indexes only work on BSON Date fields. Storing timestamps as Unix integers or strings won't trigger automatic deletion. Ensure your expiration field is explicitly a Date.

Forgetting partial index predicates in queries: A partial index only helps if your query matches its filter expression. If you create a partial index for { status: "active" } but then query all statuses, the index won't be used, and you'll hit an unindexed collection scan.

Over-indexing with partials: It's tempting to create many specialized partial indexes, but each adds memory overhead and slows writes. Start with your most common query patterns, then add partial indexes only where measurements show they help.

Key takeaways

  • Text indexes enable full-text search with tokenization and relevance ranking; use them for product search, content search, and natural language queries.
  • 2dsphere indexes handle geospatial queries efficiently; always store coordinates in GeoJSON format with longitude before latitude.
  • TTL indexes automatically expire documents after a specified duration; use them for sessions, OTP records, and time-bound temporary data.
  • Partial indexes reduce memory footprint and index maintenance by indexing only a filtered subset of your collection; apply them to soft deletes, state machines, and queries on active subsets.
  • Choose the right special index type based on your query pattern, not just availability; measure impact and avoid index bloat.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles