From e405bf574c9ae81905a8d5c124765091b2aa83f7 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 6 Oct 2011 22:57:27 +0200 Subject: [PATCH] DATADOC-291 - MongoQueryCreator now considers mapping information for query building. Instead of using the pure PropertyPath of the PartTree we ask the MappingContext for a PersistentPropertyPath and create a field name based path expression from it. --- .../core/mapping/MongoPersistentProperty.java | 19 ++++++ .../repository/query/MongoQueryCreator.java | 51 ++++++++++++---- .../repository/query/PartTreeMongoQuery.java | 6 +- .../mongodb/core/mapping/MappingTests.java | 2 +- .../query/MongoQueryCreatorUnitTests.java | 60 +++++++++++++------ 5 files changed, 105 insertions(+), 33 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java index 345ebaa35..0803bffe5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core.mapping; +import org.springframework.core.convert.converter.Converter; import org.springframework.data.mapping.PersistentProperty; /** @@ -53,4 +54,22 @@ public interface MongoPersistentProperty extends PersistentProperty { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) + */ + public String convert(MongoPersistentProperty source) { + return source.getFieldName(); + } + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java index fa3e3451e..d36213c74 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java @@ -22,19 +22,22 @@ import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.data.domain.Sort; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.context.PersistentPropertyPath; import org.springframework.data.mongodb.core.geo.Box; import org.springframework.data.mongodb.core.geo.Circle; import org.springframework.data.mongodb.core.geo.Distance; import org.springframework.data.mongodb.core.geo.Point; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator; -import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.util.Assert; /** * Custom query creator to create Mongo criterias. @@ -47,24 +50,41 @@ class MongoQueryCreator extends AbstractQueryCreator { private final MongoParameterAccessor accessor; private final boolean isGeoNearQuery; - public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor) { - this(tree, accessor, false); - } - + private final MappingContext context; + /** - * Creates a new {@link MongoQueryCreator} from the given {@link PartTree} and {@link ParametersParameterAccessor}. + * Creates a new {@link MongoQueryCreator} from the given {@link PartTree}, {@link ConvertingParameterAccessor} and + * {@link MappingContext}. * * @param tree * @param accessor + * @param context + */ + public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, + MappingContext context) { + this(tree, accessor, context, false); + } + + /** + * Creates a new {@link MongoQueryCreator} from the given {@link PartTree}, {@link ConvertingParameterAccessor} and + * {@link MappingContext}. + * + * @param tree + * @param accessor + * @param context * @param isGeoNearQuery */ - public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, boolean isGeoNearQuery) { + public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, + MappingContext context, boolean isGeoNearQuery) { super(tree, accessor); + + Assert.notNull(context); + this.accessor = accessor; this.isGeoNearQuery = isGeoNearQuery; + this.context = context; } - /* * (non-Javadoc) @@ -72,12 +92,14 @@ class MongoQueryCreator extends AbstractQueryCreator { */ @Override protected Query create(Part part, Iterator iterator) { - + if (isGeoNearQuery && part.getType().equals(Type.NEAR)) { return null; } - Criteria criteria = from(part.getType(), where(part.getProperty().toDotPath()), + PersistentPropertyPath path = context.getPersistentPropertyPath(part.getProperty()); + Criteria criteria = from(part.getType(), + where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)), (PotentiallyConvertingIterator) iterator); return new Query(criteria); @@ -89,12 +111,15 @@ class MongoQueryCreator extends AbstractQueryCreator { */ @Override protected Query and(Part part, Query base, Iterator iterator) { - + if (base == null) { return create(part, iterator); } - Criteria criteria = from(part.getType(), where(part.getProperty().toDotPath()), + PersistentPropertyPath path2 = context.getPersistentPropertyPath(part.getProperty()); + + Criteria criteria = from(part.getType(), + where(path2.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)), (PotentiallyConvertingIterator) iterator); return base.addCriteria(criteria); } @@ -121,7 +146,7 @@ class MongoQueryCreator extends AbstractQueryCreator { */ @Override protected Query complete(Query query, Sort sort) { - + if (query == null) { return null; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java index 159b53518..5be857360 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java @@ -15,7 +15,9 @@ */ package org.springframework.data.mongodb.repository.query; +import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.RepositoryQuery; @@ -30,6 +32,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { private final PartTree tree; private final boolean isGeoNearQuery; + private final MappingContext context; /** * Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}. @@ -42,6 +45,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { super(method, template); this.tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType()); this.isGeoNearQuery = method.isGeoNearQuery(); + this.context = template.getConverter().getMappingContext(); } /** @@ -61,7 +65,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { @Override protected Query createQuery(ConvertingParameterAccessor accessor) { - MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, isGeoNearQuery); + MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery); return creator.createQuery(); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java index 45fa168e0..9dc5a7534 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java @@ -144,7 +144,7 @@ public class MappingTests { @Test public void testPersonMapProperty() { - PersonMapProperty p = new PersonMapProperty(1234567, "Map", "Property"); + PersonMapProperty p = new PersonMapProperty(1234567, "Map", "PropertyPath"); Map accounts = new HashMap(); AccountPojo checking = new AccountPojo("checking", 1000.0f); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java index ddbfb1b59..50013974a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java @@ -34,12 +34,15 @@ import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; +import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.Person; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.geo.Distance; import org.springframework.data.mongodb.core.geo.Metrics; import org.springframework.data.mongodb.core.geo.Point; +import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.support.DefaultEntityInformationCreator; @@ -63,8 +66,12 @@ public class MongoQueryCreatorUnitTests { @Mock MongoConverter converter; + MappingContext context; + @Before public void setUp() throws SecurityException, NoSuchMethodException { + + context = new MongoMappingContext(); doAnswer(new Answer() { public Void answer(InvocationOnMock invocation) throws Throwable { @@ -73,7 +80,6 @@ public class MongoQueryCreatorUnitTests { return null; } }).when(converter).write(any(), Mockito.any(DBObject.class)); - } @Test @@ -81,12 +87,12 @@ public class MongoQueryCreatorUnitTests { PartTree tree = new PartTree("findByFirstName", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Oliver")); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Oliver"), context); creator.createQuery(); creator = new MongoQueryCreator(new PartTree("findByFirstNameAndFriend", Person.class), getAccessor(converter, - "Oliver", new Person())); + "Oliver", new Person()), context); creator.createQuery(); } @@ -94,7 +100,7 @@ public class MongoQueryCreatorUnitTests { public void createsNotNullQueryCorrectly() { PartTree tree = new PartTree("findByFirstNameNotNull", Person.class); - Query query = new MongoQueryCreator(tree, getAccessor(converter)).createQuery(); + Query query = new MongoQueryCreator(tree, getAccessor(converter), context).createQuery(); assertThat(query.getQueryObject(), is(new Query(Criteria.where("firstName").ne(null)).getQueryObject())); } @@ -103,7 +109,7 @@ public class MongoQueryCreatorUnitTests { public void createsIsNullQueryCorrectly() { PartTree tree = new PartTree("findByFirstNameIsNull", Person.class); - Query query = new MongoQueryCreator(tree, getAccessor(converter)).createQuery(); + Query query = new MongoQueryCreator(tree, getAccessor(converter), context).createQuery(); assertThat(query.getQueryObject(), is(new Query(Criteria.where("firstName").is(null)).getQueryObject())); } @@ -132,24 +138,36 @@ public class MongoQueryCreatorUnitTests { @Test public void createsLessThanEqualQueryCorrectly() throws Exception { - + PartTree tree = new PartTree("findByAgeLessThanEqual", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18)); - + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context); + Query reference = query(where("age").lte(18)); - assertThat(creator.createQuery().getQueryObject(), is (reference.getQueryObject())); + assertThat(creator.createQuery().getQueryObject(), is(reference.getQueryObject())); } - + @Test - public void createsGreaterThanEqualQueryCorrectly() throws Exception { - + public void createsGreaterThanEqualQueryCorrectly() throws Exception { + PartTree tree = new PartTree("findByAgeGreaterThanEqual", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18)); - + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context); + Query reference = query(where("age").gte(18)); - assertThat(creator.createQuery().getQueryObject(), is (reference.getQueryObject())); + assertThat(creator.createQuery().getQueryObject(), is(reference.getQueryObject())); + } + + /** + * DATADOC-291 + */ + @Test + public void honoursMappingInformationForPropertyPaths() { + + PartTree partTree = new PartTree("findByUsername", User.class); + + MongoQueryCreator creator = new MongoQueryCreator(partTree, getAccessor(converter, "Oliver"), context); + Query reference = query(where("foo").is("Oliver")); + assertThat(creator.createQuery().getQueryObject(), is(reference.getQueryObject())); } - private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception { @@ -164,13 +182,19 @@ public class MongoQueryCreatorUnitTests { MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] { point, distance, "Dave" }); - Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor)).createQuery(); + Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor), context) + .createQuery(); assertThat(query.getQueryObject(), is(query.getQueryObject())); - } interface PersonRepository extends Repository { List findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname); } + + class User { + + @Field("foo") + String username; + } }