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 138801506..7f2ee8403 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 @@ -46,6 +46,7 @@ import java.lang.annotation.Target; * @author Johno Crawford * @author Christoph Strobl * @author Dave Perryman + * @author Stefan Tirea */ @Target({ ElementType.TYPE }) @Documented @@ -163,4 +164,19 @@ public @interface CompoundIndex { * @since 3.1 */ String partialFilter() default ""; + + /** + * The actual collation definition in JSON format or a {@link org.springframework.expression.spel.standard.SpelExpression + * template expression} resolving to either a JSON String or a {@link org.bson.Document}. The keys of the JSON + * document are configuration options for the collation (language-specific rules for string comparison). + *

+ * TODO write code documentation & example!!! + *
+ * + * @return empty String by default. + * @see https://www.mongodb.com/docs/manual/reference/collation/ + * @since 3.4 + */ + String collation() default ""; } 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 3b41a4d80..ca82e95dc 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 @@ -31,6 +31,7 @@ import java.lang.annotation.Target; * @author Christoph Strobl * @author Jordi Llach * @author Mark Paluch + * @author Stefan Tirea */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @@ -173,4 +174,14 @@ public @interface Indexed { * @since 3.1 */ String partialFilter() default ""; + + /** + * Apply collation configuration for field
+ * + * @return empty by default. + * @see https://www.mongodb.com/docs/manual/reference/collation// + * @since 3.1 + */ + String collation() default ""; } 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 a6424161e..4e4f308c6 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 @@ -74,6 +74,7 @@ import org.springframework.util.StringUtils; * @author Martin Macko * @author Mark Paluch * @author Dave Perryman + * @author Stefan Tirea * @since 1.5 */ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -453,6 +454,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { indexDefinition.partial(evaluatePartialFilter(index.partialFilter(), entity)); } + if (StringUtils.hasText(index.collation())) { + indexDefinition.collation(Collation.parse(index.collation())); + } + return new IndexDefinitionHolder(dotPath, indexDefinition, collection); } @@ -572,6 +577,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { indexDefinition.partial(evaluatePartialFilter(index.partialFilter(), persistentProperty.getOwner())); } + if (StringUtils.hasText(index.collation())) { + indexDefinition.collation(Collation.parse(index.collation())); + } + return new IndexDefinitionHolder(dotPath, indexDefinition, collection); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java index 2a5f0fb57..f0d642911 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java @@ -29,6 +29,7 @@ import org.springframework.data.domain.Sort.Direction; * * @author Oliver Gierke * @author Christoph Strobl + * @author Stefan Tirea */ public class IndexInfoUnitTests { @@ -37,6 +38,7 @@ public class IndexInfoUnitTests { static final String INDEX_WITH_EXPIRATION_TIME = "{ \"v\" : 2, \"key\" : { \"lastModifiedDate\" : 1 },\"name\" : \"expire-after-last-modified\", \"ns\" : \"db.collectio\", \"expireAfterSeconds\" : 3600 }"; static final String HASHED_INDEX = "{ \"v\" : 2, \"key\" : { \"score\" : \"hashed\" }, \"name\" : \"score_hashed\", \"ns\" : \"db.collection\" }"; static final String WILDCARD_INDEX = "{ \"v\" : 2, \"key\" : { \"$**\" : 1 }, \"name\" : \"$**_1\", \"wildcardProjection\" : { \"fieldA\" : 0, \"fieldB.fieldC\" : 0 } }"; + static final String INDEX_WITH_COLLATION = "{ \"v\" : 2, \"key\" : { \"_id\" : 1 }, \"name\" : \"projectName\", \"collation\": { \"locale\": \"en_US\", \"strength\": 2 } }"; @Test public void isIndexForFieldsCorrectly() { @@ -87,7 +89,14 @@ public class IndexInfoUnitTests { @Test // GH-3225 public void readsWildcardIndexProjectionCorrectly() { - assertThat(getIndexInfo(WILDCARD_INDEX).getWildcardProjection()).contains(new Document("fieldA", 0).append("fieldB.fieldC", 0)); + assertThat(getIndexInfo(WILDCARD_INDEX).getWildcardProjection()) + .contains(new Document("fieldA", 0).append("fieldB.fieldC", 0)); + } + + @Test // DATAMONGO-2133 + public void collationParsedCorrectly() { + assertThat(getIndexInfo(INDEX_WITH_COLLATION).getCollation()) + .contains(Document.parse("{ \"locale\": \"en_US\", \"strength\": 2 }")); } private static IndexInfo getIndexInfo(String documentJson) { 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 e7ce8d168..3e4e4226b 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 @@ -61,6 +61,7 @@ import org.springframework.data.util.ClassTypeInformation; * @author Christoph Strobl * @author Mark Paluch * @author Dave Perryman + * @author Stefan Tirea */ @RunWith(Suite.class) @SuiteClasses({ IndexResolutionTests.class, GeoSpatialIndexResolutionTests.class, CompoundIndexResolutionTests.class, @@ -699,6 +700,19 @@ public class MongoPersistentEntityIndexResolverUnitTests { org.bson.Document.parse("{'value': {'$exists': true}}")); } + @Test // DATAMONGO-2133 + public void compoundIndexWithCollation() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType( + CompoundIndexWithCollation.class); + + IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); + assertThat(indexDefinition.getIndexOptions()) + .isEqualTo(new org.bson.Document().append("name", "compound_index_with_collation").append("collation", + new org.bson.Document().append("locale", "en_US").append("strength", 2))); + assertThat(indexDefinition.getIndexKeys()).isEqualTo(new org.bson.Document().append("foo", 1)); + } + @Document("CompoundIndexOnLevelOne") class CompoundIndexOnLevelOne { @@ -774,6 +788,11 @@ public class MongoPersistentEntityIndexResolverUnitTests { @CompoundIndex(name = "compound_index_with_partial", def = "{'foo': 1, 'bar': -1}", background = true, unique = true, partialFilter = "{'value': {'$exists': true}}") class SingleCompoundIndexWithPartialFilter {} + + @Document + @CompoundIndex(name = "compound_index_with_collation", def = "{'foo': 1}", + collation = "{'locale': 'en_US', 'strength': 2}") + class CompoundIndexWithCollation {} } public static class TextIndexedResolutionTests { @@ -1400,6 +1419,18 @@ public class MongoPersistentEntityIndexResolverUnitTests { assertThat(indexDefinitions).hasSize(1); } + @Test // DATAMONGO-2133 + public void indexedWithCollation() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType( + IndexedWithCollation.class); + + IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); + assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document().append("name", "value") + .append("unique", true) + .append("collation", new org.bson.Document().append("locale", "en_US").append("strength", 2))); + } + @Document class MixedIndexRoot { @@ -1717,6 +1748,12 @@ public class MongoPersistentEntityIndexResolverUnitTests { @ComposedHashIndexed(name = "idx-name") String value; } + @Document + class IndexedWithCollation { + @Indexed(collation = "{'locale': 'en_US', 'strength': 2}", unique = true) // + private String value; + } + @HashIndexed @Indexed @Retention(RetentionPolicy.RUNTIME)