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:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user