Skip to content

Commit cbd56a0

Browse files
Avoid creating invalid index definitions for Map like structures.
This commit makes sure to exclude Map like structures from index inspection unless annotated with WilcardIndexed.
1 parent ad54789 commit cbd56a0

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-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

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

160160
try {
161+
if (isMapWithoutWildcardIndex(persistentProperty)) {
162+
return;
163+
}
164+
161165
if (persistentProperty.isEntity()) {
162166
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
163167
persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty),
@@ -220,6 +224,10 @@ private void guardAndPotentiallyAddIndexForProperty(MongoPersistentProperty pers
220224
Path propertyPath = path.append(persistentProperty);
221225
guard.protect(persistentProperty, propertyPath);
222226

227+
if (isMapWithoutWildcardIndex(persistentProperty)) {
228+
return;
229+
}
230+
223231
if (persistentProperty.isEntity()) {
224232
try {
225233
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
@@ -801,6 +809,10 @@ private static Object evaluate(String value, EvaluationContext evaluationContext
801809
return expression.getValue(evaluationContext, Object.class);
802810
}
803811

812+
private static boolean isMapWithoutWildcardIndex(MongoPersistentProperty property) {
813+
return property.isMap() && !property.isAnnotationPresent(WildcardIndexed.class);
814+
}
815+
804816
/**
805817
* {@link CycleGuard} holds information about properties and the paths for accessing those. This information is used
806818
* 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)