diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java index e2137ac04..26503508b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java @@ -25,6 +25,15 @@ import org.springframework.util.Assert; /** * {@link IndexResolver} finds those {@link IndexDefinition}s to be created for a given class. + *

+ * The {@link IndexResolver} considers index annotations like {@link Indexed}, {@link GeoSpatialIndexed}, + * {@link HashIndexed}, {@link TextIndexed} and {@link WildcardIndexed} on properties as well as {@link CompoundIndex} + * and {@link WildcardIndexed} on types. + *

+ * Unless specified otherwise the index name will be created out of the keys/path involved in the index.
+ * {@link TextIndexed} properties are collected into a single index that covers the detected fields.
+ * {@link java.util.Map} like structures, unless annotated with {@link WildcardIndexed}, are skipped because the + * {@link java.util.Map.Entry#getKey() map key}, which cannot be resolved from static metadata, needs to be part of the index. * * @author Christoph Strobl * @author Thomas Darimont diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java index 5fdb1cbc4..28ce6c0f2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java @@ -157,6 +157,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { List indexes, CycleGuard guard) { try { + if (isMapWithoutWildcardIndex(persistentProperty)) { + return; + } + if (persistentProperty.isEntity()) { indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty), @@ -217,6 +221,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { Path propertyPath = path.append(persistentProperty); guard.protect(persistentProperty, propertyPath); + if (isMapWithoutWildcardIndex(persistentProperty)) { + return; + } + if (persistentProperty.isEntity()) { try { indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), @@ -346,6 +354,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName()); } + if(persistentProperty.isMap()) { + return; + } + TextIndexed indexed = persistentProperty.findAnnotation(TextIndexed.class); if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) { @@ -798,6 +810,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { return expression.getValue(evaluationContext, Object.class); } + private static boolean isMapWithoutWildcardIndex(MongoPersistentProperty property) { + return property.isMap() && !property.isAnnotationPresent(WildcardIndexed.class); + } + /** * {@link CycleGuard} holds information about properties and the paths for accessing those. This information is used * to detect potential cycles within the references. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java index 30f6a9bfc..b4f1bcd55 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java @@ -1393,6 +1393,15 @@ public class MongoPersistentEntityIndexResolverUnitTests { }); } + @Test // GH-3914 + public void shouldSkipMapStructuresUnlessAnnotatedWithWildcardIndex() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType( + WithMapStructures.class); + + assertThat(indexDefinitions).hasSize(1); + } + @Document class MixedIndexRoot { @@ -1626,6 +1635,17 @@ public class MongoPersistentEntityIndexResolverUnitTests { T entity; } + @Document + class WithMapStructures { + Map rootMap; + NestedInMapWithStructures nested; + ValueObject plainValue; + } + + class NestedInMapWithStructures { + Map nestedMap; + } + @Document class EntityWithGenericTypeWrapperAsElement { List> listWithGeneircTypeElement; diff --git a/src/main/asciidoc/reference/mapping.adoc b/src/main/asciidoc/reference/mapping.adoc index e30182669..9ba17cb1f 100644 --- a/src/main/asciidoc/reference/mapping.adoc +++ b/src/main/asciidoc/reference/mapping.adoc @@ -402,12 +402,17 @@ Indexes are automatically created for the initial entity set on application star 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`. +`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 structures, unless annotated with `@WildcardIndexed`, are skipped by the `IndexResolver` because the _map key_, which cannot be resolved from static metadata, needs to be part of the index definition. +=== + .Programmatic Index Creation for a single Domain Type ==== [source,java]