DATAMONGO-937 - Add support for creating text index.
We now support creating text index based on information gathered on domain types. Using @TextIndexed marks properties to be considered for the full text index. Use the weight attribute to influence document scoring during search operations. Please note that using @TextIndexed on entity properties forces all properties of any sub document to be considered as part of the text index. Any set weight will in that case be propagated to the siblings taking the most recent weight information into account, which means that a the weight attribute can be overridden for properties in sub documents. The setting the index default language can be done via @Document(language) while @Language can be used to define the language_override field. As text search is disabled by default for mongodb 2.4 we use a jUnit ClassRule to restrict integration tests potentially creating text index (as the entities for testing are found in the classpath) to only be executed in when a 2.6+ mongodb server is present. For usage hints please see section 6.3.4 (Text Indexes) of reference manual. Original pull request: #198.
This commit is contained in:
committed by
Thomas Darimont
parent
998bb09a92
commit
84913cecab
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,6 +36,7 @@ import com.mongodb.MongoException;
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Komi Innocent
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
@@ -142,6 +143,13 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
if ("2d".equals(value)) {
|
||||
indexFields.add(IndexField.geo(key));
|
||||
} else if ("text".equals(value)) {
|
||||
|
||||
DBObject weights = (DBObject) ix.get("weights");
|
||||
for (String fieldName : weights.keySet()) {
|
||||
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Double keyValue = new Double(value.toString());
|
||||
@@ -159,8 +167,8 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
boolean unique = ix.containsField("unique") ? (Boolean) ix.get("unique") : false;
|
||||
boolean dropDuplicates = ix.containsField("dropDups") ? (Boolean) ix.get("dropDups") : false;
|
||||
boolean sparse = ix.containsField("sparse") ? (Boolean) ix.get("sparse") : false;
|
||||
|
||||
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse));
|
||||
String language = ix.containsField("default_language") ? (String) ix.get("default_language") : "";
|
||||
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language));
|
||||
}
|
||||
|
||||
return indexInfoList;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,22 +24,33 @@ import org.springframework.util.ObjectUtils;
|
||||
* Value object for an index field.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public final class IndexField {
|
||||
|
||||
enum Type {
|
||||
GEO, TEXT, DEFAULT;
|
||||
}
|
||||
|
||||
private final String key;
|
||||
private final Direction direction;
|
||||
private final boolean isGeo;
|
||||
private final Type type;
|
||||
private final Float weight;
|
||||
|
||||
private IndexField(String key, Direction direction, boolean isGeo) {
|
||||
private IndexField(String key, Direction direction, Type type) {
|
||||
this(key, direction, type, Float.NaN);
|
||||
}
|
||||
|
||||
private IndexField(String key, Direction direction, Type type, Float weight) {
|
||||
|
||||
Assert.hasText(key);
|
||||
Assert.isTrue(direction != null ^ isGeo);
|
||||
Assert.isTrue(direction != null ^ (Type.GEO.equals(type) || Type.TEXT.equals(type)));
|
||||
|
||||
this.key = key;
|
||||
this.direction = direction;
|
||||
this.isGeo = isGeo;
|
||||
this.type = type == null ? Type.DEFAULT : type;
|
||||
this.weight = weight == null ? Float.NaN : weight;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,12 +64,12 @@ public final class IndexField {
|
||||
@Deprecated
|
||||
public static IndexField create(String key, Order order) {
|
||||
Assert.notNull(order);
|
||||
return new IndexField(key, order.toDirection(), false);
|
||||
return new IndexField(key, order.toDirection(), Type.DEFAULT);
|
||||
}
|
||||
|
||||
public static IndexField create(String key, Direction order) {
|
||||
Assert.notNull(order);
|
||||
return new IndexField(key, order, false);
|
||||
return new IndexField(key, order, Type.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +79,16 @@ public final class IndexField {
|
||||
* @return
|
||||
*/
|
||||
public static IndexField geo(String key) {
|
||||
return new IndexField(key, null, true);
|
||||
return new IndexField(key, null, Type.GEO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text {@link IndexField} for the given key.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public static IndexField text(String key, Float weight) {
|
||||
return new IndexField(key, null, Type.TEXT, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,10 +121,20 @@ public final class IndexField {
|
||||
/**
|
||||
* Returns whether the {@link IndexField} is a geo index field.
|
||||
*
|
||||
* @return the isGeo
|
||||
* @return true if type is {@link Type#GEO}.
|
||||
*/
|
||||
public boolean isGeo() {
|
||||
return isGeo;
|
||||
return Type.GEO.equals(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wheter the {@link IndexField} is a text index field.
|
||||
*
|
||||
* @return true if type is {@link Type#TEXT}
|
||||
* @since 1.6
|
||||
*/
|
||||
public boolean isText() {
|
||||
return Type.TEXT.equals(type);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -125,7 +155,7 @@ public final class IndexField {
|
||||
IndexField that = (IndexField) obj;
|
||||
|
||||
return this.key.equals(that.key) && ObjectUtils.nullSafeEquals(this.direction, that.direction)
|
||||
&& this.isGeo == that.isGeo;
|
||||
&& this.type == that.type;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -138,7 +168,8 @@ public final class IndexField {
|
||||
int result = 17;
|
||||
result += 31 * ObjectUtils.nullSafeHashCode(key);
|
||||
result += 31 * ObjectUtils.nullSafeHashCode(direction);
|
||||
result += 31 * ObjectUtils.nullSafeHashCode(isGeo);
|
||||
result += 31 * ObjectUtils.nullSafeHashCode(type);
|
||||
result += 31 * ObjectUtils.nullSafeHashCode(weight);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -148,6 +179,7 @@ public final class IndexField {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("IndexField [ key: %s, direction: %s, isGeo: %s]", key, direction, isGeo);
|
||||
return String.format("IndexField [ key: %s, direction: %s, type: %s, weight: %s]", key, direction, type, weight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,11 @@ import java.util.List;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class IndexInfo {
|
||||
|
||||
private final List<IndexField> indexFields;
|
||||
@@ -31,14 +36,30 @@ public class IndexInfo {
|
||||
private final boolean unique;
|
||||
private final boolean dropDuplicates;
|
||||
private final boolean sparse;
|
||||
private final String language;
|
||||
|
||||
/**
|
||||
* @deprecated Will be removed in 1.7. Please use {@link #IndexInfo(List, String, boolean, boolean, boolean, String)}
|
||||
* @param indexFields
|
||||
* @param name
|
||||
* @param unique
|
||||
* @param dropDuplicates
|
||||
* @param sparse
|
||||
*/
|
||||
@Deprecated
|
||||
public IndexInfo(List<IndexField> indexFields, String name, boolean unique, boolean dropDuplicates, boolean sparse) {
|
||||
this(indexFields, name, unique, dropDuplicates, sparse, "");
|
||||
}
|
||||
|
||||
public IndexInfo(List<IndexField> indexFields, String name, boolean unique, boolean dropDuplicates, boolean sparse,
|
||||
String language) {
|
||||
|
||||
this.indexFields = Collections.unmodifiableList(indexFields);
|
||||
this.name = name;
|
||||
this.unique = unique;
|
||||
this.dropDuplicates = dropDuplicates;
|
||||
this.sparse = sparse;
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,14 +105,23 @@ public class IndexInfo {
|
||||
return sparse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 1.6
|
||||
*/
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IndexInfo [indexFields=" + indexFields + ", name=" + name + ", unique=" + unique + ", dropDuplicates="
|
||||
+ dropDuplicates + ", sparse=" + sparse + "]";
|
||||
+ dropDuplicates + ", sparse=" + sparse + ", language=" + language + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (dropDuplicates ? 1231 : 1237);
|
||||
@@ -99,6 +129,7 @@ public class IndexInfo {
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + (sparse ? 1231 : 1237);
|
||||
result = prime * result + (unique ? 1231 : 1237);
|
||||
result = prime * result + ObjectUtils.nullSafeHashCode(language);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -137,6 +168,9 @@ public class IndexInfo {
|
||||
if (unique != other.unique) {
|
||||
return false;
|
||||
}
|
||||
if (!ObjectUtils.nullSafeEquals(language, other.language)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -29,6 +30,9 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mongodb.core.index.Index.Duplicates;
|
||||
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.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
@@ -93,6 +97,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root.getType()));
|
||||
indexInformation.addAll(potentiallyCreateTextIndexDefinition(root));
|
||||
|
||||
final CycleGuard guard = new CycleGuard();
|
||||
|
||||
@@ -188,6 +193,82 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
return createCompoundIndexDefinitions(dotPath, collection, type);
|
||||
}
|
||||
|
||||
private Collection<? extends IndexDefinitionHolder> potentiallyCreateTextIndexDefinition(MongoPersistentEntity<?> root) {
|
||||
|
||||
TextIndexDefinitionBuilder indexDefinitionBuilder = new TextIndexDefinitionBuilder().named(root.getType()
|
||||
.getSimpleName() + "_TextIndex");
|
||||
|
||||
if (StringUtils.hasText(root.getLanguage())) {
|
||||
indexDefinitionBuilder.withDefaultLanguage(root.getLanguage());
|
||||
}
|
||||
|
||||
try {
|
||||
appendTextIndexInformation("", indexDefinitionBuilder, root,
|
||||
new TextIndexIncludeOptions(IncludeStrategy.DEFAULT), new CycleGuard());
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.warn(e.getMessage());
|
||||
}
|
||||
|
||||
TextIndexDefinition indexDefinition = indexDefinitionBuilder.build();
|
||||
|
||||
if (!indexDefinition.hasFieldSpec()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
IndexDefinitionHolder holder = new IndexDefinitionHolder("", indexDefinition, root.getCollection());
|
||||
return Collections.singletonList(holder);
|
||||
|
||||
}
|
||||
|
||||
private void appendTextIndexInformation(final String dotPath,
|
||||
final TextIndexDefinitionBuilder indexDefinitionBuilder, MongoPersistentEntity<?> entity,
|
||||
final TextIndexIncludeOptions includeOptions, final CycleGuard guard) {
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
|
||||
@Override
|
||||
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
guard.protect(persistentProperty, dotPath);
|
||||
|
||||
if (persistentProperty.isLanguageProperty()) {
|
||||
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
|
||||
}
|
||||
|
||||
TextIndexed indexed = persistentProperty.findAnnotation(TextIndexed.class);
|
||||
|
||||
if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) {
|
||||
|
||||
String propertyDotPath = (StringUtils.hasText(dotPath) ? dotPath + "." : "")
|
||||
+ persistentProperty.getFieldName();
|
||||
|
||||
Float weight = indexed != null ? indexed.weight()
|
||||
: (includeOptions.getParentFieldSpec() != null ? includeOptions.getParentFieldSpec().getWeight() : 1.0F);
|
||||
|
||||
if (persistentProperty.isEntity()) {
|
||||
|
||||
TextIndexIncludeOptions optionsForNestedType = includeOptions;
|
||||
if (!IncludeStrategy.FORCE.equals(includeOptions.getStrategy()) && indexed != null) {
|
||||
optionsForNestedType = new TextIndexIncludeOptions(IncludeStrategy.FORCE, new TextIndexedFieldSpec(
|
||||
propertyDotPath, weight));
|
||||
}
|
||||
|
||||
try {
|
||||
appendTextIndexInformation(propertyDotPath, indexDefinitionBuilder,
|
||||
mappingContext.getPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard);
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.warn(e.getMessage(), e);
|
||||
}
|
||||
} else if (includeOptions.isForce() || indexed != null) {
|
||||
indexDefinitionBuilder.onField(propertyDotPath, weight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link IndexDefinition} wrapped in {@link IndexDefinitionHolder} for {@link CompoundIndexes} of given type.
|
||||
*
|
||||
@@ -549,4 +630,41 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
return indexDefinition.getIndexOptions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
static class TextIndexIncludeOptions {
|
||||
|
||||
enum IncludeStrategy {
|
||||
FORCE, DEFAULT;
|
||||
}
|
||||
|
||||
private final IncludeStrategy strategy;
|
||||
|
||||
private final TextIndexedFieldSpec parentFieldSpec;
|
||||
|
||||
public TextIndexIncludeOptions(IncludeStrategy strategy, TextIndexedFieldSpec parentFieldSpec) {
|
||||
this.strategy = strategy;
|
||||
this.parentFieldSpec = parentFieldSpec;
|
||||
}
|
||||
|
||||
public TextIndexIncludeOptions(IncludeStrategy strategy) {
|
||||
this(strategy, null);
|
||||
}
|
||||
|
||||
public IncludeStrategy getStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
public TextIndexedFieldSpec getParentFieldSpec() {
|
||||
return parentFieldSpec;
|
||||
}
|
||||
|
||||
public boolean isForce() {
|
||||
return IncludeStrategy.FORCE.equals(strategy);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* {@link IndexDefinition} to span multiple keys for text search.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
public class TextIndexDefinition implements IndexDefinition {
|
||||
|
||||
private String name;
|
||||
private Set<TextIndexedFieldSpec> fieldSpecs;
|
||||
private String defaultLanguage;
|
||||
private String languageOverride;
|
||||
|
||||
TextIndexDefinition() {
|
||||
fieldSpecs = new LinkedHashSet<TextIndexedFieldSpec>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link TextIndexDefinition} for all fields in the document.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static TextIndexDefinition forAllFields() {
|
||||
return new TextIndexDefinitionBuilder().onAllFields().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link TextIndexDefinitionBuilder} to create {@link TextIndexDefinition}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static TextIndexDefinitionBuilder builder() {
|
||||
return new TextIndexDefinitionBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fieldSpec
|
||||
*/
|
||||
public void addFieldSpec(TextIndexedFieldSpec fieldSpec) {
|
||||
this.fieldSpecs.add(fieldSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fieldSpecs
|
||||
*/
|
||||
public void addFieldSpecs(Collection<TextIndexedFieldSpec> fieldSpecs) {
|
||||
this.fieldSpecs.addAll(fieldSpecs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the {@link TextIndexDefinition} has fields assigned.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasFieldSpec() {
|
||||
return !fieldSpecs.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getIndexKeys()
|
||||
*/
|
||||
@Override
|
||||
public DBObject getIndexKeys() {
|
||||
|
||||
DBObject keys = new BasicDBObject();
|
||||
for (TextIndexedFieldSpec fieldSpec : fieldSpecs) {
|
||||
keys.put(fieldSpec.fieldname, "text");
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getIndexOptions()
|
||||
*/
|
||||
@Override
|
||||
public DBObject getIndexOptions() {
|
||||
|
||||
DBObject options = new BasicDBObject();
|
||||
if (StringUtils.hasText(name)) {
|
||||
options.put("name", name);
|
||||
}
|
||||
if (StringUtils.hasText(defaultLanguage)) {
|
||||
options.put("default_language", defaultLanguage);
|
||||
}
|
||||
|
||||
BasicDBObject weightsDbo = new BasicDBObject();
|
||||
for (TextIndexedFieldSpec fieldSpec : fieldSpecs) {
|
||||
if (fieldSpec.isWeighted()) {
|
||||
weightsDbo.put(fieldSpec.getFieldname(), fieldSpec.getWeight());
|
||||
}
|
||||
}
|
||||
|
||||
if (!weightsDbo.isEmpty()) {
|
||||
options.put("weights", weightsDbo);
|
||||
}
|
||||
if (StringUtils.hasText(languageOverride)) {
|
||||
options.put("language_override", languageOverride);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
public static class TextIndexedFieldSpec {
|
||||
|
||||
private final String fieldname;
|
||||
private final Float weight;
|
||||
|
||||
/**
|
||||
* Create new {@link TextIndexedFieldSpec} for given fieldname without any weight.
|
||||
*
|
||||
* @param fieldname
|
||||
*/
|
||||
public TextIndexedFieldSpec(String fieldname) {
|
||||
this(fieldname, 1.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link TextIndexedFieldSpec} for given fieldname and weight.
|
||||
*
|
||||
* @param fieldname
|
||||
* @param weight
|
||||
*/
|
||||
public TextIndexedFieldSpec(String fieldname, Float weight) {
|
||||
|
||||
Assert.hasText(fieldname, "Text index field cannot be blank.");
|
||||
this.fieldname = fieldname;
|
||||
this.weight = weight != null ? weight : 1.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fieldname associated with the {@link TextIndexedFieldSpec}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getFieldname() {
|
||||
return fieldname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weight associated with the {@link TextIndexedFieldSpec}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Float getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if {@link #weight} has a value that is a valid number.
|
||||
*/
|
||||
public boolean isWeighted() {
|
||||
return this.weight != null && this.weight.compareTo(1.0F) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(fieldname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof TextIndexedFieldSpec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TextIndexedFieldSpec other = (TextIndexedFieldSpec) obj;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(this.fieldname, other.fieldname);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TextIndexDefinitionBuilder} helps defining options for creating {@link TextIndexDefinition}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
public static class TextIndexDefinitionBuilder {
|
||||
|
||||
private TextIndexDefinition instance;
|
||||
private static final TextIndexedFieldSpec ALL_FIELDS = new TextIndexedFieldSpec("$**");
|
||||
|
||||
public TextIndexDefinitionBuilder() {
|
||||
this.instance = new TextIndexDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the name to be used when creating the index in the store.
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public TextIndexDefinitionBuilder named(String name) {
|
||||
this.instance.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the index to span all fields using wilcard. <br/>
|
||||
* <strong>NOTE</strong> {@link TextIndexDefinition} cannot contain any other fields when defined with wildcard.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TextIndexDefinitionBuilder onAllFields() {
|
||||
|
||||
if (!instance.fieldSpecs.isEmpty()) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot add wildcard fieldspect to non empty.");
|
||||
}
|
||||
|
||||
this.instance.fieldSpecs.add(ALL_FIELDS);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include given fields with default weight.
|
||||
*
|
||||
* @param fieldnames
|
||||
* @return
|
||||
*/
|
||||
public TextIndexDefinitionBuilder onFields(String... fieldnames) {
|
||||
|
||||
for (String fieldname : fieldnames) {
|
||||
onField(fieldname);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include given field with default weight.
|
||||
*
|
||||
* @param fieldname
|
||||
* @return
|
||||
*/
|
||||
public TextIndexDefinitionBuilder onField(String fieldname) {
|
||||
return onField(fieldname, Float.NaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Include given field with weight.
|
||||
*
|
||||
* @param fieldname
|
||||
* @return
|
||||
*/
|
||||
public TextIndexDefinitionBuilder onField(String fieldname, Float weight) {
|
||||
|
||||
if (this.instance.fieldSpecs.contains(ALL_FIELDS)) {
|
||||
throw new InvalidDataAccessApiUsageException(String.format("Cannot add %s to field spec for all fields.",
|
||||
fieldname));
|
||||
}
|
||||
|
||||
this.instance.fieldSpecs.add(new TextIndexedFieldSpec(fieldname, weight));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the default language to be used when indexing documents.
|
||||
*
|
||||
* @param language
|
||||
* @see http://docs.mongodb.org/manual/tutorial/specify-language-for-text-index/#specify-default-language-text-index
|
||||
* @return
|
||||
*/
|
||||
public TextIndexDefinitionBuilder withDefaultLanguage(String language) {
|
||||
|
||||
this.instance.defaultLanguage = language;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define field for language override.
|
||||
*
|
||||
* @param fieldname
|
||||
* @return
|
||||
*/
|
||||
public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) {
|
||||
|
||||
if (StringUtils.hasText(this.instance.languageOverride)) {
|
||||
throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"Cannot set language override on %s as it is already defined on %s.", fieldname,
|
||||
this.instance.languageOverride));
|
||||
}
|
||||
|
||||
this.instance.languageOverride = fieldname;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TextIndexDefinition build() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* {@link TextIndexed} marks a field to be part of the text index. As there can be only one text index per collection
|
||||
* all fields marked with {@link TextIndexed} are combined into one single index. <br />
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
@Documented
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TextIndexed {
|
||||
|
||||
/**
|
||||
* Defines the significance of the filed relative to other indexed fields. The value directly influences the documents
|
||||
* score. <br/>
|
||||
* Defaulted to {@literal 1.0}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
float weight() default 1.0F;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,12 +47,14 @@ import org.springframework.util.StringUtils;
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, MongoPersistentProperty> implements
|
||||
MongoPersistentEntity<T>, ApplicationContextAware {
|
||||
|
||||
private static final String AMBIGUOUS_FIELD_MAPPING = "Ambiguous field mapping detected! Both %s and %s map to the same field name %s! Disambiguate using @DocumentField annotation!";
|
||||
private final String collection;
|
||||
private final String language;
|
||||
private final SpelExpressionParser parser;
|
||||
private final StandardEvaluationContext context;
|
||||
|
||||
@@ -75,8 +77,10 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
if (rawType.isAnnotationPresent(Document.class)) {
|
||||
Document d = rawType.getAnnotation(Document.class);
|
||||
this.collection = StringUtils.hasText(d.collection()) ? d.collection() : fallback;
|
||||
this.language = StringUtils.hasText(d.language()) ? d.language() : "";
|
||||
} else {
|
||||
this.collection = fallback;
|
||||
this.language = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +104,15 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
return expression.getValue(context, String.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentEntity#getLanguage()
|
||||
*/
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return this.language;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.BasicPersistentEntity#verify()
|
||||
@@ -226,4 +239,5 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
properties.put(fieldName, property);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.mongodb.DBObject;
|
||||
* @author Oliver Gierke
|
||||
* @author Patryk Wasik
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class BasicMongoPersistentProperty extends AnnotationBasedPersistentProperty<MongoPersistentProperty> implements
|
||||
MongoPersistentProperty {
|
||||
@@ -48,6 +49,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BasicMongoPersistentProperty.class);
|
||||
|
||||
private static final String ID_FIELD_NAME = "_id";
|
||||
private static final String LANGUAGE_FIELD_NAME = "language";
|
||||
private static final Set<Class<?>> SUPPORTED_ID_TYPES = new HashSet<Class<?>>();
|
||||
private static final Set<String> SUPPORTED_ID_PROPERTY_NAMES = new HashSet<String>();
|
||||
|
||||
@@ -181,4 +183,13 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
public DBRef getDBRef() {
|
||||
return findAnnotation(DBRef.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#isLanguageProperty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isLanguageProperty() {
|
||||
return getFieldName().equals(LANGUAGE_FIELD_NAME) || isAnnotationPresent(Language.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
* Copyright (c) 2011-2014 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import org.springframework.data.annotation.Persistent;
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke ogierke@vmware.com
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Persistent
|
||||
@Inherited
|
||||
@@ -37,4 +38,13 @@ import org.springframework.data.annotation.Persistent;
|
||||
public @interface Document {
|
||||
|
||||
String collection() default "";
|
||||
|
||||
/**
|
||||
* Defines the default language to be used with this document.
|
||||
*
|
||||
* @since 1.6
|
||||
* @return
|
||||
*/
|
||||
String language() default "";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.mapping;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Mark property as language field.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
@Documented
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Language {
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import org.springframework.data.mapping.PersistentEntity;
|
||||
* MongoDB specific {@link PersistentEntity} abstraction.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface MongoPersistentEntity<T> extends PersistentEntity<T, MongoPersistentProperty> {
|
||||
|
||||
@@ -30,4 +31,13 @@ public interface MongoPersistentEntity<T> extends PersistentEntity<T, MongoPersi
|
||||
* @return
|
||||
*/
|
||||
String getCollection();
|
||||
|
||||
/**
|
||||
* Returns the default language to be used for this entity.
|
||||
*
|
||||
* @since 1.6
|
||||
* @return
|
||||
*/
|
||||
String getLanguage();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.data.mapping.PersistentProperty;
|
||||
* @author Oliver Gierke
|
||||
* @author Patryk Wasik
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface MongoPersistentProperty extends PersistentProperty<MongoPersistentProperty> {
|
||||
|
||||
@@ -59,6 +60,15 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
|
||||
*/
|
||||
boolean isExplicitIdProperty();
|
||||
|
||||
/**
|
||||
* Returns whether the property indicates the documents language either by having a {@link #getFieldName()} equal to
|
||||
* {@literal language} or being annotated with {@link Language}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.6
|
||||
*/
|
||||
boolean isLanguageProperty();
|
||||
|
||||
/**
|
||||
* Returns the {@link DBRef} if the property is a reference.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,11 +22,14 @@ import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.test.util.MongoVersionRule;
|
||||
import org.springframework.data.util.Version;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -34,18 +37,17 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
* Integration tests for {@link MongoPersistentEntityIndexCreator}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class MongoPersistentEntityIndexCreatorIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("mongo1")
|
||||
MongoOperations templateOne;
|
||||
public static @ClassRule MongoVersionRule version = MongoVersionRule.atLeast(new Version(2, 6));
|
||||
|
||||
@Autowired
|
||||
@Qualifier("mongo2")
|
||||
MongoOperations templateTwo;
|
||||
@Autowired @Qualifier("mongo1") MongoOperations templateOne;
|
||||
|
||||
@Autowired @Qualifier("mongo2") MongoOperations templateTwo;
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
|
||||
@@ -27,32 +27,38 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.collection.IsEmptyCollection;
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.CompoundIndexResolutionTests;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.GeoSpatialIndexResolutionTests;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.IndexResolutionTests;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.MixedIndexResolutionTests;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.TextIndexedResolutionTests;
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.Language;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntityTestDummy.MongoPersistentEntityDummyBuilder;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({ IndexResolutionTests.class, GeoSpatialIndexResolutionTests.class, CompoundIndexResolutionTests.class,
|
||||
MixedIndexResolutionTests.class })
|
||||
TextIndexedResolutionTests.class, MixedIndexResolutionTests.class })
|
||||
public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
/**
|
||||
@@ -429,6 +435,161 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
public static class TextIndexedResolutionTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldResolveSingleFieldTextIndexCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(TextIndexOnSinglePropertyInRoot.class);
|
||||
assertThat(indexDefinitions.size(), equalTo(1));
|
||||
assertIndexPathAndCollection("bar", "textIndexOnSinglePropertyInRoot", indexDefinitions.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldResolveMultiFieldTextIndexCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(TextIndexOnMutiplePropertiesInRoot.class);
|
||||
assertThat(indexDefinitions.size(), equalTo(1));
|
||||
assertIndexPathAndCollection(new String[] { "foo", "bar" }, "textIndexOnMutiplePropertiesInRoot",
|
||||
indexDefinitions.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldResolveTextIndexOnElementCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(TextIndexOnNestedRoot.class);
|
||||
assertThat(indexDefinitions.size(), equalTo(1));
|
||||
assertIndexPathAndCollection(new String[] { "nested.foo" }, "textIndexOnNestedRoot", indexDefinitions.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldResolveTextIndexOnElementWithWeightCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(TextIndexOnNestedWithWeightRoot.class);
|
||||
assertThat(indexDefinitions.size(), equalTo(1));
|
||||
assertIndexPathAndCollection(new String[] { "nested.foo" }, "textIndexOnNestedWithWeightRoot",
|
||||
indexDefinitions.get(0));
|
||||
|
||||
DBObject weights = DBObjectTestUtils.getAsDBObject(indexDefinitions.get(0).getIndexOptions(), "weights");
|
||||
assertThat(weights.get("nested.foo"), IsEqual.<Object> equalTo(5F));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldResolveTextIndexOnElementWithMostSpecificWeightCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(TextIndexOnNestedWithMostSpecificValueRoot.class);
|
||||
assertThat(indexDefinitions.size(), equalTo(1));
|
||||
assertIndexPathAndCollection(new String[] { "nested.foo", "nested.bar" },
|
||||
"textIndexOnNestedWithMostSpecificValueRoot", indexDefinitions.get(0));
|
||||
|
||||
DBObject weights = DBObjectTestUtils.getAsDBObject(indexDefinitions.get(0).getIndexOptions(), "weights");
|
||||
assertThat(weights.get("nested.foo"), IsEqual.<Object> equalTo(5F));
|
||||
assertThat(weights.get("nested.bar"), IsEqual.<Object> equalTo(10F));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldSetDefaultLanguageCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithDefaultLanguage.class);
|
||||
assertThat(indexDefinitions.get(0).getIndexOptions().get("default_language"), IsEqual.<Object> equalTo("spanish"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldResolveTextIndexLanguageOverrideCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithLanguageOverrideOnNestedElementRoot.class);
|
||||
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), IsEqual.<Object> equalTo("lang"));
|
||||
}
|
||||
|
||||
@Document
|
||||
static class TextIndexOnSinglePropertyInRoot {
|
||||
|
||||
String foo;
|
||||
|
||||
@TextIndexed String bar;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class TextIndexOnMutiplePropertiesInRoot {
|
||||
|
||||
@TextIndexed String foo;
|
||||
|
||||
@TextIndexed(weight = 5) String bar;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class TextIndexOnNestedRoot {
|
||||
|
||||
String bar;
|
||||
|
||||
@TextIndexed TextIndexOnNested nested;
|
||||
}
|
||||
|
||||
static class TextIndexOnNested {
|
||||
|
||||
String foo;
|
||||
|
||||
}
|
||||
|
||||
@Document
|
||||
static class TextIndexOnNestedWithWeightRoot {
|
||||
|
||||
@TextIndexed(weight = 5) TextIndexOnNested nested;
|
||||
|
||||
}
|
||||
|
||||
@Document
|
||||
static class TextIndexOnNestedWithMostSpecificValueRoot {
|
||||
@TextIndexed(weight = 5) TextIndexOnNestedWithMostSpecificValue nested;
|
||||
}
|
||||
|
||||
static class TextIndexOnNestedWithMostSpecificValue {
|
||||
|
||||
String foo;
|
||||
@TextIndexed(weight = 10) String bar;
|
||||
}
|
||||
|
||||
@Document(language = "spanish")
|
||||
static class DocumentWithDefaultLanguage {
|
||||
@TextIndexed String foo;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithLanguageOverrideOnNestedElementRoot {
|
||||
|
||||
DocumentWithLanguageOverrideOnNestedElement nested;
|
||||
}
|
||||
|
||||
static class DocumentWithLanguageOverrideOnNestedElement {
|
||||
|
||||
@TextIndexed String foo;
|
||||
|
||||
@Language String lang;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class MixedIndexResolutionTests {
|
||||
|
||||
/**
|
||||
@@ -561,16 +722,23 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
public String collection() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String language() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
MongoPersistentProperty propertyMock = mock(MongoPersistentProperty.class);
|
||||
when(propertyMock.isEntity()).thenReturn(true);
|
||||
when(propertyMock.getOwner()).thenReturn(
|
||||
(PersistentEntity) MongoPersistentEntityDummyBuilder.forClass(Object.class).build());
|
||||
when(propertyMock.getActualType()).thenThrow(
|
||||
new MongoPersistentEntityIndexResolver.CyclicPropertyReferenceException("foo", Object.class, "bar"));
|
||||
|
||||
MongoPersistentEntity<SelfCyclingViaCollectionType> dummy = MongoPersistentEntityDummyBuilder
|
||||
.forClass(SelfCyclingViaCollectionType.class).withCollection("foo").and(propertyMock)
|
||||
.and(documentDummy).build();
|
||||
.forClass(SelfCyclingViaCollectionType.class).withCollection("foo").and(propertyMock).and(documentDummy)
|
||||
.build();
|
||||
|
||||
new MongoPersistentEntityIndexResolver(prepareMappingContext(SelfCyclingViaCollectionType.class))
|
||||
.resolveIndexForEntity(dummy);
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
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.IndexOperations;
|
||||
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.test.util.MongoVersionRule;
|
||||
import org.springframework.data.util.Version;
|
||||
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class TextIndexTests extends AbstractIntegrationTests {
|
||||
|
||||
public static @ClassRule MongoVersionRule version = MongoVersionRule.atLeast(new Version(2, 6));
|
||||
|
||||
private @Autowired MongoTemplate template;
|
||||
private IndexOperations indexOps;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
template.setWriteConcern(WriteConcern.FSYNC_SAFE);
|
||||
this.indexOps = template.indexOps(TextIndexedDocumentRoot.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void indexInfoShouldHaveBeenCreatedCorrectly() {
|
||||
|
||||
List<IndexInfo> indexInfos = indexOps.getIndexInfo();
|
||||
|
||||
assertThat(indexInfos.size(), is(2));
|
||||
|
||||
List<IndexField> fields = indexInfos.get(0).getIndexFields();
|
||||
assertThat(fields.size(), is(1));
|
||||
assertThat(fields, hasItem(IndexField.create("_id", Direction.ASC)));
|
||||
|
||||
IndexInfo textIndexInfo = indexInfos.get(1);
|
||||
List<IndexField> textIndexFields = textIndexInfo.getIndexFields();
|
||||
assertThat(textIndexFields.size(), is(4));
|
||||
assertThat(textIndexFields, hasItem(IndexField.text("textIndexedPropertyWithDefaultWeight", 1F)));
|
||||
assertThat(textIndexFields, hasItem(IndexField.text("textIndexedPropertyWithWeight", 5F)));
|
||||
assertThat(textIndexFields, hasItem(IndexField.text("nestedDocument.textIndexedPropertyInNestedDocument", 1F)));
|
||||
assertThat(textIndexFields, hasItem(IndexField.create("_ftsx", Direction.ASC)));
|
||||
assertThat(textIndexInfo.getLanguage(), is("spanish"));
|
||||
}
|
||||
|
||||
@Document(language = "spanish")
|
||||
static class TextIndexedDocumentRoot {
|
||||
|
||||
@TextIndexed String textIndexedPropertyWithDefaultWeight;
|
||||
@TextIndexed(weight = 5) String textIndexedPropertyWithWeight;
|
||||
|
||||
TextIndexedDocumentWihtLanguageOverride nestedDocument;
|
||||
}
|
||||
|
||||
static class TextIndexedDocumentWihtLanguageOverride {
|
||||
|
||||
@Language String lang;
|
||||
|
||||
@TextIndexed String textIndexedPropertyInNestedDocument;
|
||||
|
||||
String nonTextIndexedProperty;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 by the original author(s).
|
||||
* Copyright 2011-2014 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,12 +30,12 @@ import org.springframework.data.util.ClassTypeInformation;
|
||||
* Unit tests for {@link BasicMongoPersistentEntity}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class BasicMongoPersistentEntityUnitTests {
|
||||
|
||||
@Mock
|
||||
ApplicationContext context;
|
||||
@Mock ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void subclassInheritsAtDocumentAnnotation() {
|
||||
@@ -69,6 +69,17 @@ public class BasicMongoPersistentEntityUnitTests {
|
||||
assertThat(entity.getCollection(), is("reference"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldDetectLanguageCorrectly() {
|
||||
|
||||
BasicMongoPersistentEntity<DocumentWithLanguage> entity = new BasicMongoPersistentEntity<DocumentWithLanguage>(
|
||||
ClassTypeInformation.from(DocumentWithLanguage.class));
|
||||
assertThat(entity.getLanguage(), is("spanish"));
|
||||
}
|
||||
|
||||
@Document(collection = "contacts")
|
||||
class Contact {
|
||||
|
||||
@@ -95,4 +106,9 @@ public class BasicMongoPersistentEntityUnitTests {
|
||||
return collectionName;
|
||||
}
|
||||
}
|
||||
|
||||
@Document(language = "spanish")
|
||||
static class DocumentWithLanguage {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 by the original author(s).
|
||||
* Copyright 2011-2014 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* Unit test for {@link BasicMongoPersistentProperty}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class BasicMongoPersistentPropertyUnitTests {
|
||||
|
||||
@@ -123,8 +124,42 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
property.getFieldName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldDetectAnnotatedLanguagePropertyCorrectly() {
|
||||
|
||||
BasicMongoPersistentEntity<DocumentWithLanguageProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithLanguageProperty>(
|
||||
ClassTypeInformation.from(DocumentWithLanguageProperty.class));
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(persistentEntity, "lang");
|
||||
assertThat(property.isLanguageProperty(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
*/
|
||||
@Test
|
||||
public void shouldDetectIplicitLanguagePropertyCorrectly() {
|
||||
|
||||
BasicMongoPersistentEntity<DocumentWithImplicitLanguageProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithImplicitLanguageProperty>(
|
||||
ClassTypeInformation.from(DocumentWithImplicitLanguageProperty.class));
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(persistentEntity, "language");
|
||||
assertThat(property.isLanguageProperty(), is(true));
|
||||
}
|
||||
|
||||
private MongoPersistentProperty getPropertyFor(Field field) {
|
||||
return new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder(),
|
||||
return getPropertyFor(entity, field);
|
||||
}
|
||||
|
||||
private MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> persistentEntity, String fieldname) {
|
||||
return getPropertyFor(persistentEntity, ReflectionUtils.findField(persistentEntity.getType(), fieldname));
|
||||
}
|
||||
|
||||
private MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> persistentEntity, Field field) {
|
||||
return new BasicMongoPersistentProperty(field, null, persistentEntity, new SimpleTypeHolder(),
|
||||
PropertyNameFieldNamingStrategy.INSTANCE);
|
||||
}
|
||||
|
||||
@@ -155,4 +190,14 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class DocumentWithLanguageProperty {
|
||||
|
||||
@Language String lang;
|
||||
}
|
||||
|
||||
static class DocumentWithImplicitLanguageProperty {
|
||||
|
||||
String language;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,4 +209,9 @@ public class MongoPersistentEntityTestDummy<T> implements MongoPersistentEntity<
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,10 +20,13 @@ import static org.junit.Assert.*;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.test.util.MongoVersionRule;
|
||||
import org.springframework.data.util.Version;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -33,13 +36,15 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
* @see DATAMONGO-36
|
||||
* @author Maciej Walkowiak
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class ValidatingMongoEventListenerTests {
|
||||
|
||||
@Autowired
|
||||
MongoTemplate mongoTemplate;
|
||||
public static @ClassRule MongoVersionRule version = MongoVersionRule.atLeast(new Version(2, 6));
|
||||
|
||||
@Autowired MongoTemplate mongoTemplate;
|
||||
|
||||
@Test
|
||||
public void shouldThrowConstraintViolationException() {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.test.util;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.internal.AssumptionViolatedException;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.data.util.Version;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.CommandResult;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* {@link TestRule} verifying server tests are executed against match a given version. This one can be used as
|
||||
* {@link ClassRule} eg. in context depending tests run with {@link SpringJUnit4ClassRunner} when the context would fail
|
||||
* to start in case of invalid version, or as simple {@link Rule} on specific tests.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
public class MongoVersionRule implements TestRule {
|
||||
|
||||
private String host = "localhost";
|
||||
private int port = 27017;
|
||||
|
||||
private final Version minVersion;
|
||||
private final Version maxVersion;
|
||||
|
||||
private Version currentVersion;
|
||||
|
||||
public MongoVersionRule(Version min, Version max) {
|
||||
this.minVersion = min;
|
||||
this.maxVersion = max;
|
||||
}
|
||||
|
||||
public static MongoVersionRule any() {
|
||||
return new MongoVersionRule(new Version(0, 0, 0), new Version(9999, 9999, 9999));
|
||||
}
|
||||
|
||||
public static MongoVersionRule atLeast(Version minVersion) {
|
||||
return new MongoVersionRule(minVersion, new Version(9999, 9999, 9999));
|
||||
}
|
||||
|
||||
public static MongoVersionRule atMost(Version maxVersion) {
|
||||
return new MongoVersionRule(new Version(0, 0, 0), maxVersion);
|
||||
}
|
||||
|
||||
public MongoVersionRule withServerRunningAt(String host, int port) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(final Statement base, Description description) {
|
||||
|
||||
initCurrentVersion();
|
||||
return new Statement() {
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
if (currentVersion != null) {
|
||||
if (currentVersion.isLessThan(minVersion) || currentVersion.isGreaterThan(maxVersion)) {
|
||||
throw new AssumptionViolatedException(String.format(
|
||||
"Expected mongodb server to be in range %s to %s but found %s", minVersion, maxVersion, currentVersion));
|
||||
}
|
||||
}
|
||||
base.evaluate();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void initCurrentVersion() {
|
||||
|
||||
if (currentVersion == null) {
|
||||
try {
|
||||
MongoClient client;
|
||||
client = new MongoClient(host, port);
|
||||
DB db = client.getDB("test");
|
||||
CommandResult result = db.command(new BasicDBObjectBuilder().add("buildInfo", 1).get());
|
||||
this.currentVersion = Version.parse(result.get("version").toString());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -357,6 +357,16 @@ public class Person {
|
||||
level to describe how to geoindex the field.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>@TextIndexed</literal> - applied at the field level
|
||||
to mark the field to be included in the text index.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>@Language</literal> - applied at the field level to
|
||||
set the language override property for text index.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>@Transient</literal> - by default all private fields
|
||||
are mapped to the document, this annotation excludes the field where
|
||||
@@ -526,7 +536,7 @@ OrderItem item = converter.read(OrderItem.class, input);</programlisting>
|
||||
test suite.</para>
|
||||
</section>
|
||||
|
||||
<section id="mapping-usage-indexes">
|
||||
<section id="mapping-usage-indexes.compound-index">
|
||||
<title>Compound Indexes</title>
|
||||
|
||||
<para>Compound indexes are also supported. They are defined at the class
|
||||
@@ -561,6 +571,51 @@ public class Person {
|
||||
</example></para>
|
||||
</section>
|
||||
|
||||
<section id="mapping-usage-indexes.text-index">
|
||||
<title>Text Indexes</title>
|
||||
|
||||
<note>
|
||||
<para>The text index feature is disabled by default for mongodb
|
||||
v.2.4.</para>
|
||||
</note>
|
||||
|
||||
<para>Creating a text index allows to accumulate several fields into a
|
||||
searchable full text index. It is only possible to have one text index
|
||||
per collection so all fields marked with
|
||||
<interfacename>@TextIndexed</interfacename> are combined into this
|
||||
index. Properties can be weighted to influence document score for
|
||||
ranking results. The default language for the text index is english, to
|
||||
change the default language set
|
||||
<interfacename>@Document(language="spanish")</interfacename> to any
|
||||
language you want. Using a property called <literal>language</literal>
|
||||
or <interfacename>@Language</interfacename> allows to define a language
|
||||
override on a per document base.</para>
|
||||
|
||||
<example>
|
||||
<title>Example Text Index Usage</title>
|
||||
|
||||
<programlisting language="java">@Document(language = "spanish")
|
||||
class SomeEntity {
|
||||
|
||||
@TextIndexed String foo;
|
||||
|
||||
@Language String lang;
|
||||
|
||||
Nested nested;
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Nested {
|
||||
|
||||
@TextIndexed(weight=5) String bar;
|
||||
|
||||
String roo;
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
<section id="mapping-usage-references">
|
||||
<title>Using DBRefs</title>
|
||||
|
||||
|
||||
@@ -3046,11 +3046,12 @@ class MyConverter implements Converter<String, Person> { … }</programlis
|
||||
</listitem>
|
||||
</itemizedlist></para>
|
||||
|
||||
<para>You can create both standard indexes and geospatial indexes using
|
||||
the classes <classname>IndexDefinition</classname> and
|
||||
<classname>GeoSpatialIndex</classname> respectfully. For example, given
|
||||
the Venue class defined in a previous section, you would declare a
|
||||
geospatial query as shown below</para>
|
||||
<para>You can create standard, geospatial and text indexes using the
|
||||
classes <classname>IndexDefinition</classname>,
|
||||
<classname>GeoSpatialIndex</classname> and
|
||||
<classname>TextIndexDefinition</classname>. For example, given the Venue
|
||||
class defined in a previous section, you would declare a geospatial
|
||||
query as shown below.</para>
|
||||
|
||||
<programlisting language="java">mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location"));</programlisting>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user