From ad54789b3b5850c6d5d467029adee00a7965717c Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 16 Dec 2021 12:08:18 +0100 Subject: [PATCH 1/2] Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-benchmarks/pom.xml | 2 +- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index b6410e6887..544cd5edcd 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 3.4.0-SNAPSHOT + 3.4.0-GH-3914-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index e2704a6753..c1d664ac19 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 3.4.0-SNAPSHOT + 3.4.0-GH-3914-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index b75f8bf624..67ae271dcf 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-mongodb-parent - 3.4.0-SNAPSHOT + 3.4.0-GH-3914-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index ca96626cc9..d8c5170101 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 3.4.0-SNAPSHOT + 3.4.0-GH-3914-SNAPSHOT ../pom.xml From 525240fb37f4b80d0c5ac8a84adae9cfc53a053f Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Dec 2021 08:57:48 +0100 Subject: [PATCH 2/2] 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. --- .../mongodb/core/index/IndexResolver.java | 9 +++++++++ .../MongoPersistentEntityIndexResolver.java | 16 +++++++++++++++ ...ersistentEntityIndexResolverUnitTests.java | 20 +++++++++++++++++++ src/main/asciidoc/reference/mapping.adoc | 7 ++++++- 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java index e2137ac047..26503508b7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexResolver.java @@ -25,6 +25,15 @@ /** * {@link IndexResolver} finds those {@link IndexDefinition}s to be created for a given class. + *

+ * 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. + *

+ * Unless specified otherwise the index name will be created out of the keys/path involved in the index.
+ * {@link TextIndexed} properties are collected into a single index that covers the detected fields.
+ * {@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 diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java index 5d773cefc9..baa4b1c36a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java @@ -158,6 +158,10 @@ private void potentiallyAddIndexForProperty(MongoPersistentEntity root, Mongo List indexes, CycleGuard guard) { try { + if (isMapWithoutWildcardIndex(persistentProperty)) { + return; + } + if (persistentProperty.isEntity()) { indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty), @@ -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), @@ -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()) { @@ -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. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java index 30f6a9bfc5..b4f1bcd555 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java @@ -1393,6 +1393,15 @@ public void rejectsWildcardProjectionOnNestedPaths() { }); } + @Test // GH-3914 + public void shouldSkipMapStructuresUnlessAnnotatedWithWildcardIndex() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType( + WithMapStructures.class); + + assertThat(indexDefinitions).hasSize(1); + } + @Document class MixedIndexRoot { @@ -1626,6 +1635,17 @@ class GenericEntityWrapper { T entity; } + @Document + class WithMapStructures { + Map rootMap; + NestedInMapWithStructures nested; + ValueObject plainValue; + } + + class NestedInMapWithStructures { + Map nestedMap; + } + @Document class EntityWithGenericTypeWrapperAsElement { List> listWithGeneircTypeElement; diff --git a/src/main/asciidoc/reference/mapping.adoc b/src/main/asciidoc/reference/mapping.adoc index e301826697..9ba17cb1f3 100644 --- a/src/main/asciidoc/reference/mapping.adoc +++ b/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. -`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]