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:
committed by
Oliver Gierke
parent
b2fe54c0a1
commit
d6c5907940
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user