Skip to content

Avoid creating invalid index definitions for Map-like properties #3915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.4.0-SNAPSHOT</version>
<version>3.4.0-GH-3914-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data MongoDB</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-benchmarks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.4.0-SNAPSHOT</version>
<version>3.4.0-GH-3914-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.4.0-SNAPSHOT</version>
<version>3.4.0-GH-3914-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.4.0-SNAPSHOT</version>
<version>3.4.0-GH-3914-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@

/**
* {@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ private void potentiallyAddIndexForProperty(MongoPersistentEntity<?> root, Mongo
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),
Expand Down Expand Up @@ -220,6 +224,10 @@ private void guardAndPotentiallyAddIndexForProperty(MongoPersistentProperty pers
Path propertyPath = path.append(persistentProperty);
guard.protect(persistentProperty, propertyPath);

if (isMapWithoutWildcardIndex(persistentProperty)) {
return;
}

if (persistentProperty.isEntity()) {
try {
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
Expand Down Expand Up @@ -349,6 +357,10 @@ public void doWithPersistentProperty(MongoPersistentProperty persistentProperty)
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
}

if(persistentProperty.isMap()) {
return;
}

TextIndexed indexed = persistentProperty.findAnnotation(TextIndexed.class);

if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) {
Expand Down Expand Up @@ -801,6 +813,10 @@ private static Object evaluate(String value, EvaluationContext evaluationContext
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,15 @@ public void rejectsWildcardProjectionOnNestedPaths() {
});
}

@Test // GH-3914
public void shouldSkipMapStructuresUnlessAnnotatedWithWildcardIndex() {

List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(
WithMapStructures.class);

assertThat(indexDefinitions).hasSize(1);
}

@Document
class MixedIndexRoot {

Expand Down Expand Up @@ -1626,6 +1635,17 @@ class GenericEntityWrapper<T> {
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;
Expand Down
7 changes: 6 additions & 1 deletion src/main/asciidoc/reference/mapping.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down