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.
This commit is contained in:
Oliver Gierke
2011-10-06 22:57:27 +02:00
parent 9f8e406aff
commit e405bf574c
5 changed files with 105 additions and 33 deletions

View File

@@ -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<MongoPersist
* @return
*/
DBRef getDBRef();
/**
* Simple {@link Converter} implementation to transform a {@link MongoPersistentProperty} into its field name.
*
* @author Oliver Gierke
*/
public enum PropertyToFieldNameConverter implements Converter<MongoPersistentProperty, String> {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
public String convert(MongoPersistentProperty source) {
return source.getFieldName();
}
}
}

View File

@@ -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<Query, Query> {
private final MongoParameterAccessor accessor;
private final boolean isGeoNearQuery;
public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor) {
this(tree, accessor, false);
}
private final MappingContext<?, MongoPersistentProperty> 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<?, MongoPersistentProperty> 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<?, MongoPersistentProperty> 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<Query, Query> {
*/
@Override
protected Query create(Part part, Iterator<Object> iterator) {
if (isGeoNearQuery && part.getType().equals(Type.NEAR)) {
return null;
}
Criteria criteria = from(part.getType(), where(part.getProperty().toDotPath()),
PersistentPropertyPath<MongoPersistentProperty> 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<Query, Query> {
*/
@Override
protected Query and(Part part, Query base, Iterator<Object> iterator) {
if (base == null) {
return create(part, iterator);
}
Criteria criteria = from(part.getType(), where(part.getProperty().toDotPath()),
PersistentPropertyPath<MongoPersistentProperty> 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<Query, Query> {
*/
@Override
protected Query complete(Query query, Sort sort) {
if (query == null) {
return null;
}

View File

@@ -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<?, MongoPersistentProperty> 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();
}
}

View File

@@ -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<String, AccountPojo> accounts = new HashMap<String, AccountPojo>();
AccountPojo checking = new AccountPojo("checking", 1000.0f);

View File

@@ -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<?, MongoPersistentProperty> context;
@Before
public void setUp() throws SecurityException, NoSuchMethodException {
context = new MongoMappingContext();
doAnswer(new Answer<Void>() {
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<Person, Long> {
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
}
class User {
@Field("foo")
String username;
}
}