DATAMONGO-2316 - Configure simple collation on resolved text-indexes for non-simple collation Collections.

We now configure resolved text-indexes with a simple collation when the text index is created in a collection that uses a non-simple collation.

This explicit setting is required by MongoDB to properly create a text index.

Previously, index creation failed.
This commit is contained in:
Mark Paluch
2019-07-08 13:50:37 +02:00
parent f2ac413c61
commit 5b3a9434f1
4 changed files with 66 additions and 11 deletions

View File

@@ -33,6 +33,7 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.Association;
@@ -41,13 +42,13 @@ import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.CycleGuard.Path;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.TextIndexIncludeOptions.IncludeStrategy;
import org.springframework.data.mongodb.core.index.TextIndexDefinition.TextIndexDefinitionBuilder;
import org.springframework.data.mongodb.core.index.TextIndexDefinition.TextIndexedFieldSpec;
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.spel.EvaluationContextProvider;
@@ -80,7 +81,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoPersistentEntityIndexResolver.class);
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private final MongoMappingContext mappingContext;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private EvaluationContextProvider evaluationContextProvider = EvaluationContextProvider.DEFAULT;
/**
@@ -88,7 +89,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
*
* @param mappingContext must not be {@literal null}.
*/
public MongoPersistentEntityIndexResolver(MongoMappingContext mappingContext) {
public MongoPersistentEntityIndexResolver(
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
Assert.notNull(mappingContext, "Mapping context must not be null in order to resolve index definitions");
this.mappingContext = mappingContext;
@@ -261,6 +263,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
LOGGER.info(e.getMessage());
}
if (root.hasCollation()) {
indexDefinitionBuilder.withSimpleCollation();
}
TextIndexDefinition indexDefinition = indexDefinitionBuilder.build();
if (!indexDefinition.hasFieldSpec()) {

View File

@@ -21,6 +21,7 @@ import java.util.Set;
import org.bson.Document;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -40,9 +41,10 @@ public class TextIndexDefinition implements IndexDefinition {
private @Nullable String defaultLanguage;
private @Nullable String languageOverride;
private @Nullable IndexFilter filter;
private @Nullable Collation collation;
TextIndexDefinition() {
fieldSpecs = new LinkedHashSet<TextIndexedFieldSpec>();
fieldSpecs = new LinkedHashSet<>();
}
/**
@@ -116,6 +118,10 @@ public class TextIndexDefinition implements IndexDefinition {
options.put("default_language", defaultLanguage);
}
if (collation != null) {
options.put("collation", collation.toDocument());
}
Document weightsDocument = new Document();
for (TextIndexedFieldSpec fieldSpec : fieldSpecs) {
if (fieldSpec.isWeighted()) {
@@ -348,6 +354,17 @@ public class TextIndexDefinition implements IndexDefinition {
return this;
}
/**
* Configure to use simple {@link Collation}. Required if the collection uses a non-simple collation.
*
* @since 2.2
*/
public TextIndexDefinitionBuilder withSimpleCollation() {
this.instance.collation = Collation.simple();
return this;
}
public TextIndexDefinition build() {
return this.instance;
}

View File

@@ -744,16 +744,28 @@ public class MongoPersistentEntityIndexResolverUnitTests {
assertThat(indexDefinitions).hasSize(1);
assertIndexPathAndCollection("bar", "textIndexOnSinglePropertyInRoot", indexDefinitions.get(0));
assertThat(indexDefinitions.get(0).getIndexOptions()).doesNotContainKey("collation");
}
@Test // DATAMONGO-2316
public void shouldEnforceSimpleCollationOnTextIndex() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(
TextIndexWithCollation.class);
assertThat(indexDefinitions).hasSize(1);
assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("collation",
new org.bson.Document("locale", "simple"));
}
@Test // DATAMONGO-937
public void shouldResolveMultiFieldTextIndexCorrectly() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(
TextIndexOnMutiplePropertiesInRoot.class);
TextIndexOnMultiplePropertiesInRoot.class);
assertThat(indexDefinitions).hasSize(1);
assertIndexPathAndCollection(new String[] { "foo", "bar" }, "textIndexOnMutiplePropertiesInRoot",
assertIndexPathAndCollection(new String[] { "foo", "bar" }, "textIndexOnMultiplePropertiesInRoot",
indexDefinitions.get(0));
}
@@ -862,8 +874,14 @@ public class MongoPersistentEntityIndexResolverUnitTests {
@TextIndexed String bar;
}
@Document(collation = "de_AT")
static class TextIndexWithCollation {
@TextIndexed String foo;
}
@Document
static class TextIndexOnMutiplePropertiesInRoot {
static class TextIndexOnMultiplePropertiesInRoot {
@TextIndexed String foo;

View File

@@ -22,12 +22,15 @@ import java.util.List;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.config.AbstractIntegrationTests;
import org.springframework.data.mongodb.core.CollectionOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Language;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.test.util.MongoVersionRule;
import org.springframework.data.util.Version;
@@ -35,6 +38,7 @@ import com.mongodb.WriteConcern;
/**
* @author Christoph Strobl
* @author Mark Paluch
*/
public class TextIndexTests extends AbstractIntegrationTests {
@@ -48,11 +52,21 @@ public class TextIndexTests extends AbstractIntegrationTests {
template.setWriteConcern(WriteConcern.JOURNALED);
this.indexOps = template.indexOps(TextIndexedDocumentRoot.class);
template.dropCollection(TextIndexedDocumentRoot.class);
template.createCollection(TextIndexedDocumentRoot.class,
CollectionOptions.empty().collation(Collation.of("de_AT")));
}
@Test // DATAMONGO-937
@Test // DATAMONGO-937, DATAMONGO-2316
public void indexInfoShouldHaveBeenCreatedCorrectly() {
IndexResolver indexResolver = IndexResolver.create(template.getConverter().getMappingContext());
for (IndexDefinition indexDefinition : indexResolver.resolveIndexFor(TextIndexedDocumentRoot.class)) {
indexOps.ensureIndex(indexDefinition);
}
List<IndexInfo> indexInfos = indexOps.getIndexInfo();
assertThat(indexInfos.size()).isEqualTo(2);
@@ -69,16 +83,16 @@ public class TextIndexTests extends AbstractIntegrationTests {
assertThat(textIndexInfo.getLanguage()).isEqualTo("spanish");
}
@Document(language = "spanish")
@Document(language = "spanish", collation = "de_AT")
static class TextIndexedDocumentRoot {
@TextIndexed String textIndexedPropertyWithDefaultWeight;
@TextIndexed(weight = 5) String textIndexedPropertyWithWeight;
TextIndexedDocumentWihtLanguageOverride nestedDocument;
TextIndexedDocumentWithLanguageOverride nestedDocument;
}
static class TextIndexedDocumentWihtLanguageOverride {
static class TextIndexedDocumentWithLanguageOverride {
@Language String lang;