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; @@ -25,6 +25,15 @@ import org.springframework.util.Assert;
/**
* {@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 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 { @@ -157,6 +157,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
List<IndexDefinitionHolder> 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 { @@ -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 { @@ -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 { @@ -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.

20
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java

@ -1393,6 +1393,15 @@ public class MongoPersistentEntityIndexResolverUnitTests { @@ -1393,6 +1393,15 @@ public class MongoPersistentEntityIndexResolverUnitTests {
});
}
@Test // GH-3914
public void shouldSkipMapStructuresUnlessAnnotatedWithWildcardIndex() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(
WithMapStructures.class);
assertThat(indexDefinitions).hasSize(1);
}
@Document
class MixedIndexRoot {
@ -1626,6 +1635,17 @@ public class MongoPersistentEntityIndexResolverUnitTests { @@ -1626,6 +1635,17 @@ public class MongoPersistentEntityIndexResolverUnitTests {
T entity;
}
@Document
class WithMapStructures {
Map<String, ValueObject> rootMap;
NestedInMapWithStructures nested;
ValueObject plainValue;
}
class NestedInMapWithStructures {
Map<String, ValueObject> nestedMap;
}
@Document
class EntityWithGenericTypeWrapperAsElement {
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 @@ -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]

Loading…
Cancel
Save