Add collation for an index via @CompoundIndex and @Index annotations.

Closes #3002, closes #4130

Original Pull Request: #4131
This commit is contained in:
Stefan Tirea
2022-07-29 02:05:09 +02:00
committed by Christoph Strobl
parent 2a4ee12363
commit ff9d338bd7
5 changed files with 83 additions and 1 deletions

View File

@@ -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).
* <br><br>
* TODO write code documentation & example!!!
* <br>
*
* @return empty String by default.
* @see <a href=
* "https://www.mongodb.com/docs/manual/reference/collation/">https://www.mongodb.com/docs/manual/reference/collation/</a>
* @since 3.4
*/
String collation() default "";
}

View File

@@ -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 <br />
*
* @return empty by default.
* @see <a href=
* "https://www.mongodb.com/docs/manual/reference/collation/">https://www.mongodb.com/docs/manual/reference/collation//</a>
* @since 3.1
*/
String collation() default "";
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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<IndexDefinitionHolder> 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<IndexDefinitionHolder> 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)