Browse Source
Index creation did not consider the properties path when creating the index. This lead to broken index creation when nesting entities that might require index structures. Off now index creation traverses the entities property path for all top level entities (namely those holding the @Document annotation) and creates the index using the full property path. This is a breaking change as having an entity to carry the @Document annotation has not been required by now. Original Pull Request: #168pull/95/merge
12 changed files with 1066 additions and 156 deletions
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* 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 com.mongodb.BasicDBObject; |
||||
import com.mongodb.DBObject; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 1.5 |
||||
*/ |
||||
public class CompoundIndexDefinition extends Index { |
||||
|
||||
private DBObject keys; |
||||
|
||||
public CompoundIndexDefinition(DBObject keys) { |
||||
this.keys = keys; |
||||
} |
||||
|
||||
@Override |
||||
public DBObject getIndexKeys() { |
||||
|
||||
BasicDBObject dbo = new BasicDBObject(); |
||||
dbo.putAll(this.keys); |
||||
dbo.putAll(super.getIndexKeys()); |
||||
return dbo; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,35 @@
@@ -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.index; |
||||
|
||||
/** |
||||
* {@link IndexResolver} finds those {@link IndexDefinition}s to be created for a given class. |
||||
* |
||||
* @author Christoph Strobl |
||||
* @since 1.5 |
||||
*/ |
||||
public interface IndexResolver { |
||||
|
||||
/** |
||||
* Find and create {@link IndexDefinition}s for properties of given {@code type}. {@link IndexDefinition}s are created |
||||
* for properties and types with {@link Indexed}, {@link CompoundIndexes} or {@link GeoSpatialIndexed}. |
||||
* |
||||
* @param type |
||||
* @return Empty {@link Iterable} in case no {@link IndexDefinition} could be resolved for type. |
||||
*/ |
||||
Iterable<? extends IndexDefinition> resolveIndexForClass(Class<?> type); |
||||
|
||||
} |
||||
@ -0,0 +1,351 @@
@@ -0,0 +1,351 @@
|
||||
/* |
||||
* 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.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
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.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.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import com.mongodb.DBObject; |
||||
import com.mongodb.util.JSON; |
||||
|
||||
/** |
||||
* {@link IndexResolver} implementation inspecting {@link MongoPersistentEntity} for {@link MongoPersistentEntity} to be |
||||
* indexed. <br /> |
||||
* All {@link MongoPersistentProperty} of the {@link MongoPersistentEntity} are inspected for potential indexes by |
||||
* scanning related annotations. |
||||
* |
||||
* @author Christoph Strobl |
||||
* @since 1.5 |
||||
*/ |
||||
public class MongoPersistentEntityIndexResolver implements IndexResolver { |
||||
|
||||
private final MongoMappingContext mappingContext; |
||||
|
||||
/** |
||||
* Create new {@link MongoPersistentEntityIndexResolver}. |
||||
* |
||||
* @param mappingContext must not be {@literal null}. |
||||
*/ |
||||
public MongoPersistentEntityIndexResolver(MongoMappingContext mappingContext) { |
||||
|
||||
Assert.notNull(mappingContext, "Mapping context must not be null in order to resolve index definitions"); |
||||
this.mappingContext = mappingContext; |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.data.mongodb.core.index.IndexResolver#resolveIndexForClass(java.lang.Class) |
||||
*/ |
||||
@Override |
||||
public List<IndexDefinitionHolder> resolveIndexForClass(Class<?> type) { |
||||
return resolveIndexForEntity(mappingContext.getPersistentEntity(type)); |
||||
} |
||||
|
||||
/** |
||||
* Resolve the {@link IndexDefinition}s for given {@literal root} entity by traversing {@link MongoPersistentProperty} |
||||
* scanning for index annotations {@link Indexed}, {@link CompoundIndex} and {@link GeospatialIndex}. The given |
||||
* {@literal root} has therefore to be annotated with {@link Document}. |
||||
* |
||||
* @param root must not be null. |
||||
* @return List of {@link IndexDefinitionHolder}. Will never be {@code null}. |
||||
* @throws IllegalArgumentException in case of missing {@link Document} annotation marking root entities. |
||||
*/ |
||||
public List<IndexDefinitionHolder> resolveIndexForEntity(final MongoPersistentEntity<?> root) { |
||||
|
||||
Assert.notNull(root, "Index cannot be resolved for given 'null' entity."); |
||||
Document document = root.findAnnotation(Document.class); |
||||
Assert.notNull(document, "Given entity is not collection root."); |
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>(); |
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root.getType())); |
||||
|
||||
root.doWithProperties(new PropertyHandler<MongoPersistentProperty>() { |
||||
|
||||
@Override |
||||
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) { |
||||
|
||||
if (persistentProperty.isEntity()) { |
||||
indexInformation.addAll(resolveIndexForClass(persistentProperty.getActualType(), |
||||
persistentProperty.getFieldName(), root.getCollection())); |
||||
} |
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty( |
||||
persistentProperty.getFieldName(), root.getCollection(), persistentProperty); |
||||
if (indexDefinitionHolder != null) { |
||||
indexInformation.add(indexDefinitionHolder); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
return indexInformation; |
||||
} |
||||
|
||||
/** |
||||
* Recursively resolve and inspect properties of given {@literal type} for indexes to be created. |
||||
* |
||||
* @param type |
||||
* @param path The {@literal "dot} path. |
||||
* @param collection |
||||
* @return List of {@link IndexDefinitionHolder} representing indexes for given type and its referenced property |
||||
* types. Will never be {@code null}. |
||||
*/ |
||||
private List<IndexDefinitionHolder> resolveIndexForClass(Class<?> type, final String path, final String collection) { |
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>(); |
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(path, collection, type)); |
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(type); |
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() { |
||||
|
||||
@Override |
||||
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) { |
||||
|
||||
String propertyDotPath = (StringUtils.hasText(path) ? (path + ".") : "") + persistentProperty.getFieldName(); |
||||
|
||||
if (persistentProperty.isEntity()) { |
||||
indexInformation.addAll(resolveIndexForClass(persistentProperty.getActualType(), propertyDotPath, collection)); |
||||
} |
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(propertyDotPath, |
||||
collection, persistentProperty); |
||||
if (indexDefinitionHolder != null) { |
||||
indexInformation.add(indexDefinitionHolder); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
return indexInformation; |
||||
} |
||||
|
||||
private IndexDefinitionHolder createIndexDefinitionHolderForProperty(String dotPath, String collection, |
||||
MongoPersistentProperty persistentProperty) { |
||||
|
||||
if (persistentProperty.isAnnotationPresent(Indexed.class)) { |
||||
return createIndexDefinition(dotPath, collection, persistentProperty); |
||||
} else if (persistentProperty.isAnnotationPresent(GeoSpatialIndexed.class)) { |
||||
return createGeoSpatialIndexDefinition(dotPath, collection, persistentProperty); |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
private List<IndexDefinitionHolder> potentiallyCreateCompoundIndexDefinitions(String dotPath, String collection, |
||||
Class<?> type) { |
||||
|
||||
if (AnnotationUtils.findAnnotation(type, CompoundIndexes.class) == null) { |
||||
return Collections.emptyList(); |
||||
} |
||||
|
||||
return createCompoundIndexDefinitions(dotPath, collection, type); |
||||
} |
||||
|
||||
/** |
||||
* Create {@link IndexDefinition} wrapped in {@link IndexDefinitionHolder} for {@link CompoundIndexes} of given type. |
||||
* |
||||
* @param dotPath The properties {@literal "dot"} path representation from its document root. |
||||
* @param collection |
||||
* @param type |
||||
* @return |
||||
*/ |
||||
protected List<IndexDefinitionHolder> createCompoundIndexDefinitions(String dotPath, String collection, Class<?> type) { |
||||
|
||||
CompoundIndexes indexes = AnnotationUtils.findAnnotation(type, CompoundIndexes.class); |
||||
List<IndexDefinitionHolder> indexDefinitions = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>( |
||||
indexes.value().length); |
||||
|
||||
for (CompoundIndex index : indexes.value()) { |
||||
|
||||
IndexDefinitionHolder holder = new IndexDefinitionHolder(StringUtils.hasText(index.name()) ? index.name() |
||||
: dotPath); |
||||
|
||||
CompoundIndexDefinition indexDefinition = new CompoundIndexDefinition((DBObject) JSON.parse(index.def())); |
||||
indexDefinition.named(index.name()); |
||||
indexDefinition.setCollection(StringUtils.hasText(index.collection()) ? index.collection() : collection); |
||||
if (index.unique()) { |
||||
indexDefinition.unique(index.dropDups() ? Duplicates.DROP : Duplicates.RETAIN); |
||||
} |
||||
if (index.sparse()) { |
||||
indexDefinition.sparse(); |
||||
} |
||||
if (index.background()) { |
||||
indexDefinition.background(); |
||||
} |
||||
if (index.expireAfterSeconds() >= 0) { |
||||
indexDefinition.expire(index.expireAfterSeconds(), TimeUnit.SECONDS); |
||||
} |
||||
|
||||
holder.setIndexDefinition(indexDefinition); |
||||
indexDefinitions.add(holder); |
||||
} |
||||
|
||||
return indexDefinitions; |
||||
} |
||||
|
||||
/** |
||||
* Creates {@link IndexDefinition} wrapped in {@link IndexDefinitionHolder} out of {@link Indexed} for given |
||||
* {@link MongoPersistentProperty}. |
||||
* |
||||
* @param dotPath The properties {@literal "dot"} path representation from its document root. |
||||
* @param collection |
||||
* @param persitentProperty |
||||
* @return |
||||
*/ |
||||
protected IndexDefinitionHolder createIndexDefinition(String dotPath, String collection, |
||||
MongoPersistentProperty persitentProperty) { |
||||
|
||||
Indexed index = persitentProperty.findAnnotation(Indexed.class); |
||||
|
||||
IndexDefinitionHolder holder = new IndexDefinitionHolder(dotPath); |
||||
|
||||
Index indexDefinition = new Index(); |
||||
indexDefinition.setCollection(StringUtils.hasText(index.collection()) ? index.collection() : collection); |
||||
indexDefinition.named(StringUtils.hasText(index.name()) ? index.name() : persitentProperty.getFieldName()); |
||||
indexDefinition.on(persitentProperty.getFieldName(), |
||||
IndexDirection.ASCENDING.equals(index.direction()) ? Sort.Direction.ASC : Sort.Direction.DESC); |
||||
|
||||
if (index.unique()) { |
||||
indexDefinition.unique(index.dropDups() ? Duplicates.DROP : Duplicates.RETAIN); |
||||
} |
||||
if (index.sparse()) { |
||||
indexDefinition.sparse(); |
||||
} |
||||
if (index.background()) { |
||||
indexDefinition.background(); |
||||
} |
||||
if (index.expireAfterSeconds() >= 0) { |
||||
indexDefinition.expire(index.expireAfterSeconds(), TimeUnit.SECONDS); |
||||
} |
||||
|
||||
holder.setIndexDefinition(indexDefinition); |
||||
return holder; |
||||
} |
||||
|
||||
/** |
||||
* Creates {@link IndexDefinition} wrapped in {@link IndexDefinitionHolder} out of {@link GeoSpatialIndexed} for |
||||
* {@link MongoPersistentProperty}. |
||||
* |
||||
* @param dotPath The properties {@literal "dot"} path representation from its document root. |
||||
* @param collection |
||||
* @param persistentProperty |
||||
* @return |
||||
*/ |
||||
protected IndexDefinitionHolder createGeoSpatialIndexDefinition(String dotPath, String collection, |
||||
MongoPersistentProperty persistentProperty) { |
||||
|
||||
GeoSpatialIndexed index = persistentProperty.findAnnotation(GeoSpatialIndexed.class); |
||||
|
||||
IndexDefinitionHolder holder = new IndexDefinitionHolder(dotPath); |
||||
|
||||
GeospatialIndex indexDefinition = new GeospatialIndex(dotPath); |
||||
indexDefinition.setCollection(StringUtils.hasText(index.collection()) ? index.collection() : collection); |
||||
indexDefinition.withBits(index.bits()); |
||||
indexDefinition.withMin(index.min()).withMax(index.max()); |
||||
indexDefinition.named(StringUtils.hasText(index.name()) ? index.name() : persistentProperty.getName()); |
||||
indexDefinition.typed(index.type()).withBucketSize(index.bucketSize()).withAdditionalField(index.additionalField()); |
||||
|
||||
holder.setIndexDefinition(indexDefinition); |
||||
return holder; |
||||
} |
||||
|
||||
/** |
||||
* Implementation of {@link IndexDefinition} holding additional (property)path information used for creating the |
||||
* index. The path itself is the properties {@literal "dot"} path representation from its root document. |
||||
* |
||||
* @author Christoph Strobl |
||||
* @since 1.5 |
||||
*/ |
||||
public static class IndexDefinitionHolder implements IndexDefinition { |
||||
|
||||
private String path; |
||||
private IndexDefinition indexDefinition; |
||||
|
||||
/** |
||||
* Create |
||||
* |
||||
* @param path |
||||
*/ |
||||
public IndexDefinitionHolder(String path) { |
||||
this.path = path; |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getCollection() |
||||
*/ |
||||
@Override |
||||
public String getCollection() { |
||||
return indexDefinition != null ? indexDefinition.getCollection() : null; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@liteal "dot"} path used to create the index. |
||||
* |
||||
* @return |
||||
*/ |
||||
public String getPath() { |
||||
return path; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@literal raw} {@link IndexDefinition}. |
||||
* |
||||
* @return |
||||
*/ |
||||
public IndexDefinition getIndexDefinition() { |
||||
return indexDefinition; |
||||
} |
||||
|
||||
/** |
||||
* Set the {@literal raw} {@link IndexDefinition}. |
||||
* |
||||
* @param indexDefinition |
||||
*/ |
||||
public void setIndexDefinition(IndexDefinition indexDefinition) { |
||||
this.indexDefinition = indexDefinition; |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getIndexKeys() |
||||
*/ |
||||
@Override |
||||
public DBObject getIndexKeys() { |
||||
return indexDefinition.getIndexKeys(); |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getIndexOptions() |
||||
*/ |
||||
@Override |
||||
public DBObject getIndexOptions() { |
||||
return indexDefinition.getIndexOptions(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,390 @@
@@ -0,0 +1,390 @@
|
||||
/* |
||||
* 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.collection.IsCollectionWithSize.*; |
||||
import static org.hamcrest.collection.IsEmptyCollection.*; |
||||
import static org.hamcrest.core.IsEqual.*; |
||||
import static org.hamcrest.core.IsInstanceOf.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
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.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.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.MongoMappingContext; |
||||
|
||||
import com.mongodb.BasicDBObjectBuilder; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
*/ |
||||
@RunWith(Suite.class) |
||||
@SuiteClasses({ IndexResolutionTests.class, GeoSpatialIndexResolutionTests.class, CompoundIndexResolutionTests.class, |
||||
MixedIndexResolutionTests.class }) |
||||
public class MongoPersistentEntityIndexResolverUnitTests { |
||||
|
||||
/** |
||||
* Test resolution of {@link Indexed}. |
||||
* |
||||
* @author Christoph Strobl |
||||
*/ |
||||
public static class IndexResolutionTests { |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void indexPathOnLevelZeroIsResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelZero.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertIndexPathAndCollection("indexedProperty", "Zero", indexDefinitions.get(0)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void indexPathOnLevelOneIsResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelOne.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertIndexPathAndCollection("zero.indexedProperty", "One", indexDefinitions.get(0)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void depplyNestedIndexPathIsResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelTwo.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertIndexPathAndCollection("one.zero.indexedProperty", "Two", indexDefinitions.get(0)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void resolvesIndexPathNameForNamedPropertiesCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelOneWithExplicitlyNamedField.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertIndexPathAndCollection("customZero.customFieldName", "indexOnLevelOneWithExplicitlyNamedField", |
||||
indexDefinitions.get(0)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void resolvesIndexDefinitionCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelZero.class); |
||||
|
||||
IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); |
||||
assertThat(indexDefinition.getIndexOptions(), equalTo(new BasicDBObjectBuilder().add("name", "indexedProperty") |
||||
.get())); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void resolvesIndexDefinitionOptionsCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(WithOptionsOnIndexedProperty.class); |
||||
|
||||
IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); |
||||
assertThat(indexDefinition.getIndexOptions(), |
||||
equalTo(new BasicDBObjectBuilder().add("name", "indexedProperty").add("unique", true).add("dropDups", true) |
||||
.add("sparse", true).add("background", true).add("expireAfterSeconds", 10L).get())); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void resolvesIndexCollectionNameCorrectlyWhenDefinedInAnnotation() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(WithOptionsOnIndexedProperty.class); |
||||
assertThat(indexDefinitions.get(0).getCollection(), equalTo("CollectionOverride")); |
||||
} |
||||
|
||||
@Document(collection = "Zero") |
||||
static class IndexOnLevelZero { |
||||
@Indexed String indexedProperty; |
||||
} |
||||
|
||||
@Document(collection = "One") |
||||
static class IndexOnLevelOne { |
||||
IndexOnLevelZero zero; |
||||
} |
||||
|
||||
@Document(collection = "Two") |
||||
static class IndexOnLevelTwo { |
||||
IndexOnLevelOne one; |
||||
} |
||||
|
||||
@Document(collection = "WithOptionsOnIndexedProperty") |
||||
static class WithOptionsOnIndexedProperty { |
||||
|
||||
@Indexed(background = true, collection = "CollectionOverride", direction = IndexDirection.DESCENDING, |
||||
dropDups = true, expireAfterSeconds = 10, sparse = true, unique = true)//
|
||||
String indexedProperty; |
||||
} |
||||
|
||||
@Document |
||||
static class IndexOnLevelOneWithExplicitlyNamedField { |
||||
|
||||
@Field("customZero") IndexOnLevelZeroWithExplicityNamedField zero; |
||||
} |
||||
|
||||
static class IndexOnLevelZeroWithExplicityNamedField { |
||||
|
||||
@Indexed @Field("customFieldName") String namedProperty; |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Test resolution of {@link GeoSpatialIndexed}. |
||||
* |
||||
* @author Christoph Strobl |
||||
*/ |
||||
public static class GeoSpatialIndexResolutionTests { |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void geoSpatialIndexPathOnLevelZeroIsResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(GeoSpatialIndexOnLevelZero.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertIndexPathAndCollection("geoIndexedProperty", "Zero", indexDefinitions.get(0)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void geoSpatialIndexPathOnLevelOneIsResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(GeoSpatialIndexOnLevelOne.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertIndexPathAndCollection("zero.geoIndexedProperty", "One", indexDefinitions.get(0)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void depplyNestedGeoSpatialIndexPathIsResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(GeoSpatialIndexOnLevelTwo.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertIndexPathAndCollection("one.zero.geoIndexedProperty", "Two", indexDefinitions.get(0)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void resolvesIndexDefinitionOptionsCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(WithOptionsOnGeoSpatialIndexProperty.class); |
||||
|
||||
IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); |
||||
|
||||
assertThat( |
||||
indexDefinition.getIndexOptions(), |
||||
equalTo(new BasicDBObjectBuilder().add("name", "location").add("min", 1).add("max", 100).add("bits", 2).get())); |
||||
} |
||||
|
||||
@Document(collection = "Zero") |
||||
static class GeoSpatialIndexOnLevelZero { |
||||
@GeoSpatialIndexed Point geoIndexedProperty; |
||||
} |
||||
|
||||
@Document(collection = "One") |
||||
static class GeoSpatialIndexOnLevelOne { |
||||
GeoSpatialIndexOnLevelZero zero; |
||||
} |
||||
|
||||
@Document(collection = "Two") |
||||
static class GeoSpatialIndexOnLevelTwo { |
||||
GeoSpatialIndexOnLevelOne one; |
||||
} |
||||
|
||||
@Document(collection = "WithOptionsOnGeoSpatialIndexProperty") |
||||
static class WithOptionsOnGeoSpatialIndexProperty { |
||||
|
||||
@GeoSpatialIndexed(collection = "CollectionOverride", bits = 2, max = 100, min = 1, |
||||
type = GeoSpatialIndexType.GEO_2D)//
|
||||
Point location; |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Test resolution of {@link CompoundIndexes}. |
||||
* |
||||
* @author Christoph Strobl |
||||
*/ |
||||
public static class CompoundIndexResolutionTests { |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void compoundIndexPathOnLevelZeroIsResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(CompoundIndexOnLevelZero.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertIndexPathAndCollection("compound_index", "CompoundIndexOnLevelZero", indexDefinitions.get(0)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void compoundIndexOptionsResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(CompoundIndexOnLevelZero.class); |
||||
|
||||
IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); |
||||
assertThat(indexDefinition.getIndexOptions(), |
||||
equalTo(new BasicDBObjectBuilder().add("name", "compound_index").add("unique", true).add("dropDups", true) |
||||
.add("sparse", true).add("background", true).add("expireAfterSeconds", 10L).get())); |
||||
assertThat(indexDefinition.getIndexKeys(), equalTo(new BasicDBObjectBuilder().add("foo", 1).add("bar", -1).get())); |
||||
} |
||||
|
||||
@Document(collection = "CompoundIndexOnLevelZero") |
||||
@CompoundIndexes({ @CompoundIndex(name = "compound_index", def = "{'foo': 1, 'bar': -1}", background = true, |
||||
dropDups = true, expireAfterSeconds = 10, sparse = true, unique = true) }) |
||||
static class CompoundIndexOnLevelZero {} |
||||
} |
||||
|
||||
public static class MixedIndexResolutionTests { |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void multipleIndexesResolvedCorrectly() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(MixedIndexRoot.class); |
||||
|
||||
assertThat(indexDefinitions, hasSize(2)); |
||||
assertThat(indexDefinitions.get(0).getIndexDefinition(), instanceOf(Index.class)); |
||||
assertThat(indexDefinitions.get(1).getIndexDefinition(), instanceOf(GeospatialIndex.class)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void cyclicPropertyReferenceOverDBRefShouldNotBeTraversed() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(Inner.class); |
||||
assertThat(indexDefinitions, hasSize(1)); |
||||
assertThat(indexDefinitions.get(0).getIndexDefinition().getCollection(), equalTo("inner")); |
||||
assertThat(indexDefinitions.get(0).getIndexDefinition().getIndexKeys(), |
||||
equalTo(new BasicDBObjectBuilder().add("outer", 1).get())); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-899 |
||||
*/ |
||||
@Test |
||||
public void associationsShouldNotBeTraversed() { |
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(Outer.class); |
||||
assertThat(indexDefinitions, empty()); |
||||
} |
||||
|
||||
@Document |
||||
static class MixedIndexRoot { |
||||
|
||||
@Indexed String first; |
||||
NestedGeoIndex nestedGeo; |
||||
} |
||||
|
||||
static class NestedGeoIndex { |
||||
|
||||
@GeoSpatialIndexed Point location; |
||||
} |
||||
|
||||
@Document |
||||
static class Outer { |
||||
|
||||
@DBRef Inner inner; |
||||
} |
||||
|
||||
@Document |
||||
static class Inner { |
||||
|
||||
@Indexed Outer outer; |
||||
} |
||||
|
||||
} |
||||
|
||||
private static List<IndexDefinitionHolder> prepareMappingContextAndResolveIndexForType(Class<?> type) { |
||||
|
||||
MongoMappingContext mappingContext = prepareMappingContext(type); |
||||
MongoPersistentEntityIndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext); |
||||
return resolver.resolveIndexForEntity(mappingContext.getPersistentEntity(type)); |
||||
} |
||||
|
||||
private static MongoMappingContext prepareMappingContext(Class<?> type) { |
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext(); |
||||
mappingContext.setInitialEntitySet(Collections.singleton(type)); |
||||
mappingContext.initialize(); |
||||
|
||||
return mappingContext; |
||||
} |
||||
|
||||
private static void assertIndexPathAndCollection(String expectedPath, String expectedCollection, |
||||
IndexDefinitionHolder holder) { |
||||
|
||||
assertThat(holder.getPath(), equalTo(expectedPath)); |
||||
assertThat(holder.getCollection(), equalTo(expectedCollection)); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue