Browse Source

DATAMONGO-185 - Expose hints on Query.

Query now exposes a withHint(…) method which will be applied to the DBCursor on query execution. Reduced CursorPreparer's visibility to the package and removed methods exposing it from MongoOperations.
pull/1/head
Oliver Gierke 14 years ago
parent
commit
2eda0f1701
  1. 2
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java
  2. 40
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java
  3. 223
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  4. 21
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
  5. 58
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java

2
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java

@ -22,7 +22,7 @@ import com.mongodb.DBCursor;
* *
* @author Oliver Gierke * @author Oliver Gierke
*/ */
public interface CursorPreparer { interface CursorPreparer {
/** /**
* Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor. * Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor.

40
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java

@ -91,18 +91,6 @@ public interface MongoOperations {
*/ */
void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch); void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch);
/**
* Execute a MongoDB query and iterate over the query results on a per-document basis with a DocumentCallbackHandler using the
* provided CursorPreparer.
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification
* @param collectionName name of the collection to retrieve the objects from
* @param dch the handler that will extract results, one document at a time
* @param preparer allows for customization of the DBCursor used when iterating over the result set, (apply limits,
* skips and so on).
*/
void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch, CursorPreparer preparer);
/** /**
* Executes a {@link DbCallback} translating any exceptions as necessary. * Executes a {@link DbCallback} translating any exceptions as necessary.
* <p/> * <p/>
@ -271,7 +259,7 @@ public interface MongoOperations {
/** /**
* Execute a group operation over the entire collection. * Execute a group operation over the entire collection.
* The group operation entity class should match the 'shape' of the returned object that takes int account the * The group operation entity class should match the 'shape' of the returned object that takes int account the
* initial document structure as well as any finalize functions. * initial document structure as well as any finalize functions.
* *
@ -281,7 +269,7 @@ public interface MongoOperations {
* @param entityClass The parameterized type of the returned list * @param entityClass The parameterized type of the returned list
* @return The results of the group operation * @return The results of the group operation
*/ */
<T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass); <T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass);
/** /**
* Execute a group operation restricting the rows to those which match the provided Criteria. * Execute a group operation restricting the rows to those which match the provided Criteria.
@ -452,26 +440,6 @@ public interface MongoOperations {
*/ */
<T> List<T> find(Query query, Class<T> entityClass, String collectionName); <T> List<T> find(Query query, Class<T> entityClass, String collectionName);
/**
* Map the results of an ad-hoc query on the specified collection to a List of the specified type.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* <p/>
* The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more
* feature rich {@link Query}.
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification
* @param entityClass the parameterized type of the returned list.
* @param preparer allows for customization of the DBCursor used when iterating over the result set, (apply limits,
* skips and so on).
* @param collectionName name of the collection to retrieve the objects from
*
* @return the List of converted objects.
*/
<T> List<T> find(Query query, Class<T> entityClass, CursorPreparer preparer, String collectionName);
/** /**
* Returns a document with the given id mapped onto the given class. The collection the query is ran against will be * Returns a document with the given id mapped onto the given class. The collection the query is ran against will be
* derived from the given target class as well. * derived from the given target class as well.
@ -647,7 +615,7 @@ public interface MongoOperations {
void save(Object objectToSave, String collectionName); void save(Object objectToSave, String collectionName);
/** /**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted * Performs an upsert. If no document is found that matches the query, a new document is created and inserted
* by combining the query document and the update document. * by combining the query document and the update document.
* *
* @param query the query document that specifies the criteria used to select a record to be upserted * @param query the query document that specifies the criteria used to select a record to be upserted
@ -659,7 +627,7 @@ public interface MongoOperations {
/** /**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted * Performs an upsert. If no document is found that matches the query, a new document is created and inserted
* by combining the query document and the update document. * by combining the query document and the update document.
* *
* @param query the query document that specifies the criteria used to select a record to be updated * @param query the query document that specifies the criteria used to select a record to be updated

223
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

@ -2,6 +2,8 @@
* Copyright 2010-2011 the original author or authors. * Copyright 2010-2011 the original author or authors.
* *
* Licensed under t * Licensed under t
import org.springframework.util.ResourceUtils;
import org.springframework.data.convert.EntityReader; import org.springframework.data.convert.EntityReader;
he Apache License, Version 2.0 (the "License"); he Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -125,7 +127,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* the DB or Collection. * the DB or Collection.
*/ */
private WriteConcern writeConcern = null; private WriteConcern writeConcern = null;
private WriteConcernResolver writeConcernResolver = new DefaultWriteConcernResolver(); private WriteConcernResolver writeConcernResolver = new DefaultWriteConcernResolver();
/* /*
@ -135,7 +137,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
private WriteResultChecking writeResultChecking = WriteResultChecking.NONE; private WriteResultChecking writeResultChecking = WriteResultChecking.NONE;
/** /**
* Set the ReadPreference when operating on a collection. See {@link #prepareCollection(DBCollection)} * Set the ReadPreference when operating on a collection. See {@link #prepareCollection(DBCollection)}
*/ */
private ReadPreference readPreference = null; private ReadPreference readPreference = null;
@ -187,11 +189,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @param mongoConverter * @param mongoConverter
*/ */
public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) { public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) {
Assert.notNull(mongoDbFactory); Assert.notNull(mongoDbFactory);
this.mongoDbFactory = mongoDbFactory; this.mongoDbFactory = mongoDbFactory;
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter; this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
this.mapper = new QueryMapper(this.mongoConverter.getConversionService()); this.mapper = new QueryMapper(this.mongoConverter);
// We always have a mapping context in the converter, whether it's a simple one or not // We always have a mapping context in the converter, whether it's a simple one or not
mappingContext = this.mongoConverter.getMappingContext(); mappingContext = this.mongoConverter.getMappingContext();
@ -233,9 +236,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) { public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) {
this.writeConcernResolver = writeConcernResolver; this.writeConcernResolver = writeConcernResolver;
} }
/** /**
* Used by @{link {@link #prepareCollection(DBCollection)} to set the {@link ReadPreference} before any operations are performed. * Used by @{link {@link #prepareCollection(DBCollection)} to set the {@link ReadPreference} before any operations are
* performed.
*
* @param readPreference * @param readPreference
*/ */
public void setReadPreference(ReadPreference readPreference) { public void setReadPreference(ReadPreference readPreference) {
@ -307,16 +312,32 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} }
public void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch) { public void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch) {
executeQuery(query, collectionName, dch, null); executeQuery(query, collectionName, dch, new QueryCursorPreparer(query));
} }
public void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch, CursorPreparer preparer) { /**
* Execute a MongoDB query and iterate over the query results on a per-document basis with a
* {@link DocumentCallbackHandler} using the provided CursorPreparer.
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification, must not be {@literal null}.
* @param collectionName name of the collection to retrieve the objects from
* @param dch the handler that will extract results, one document at a time
* @param preparer allows for customization of the {@link DBCursor} used when iterating over the result set, (apply
* limits, skips and so on).
*/
protected void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch, CursorPreparer preparer) {
Assert.notNull(query);
DBObject queryObject = query.getQueryObject(); DBObject queryObject = query.getQueryObject();
DBObject fieldsObject = query.getFieldsObject(); DBObject fieldsObject = query.getFieldsObject();
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("find using query: " + queryObject + " fields: " + fieldsObject + " in collection: " LOGGER.debug("find using query: " + queryObject + " fields: " + fieldsObject + " in collection: "
+ collectionName); + collectionName);
} }
this.executeQueryInternal(new FindCallback(queryObject, fieldsObject), preparer, dch, collectionName); this.executeQueryInternal(new FindCallback(queryObject, fieldsObject), preparer, dch, collectionName);
} }
@ -456,36 +477,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} }
public <T> List<T> find(final Query query, Class<T> entityClass, String collectionName) { public <T> List<T> find(final Query query, Class<T> entityClass, String collectionName) {
CursorPreparer cursorPreparer = null; CursorPreparer cursorPreparer = query == null ? null : new QueryCursorPreparer(query);
if (query.getSkip() > 0 || query.getLimit() > 0 || query.getSortObject() != null) {
cursorPreparer = new CursorPreparer() {
public DBCursor prepare(DBCursor cursor) {
DBCursor cursorToUse = cursor;
try {
if (query.getSkip() > 0) {
cursorToUse = cursorToUse.skip(query.getSkip());
}
if (query.getLimit() > 0) {
cursorToUse = cursorToUse.limit(query.getLimit());
}
if (query.getSortObject() != null) {
cursorToUse = cursorToUse.sort(query.getSortObject());
}
} catch (RuntimeException e) {
throw potentiallyConvertRuntimeException(e);
}
return cursorToUse;
}
};
}
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass, cursorPreparer); return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass, cursorPreparer);
} }
public <T> List<T> find(Query query, Class<T> entityClass, CursorPreparer preparer, String collectionName) {
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass, preparer);
}
public <T> T findById(Object id, Class<T> entityClass) { public <T> T findById(Object id, Class<T> entityClass) {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityClass); MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityClass);
return findById(id, entityClass, persistentEntity.getCollection()); return findById(id, entityClass, persistentEntity.getCollection());
@ -542,7 +537,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(), query.getSortObject(), return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(), query.getSortObject(),
entityClass, update, options); entityClass, update, options);
} }
// Find methods that take a Query to express the query and that return a single object that is also removed from the // Find methods that take a Query to express the query and that return a single object that is also removed from the
// collection in the database. // collection in the database.
@ -563,13 +558,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public long count(final Query query, String collectionName) { public long count(final Query query, String collectionName) {
return count(query, null, collectionName); return count(query, null, collectionName);
} }
private long count(Query query, Class<?> entityClass, String collectionName) { private long count(Query query, Class<?> entityClass, String collectionName) {
Assert.hasText(collectionName); Assert.hasText(collectionName);
final DBObject dbObject = query == null ? null : mapper.getMappedObject(query.getQueryObject(), final DBObject dbObject = query == null ? null : mapper.getMappedObject(query.getQueryObject(),
entityClass == null ? null : mappingContext.getPersistentEntity(entityClass)); entityClass == null ? null : mappingContext.getPersistentEntity(entityClass));
return execute(collectionName, new CollectionCallback<Long>() { return execute(collectionName, new CollectionCallback<Long>() {
public Long doInCollection(DBCollection collection) throws MongoException, DataAccessException { public Long doInCollection(DBCollection collection) throws MongoException, DataAccessException {
return collection.count(dbObject); return collection.count(dbObject);
@ -729,7 +724,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} }
return execute(collectionName, new CollectionCallback<Object>() { return execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName, entityClass, dbDoc, null); MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName,
entityClass, dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (writeConcernToUse == null) { if (writeConcernToUse == null) {
collection.insert(dbDoc); collection.insert(dbDoc);
@ -751,7 +747,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} }
execute(collectionName, new CollectionCallback<Void>() { execute(collectionName, new CollectionCallback<Void>() {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null, null, null); MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null,
null, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (writeConcernToUse == null) { if (writeConcernToUse == null) {
collection.insert(dbDocList); collection.insert(dbDocList);
@ -781,7 +778,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} }
return execute(collectionName, new CollectionCallback<Object>() { return execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass, dbDoc, null); MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass,
dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (writeConcernToUse == null) { if (writeConcernToUse == null) {
collection.save(dbDoc); collection.save(dbDoc);
@ -793,15 +791,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}); });
} }
public WriteResult upsert(Query query, Update update, Class<?> entityClass) { public WriteResult upsert(Query query, Update update, Class<?> entityClass) {
return doUpdate(determineCollectionName(entityClass), query, update, entityClass, true, false); return doUpdate(determineCollectionName(entityClass), query, update, entityClass, true, false);
} }
public WriteResult upsert(Query query, Update update, String collectionName) { public WriteResult upsert(Query query, Update update, String collectionName) {
return doUpdate(collectionName, query, update, null, true, false); return doUpdate(collectionName, query, update, null, true, false);
} }
public WriteResult updateFirst(Query query, Update update, Class<?> entityClass) { public WriteResult updateFirst(Query query, Update update, Class<?> entityClass) {
return doUpdate(determineCollectionName(entityClass), query, update, entityClass, false, false); return doUpdate(determineCollectionName(entityClass), query, update, entityClass, false, false);
} }
@ -840,7 +837,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} }
WriteResult wr; WriteResult wr;
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName, entityClass, updateObj, queryObj); MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName,
entityClass, updateObj, queryObj);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (writeConcernToUse == null) { if (writeConcernToUse == null) {
wr = collection.update(queryObj, updateObj, upsert, multi); wr = collection.update(queryObj, updateObj, upsert, multi);
@ -861,18 +859,18 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
remove(getIdQueryFor(object), object.getClass()); remove(getIdQueryFor(object), object.getClass());
} }
public void remove(Object object, String collection) { public void remove(Object object, String collection) {
Assert.hasText(collection); Assert.hasText(collection);
if (object == null) { if (object == null) {
return; return;
} }
remove(getIdQueryFor(object), collection); remove(getIdQueryFor(object), collection);
} }
/** /**
* Returns a {@link Query} for the given entity by its id. * Returns a {@link Query} for the given entity by its id.
* *
@ -880,9 +878,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @return * @return
*/ */
private Query getIdQueryFor(Object object) { private Query getIdQueryFor(Object object) {
Assert.notNull(object); Assert.notNull(object);
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(object.getClass()); MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(object.getClass());
MongoPersistentProperty idProp = entity.getIdProperty(); MongoPersistentProperty idProp = entity.getIdProperty();
@ -894,10 +892,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
Object idProperty = null; Object idProperty = null;
try { try {
idProperty = BeanWrapper.create(object, service).getProperty(idProp, Object.class, true); idProperty = BeanWrapper.create(object, service).getProperty(idProp, Object.class, true);
return new Query(where(idProp.getFieldName()).is(idProperty)); return new Query(where(idProp.getFieldName()).is(idProperty));
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e); throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
@ -920,7 +918,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject dboq = mapper.getMappedObject(queryObject, entity); DBObject dboq = mapper.getMappedObject(queryObject, entity);
WriteResult wr = null; WriteResult wr = null;
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName, entityClass, null, queryObject); MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName,
entityClass, null, queryObject);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("remove using query: " + queryObject + " in collection: " + collection.getName()); LOGGER.debug("remove using query: " + queryObject + " in collection: " + collection.getName());
@ -1012,28 +1011,30 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return mapReduceResult; return mapReduceResult;
} }
public <T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass) { public <T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass) {
return group(null, inputCollectionName, groupBy, entityClass); return group(null, inputCollectionName, groupBy, entityClass);
} }
public <T> GroupByResults<T> group(Criteria criteria, String inputCollectionName, GroupBy groupBy, Class<T> entityClass) { public <T> GroupByResults<T> group(Criteria criteria, String inputCollectionName, GroupBy groupBy,
Class<T> entityClass) {
DBObject dbo = groupBy.getGroupByObject(); DBObject dbo = groupBy.getGroupByObject();
dbo.put("ns", inputCollectionName); dbo.put("ns", inputCollectionName);
if (criteria == null) { if (criteria == null) {
dbo.put("cond", null); dbo.put("cond", null);
} else { } else {
dbo.put("cond", criteria.getCriteriaObject()); dbo.put("cond", criteria.getCriteriaObject());
} }
//If initial document was a JavaScript string, potentially loaded by Spring's Resource abstraction, load it and convert to DBObject // If initial document was a JavaScript string, potentially loaded by Spring's Resource abstraction, load it and
// convert to DBObject
if (dbo.containsField("initial")) { if (dbo.containsField("initial")) {
Object initialObj = dbo.get("initial"); Object initialObj = dbo.get("initial");
if (initialObj instanceof String) { if (initialObj instanceof String) {
String initialAsString = replaceWithResourceIfNecessary((String)initialObj); String initialAsString = replaceWithResourceIfNecessary((String) initialObj);
dbo.put("initial", JSON.parse(initialAsString)); dbo.put("initial", JSON.parse(initialAsString));
} }
} }
@ -1046,16 +1047,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
if (dbo.containsField("finalize")) { if (dbo.containsField("finalize")) {
dbo.put("finalize", replaceWithResourceIfNecessary(dbo.get("finalize").toString())); dbo.put("finalize", replaceWithResourceIfNecessary(dbo.get("finalize").toString()));
} }
DBObject commandObject = new BasicDBObject("group", dbo); DBObject commandObject = new BasicDBObject("group", dbo);
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Executing Group with DBObject [" + commandObject.toString() + "]"); LOGGER.debug("Executing Group with DBObject [" + commandObject.toString() + "]");
} }
CommandResult commandResult = null; CommandResult commandResult = null;
try { try {
commandResult = executeCommand(commandObject, getDb().getOptions()); commandResult = executeCommand(commandObject, getDb().getOptions());
commandResult.throwOnError(); commandResult.throwOnError();
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
this.potentiallyConvertRuntimeException(ex); this.potentiallyConvertRuntimeException(ex);
} }
@ -1068,10 +1069,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Group command result = [" + commandResult + "]"); LOGGER.debug("Group command result = [" + commandResult + "]");
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Iterable<DBObject> resultSet = (Iterable<DBObject>) commandResult.get( "retval" ); Iterable<DBObject> resultSet = (Iterable<DBObject>) commandResult.get("retval");
List<T> mappedResults = new ArrayList<T>(); List<T> mappedResults = new ArrayList<T>();
DbObjectCallback<T> callback = new ReadDbObjectCallback<T>(mongoConverter, entityClass); DbObjectCallback<T> callback = new ReadDbObjectCallback<T>(mongoConverter, entityClass);
for (DBObject dbObject : resultSet) { for (DBObject dbObject : resultSet) {
@ -1079,7 +1080,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} }
GroupByResults<T> groupByResult = new GroupByResults<T>(mappedResults, commandResult); GroupByResults<T> groupByResult = new GroupByResults<T>(mappedResults, commandResult);
return groupByResult; return groupByResult;
} }
@ -1298,30 +1298,28 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return executeFindOneInternal(new FindAndRemoveCallback(mapper.getMappedObject(query, entity), fields, sort), return executeFindOneInternal(new FindAndRemoveCallback(mapper.getMappedObject(query, entity), fields, sort),
new ReadDbObjectCallback<T>(readerToUse, entityClass), collectionName); new ReadDbObjectCallback<T>(readerToUse, entityClass), collectionName);
} }
protected <T> T doFindAndModify(String collectionName, DBObject query, DBObject fields, DBObject sort, protected <T> T doFindAndModify(String collectionName, DBObject query, DBObject fields, DBObject sort,
Class<T> entityClass, Update update, FindAndModifyOptions options ) { Class<T> entityClass, Update update, FindAndModifyOptions options) {
EntityReader<? super T, DBObject> readerToUse = this.mongoConverter; EntityReader<? super T, DBObject> readerToUse = this.mongoConverter;
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass); MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
DBObject updateObj = update.getUpdateObject(); DBObject updateObj = update.getUpdateObject();
for (String key : updateObj.keySet()) { for (String key : updateObj.keySet()) {
updateObj.put(key, mongoConverter.convertToMongoType(updateObj.get(key))); updateObj.put(key, mongoConverter.convertToMongoType(updateObj.get(key)));
} }
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("findAndModify using query: " + query + " fields: " + fields + " sort: " + sort + " for class: " LOGGER.debug("findAndModify using query: " + query + " fields: " + fields + " sort: " + sort + " for class: "
+ entityClass + " and update: " + updateObj + " in collection: " + collectionName); + entityClass + " and update: " + updateObj + " in collection: " + collectionName);
} }
return executeFindOneInternal(new FindAndModifyCallback(mapper.getMappedObject(query, entity), fields, sort,
return executeFindOneInternal(new FindAndModifyCallback(mapper.getMappedObject(query, entity), fields, sort, updateObj, options), updateObj, options), new ReadDbObjectCallback<T>(readerToUse, entityClass), collectionName);
new ReadDbObjectCallback<T>(readerToUse, entityClass), collectionName);
} }
/** /**
* Populates the id property of the saved object, if it's not set already. * Populates the id property of the saved object, if it's not set already.
* *
@ -1478,7 +1476,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/** /**
* Checks and handles any errors. * Checks and handles any errors.
* <p/> * <p/>
* Current implementation logs errors. Future version may make this configurable to log warning, errors or throw exception. * Current implementation logs errors. Future version may make this configurable to log warning, errors or throw
* exception.
*/ */
private void handleAnyWriteResultErrors(WriteResult wr, DBObject query, String operation) { private void handleAnyWriteResultErrors(WriteResult wr, DBObject query, String operation) {
if (WriteResultChecking.NONE == this.writeResultChecking) { if (WriteResultChecking.NONE == this.writeResultChecking) {
@ -1611,7 +1610,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return collection.findAndModify(query, fields, sort, true, null, false, false); return collection.findAndModify(query, fields, sort, true, null, false, false);
} }
} }
private static class FindAndModifyCallback implements CollectionCallback<DBObject> { private static class FindAndModifyCallback implements CollectionCallback<DBObject> {
private final DBObject query; private final DBObject query;
@ -1620,7 +1619,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
private final DBObject update; private final DBObject update;
private final FindAndModifyOptions options; private final FindAndModifyOptions options;
public FindAndModifyCallback(DBObject query, DBObject fields, DBObject sort, DBObject update, FindAndModifyOptions options) { public FindAndModifyCallback(DBObject query, DBObject fields, DBObject sort, DBObject update,
FindAndModifyOptions options) {
this.query = query; this.query = query;
this.fields = fields; this.fields = fields;
this.sort = sort; this.sort = sort;
@ -1629,7 +1629,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} }
public DBObject doInCollection(DBCollection collection) throws MongoException, DataAccessException { public DBObject doInCollection(DBCollection collection) throws MongoException, DataAccessException {
return collection.findAndModify(query, fields, sort, options.isRemove(), update, options.isReturnNew(), options.isUpsert()); return collection.findAndModify(query, fields, sort, options.isRemove(), update, options.isReturnNew(),
options.isUpsert());
} }
} }
@ -1673,15 +1674,61 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return source; return source;
} }
} }
private class DefaultWriteConcernResolver implements WriteConcernResolver { private class DefaultWriteConcernResolver implements WriteConcernResolver {
public WriteConcern resolve(MongoAction action) { public WriteConcern resolve(MongoAction action) {
return action.getDefaultWriteConcern(); return action.getDefaultWriteConcern();
} }
} }
class QueryCursorPreparer implements CursorPreparer {
private final Query query;
public QueryCursorPreparer(Query query) {
this.query = query;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.CursorPreparer#prepare(com.mongodb.DBCursor)
*/
public DBCursor prepare(DBCursor cursor) {
if (query == null) {
return cursor;
}
if (query.getSkip() <= 0 && query.getLimit() <= 0 && query.getSortObject() == null && !StringUtils
.hasText(query.getHint())) {
return cursor;
}
DBCursor cursorToUse = cursor;
try {
if (query.getSkip() > 0) {
cursorToUse = cursorToUse.skip(query.getSkip());
}
if (query.getLimit() > 0) {
cursorToUse = cursorToUse.limit(query.getLimit());
}
if (query.getSortObject() != null) {
cursorToUse = cursorToUse.sort(query.getSortObject());
}
if (StringUtils.hasText(query.getHint())) {
cursorToUse = cursorToUse.hint(query.getHint());
}
} catch (RuntimeException e) {
throw potentiallyConvertRuntimeException(e);
}
return cursorToUse;
}
}
/** /**
* {@link DbObjectCallback} that assumes a {@link GeoResult} to be created, delegates actual content unmarshalling to * {@link DbObjectCallback} that assumes a {@link GeoResult} to be created, delegates actual content unmarshalling to
* a delegate and creates a {@link GeoResult} from the result. * a delegate and creates a {@link GeoResult} from the result.

21
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java

@ -19,9 +19,11 @@ import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject; import com.mongodb.DBObject;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
public class Query { public class Query {
@ -30,6 +32,7 @@ public class Query {
private Sort sort; private Sort sort;
private int skip; private int skip;
private int limit; private int limit;
private String hint;
/** /**
* Static factory method to create a Query using the provided criteria * Static factory method to create a Query using the provided criteria
@ -79,6 +82,18 @@ public class Query {
return this; return this;
} }
/**
* Configures the query to use the given hint when being executed.
*
* @param name must not be {@literal null} or empty.
* @return
*/
public Query withHint(String name) {
Assert.hasText(name, "Hint must not be empty or null!");
this.hint = name;
return this;
}
public Sort sort() { public Sort sort() {
if (this.sort == null) { if (this.sort == null) {
this.sort = new Sort(); this.sort = new Sort();
@ -119,6 +134,10 @@ public class Query {
return this.limit; return this.limit;
} }
public String getHint() {
return hint;
}
protected List<Criteria> getCriteria() { protected List<Criteria> getCriteria() {
return new ArrayList<Criteria>(this.criteria.values()); return new ArrayList<Criteria>(this.criteria.values());
} }

58
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java

@ -0,0 +1,58 @@
/*
* 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 static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate.QueryCursorPreparer;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.DBCursor;
/**
* Unit tests for {@link QueryCursorPreparer}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class QueryCursorPreparerUnitTests {
@Mock
MongoDbFactory factory;
@Mock
DBCursor cursor;
/**
* @see DATAMONGO-185
*/
@Test
public void appliesHintsCorrectly() {
Query query = query(where("foo").is("bar")).withHint("hint");
CursorPreparer preparer = new MongoTemplate(factory).new QueryCursorPreparer(query);
preparer.prepare(cursor);
verify(cursor).hint("hint");
}
}
Loading…
Cancel
Save