How do @Indexed and @CompoundIndex work in Spring Data MongoDB, and what governs whether the indexes are actually created?
answer
- @Indexed = one field; @CompoundIndex = many fields, on the class
- def = "{'a':1,'b':-1}" — 1 asc, -1 desc, order = prefix rule
- auto-index-creation OFF by default since 3.0
- enable via spring.data.mongodb.auto-index-creation=true
- expireAfter = TTL index; unique = uniqueness
basics
~10 s@Indexed declares a secondary index on one field; @CompoundIndex (on the class) declares a multi-field index. Spring only creates them automatically if auto-index-creation is enabled, which is off by default in recent versions.
solid answer
~40 s@Indexed is a field-level annotation that declares a single-field secondary index (with options like unique, direction, sparse, TTL via expireAfter, name). @CompoundIndex is a type-level annotation declaring an index across several fields, defined by a JSON key spec like "{'lastName': 1, 'firstName': 1}", where 1/-1 is ascending/descending — order matters for query prefix matching. Crucially, Spring Data does NOT create these automatically unless auto-index-creation is enabled. Since Spring Data MongoDB 3.0 it is OFF by default; you opt in via spring.data.mongodb.auto-index-creation=true or by overriding autoIndexCreation() in your MongoConfig. When enabled, MongoMappingContext + MongoPersistentEntityIndexResolver scan the entity and create the indexes on the collection. In production many teams keep it off and manage indexes via migrations/ops to avoid startup coupling and accidental index builds.
code
java · 22 linesimport org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document("users")
@CompoundIndex(name = "name_idx", def = "{'lastName': 1, 'firstName': 1}")
public class User {
@Id private String id;
@Indexed(unique = true) // unique secondary index
private String email;
private String firstName;
private String lastName;
@Indexed(expireAfter = "30d") // TTL index: auto-delete 30 days after this date
private java.time.Instant createdAt;
}
// application.properties (indexes are NOT created without this on Spring Data MongoDB 3.0+):
// spring.data.mongodb.auto-index-creation=truego deeper
Know @Indexed indexes one field and @CompoundIndex indexes several.
Know auto-index-creation is off by default and how to enable it; know the prefix rule.
Explain managing indexes via IndexOperations/migrations and why auto-creation is risky in prod.
Weigh index build cost, startup coupling, unique-index backfill failures, and ops-driven index lifecycle at scale.
Indexes make queries fast by avoiding full collection scans. Spring Data MongoDB lets you declare them alongside your mapping. **@Indexed** (`org.springframework.data.mongodb.core.index.Indexed`) is placed on a **single property**. It declares a secondary index on that field. Options include: - `unique = true` — enforce uniqueness (a unique index; duplicate inserts fail). - `direction = IndexDirection.ASCENDING/DESCENDING` — sort direction. - `sparse = true` — index only documents where the field exists. - `name` — custom index name. - `expireAfter` (e.g. `"10s"` / a `Duration`) — creates a **TTL index** so MongoDB auto-deletes documents after that time (only valid on a date field). - `background`, `partialFilter`, `collation`. **@CompoundIndex** / **@CompoundIndexes** (`org.springframework.data.mongodb.core.index.CompoundIndex`) is placed on the **class** and defines a **multi-field** index via a `def` JSON key spec: `@CompoundIndex(def = "{'lastName': 1, 'firstName': 1}")`. The numbers are direction: `1` ascending, `-1` descending. **Field order is significant**: MongoDB can use a compound index for queries that filter on a **prefix** of its keys (e.g. an index on `{a,b,c}` supports queries on `a`, `a+b`, `a+b+c`, but not `b` alone). It also supports `unique`, `name`, `sparse`, `partialFilter`. **When are indexes actually created?** This is the key gotcha. Declaring the annotation does nothing by itself. Index creation is driven by **automatic index creation**, which: - Was **on** by default before Spring Data MongoDB 3.0. - Is **OFF by default since 3.0** (Spring Boot 2.4+). To enable it you either set `spring.data.mongodb.auto-index-creation=true` (Boot property) or override `autoIndexCreation()` to return `true` in a config class extending `AbstractMongoClientConfiguration`. When enabled, the `MongoMappingContext` (with `autoIndexCreation` true) and the `MongoPersistentEntityIndexResolver` scan entities and the `MongoPersistentEntityIndexCreator` issues `createIndex` calls, typically on application startup / first use of the entity. **Why it defaults off:** implicit index creation at startup couples app boot to DDL-like operations, can silently build expensive indexes on large collections, and makes index management non-explicit. Best practice in production is to **manage indexes deliberately** — via an ops/migration process or programmatically through `IndexOperations` (`mongoTemplate.indexOps(Entity.class).ensureIndex(...)`) run in a controlled step — and keep auto-creation off. **Gotchas:** - Adding `@Indexed` in code but leaving auto-index-creation off means **no index exists** — queries silently do full scans. - Auto-creation does not **drop** indexes you removed from code; it only ensures declared ones. Stale indexes linger. - Unique indexes can fail to build if existing data already has duplicates. - TTL (`expireAfter`) only works on a single date field and deletion is background/approximate (not instant). **When to use:** declare indexes near the model for documentation and dev convenience, but decide consciously whether creation is automatic (fine for dev/small collections) or managed externally (preferred for large production data).
- You added @Indexed(unique=true) on email but duplicates still get inserted. Why?Most likely auto-index-creation is off (default since Spring Data MongoDB 3.0), so the unique index was never created. Enable spring.data.mongodb.auto-index-creation=true or create the index via IndexOperations/ops. Also note pre-existing duplicates would block building a unique index.
- For a compound index on {status:1, createdAt:-1}, which queries can use it?Queries filtering on status alone, or status + createdAt (a left prefix). A query filtering only on createdAt cannot use this index efficiently, because MongoDB requires a prefix of the compound key.
saying these in an interview costs you the question
- Assuming @Indexed/@CompoundIndex always create indexes automatically (off by default since 3.0).
- Ignoring field order in a compound index / the prefix rule.
- Thinking auto-creation drops indexes removed from code.
- Believing TTL deletion is instantaneous.