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 037b753ef..3eeda7911 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 @@ -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 - *

- * 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 doFindOne(String collectionName, DBObject query, DBObject fields, Class entityClass) { - EntityReader readerToUse = this.mongoConverter; + MongoPersistentEntity entity = mappingContext.getPersistentEntity(entityClass); DBObject mappedQuery = queryMapper.getMappedObject(query, entity); - - return executeFindOneInternal(new FindOneCallback(mappedQuery, fields), new ReadDbObjectCallback(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 List doFind(String collectionName, DBObject query, DBObject fields, Class entityClass, - CursorPreparer preparer) { - return doFind(collectionName, query, fields, entityClass, preparer, new ReadDbObjectCallback(mongoConverter, - entityClass)); - } - - protected List doFind(String collectionName, DBObject query, DBObject fields, Class entityClass, - CursorPreparer preparer, DbObjectCallback 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( + this.mongoConverter, entityClass), collectionName); } /** @@ -1380,14 +1351,43 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * @return the List of converted objects. */ protected List doFind(String collectionName, DBObject query, DBObject fields, Class entityClass) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + entityClass - + " in collection: " + collectionName); - } - EntityReader readerToUse = this.mongoConverter; + return doFind(collectionName, query, fields, entityClass, null, new ReadDbObjectCallback(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 List doFind(String collectionName, DBObject query, DBObject fields, Class entityClass, + CursorPreparer preparer) { + return doFind(collectionName, query, fields, entityClass, preparer, new ReadDbObjectCallback(mongoConverter, + entityClass)); + } + + protected List doFind(String collectionName, DBObject query, DBObject fields, Class entityClass, + CursorPreparer preparer, DbObjectCallback objectCallback) { + MongoPersistentEntity entity = mappingContext.getPersistentEntity(entityClass); - return executeFindMultiInternal(new FindCallback(queryMapper.getMappedObject(query, entity), fields), null, - new ReadDbObjectCallback(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) { 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 287f339e0..f2673e7d1 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 @@ -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 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; + } }