DATAMONGO-702 - Allow usage of property names in field specifications.

MongoTemplate now translates property names used in a Query's field specification into the according field names. Refactored delegation in various doFind(…) methods and polished JavaDoc.

Original pull request: #50.
This commit is contained in:
Thomas Darimont
2013-07-09 01:10:09 +02:00
committed by Oliver Gierke
parent b2fe54c0a1
commit d6c5907940
2 changed files with 197 additions and 48 deletions

View File

@@ -1316,57 +1316,28 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
/**
* Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter
* <p/>
* The query document is specified as a standard DBObject and so is the fields specification.
* Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter.
* The query document is specified as a standard {@link DBObject} and so is the fields specification.
*
* @param collectionName name of the collection to retrieve the objects from
* @param query the query document that specifies the criteria used to find a record
* @param fields the document that specifies the fields to be returned
* @param collectionName name of the collection to retrieve the objects from.
* @param query the query document that specifies the criteria used to find a record.
* @param fields the document that specifies the fields to be returned.
* @param entityClass the parameterized type of the returned list.
* @return the List of converted objects.
* @return the {@link List} of converted objects.
*/
protected <T> T doFindOne(String collectionName, DBObject query, DBObject fields, Class<T> entityClass) {
EntityReader<? super T, DBObject> readerToUse = this.mongoConverter;
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
DBObject mappedQuery = queryMapper.getMappedObject(query, entity);
return executeFindOneInternal(new FindOneCallback(mappedQuery, fields), new ReadDbObjectCallback<T>(readerToUse,
entityClass), collectionName);
}
/**
* Map the results of an ad-hoc query on the default MongoDB collection to a List of the specified type. The object is
* converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless configured
* otherwise, an instance of MappingMongoConverter will be used. The query document is specified as a standard
* DBObject and so is the fields specification. Can be overridden by subclasses.
*
* @param collectionName name of the collection to retrieve the objects from
* @param query the query document that specifies the criteria used to find a record
* @param fields the document that specifies the fields to be returned
* @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).
* @return the List of converted objects.
*/
protected <T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<T> entityClass,
CursorPreparer preparer) {
return doFind(collectionName, query, fields, entityClass, preparer, new ReadDbObjectCallback<T>(mongoConverter,
entityClass));
}
protected <S, T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<S> entityClass,
CursorPreparer preparer, DbObjectCallback<T> objectCallback) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
DBObject mappedFields = fields == null ? null : queryMapper.getMappedObject(fields, entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("find using query: %s fields: %s for class: %s in collection: %s",
serializeToJsonSafely(query), fields, entityClass, collectionName));
LOGGER.debug(String.format("findOne using query: %s fields: %s for class: %s in collection: %s",
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
}
return executeFindMultiInternal(new FindCallback(queryMapper.getMappedObject(query, entity), fields), preparer,
objectCallback, collectionName);
return executeFindOneInternal(new FindOneCallback(mappedQuery, mappedFields), new ReadDbObjectCallback<T>(
this.mongoConverter, entityClass), collectionName);
}
/**
@@ -1380,14 +1351,43 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @return the List of converted objects.
*/
protected <T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<T> entityClass) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + entityClass
+ " in collection: " + collectionName);
}
EntityReader<? super T, DBObject> readerToUse = this.mongoConverter;
return doFind(collectionName, query, fields, entityClass, null, new ReadDbObjectCallback<T>(this.mongoConverter,
entityClass));
}
/**
* Map the results of an ad-hoc query on the default MongoDB collection to a List of the specified type. The object is
* converted from the MongoDB native representation using an instance of {@see MongoConverter}. The query document is
* specified as a standard DBObject and so is the fields specification.
*
* @param collectionName name of the collection to retrieve the objects from.
* @param query the query document that specifies the criteria used to find a record.
* @param fields the document that specifies the fields to be returned.
* @param entityClass the parameterized type of the returned list.
* @param preparer allows for customization of the {@link DBCursor} used when iterating over the result set, (apply
* limits, skips and so on).
* @return the {@link List} of converted objects.
*/
protected <T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<T> entityClass,
CursorPreparer preparer) {
return doFind(collectionName, query, fields, entityClass, preparer, new ReadDbObjectCallback<T>(mongoConverter,
entityClass));
}
protected <S, T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<S> entityClass,
CursorPreparer preparer, DbObjectCallback<T> objectCallback) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
return executeFindMultiInternal(new FindCallback(queryMapper.getMappedObject(query, entity), fields), null,
new ReadDbObjectCallback<T>(readerToUse, entityClass), collectionName);
DBObject mappedFields = fields == null ? null : queryMapper.getMappedObject(fields, entity);
DBObject mappedQuery = queryMapper.getMappedObject(query, entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("find using query: %s fields: %s for class: %s in collection: %s",
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
}
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields), preparer, objectCallback,
collectionName);
}
protected DBObject convertToDbObject(CollectionOptions collectionOptions) {

View File

@@ -154,6 +154,8 @@ public class MongoTemplateTests {
template.dropCollection("collection");
template.dropCollection("personX");
template.dropCollection(Document.class);
template.dropCollection(ObjectWith3AliasedFields.class);
template.dropCollection(ObjectWith3AliasedFieldsAndNestedAddress.class);
}
@Test
@@ -1706,6 +1708,141 @@ public class MongoTemplateTests {
assertThat(result.model.value(), is(newModelValue));
}
/**
* @see DATAMONGO-702
*/
@Test
public void queryShouldSupportRealAndAliasedPropertyNamesForFieldInclusions() {
ObjectWith3AliasedFields obj = new ObjectWith3AliasedFields();
obj.id = "4711";
obj.property1 = "P1";
obj.property2 = "P2";
obj.property3 = "P3";
template.insert(obj);
Query query = new Query(Criteria.where("id").is(obj.id));
query.fields() //
.include("property2") // real property name
.include("prop3"); // aliased property name
ObjectWith3AliasedFields result = template.findOne(query, ObjectWith3AliasedFields.class);
assertThat(result.id, is(obj.id));
assertThat(result.property1, is(nullValue()));
assertThat(result.property2, is(obj.property2));
assertThat(result.property3, is(obj.property3));
}
/**
* @see DATAMONGO-702
*/
@Test
public void queryShouldSupportRealAndAliasedPropertyNamesForFieldExclusions() {
ObjectWith3AliasedFields obj = new ObjectWith3AliasedFields();
obj.id = "4711";
obj.property1 = "P1";
obj.property2 = "P2";
obj.property3 = "P3";
template.insert(obj);
Query query = new Query(Criteria.where("id").is(obj.id));
query.fields() //
.exclude("property2") // real property name
.exclude("prop3"); // aliased property name
ObjectWith3AliasedFields result = template.findOne(query, ObjectWith3AliasedFields.class);
assertThat(result.id, is(obj.id));
assertThat(result.property1, is(obj.property1));
assertThat(result.property2, is(nullValue()));
assertThat(result.property3, is(nullValue()));
}
/**
* @see DATAMONGO-702
*/
@Test
public void findMultipleWithQueryShouldSupportRealAndAliasedPropertyNamesForFieldExclusions() {
ObjectWith3AliasedFields obj0 = new ObjectWith3AliasedFields();
obj0.id = "4711";
obj0.property1 = "P10";
obj0.property2 = "P20";
obj0.property3 = "P30";
ObjectWith3AliasedFields obj1 = new ObjectWith3AliasedFields();
obj1.id = "4712";
obj1.property1 = "P11";
obj1.property2 = "P21";
obj1.property3 = "P31";
template.insert(obj0);
template.insert(obj1);
Query query = new Query(Criteria.where("id").in(obj0.id, obj1.id));
query.fields() //
.exclude("property2") // real property name
.exclude("prop3"); // aliased property name
List<ObjectWith3AliasedFields> results = template.find(query, ObjectWith3AliasedFields.class);
assertThat(results, is(notNullValue()));
assertThat(results.size(), is(2));
ObjectWith3AliasedFields result0 = results.get(0);
assertThat(result0, is(notNullValue()));
assertThat(result0.id, is(obj0.id));
assertThat(result0.property1, is(obj0.property1));
assertThat(result0.property2, is(nullValue()));
assertThat(result0.property3, is(nullValue()));
ObjectWith3AliasedFields result1 = results.get(1);
assertThat(result1, is(notNullValue()));
assertThat(result1.id, is(obj1.id));
assertThat(result1.property1, is(obj1.property1));
assertThat(result1.property2, is(nullValue()));
assertThat(result1.property3, is(nullValue()));
}
/**
* @see DATAMONGO-702
*/
@Test
public void queryShouldSupportNestedPropertyNamesForFieldInclusions() {
ObjectWith3AliasedFieldsAndNestedAddress obj = new ObjectWith3AliasedFieldsAndNestedAddress();
obj.id = "4711";
obj.property1 = "P1";
obj.property2 = "P2";
obj.property3 = "P3";
Address address = new Address();
String stateValue = "WA";
address.state = stateValue;
address.city = "Washington";
obj.address = address;
template.insert(obj);
Query query = new Query(Criteria.where("id").is(obj.id));
query.fields() //
.include("property2") // real property name
.include("address.state"); // aliased property name
ObjectWith3AliasedFieldsAndNestedAddress result = template.findOne(query,
ObjectWith3AliasedFieldsAndNestedAddress.class);
assertThat(result.id, is(obj.id));
assertThat(result.property1, is(nullValue()));
assertThat(result.property2, is(obj.property2));
assertThat(result.property3, is(nullValue()));
assertThat(result.address, is(notNullValue()));
assertThat(result.address.city, is(nullValue()));
assertThat(result.address.state, is(stateValue));
}
static interface Model {
String value();
@@ -1819,4 +1956,16 @@ public class MongoTemplateTests {
@Id String id;
Date date;
}
static class ObjectWith3AliasedFields {
@Id String id;
@Field("prop1") String property1;
@Field("prop2") String property2;
@Field("prop3") String property3;
}
static class ObjectWith3AliasedFieldsAndNestedAddress extends ObjectWith3AliasedFields {
@Field("adr") Address address;
}
}