Browse Source

Avoid creating invalid index definitions for Map-like properties.

This commit makes sure to exclude Map like structures from index inspection unless annotated with WilcardIndexed.

Closes #3914, closes #3901
Original pull request: #3915.
pull/3960/head
Christoph Strobl 4 years ago committed by Mark Paluch
parent
commit
7f223d1332
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java
  2. 16
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java
  3. 20
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java
  4. 7
      src/main/asciidoc/reference/mapping.adoc

9
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. * {@link IndexResolver} finds those {@link IndexDefinition}s to be created for a given class.
* <p>
* 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.
* <p>
* Unless specified otherwise the index name will be created out of the keys/path involved in the index. <br />
* {@link TextIndexed} properties are collected into a single index that covers the detected fields. <br />
* {@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 Christoph Strobl
* @author Thomas Darimont * @author Thomas Darimont

16
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java

@ -157,6 +157,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
List<IndexDefinitionHolder> indexes, CycleGuard guard) { List<IndexDefinitionHolder> indexes, CycleGuard guard) {
try { try {
if (isMapWithoutWildcardIndex(persistentProperty)) {
return;
}
if (persistentProperty.isEntity()) { if (persistentProperty.isEntity()) {
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty), persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty),
@ -217,6 +221,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
Path propertyPath = path.append(persistentProperty); Path propertyPath = path.append(persistentProperty);
guard.protect(persistentProperty, propertyPath); guard.protect(persistentProperty, propertyPath);
if (isMapWithoutWildcardIndex(persistentProperty)) {
return;
}
if (persistentProperty.isEntity()) { if (persistentProperty.isEntity()) {
try { try {
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
@ -346,6 +354,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName()); indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
} }
if(persistentProperty.isMap()) {
return;
}
TextIndexed indexed = persistentProperty.findAnnotation(TextIndexed.class); TextIndexed indexed = persistentProperty.findAnnotation(TextIndexed.class);
if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) { if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) {
@ -798,6 +810,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
return expression.getValue(evaluationContext, Object.class); 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 * {@link CycleGuard} holds information about properties and the paths for accessing those. This information is used
* to detect potential cycles within the references. * to detect potential cycles within the references.

20
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<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(
WithMapStructures.class);
assertThat(indexDefinitions).hasSize(1);
}
@Document @Document
class MixedIndexRoot { class MixedIndexRoot {
@ -1626,6 +1635,17 @@ public class MongoPersistentEntityIndexResolverUnitTests {
T entity; T entity;
} }
@Document
class WithMapStructures {
Map<String, ValueObject> rootMap;
NestedInMapWithStructures nested;
ValueObject plainValue;
}
class NestedInMapWithStructures {
Map<String, ValueObject> nestedMap;
}
@Document @Document
class EntityWithGenericTypeWrapperAsElement { class EntityWithGenericTypeWrapperAsElement {
List<GenericEntityWrapper<DocumentWithNamedIndex>> listWithGeneircTypeElement; List<GenericEntityWrapper<DocumentWithNamedIndex>> listWithGeneircTypeElement;

7
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. 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. 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`. 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. 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. 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 .Programmatic Index Creation for a single Domain Type
==== ====
[source,java] [source,java]

Loading…
Cancel
Save