diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java index 55fee13fa..eb31e7a96 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java @@ -73,7 +73,43 @@ public @interface CompoundIndex { boolean dropDups() default false; /** - * The name of the index to be created. + * The name of the index to be created.
+ *
+ * The name will only be applied as is when defined on root level. For usage on nested or embedded structures the + * provided name will be prefixed with the path leading to the entity.
+ *
+ * The structure below + * + *
+	 * 
+	 * @Document
+	 * class Root {
+	 *   Hybrid hybrid;
+	 *   Nested nested;
+	 * }
+	 * 
+	 * @Document
+	 * @CompoundIndex(name = "compound_index", def = "{'h1': 1, 'h2': 1}")
+	 * class Hybrid {
+	 *   String h1, h2;
+	 * }
+	 * 
+	 * @CompoundIndex(name = "compound_index", def = "{'n1': 1, 'n2': 1}")
+	 * class Nested {
+	 *   String n1, n2;
+	 * }
+	 * 
+	 * 
+ * + * resolves in the following index structures + * + *
+	 * 
+	 * db.root.ensureIndex( { hybrid.h1: 1, hybrid.h2: 1 } , { name: "hybrid.compound_index" } )
+	 * db.root.ensureIndex( { nested.n1: 1, nested.n2: 1 } , { name: "nested.compound_index" } )
+	 * db.hybrid.ensureIndex( { h1: 1, h2: 1 } , { name: "compound_index" } )
+	 * 
+	 * 
* * @return */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java index 3bf128c14..7b4c09c79 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java @@ -32,7 +32,41 @@ import java.lang.annotation.Target; public @interface GeoSpatialIndexed { /** - * Name of the property in the document that contains the [x, y] or radial coordinates to index. + * Index name.
+ *
+ * The name will only be applied as is when defined on root level. For usage on nested or embedded structures the + * provided name will be prefixed with the path leading to the entity.
+ *
+ * The structure below + * + *
+	 * 
+	 * @Document
+	 * class Root {
+	 *   Hybrid hybrid;
+	 *   Nested nested;
+	 * }
+	 * 
+	 * @Document
+	 * class Hybrid {
+	 *   @GeoSpatialIndexed(name="index") Point h1;
+	 * }
+	 * 
+	 * class Nested {
+	 *   @GeoSpatialIndexed(name="index") Point n1;
+	 * }
+	 * 
+	 * 
+ * + * resolves in the following index structures + * + *
+	 * 
+	 * db.root.ensureIndex( { hybrid.h1: "2d" } , { name: "hybrid.index" } )
+	 * db.root.ensureIndex( { nested.n1: "2d" } , { name: "nested.index" } )
+	 * db.hybrid.ensureIndex( { h1: "2d" } , { name: "index" } )
+	 * 
+	 * 
* * @return */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java index 73aaeb15e..a907d4770 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java @@ -58,7 +58,41 @@ public @interface Indexed { boolean dropDups() default false; /** - * Index name. + * Index name.
+ *
+ * The name will only be applied as is when defined on root level. For usage on nested or embedded structures the + * provided name will be prefixed with the path leading to the entity.
+ *
+ * The structure below + * + *
+	 * 
+	 * @Document
+	 * class Root {
+	 *   Hybrid hybrid;
+	 *   Nested nested;
+	 * }
+	 * 
+	 * @Document
+	 * class Hybrid {
+	 *   @Indexed(name="index") String h1;
+	 * }
+	 * 
+	 * class Nested {
+	 *   @Indexed(name="index") String n1;
+	 * }
+	 * 
+	 * 
+ * + * resolves in the following index structures + * + *
+	 * 
+	 * db.root.ensureIndex( { hybrid.h1: 1 } , { name: "hybrid.index" } )
+	 * db.root.ensureIndex( { nested.n1: 1 } , { name: "nested.index" } )
+	 * db.hybrid.ensureIndex( { h1: 1} , { name: "index" } )
+	 * 
+	 * 
* * @return */ 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 d51089a35..947a56b9a 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 @@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.mapping.PropertyHandler; @@ -96,7 +95,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { Assert.notNull(document, "Given entity is not collection root."); final List indexInformation = new ArrayList(); - indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root.getType())); + indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root)); indexInformation.addAll(potentiallyCreateTextIndexDefinition(root)); final CycleGuard guard = new CycleGuard(); @@ -138,10 +137,11 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { private List resolveIndexForClass(final Class type, final String path, final String collection, final CycleGuard guard) { + MongoPersistentEntity entity = mappingContext.getPersistentEntity(type); + final List indexInformation = new ArrayList(); - indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(path, collection, type)); + indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(path, collection, entity)); - MongoPersistentEntity entity = mappingContext.getPersistentEntity(type); entity.doWithProperties(new PropertyHandler() { @Override @@ -183,14 +183,13 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { } private List potentiallyCreateCompoundIndexDefinitions(String dotPath, String collection, - Class type) { + MongoPersistentEntity entity) { - if (AnnotationUtils.findAnnotation(type, CompoundIndexes.class) == null - && AnnotationUtils.findAnnotation(type, CompoundIndex.class) == null) { + if (entity.findAnnotation(CompoundIndexes.class) == null && entity.findAnnotation(CompoundIndex.class) == null) { return Collections.emptyList(); } - return createCompoundIndexDefinitions(dotPath, collection, type); + return createCompoundIndexDefinitions(dotPath, collection, entity); } private Collection potentiallyCreateTextIndexDefinition(MongoPersistentEntity root) { @@ -278,21 +277,21 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { * @return */ protected List createCompoundIndexDefinitions(String dotPath, String fallbackCollection, - Class type) { + MongoPersistentEntity entity) { List indexDefinitions = new ArrayList(); - CompoundIndexes indexes = AnnotationUtils.findAnnotation(type, CompoundIndexes.class); + CompoundIndexes indexes = entity.findAnnotation(CompoundIndexes.class); if (indexes != null) { for (CompoundIndex index : indexes.value()) { - indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index)); + indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity)); } } - CompoundIndex index = AnnotationUtils.findAnnotation(type, CompoundIndex.class); + CompoundIndex index = entity.findAnnotation(CompoundIndex.class); if (index != null) { - indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index)); + indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity)); } return indexDefinitions; @@ -300,13 +299,13 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @SuppressWarnings("deprecation") protected IndexDefinitionHolder createCompoundIndexDefinition(String dotPath, String fallbackCollection, - CompoundIndex index) { + CompoundIndex index, MongoPersistentEntity entity) { CompoundIndexDefinition indexDefinition = new CompoundIndexDefinition(resolveCompoundIndexKeyFromStringDefinition( dotPath, index.def())); if (!index.useGeneratedName()) { - indexDefinition.named(index.name()); + indexDefinition.named(pathAwareIndexName(index.name(), dotPath, null)); } if (index.unique()) { @@ -377,7 +376,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { IndexDirection.ASCENDING.equals(index.direction()) ? Sort.Direction.ASC : Sort.Direction.DESC); if (!index.useGeneratedName()) { - indexDefinition.named(StringUtils.hasText(index.name()) ? index.name() : dotPath); + indexDefinition.named(pathAwareIndexName(index.name(), dotPath, persitentProperty)); } if (index.unique()) { @@ -419,7 +418,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { indexDefinition.withMin(index.min()).withMax(index.max()); if (!index.useGeneratedName()) { - indexDefinition.named(StringUtils.hasText(index.name()) ? index.name() : persistentProperty.getName()); + indexDefinition.named(pathAwareIndexName(index.name(), dotPath, persistentProperty)); } indexDefinition.typed(index.type()).withBucketSize(index.bucketSize()).withAdditionalField(index.additionalField()); @@ -427,6 +426,23 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { return new IndexDefinitionHolder(dotPath, indexDefinition, collection); } + private String pathAwareIndexName(String indexName, String dotPath, MongoPersistentProperty property) { + + String nameToUse = StringUtils.hasText(indexName) ? indexName : ""; + + if (!StringUtils.hasText(dotPath) || (property != null && dotPath.equals(property.getFieldName()))) { + return StringUtils.hasText(nameToUse) ? nameToUse : dotPath; + } + + if (StringUtils.hasText(dotPath)) { + + nameToUse = StringUtils.hasText(nameToUse) ? (property != null ? dotPath.replace("." + property.getFieldName(), + "") : dotPath) + "." + nameToUse : dotPath; + } + return nameToUse; + + } + /** * {@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/MongoPersistentEntityIndexCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java index 8887f9bde..815dd775d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java @@ -163,8 +163,8 @@ public class MongoPersistentEntityIndexCreatorUnitTests { new MongoPersistentEntityIndexCreator(mappingContext, factory); assertThat(keysCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("company.address.location", "2d").get())); - assertThat(optionsCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("name", "location").add("min", -180) - .add("max", 180).add("bits", 26).get())); + assertThat(optionsCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("name", "company.address.location") + .add("min", -180).add("max", 180).add("bits", 26).get())); } /** 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 c05aec1df..2af355571 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 @@ -744,6 +744,60 @@ public class MongoPersistentEntityIndexResolverUnitTests { .resolveIndexForEntity(dummy); } + /** + * @see DATAMONGO-1025 + */ + @Test + public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedCompoundIndexFixedOnCollection() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedDocumentHavingNamedCompoundIndex.class); + assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), + equalTo("propertyOfTypeHavingNamedCompoundIndex.c_index")); + } + + /** + * @see DATAMONGO-1025 + */ + @Test + public void shouldUseIndexNameForNestedTypesWithNamedCompoundIndexDefinition() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedTypeHavingNamedCompoundIndex.class); + assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), + equalTo("propertyOfTypeHavingNamedCompoundIndex.c_index")); + } + + /** + * @see DATAMONGO-1025 + */ + @Test + public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedIndexFixedOnCollection() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedDocumentHavingNamedIndex.class); + assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), + equalTo("propertyOfTypeHavingNamedIndex.property_index")); + } + + /** + * @see DATAMONGO-1025 + */ + @Test + public void shouldUseIndexNameForNestedTypesWithNamedIndexDefinition() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedTypeHavingNamedIndex.class); + assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), + equalTo("propertyOfTypeHavingNamedIndex.property_index")); + } + + /** + * @see DATAMONGO-1025 + */ + @Test + public void shouldUseIndexNameOnRootLevel() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNamedIndex.class); + assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("property_index")); + } + @Document static class MixedIndexRoot { @@ -836,6 +890,54 @@ public class MongoPersistentEntityIndexResolverUnitTests { List cyclic; } + + @Document + @CompoundIndex(name = "c_index", def = "{ foo:1, bar:1 }") + static class DocumentWithNamedCompoundIndex { + + String property; + } + + @Document + static class DocumentWithNamedIndex { + + @Indexed(name = "property_index") String property; + } + + static class TypeWithNamedIndex { + + @Indexed(name = "property_index") String property; + } + + @Document + static class DocumentWithNestedDocumentHavingNamedCompoundIndex { + + DocumentWithNamedCompoundIndex propertyOfTypeHavingNamedCompoundIndex; + } + + @CompoundIndex(name = "c_index", def = "{ foo:1, bar:1 }") + static class TypeWithNamedCompoundIndex { + String property; + } + + @Document + static class DocumentWithNestedTypeHavingNamedCompoundIndex { + + TypeWithNamedCompoundIndex propertyOfTypeHavingNamedCompoundIndex; + } + + @Document + static class DocumentWithNestedDocumentHavingNamedIndex { + + DocumentWithNamedIndex propertyOfTypeHavingNamedIndex; + } + + @Document + static class DocumentWithNestedTypeHavingNamedIndex { + + TypeWithNamedIndex propertyOfTypeHavingNamedIndex; + } + } private static List prepareMappingContextAndResolveIndexForType(Class type) {