diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java new file mode 100644 index 000000000..08dae5767 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java @@ -0,0 +1,128 @@ +/* + * Copyright 2011 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; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.springframework.dao.DataAccessException; +import org.springframework.data.mongodb.core.index.IndexDefinition; +import org.springframework.data.mongodb.core.index.IndexInfo; +import org.springframework.data.mongodb.core.query.Order; +import org.springframework.util.Assert; + +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.MongoException; + +/** + * Default implementation of {@link IndexOperations} + * @author Mark Pollack + * + */ +public class DefaultIndexOperations implements IndexOperations { + + private MongoTemplate mongoTemplate; + private String collectionName; + + public DefaultIndexOperations(MongoTemplate mongoTemplate, String collectionName) { + Assert.notNull(collectionName, "collectionName can not be null"); + this.mongoTemplate = mongoTemplate; + this.collectionName = collectionName; + } + + public void ensureIndex(final IndexDefinition indexDefinition) { + mongoTemplate.execute(collectionName, new CollectionCallback() { + public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { + DBObject indexOptions = indexDefinition.getIndexOptions(); + if (indexOptions != null) { + collection.ensureIndex(indexDefinition.getIndexKeys(), indexOptions); + } else { + collection.ensureIndex(indexDefinition.getIndexKeys()); + } + return null; + } + }); + } + + public void dropIndex(final String name) { + mongoTemplate.execute(collectionName, new CollectionCallback() { + public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { + collection.dropIndex(name); + return null; + } + }); + + } + + public void dropAllIndexes() { + dropIndex("*"); + } + + public void resetIndexCache() { + mongoTemplate.execute(collectionName, new CollectionCallback() { + public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { + collection.resetIndexCache(); + return null; + } + }); + } + + public List getIndexInfo() { + return mongoTemplate.execute(collectionName, new CollectionCallback >() { + public List doInCollection(DBCollection collection) throws MongoException, DataAccessException { + List dbObjectList = collection.getIndexInfo(); + return getIndexData(dbObjectList); + //return indexInfoList; + } + + private List getIndexData(List dbObjectList) { + List indexInfoList = new ArrayList(); + for (DBObject ix : dbObjectList) { + Map keyOrderMap = new LinkedHashMap(); + DBObject keyDbObject = (DBObject) ix.get("key"); + Iterator entries = keyDbObject.toMap().entrySet().iterator(); + while (entries.hasNext()) { + Entry thisEntry = (Entry) entries.next(); + String key = thisEntry.getKey().toString(); + int value = (Integer) thisEntry.getValue(); + if (value == 1) { + keyOrderMap.put(key, Order.ASCENDING); + } else { + keyOrderMap.put(key, Order.DESCENDING); + } + } + + + String name = ix.get("name").toString(); + 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(keyOrderMap, name, unique, dropDuplicates, sparse)); + } + return indexInfoList; + } + }); + } + + + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java new file mode 100644 index 000000000..a192e8cd4 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java @@ -0,0 +1,65 @@ +/* + * Copyright 2011 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; + +import java.util.List; + +import org.springframework.data.mongodb.core.index.IndexDefinition; +import org.springframework.data.mongodb.core.index.IndexInfo; + +/** + * Index operations on a collection + * + * @author Mark Pollack + * + */ +public interface IndexOperations { + + /** + * Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity + * class. If not it will be created. + * + * @param indexDefinition + * @param entityClass class that determines the collection to use + */ + void ensureIndex(IndexDefinition indexDefinition); + + /** + * Drops an index from this collection + * @param name name of index to drop + */ + void dropIndex(String name); + + /** + * Drops all indices from this collection + */ + void dropAllIndexes(); + + + /** + * Clears all indices that have not yet been applied to this collection. + */ + void resetIndexCache(); + + + /** + * Returns the index information on the collection + * @return index information on the collection + */ + List getIndexInfo(); + + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java index d670b1499..7725c996e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java @@ -227,6 +227,18 @@ public interface MongoOperations { * @param collectionName name of the collection to drop/delete. */ void dropCollection(String collectionName); + + /** + * Returns the operations that can be performed on indexes + * @return index operations on the named collection + */ + IndexOperations indexOps(String collectionName); + + /** + * Returns the operations that can be performed on indexes + * @return index operations on the named collection associated with the given entity class + */ + IndexOperations indexOps(Class entityClass); /** * Query for a list of objects of type T from the collection used by the entity class. @@ -353,23 +365,6 @@ public interface MongoOperations { */ GeoResults geoNear(NearQuery near, Class entityClass, String collectionName); - /** - * Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity - * class. If not it will be created. - * - * @param indexDefinition - * @param entityClass class that determines the collection to use - */ - void ensureIndex(IndexDefinition indexDefinition, Class entityClass); - - /** - * Ensure that an index for the provided {@link IndexDefinition} exists. If not it will be created. - * - * @param index - * @param collectionName - */ - void ensureIndex(IndexDefinition indexDefinition, String collectionName); - /** * Map the results of an ad-hoc query on the collection for the entity class to a single instance of an object of the * specified type. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 3a2b8471d..39a6d50c0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -436,25 +436,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { } }); } - - // Indexing methods - - public void ensureIndex(IndexDefinition indexDefinition, Class entityClass) { - ensureIndex(indexDefinition, determineCollectionName(entityClass)); + + public IndexOperations indexOps(String collectionName) { + return new DefaultIndexOperations(this, collectionName); } - - public void ensureIndex(final IndexDefinition indexDefinition, String collectionName) { - execute(collectionName, new CollectionCallback() { - public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { - DBObject indexOptions = indexDefinition.getIndexOptions(); - if (indexOptions != null) { - collection.ensureIndex(indexDefinition.getIndexKeys(), indexOptions); - } else { - collection.ensureIndex(indexDefinition.getIndexKeys()); - } - return null; - } - }); + + public IndexOperations indexOps(Class entityClass) { + return new DefaultIndexOperations(this, determineCollectionName(entityClass)); } // Find methods that take a Query to express the query and that return a single object. @@ -1460,7 +1448,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { return null; } - private String determineCollectionName(Class entityClass) { + String determineCollectionName(Class entityClass) { if (entityClass == null) { throw new InvalidDataAccessApiUsageException( diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java new file mode 100644 index 000000000..d371a1672 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java @@ -0,0 +1,115 @@ +/* + * Copyright 2002-2011 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.Map; + +import org.springframework.data.mongodb.core.query.Order; + +public class IndexInfo { + + private final Map fieldSpec; + + private String name; + + private boolean unique = false; + + private boolean dropDuplicates = false; + + private boolean sparse = false; + + + public IndexInfo(Map fieldSpec, String name, boolean unique, boolean dropDuplicates, boolean sparse) { + super(); + this.fieldSpec = fieldSpec; + this.name = name; + this.unique = unique; + this.dropDuplicates = dropDuplicates; + this.sparse = sparse; + } + + public Map getFieldSpec() { + return fieldSpec; + } + + public String getName() { + return name; + } + + public boolean isUnique() { + return unique; + } + + public boolean isDropDuplicates() { + return dropDuplicates; + } + + public boolean isSparse() { + return sparse; + } + + @Override + public String toString() { + return "IndexInfo [fieldSpec=" + fieldSpec + ", name=" + name + ", unique=" + unique + ", dropDuplicates=" + + dropDuplicates + ", sparse=" + sparse + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (dropDuplicates ? 1231 : 1237); + result = prime * result + ((fieldSpec == null) ? 0 : fieldSpec.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + (sparse ? 1231 : 1237); + result = prime * result + (unique ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IndexInfo other = (IndexInfo) obj; + if (dropDuplicates != other.dropDuplicates) + return false; + if (fieldSpec == null) { + if (other.fieldSpec != null) + return false; + } else if (!fieldSpec.equals(other.fieldSpec)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (sparse != other.sparse) + return false; + if (unique != other.unique) + return false; + return true; + } + + + /** + * [{ "v" : 1 , "key" : { "_id" : 1} , "ns" : "database.person" , "name" : "_id_"}, + { "v" : 1 , "key" : { "age" : -1} , "ns" : "database.person" , "name" : "age_-1" , "unique" : true , "dropDups" : true}] + */ +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java index 6fbe90673..021c6e2c4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java @@ -86,7 +86,7 @@ class IndexEnsuringQueryCreationListener implements QueryCreationListener metadata = query.getQueryMethod().getEntityInformation(); - operations.ensureIndex(index, metadata.getCollectionName()); + operations.indexOps(metadata.getCollectionName()).ensureIndex(index); LOG.debug(String.format("Created %s!", index)); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 4ace558e2..a2067e0ba 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -47,6 +47,7 @@ import org.springframework.data.mongodb.core.convert.CustomConversions; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.index.Index; import org.springframework.data.mongodb.core.index.Index.Duplicates; +import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Order; @@ -171,11 +172,10 @@ public class MongoTemplateTests { p2.setAge(40); template.insert(p2); - template.ensureIndex(new Index().on("age", Order.DESCENDING).unique(Duplicates.DROP), Person.class); + template.indexOps(Person.class).ensureIndex(new Index().on("age", Order.DESCENDING).unique(Duplicates.DROP)); DBCollection coll = template.getCollection(template.getCollectionName(Person.class)); List indexInfo = coll.getIndexInfo(); - assertThat(indexInfo.size(), is(2)); String indexKey = null; boolean unique = false; @@ -190,6 +190,16 @@ public class MongoTemplateTests { assertThat(indexKey, is("{ \"age\" : -1}")); assertThat(unique, is(true)); assertThat(dropDupes, is(true)); + + List indexInfoList = template.indexOps(Person.class).getIndexInfo(); + System.out.println(indexInfoList); + assertThat(indexInfoList.size(), is(2)); + IndexInfo ii = indexInfoList.get(1); + assertThat(ii.isUnique(), is(true)); + assertThat(ii.isDropDuplicates(), is(true)); + assertThat(ii.isSparse(), is(false)); + assertThat(ii.getFieldSpec().containsKey("age"), is(true)); + assertThat(ii.getFieldSpec().containsValue(Order.DESCENDING), is(true)); } @Test diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java index 15f51b4da..54de1b381 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java @@ -73,7 +73,7 @@ public class GeoSpatialTests { applicationContext = new AnnotationConfigApplicationContext(GeoSpatialAppConfig.class); template = applicationContext.getBean(MongoTemplate.class); template.setWriteConcern(WriteConcern.FSYNC_SAFE); - template.ensureIndex(new GeospatialIndex("location"), Venue.class); + template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location")); indexCreated(); addVenues(); parser = new SpelExpressionParser();