Skip to content

Commit 7f223d1

Browse files
christophstroblmp911de
authored andcommitted
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.
1 parent cffee12 commit 7f223d1

File tree

4 files changed

+51
-1
lines changed

4 files changed

+51
-1
lines changed

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

+9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525

2626
/**
2727
* {@link IndexResolver} finds those {@link IndexDefinition}s to be created for a given class.
28+
* <p>
29+
* The {@link IndexResolver} considers index annotations like {@link Indexed}, {@link GeoSpatialIndexed},
30+
* {@link HashIndexed}, {@link TextIndexed} and {@link WildcardIndexed} on properties as well as {@link CompoundIndex}
31+
* and {@link WildcardIndexed} on types.
32+
* <p>
33+
* Unless specified otherwise the index name will be created out of the keys/path involved in the index. <br />
34+
* {@link TextIndexed} properties are collected into a single index that covers the detected fields. <br />
35+
* {@link java.util.Map} like structures, unless annotated with {@link WildcardIndexed}, are skipped because the
36+
* {@link java.util.Map.Entry#getKey() map key}, which cannot be resolved from static metadata, needs to be part of the index.
2837
*
2938
* @author Christoph Strobl
3039
* @author Thomas Darimont

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

+16
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ private void potentiallyAddIndexForProperty(MongoPersistentEntity<?> root, Mongo
157157
List<IndexDefinitionHolder> indexes, CycleGuard guard) {
158158

159159
try {
160+
if (isMapWithoutWildcardIndex(persistentProperty)) {
161+
return;
162+
}
163+
160164
if (persistentProperty.isEntity()) {
161165
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
162166
persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty),
@@ -217,6 +221,10 @@ private void guardAndPotentiallyAddIndexForProperty(MongoPersistentProperty pers
217221
Path propertyPath = path.append(persistentProperty);
218222
guard.protect(persistentProperty, propertyPath);
219223

224+
if (isMapWithoutWildcardIndex(persistentProperty)) {
225+
return;
226+
}
227+
220228
if (persistentProperty.isEntity()) {
221229
try {
222230
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
@@ -346,6 +354,10 @@ public void doWithPersistentProperty(MongoPersistentProperty persistentProperty)
346354
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
347355
}
348356

357+
if(persistentProperty.isMap()) {
358+
return;
359+
}
360+
349361
TextIndexed indexed = persistentProperty.findAnnotation(TextIndexed.class);
350362

351363
if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) {
@@ -798,6 +810,10 @@ private static Object evaluate(String value, EvaluationContext evaluationContext
798810
return expression.getValue(evaluationContext, Object.class);
799811
}
800812

813+
private static boolean isMapWithoutWildcardIndex(MongoPersistentProperty property) {
814+
return property.isMap() && !property.isAnnotationPresent(WildcardIndexed.class);
815+
}
816+
801817
/**
802818
* {@link CycleGuard} holds information about properties and the paths for accessing those. This information is used
803819
* to detect potential cycles within the references.

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

+20
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,15 @@ public void rejectsWildcardProjectionOnNestedPaths() {
13931393
});
13941394
}
13951395

1396+
@Test // GH-3914
1397+
public void shouldSkipMapStructuresUnlessAnnotatedWithWildcardIndex() {
1398+
1399+
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(
1400+
WithMapStructures.class);
1401+
1402+
assertThat(indexDefinitions).hasSize(1);
1403+
}
1404+
13961405
@Document
13971406
class MixedIndexRoot {
13981407

@@ -1626,6 +1635,17 @@ class GenericEntityWrapper<T> {
16261635
T entity;
16271636
}
16281637

1638+
@Document
1639+
class WithMapStructures {
1640+
Map<String, ValueObject> rootMap;
1641+
NestedInMapWithStructures nested;
1642+
ValueObject plainValue;
1643+
}
1644+
1645+
class NestedInMapWithStructures {
1646+
Map<String, ValueObject> nestedMap;
1647+
}
1648+
16291649
@Document
16301650
class EntityWithGenericTypeWrapperAsElement {
16311651
List<GenericEntityWrapper<DocumentWithNamedIndex>> listWithGeneircTypeElement;

src/main/asciidoc/reference/mapping.adoc

+6-1
Original file line numberDiff line numberDiff line change
@@ -402,12 +402,17 @@ Indexes are automatically created for the initial entity set on application star
402402

403403
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.
404404

405-
`IndexResolver` provides an abstraction for programmatic index definition creation if you want to make use of `@Indexed` annotations such as `@GeoSpatialIndexed`, `@TextIndexed`, `@CompoundIndex`.
405+
`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`.
406406
You can use index definitions with `IndexOperations` to create indexes.
407407
A good point in time for index creation is on application startup, specifically after the application context was refreshed, triggered by observing `ContextRefreshedEvent`.
408408
This event guarantees that the context is fully initialized.
409409
Note that at this time other components, especially bean factories might have access to the MongoDB database.
410410

411+
[WARNING]
412+
===
413+
`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.
414+
===
415+
411416
.Programmatic Index Creation for a single Domain Type
412417
====
413418
[source,java]

0 commit comments

Comments
 (0)