Index Creation

Spring Data MongoDB can automatically create indexes for entity types annotated with @Document. Index creation must be explicitly enabled since version 3.0 to prevent undesired effects with collection lifecyle and performance impact. Indexes are automatically created for the initial entity set on application startup and when accessing an entity type for the first time while the application runs.

We generally recommend explicit index creation for application-based control of indexes as Spring Data cannot automatically create indexes for collections that were recreated while the application was running.

IndexResolver provides an abstraction for programmatic index definition creation if you want to make use of @Indexed annotations such as @GeoSpatialIndexed, @TextIndexed, @CompoundIndex and @WildcardIndexed. You can use index definitions with IndexOperations to create indexes. A good point in time for index creation is on application startup, specifically after the application context was refreshed, triggered by observing ContextRefreshedEvent. This event guarantees that the context is fully initialized. Note that at this time other components, especially bean factories might have access to the MongoDB database.

Map-like properties are skipped by the IndexResolver unless annotated with @WildcardIndexed because the map key must be part of the index definition. Since the purpose of maps is the usage of dynamic keys and values, the keys cannot be resolved from static mapping metadata.

Example 1. Programmatic Index Creation for a single Domain Type
class MyListener {

  @EventListener(ContextRefreshedEvent.class)
  public void initIndicesAfterStartup() {

    MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = mongoTemplate
                .getConverter().getMappingContext();

    IndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext);

    IndexOperations indexOps = mongoTemplate.indexOps(DomainType.class);
    resolver.resolveIndexFor(DomainType.class).forEach(indexOps::ensureIndex);
  }
}
Example 2. Programmatic Index Creation for all Initial Entities
class MyListener{

  @EventListener(ContextRefreshedEvent.class)
  public void initIndicesAfterStartup() {

    MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = mongoTemplate
        .getConverter().getMappingContext();

    // consider only entities that are annotated with @Document
    mappingContext.getPersistentEntities()
                            .stream()
                            .filter(it -> it.isAnnotationPresent(Document.class))
                            .forEach(it -> {

    IndexOperations indexOps = mongoTemplate.indexOps(it.getType());
    resolver.resolveIndexFor(it.getType()).forEach(indexOps::ensureIndex);
    });
  }
}

Alternatively, if you want to ensure index and collection presence before any component is able to access your database from your application, declare a @Bean method for MongoTemplate and include the code from above before returning the MongoTemplate object.

To turn automatic index creation ON please override autoIndexCreation() in your configuration.

@Configuration
public class Config extends AbstractMongoClientConfiguration {

  @Override
  public boolean autoIndexCreation() {
    return true;
  }

// ...
}
Automatic index creation is turned OFF by default as of version 3.0.

Compound Indexes

Compound indexes are also supported. They are defined at the class level, rather than on individual properties.

Compound indexes are very important to improve the performance of queries that involve criteria on multiple fields

Here’s an example that creates a compound index of lastName in ascending order and age in descending order:

Example 3. Example Compound Index Usage
package com.mycompany.domain;

@Document
@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")
public class Person {

  @Id
  private ObjectId id;
  private Integer age;
  private String firstName;
  private String lastName;

}

@CompoundIndex is repeatable using @CompoundIndexes as its container.

@Document
@CompoundIndex(name = "cmp-idx-one", def = "{'firstname': 1, 'lastname': -1}")
@CompoundIndex(name = "cmp-idx-two", def = "{'address.city': -1, 'address.street': 1}")
public class Person {

  String firstname;
  String lastname;

  Address address;

  // ...
}

Hashed Indexes

Hashed indexes allow hash based sharding within a sharded cluster. Using hashed field values to shard collections results in a more random distribution. For details, refer to the MongoDB Documentation.

Here’s an example that creates a hashed index for _id:

Example 4. Example Hashed Index Usage
@Document
public class DomainType {

  @HashIndexed @Id String id;

  // ...
}

Hashed indexes can be created next to other index definitions like shown below, in that case both indices are created:

Example 5. Example Hashed Index Usage togehter with simple index
@Document
public class DomainType {

  @Indexed
  @HashIndexed
  String value;

  // ...
}

In case the example above is too verbose, a compound annotation allows to reduce the number of annotations that need to be declared on a property:

Example 6. Example Composed Hashed Index Usage
@Document
public class DomainType {

  @IndexAndHash(name = "idx...")                            (1)
  String value;

  // ...
}

@Indexed
@HashIndexed
@Retention(RetentionPolicy.RUNTIME)
public @interface IndexAndHash {

  @AliasFor(annotation = Indexed.class, attribute = "name") (1)
  String name() default "";
}
1 Potentially register an alias for certain attributes of the meta annotation.

Although index creation via annotations comes in handy for many scenarios cosider taking over more control by setting up indices manually via IndexOperations.

mongoOperations.indexOpsFor(Jedi.class)
  .ensureIndex(HashedIndex.hashed("useTheForce"));

Wildcard Indexes

A WildcardIndex is an index that can be used to include all fields or specific ones based a given (wildcard) pattern. For details, refer to the MongoDB Documentation.

The index can be set up programmatically using WildcardIndex via IndexOperations.

Example 7. Programmatic WildcardIndex setup
mongoOperations
    .indexOps(User.class)
    .ensureIndex(new WildcardIndex("userMetadata"));
db.user.createIndex({ "userMetadata.$**" : 1 }, {})

The @WildcardIndex annotation allows a declarative index setup that can used either with a document type or property.

If placed on a type that is a root level domain entity (one annotated with @Document) , the index resolver will create a wildcard index for it.

Example 8. Wildcard index on domain type
@Document
@WildcardIndexed
public class Product {
	// …
}
db.product.createIndex({ "$**" : 1 },{})

The wildcardProjection can be used to specify keys to in-/exclude in the index.

Example 9. Wildcard index with wildcardProjection
@Document
@WildcardIndexed(wildcardProjection = "{ 'userMetadata.age' : 0 }")
public class User {
    private @Id String id;
    private UserMetadata userMetadata;
}
db.user.createIndex(
  { "$**" : 1 },
  { "wildcardProjection" :
    { "userMetadata.age" : 0 }
  }
)

Wildcard indexes can also be expressed by adding the annotation directly to the field. Please note that wildcardProjection is not allowed on nested paths such as properties. Projections on types annotated with @WildcardIndexed are omitted during index creation.

Example 10. Wildcard index on property
@Document
public class User {
    private @Id String id;

    @WildcardIndexed
    private UserMetadata userMetadata;
}
db.user.createIndex({ "userMetadata.$**" : 1 }, {})

Text Indexes

The text index feature is disabled by default for MongoDB v.2.4.

Creating a text index allows accumulating several fields into a searchable full-text index. It is only possible to have one text index per collection, so all fields marked with @TextIndexed are combined into this index. Properties can be weighted to influence the document score for ranking results. The default language for the text index is English.To change the default language, set the language attribute to whichever language you want (for example,@Document(language="spanish")). Using a property called language or @Language lets you define a language override on a per-document base. The following example shows how to created a text index and set the language to Spanish:

Example 11. Example Text Index Usage
@Document(language = "spanish")
class SomeEntity {

    @TextIndexed String foo;

    @Language String lang;

    Nested nested;
}

class Nested {

    @TextIndexed(weight=5) String bar;
    String roo;
}