7 changed files with 391 additions and 387 deletions
@ -0,0 +1,340 @@ |
|||||||
|
[[mapping.index-creation]] |
||||||
|
= 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. |
||||||
|
|
||||||
|
[WARNING] |
||||||
|
==== |
||||||
|
``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. |
||||||
|
==== |
||||||
|
|
||||||
|
.Programmatic Index Creation for a single Domain Type |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
---- |
||||||
|
==== |
||||||
|
|
||||||
|
.Programmatic Index Creation for all Initial Entities |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
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. |
||||||
|
|
||||||
|
[NOTE] |
||||||
|
==== |
||||||
|
To turn automatic index creation _ON_ please override `autoIndexCreation()` in your configuration. |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@Configuration |
||||||
|
public class Config extends AbstractMongoClientConfiguration { |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean autoIndexCreation() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
---- |
||||||
|
==== |
||||||
|
|
||||||
|
IMPORTANT: Automatic index creation is turned _OFF_ by default as of version 3.0. |
||||||
|
|
||||||
|
[[mapping-usage-indexes.compound-index]] |
||||||
|
== Compound Indexes |
||||||
|
|
||||||
|
Compound indexes are also supported. They are defined at the class level, rather than on individual properties. |
||||||
|
|
||||||
|
NOTE: 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 Compound Index Usage |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
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; |
||||||
|
|
||||||
|
} |
||||||
|
---- |
||||||
|
==== |
||||||
|
|
||||||
|
[TIP] |
||||||
|
==== |
||||||
|
`@CompoundIndex` is repeatable using `@CompoundIndexes` as its container. |
||||||
|
|
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@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; |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
---- |
||||||
|
==== |
||||||
|
|
||||||
|
[[mapping-usage-indexes.hashed-index]] |
||||||
|
== 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 https://docs.mongodb.com/manual/core/index-hashed/[MongoDB Documentation]. |
||||||
|
|
||||||
|
Here's an example that creates a hashed index for `_id`: |
||||||
|
|
||||||
|
.Example Hashed Index Usage |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@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 Hashed Index Usage togehter with simple index |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@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 Composed Hashed Index Usage |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@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. |
||||||
|
==== |
||||||
|
|
||||||
|
[NOTE] |
||||||
|
==== |
||||||
|
Although index creation via annotations comes in handy for many scenarios cosider taking over more control by setting up indices manually via `IndexOperations`. |
||||||
|
|
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
mongoOperations.indexOpsFor(Jedi.class) |
||||||
|
.ensureIndex(HashedIndex.hashed("useTheForce")); |
||||||
|
---- |
||||||
|
==== |
||||||
|
|
||||||
|
[[mapping-usage-indexes.wildcard-index]] |
||||||
|
== 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 https://docs.mongodb.com/manual/core/index-wildcard/[MongoDB Documentation]. |
||||||
|
|
||||||
|
The index can be set up programmatically using `WildcardIndex` via `IndexOperations`. |
||||||
|
|
||||||
|
.Programmatic WildcardIndex setup |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
mongoOperations |
||||||
|
.indexOps(User.class) |
||||||
|
.ensureIndex(new WildcardIndex("userMetadata")); |
||||||
|
---- |
||||||
|
[source,javascript] |
||||||
|
---- |
||||||
|
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. |
||||||
|
|
||||||
|
.Wildcard index on domain type |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@Document |
||||||
|
@WildcardIndexed |
||||||
|
public class Product { |
||||||
|
// … |
||||||
|
} |
||||||
|
---- |
||||||
|
[source,javascript] |
||||||
|
---- |
||||||
|
db.product.createIndex({ "$**" : 1 },{}) |
||||||
|
---- |
||||||
|
==== |
||||||
|
|
||||||
|
The `wildcardProjection` can be used to specify keys to in-/exclude in the index. |
||||||
|
|
||||||
|
.Wildcard index with `wildcardProjection` |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@Document |
||||||
|
@WildcardIndexed(wildcardProjection = "{ 'userMetadata.age' : 0 }") |
||||||
|
public class User { |
||||||
|
private @Id String id; |
||||||
|
private UserMetadata userMetadata; |
||||||
|
} |
||||||
|
---- |
||||||
|
[source,javascript] |
||||||
|
---- |
||||||
|
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. |
||||||
|
|
||||||
|
.Wildcard index on property |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@Document |
||||||
|
public class User { |
||||||
|
private @Id String id; |
||||||
|
|
||||||
|
@WildcardIndexed |
||||||
|
private UserMetadata userMetadata; |
||||||
|
} |
||||||
|
---- |
||||||
|
[source,javascript] |
||||||
|
---- |
||||||
|
db.user.createIndex({ "userMetadata.$**" : 1 }, {}) |
||||||
|
---- |
||||||
|
==== |
||||||
|
|
||||||
|
[[mapping-usage-indexes.text-index]] |
||||||
|
== Text Indexes |
||||||
|
|
||||||
|
NOTE: 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 Text Index Usage |
||||||
|
==== |
||||||
|
[source,java] |
||||||
|
---- |
||||||
|
@Document(language = "spanish") |
||||||
|
class SomeEntity { |
||||||
|
|
||||||
|
@TextIndexed String foo; |
||||||
|
|
||||||
|
@Language String lang; |
||||||
|
|
||||||
|
Nested nested; |
||||||
|
} |
||||||
|
|
||||||
|
class Nested { |
||||||
|
|
||||||
|
@TextIndexed(weight=5) String bar; |
||||||
|
String roo; |
||||||
|
} |
||||||
|
---- |
||||||
|
==== |
||||||
Loading…
Reference in new issue