DATAMONGO-1345 - Added support for projections on repository methods.
Related tickets: DATACMNS-89.
This commit is contained in:
@@ -90,6 +90,15 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
|
||||
return delegate.getSort();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.ParameterAccessor#getDynamicProjection()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getDynamicProjection() {
|
||||
return delegate.getDynamicProjection();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.ParameterAccessor#getBindableValue(int)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.repository.Meta;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
@@ -56,12 +57,15 @@ public class MongoQueryMethod extends QueryMethod {
|
||||
/**
|
||||
* Creates a new {@link MongoQueryMethod} from the given {@link Method}.
|
||||
*
|
||||
* @param method
|
||||
* @param method must not be {@literal null}.
|
||||
* @param metadata must not be {@literal null}.
|
||||
* @param projectionFactory must not be {@literal null}.
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
*/
|
||||
public MongoQueryMethod(Method method, RepositoryMetadata metadata,
|
||||
public MongoQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory projectionFactory,
|
||||
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
|
||||
|
||||
super(method, metadata);
|
||||
super(method, metadata, projectionFactory);
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
|
||||
|
||||
@@ -20,10 +20,13 @@ import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Field;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.TextCriteria;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -87,6 +90,19 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
String fieldSpec = this.getQueryMethod().getFieldSpecification();
|
||||
|
||||
if (!StringUtils.hasText(fieldSpec)) {
|
||||
|
||||
ResultProcessor processor = getQueryMethod().getResultProcessor();
|
||||
ReturnedType returnedType = processor.withDynamicProjection(accessor).getReturnedType();
|
||||
|
||||
if (returnedType.isProjecting()) {
|
||||
|
||||
Field fields = query.fields();
|
||||
|
||||
for (String field : returnedType.getInputProperties()) {
|
||||
fields.include(field);
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
|
||||
import org.springframework.data.mongodb.repository.query.MongoQueryMethod;
|
||||
import org.springframework.data.mongodb.repository.query.PartTreeMongoQuery;
|
||||
import org.springframework.data.mongodb.repository.query.StringBasedMongoQuery;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
@@ -133,13 +134,15 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.repository.core.NamedQueries)
|
||||
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
|
||||
*/
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
|
||||
@Override
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
|
||||
NamedQueries namedQueries) {
|
||||
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, mappingContext);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, mappingContext);
|
||||
String namedQueryName = queryMethod.getNamedQueryName();
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
|
||||
@@ -53,7 +53,9 @@ import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.repository.Meta;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
@@ -69,27 +71,23 @@ import com.mongodb.WriteResult;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AbstractMongoQueryUnitTests {
|
||||
|
||||
@Mock RepositoryMetadata metadataMock;
|
||||
@Mock MongoOperations mongoOperationsMock;
|
||||
@Mock @SuppressWarnings("rawtypes") BasicMongoPersistentEntity persitentEntityMock;
|
||||
@Mock BasicMongoPersistentEntity<?> persitentEntityMock;
|
||||
@Mock MongoMappingContext mappingContextMock;
|
||||
@Mock WriteResult writeResultMock;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setUp() {
|
||||
|
||||
when(metadataMock.getDomainType()).thenReturn((Class) Person.class);
|
||||
when(metadataMock.getReturnedDomainClass(Matchers.any(Method.class))).thenReturn((Class) Person.class);
|
||||
when(persitentEntityMock.getCollection()).thenReturn("persons");
|
||||
when(mappingContextMock.getPersistentEntity(Matchers.any(Class.class))).thenReturn(persitentEntityMock);
|
||||
when(persitentEntityMock.getType()).thenReturn(Person.class);
|
||||
doReturn("persons").when(persitentEntityMock).getCollection();
|
||||
doReturn(persitentEntityMock).when(mappingContextMock).getPersistentEntity(Matchers.any(Class.class));
|
||||
doReturn(Person.class).when(persitentEntityMock).getType();
|
||||
|
||||
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDbFactory.class));
|
||||
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContextMock);
|
||||
converter.afterPropertiesSet();
|
||||
|
||||
when(mongoOperationsMock.getConverter()).thenReturn(converter);
|
||||
doReturn(converter).when(mongoOperationsMock).getConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,8 +99,8 @@ public class AbstractMongoQueryUnitTests {
|
||||
|
||||
createQueryForMethod("deletePersonByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" });
|
||||
|
||||
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons"));
|
||||
verify(this.mongoOperationsMock, times(0)).find(Matchers.any(Query.class), Matchers.any(Class.class),
|
||||
verify(mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons"));
|
||||
verify(mongoOperationsMock, times(0)).find(Matchers.any(Query.class), Matchers.any(Class.class),
|
||||
Matchers.anyString());
|
||||
}
|
||||
|
||||
@@ -114,13 +112,12 @@ public class AbstractMongoQueryUnitTests {
|
||||
@Test
|
||||
public void testDeleteExecutionLoadsListOfRemovedDocumentsWhenReturnTypeIsCollectionLike() {
|
||||
|
||||
when(this.mongoOperationsMock.find(Matchers.any(Query.class), Matchers.any(Class.class), Matchers.anyString()))
|
||||
when(mongoOperationsMock.find(Matchers.any(Query.class), Matchers.any(Class.class), Matchers.anyString()))
|
||||
.thenReturn(Arrays.asList(new Person(new ObjectId(new Date()), "bar")));
|
||||
|
||||
createQueryForMethod("deleteByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" });
|
||||
|
||||
verify(this.mongoOperationsMock, times(1)).findAllAndRemove(Matchers.any(Query.class), eq(Person.class),
|
||||
eq("persons"));
|
||||
verify(mongoOperationsMock, times(1)).findAllAndRemove(Matchers.any(Query.class), eq(Person.class), eq("persons"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,14 +140,14 @@ public class AbstractMongoQueryUnitTests {
|
||||
public void testDeleteExecutionReturnsNrDocumentsDeletedFromWriteResult() {
|
||||
|
||||
when(writeResultMock.getN()).thenReturn(100);
|
||||
when(this.mongoOperationsMock.remove(Matchers.any(Query.class), eq(Person.class), eq("persons")))
|
||||
when(mongoOperationsMock.remove(Matchers.any(Query.class), eq(Person.class), eq("persons")))
|
||||
.thenReturn(writeResultMock);
|
||||
|
||||
MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class);
|
||||
query.setDeleteQuery(true);
|
||||
|
||||
assertThat(query.execute(new Object[] { "fake" }), is((Object) 100L));
|
||||
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons"));
|
||||
verify(mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +161,7 @@ public class AbstractMongoQueryUnitTests {
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
|
||||
verify(this.mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons"));
|
||||
verify(mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons"));
|
||||
|
||||
assertThat(captor.getValue().getMeta().getComment(), nullValue());
|
||||
}
|
||||
@@ -195,7 +192,7 @@ public class AbstractMongoQueryUnitTests {
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
|
||||
verify(this.mongoOperationsMock, times(1)).count(captor.capture(), eq(Person.class), eq("persons"));
|
||||
verify(mongoOperationsMock, times(1)).count(captor.capture(), eq(Person.class), eq("persons"));
|
||||
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
|
||||
}
|
||||
|
||||
@@ -229,7 +226,7 @@ public class AbstractMongoQueryUnitTests {
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
|
||||
verify(this.mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
|
||||
verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
|
||||
|
||||
assertThat(captor.getAllValues().get(0).getSkip(), is(0));
|
||||
assertThat(captor.getAllValues().get(1).getSkip(), is(10));
|
||||
@@ -250,7 +247,7 @@ public class AbstractMongoQueryUnitTests {
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
|
||||
verify(this.mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
|
||||
verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
|
||||
|
||||
assertThat(captor.getAllValues().get(0).getLimit(), is(11));
|
||||
assertThat(captor.getAllValues().get(1).getLimit(), is(11));
|
||||
@@ -271,7 +268,7 @@ public class AbstractMongoQueryUnitTests {
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
|
||||
verify(this.mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
|
||||
verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
|
||||
|
||||
DBObject expectedSortObject = new BasicDBObjectBuilder().add("bar", -1).get();
|
||||
assertThat(captor.getAllValues().get(0).getSortObject(), is(expectedSortObject));
|
||||
@@ -298,7 +295,9 @@ public class AbstractMongoQueryUnitTests {
|
||||
try {
|
||||
|
||||
Method method = Repo.class.getMethod(methodName, paramTypes);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadataMock, mappingContextMock);
|
||||
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(Repo.class), factory,
|
||||
mappingContextMock);
|
||||
|
||||
return new MongoQueryFake(queryMethod, mongoOperationsMock);
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.TextCriteria;
|
||||
import org.springframework.data.mongodb.repository.Person;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
@@ -42,15 +44,16 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
|
||||
*/
|
||||
public class MongoParametersParameterAccessorUnitTests {
|
||||
|
||||
private static final Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS);
|
||||
private static final RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class);
|
||||
private static final MongoMappingContext context = new MongoMappingContext();
|
||||
Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS);
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class);
|
||||
MongoMappingContext context = new MongoMappingContext();
|
||||
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||
|
||||
@Test
|
||||
public void returnsNullForDistanceIfNoneAvailable() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, context);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
|
||||
new Object[] { new Point(10, 20) });
|
||||
@@ -61,10 +64,10 @@ public class MongoParametersParameterAccessorUnitTests {
|
||||
public void returnsDistanceIfAvailable() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, context);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] {
|
||||
new Point(10, 20), DISTANCE });
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
|
||||
new Object[] { new Point(10, 20), DISTANCE });
|
||||
assertThat(accessor.getDistanceRange().getUpperBound(), is(DISTANCE));
|
||||
}
|
||||
|
||||
@@ -75,10 +78,10 @@ public class MongoParametersParameterAccessorUnitTests {
|
||||
public void shouldReturnAsFullTextStringWhenNoneDefinedForMethod() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, context);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] {
|
||||
new Point(10, 20), DISTANCE });
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
|
||||
new Object[] { new Point(10, 20), DISTANCE });
|
||||
assertThat(accessor.getFullText(), IsNull.nullValue());
|
||||
}
|
||||
|
||||
@@ -89,10 +92,10 @@ public class MongoParametersParameterAccessorUnitTests {
|
||||
public void shouldProperlyConvertTextCriteria() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByFirstname", String.class, TextCriteria.class);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, context);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] { "spring",
|
||||
TextCriteria.forDefaultLanguage().matching("data") });
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
|
||||
new Object[] { "spring", TextCriteria.forDefaultLanguage().matching("data") });
|
||||
assertThat(accessor.getFullText().getCriteriaObject().toString(),
|
||||
equalTo("{ \"$text\" : { \"$search\" : \"data\"}}"));
|
||||
}
|
||||
@@ -104,13 +107,13 @@ public class MongoParametersParameterAccessorUnitTests {
|
||||
public void shouldDetectMinAndMaxDistance() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Range.class);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, context);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context);
|
||||
|
||||
Distance min = new Distance(10, Metrics.KILOMETERS);
|
||||
Distance max = new Distance(20, Metrics.KILOMETERS);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] {
|
||||
new Point(10, 20), Distance.between(min, max) });
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
|
||||
new Object[] { new Point(10, 20), Distance.between(min, max) });
|
||||
|
||||
Range<Distance> range = accessor.getDistanceRange();
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
@@ -54,6 +55,7 @@ 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.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
@@ -137,8 +139,8 @@ public class MongoQueryCreatorUnitTests {
|
||||
Point point = new Point(10, 20);
|
||||
Distance distance = new Distance(2.5, Metrics.KILOMETERS);
|
||||
|
||||
Query query = query(where("location").nearSphere(point).maxDistance(distance.getNormalizedValue()).and("firstname")
|
||||
.is("Dave"));
|
||||
Query query = query(
|
||||
where("location").nearSphere(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave"));
|
||||
assertBindsDistanceToQuery(point, distance, query);
|
||||
}
|
||||
|
||||
@@ -148,8 +150,8 @@ public class MongoQueryCreatorUnitTests {
|
||||
Point point = new Point(10, 20);
|
||||
Distance distance = new Distance(2.5);
|
||||
|
||||
Query query = query(where("location").near(point).maxDistance(distance.getNormalizedValue()).and("firstname")
|
||||
.is("Dave"));
|
||||
Query query = query(
|
||||
where("location").near(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave"));
|
||||
assertBindsDistanceToQuery(point, distance, query);
|
||||
}
|
||||
|
||||
@@ -299,9 +301,9 @@ public class MongoQueryCreatorUnitTests {
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNearAndFirstname", Point.class, Distance.class,
|
||||
String.class);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class),
|
||||
new MongoMappingContext());
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] { point, distance,
|
||||
"Dave" });
|
||||
new SpelAwareProxyProjectionFactory(), new MongoMappingContext());
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
|
||||
new Object[] { point, distance, "Dave" });
|
||||
|
||||
Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor), context)
|
||||
.createQuery();
|
||||
|
||||
@@ -36,6 +36,8 @@ import org.springframework.data.mongodb.repository.Address;
|
||||
import org.springframework.data.mongodb.repository.Contact;
|
||||
import org.springframework.data.mongodb.repository.Meta;
|
||||
import org.springframework.data.mongodb.repository.Person;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
@@ -57,10 +59,7 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void detectsCollectionFromRepoTypeIfReturnTypeNotAssignable() throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod("method");
|
||||
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
|
||||
context);
|
||||
MongoQueryMethod queryMethod = queryMethod(SampleRepository.class, "method");
|
||||
MongoEntityMetadata<?> metadata = queryMethod.getEntityInformation();
|
||||
|
||||
assertThat(metadata.getJavaType(), is(typeCompatibleWith(Address.class)));
|
||||
@@ -70,10 +69,7 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void detectsCollectionFromReturnTypeIfReturnTypeAssignable() throws Exception {
|
||||
|
||||
Method method = SampleRepository2.class.getMethod("method");
|
||||
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
|
||||
context);
|
||||
MongoQueryMethod queryMethod = queryMethod(SampleRepository2.class, "method");
|
||||
MongoEntityMetadata<?> entityInformation = queryMethod.getEntityInformation();
|
||||
|
||||
assertThat(entityInformation.getJavaType(), is(typeCompatibleWith(Person.class)));
|
||||
@@ -83,34 +79,44 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void discoversUserAsDomainTypeForGeoPageQueryMethod() throws Exception {
|
||||
|
||||
MongoQueryMethod queryMethod = queryMethod("findByLocationNear", Point.class, Distance.class, Pageable.class);
|
||||
MongoQueryMethod queryMethod = queryMethod(PersonRepository.class, "findByLocationNear", Point.class,
|
||||
Distance.class, Pageable.class);
|
||||
assertThat(queryMethod.isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod.isPageQuery(), is(true));
|
||||
|
||||
queryMethod = queryMethod("findByFirstname", String.class, Point.class);
|
||||
queryMethod = queryMethod(PersonRepository.class, "findByFirstname", String.class, Point.class);
|
||||
assertThat(queryMethod.isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod.isPageQuery(), is(false));
|
||||
assertThat(queryMethod.getEntityInformation().getJavaType(), is(typeCompatibleWith(User.class)));
|
||||
|
||||
assertThat(queryMethod("findByEmailAddress", String.class, Point.class).isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod("findByFirstname", String.class, Point.class).isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod("findByLastname", String.class, Point.class).isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod(PersonRepository.class, "findByEmailAddress", String.class, Point.class).isGeoNearQuery(),
|
||||
is(true));
|
||||
assertThat(queryMethod(PersonRepository.class, "findByFirstname", String.class, Point.class).isGeoNearQuery(),
|
||||
is(true));
|
||||
assertThat(queryMethod(PersonRepository.class, "findByLastname", String.class, Point.class).isGeoNearQuery(),
|
||||
is(true));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsGeoPageQueryWithoutPageable() throws Exception {
|
||||
queryMethod("findByLocationNear", Point.class, Distance.class);
|
||||
queryMethod(PersonRepository.class, "findByLocationNear", Point.class, Distance.class);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullMappingContext() throws Exception {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByFirstname", String.class, Point.class);
|
||||
new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class), null);
|
||||
|
||||
new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class),
|
||||
new SpelAwareProxyProjectionFactory(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void considersMethodReturningGeoPageAsPagingMethod() throws Exception {
|
||||
MongoQueryMethod method = queryMethod("findByLocationNear", Point.class, Distance.class, Pageable.class);
|
||||
|
||||
MongoQueryMethod method = queryMethod(PersonRepository.class, "findByLocationNear", Point.class, Distance.class,
|
||||
Pageable.class);
|
||||
|
||||
assertThat(method.isPageQuery(), is(true));
|
||||
assertThat(method.isCollectionQuery(), is(false));
|
||||
}
|
||||
@@ -118,8 +124,7 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void createsMongoQueryMethodObjectForMethodReturningAnInterface() throws Exception {
|
||||
|
||||
Method method = SampleRepository2.class.getMethod("methodReturningAnInterface");
|
||||
new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository2.class), context);
|
||||
queryMethod(SampleRepository2.class, "methodReturningAnInterface");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +133,8 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void createsMongoQueryMethodWithEmptyMetaCorrectly() throws Exception {
|
||||
|
||||
MongoQueryMethod method = queryMethod("emptyMetaAnnotation");
|
||||
MongoQueryMethod method = queryMethod(PersonRepository.class, "emptyMetaAnnotation");
|
||||
|
||||
assertThat(method.hasQueryMetaAttributes(), is(true));
|
||||
assertThat(method.getQueryMetaAttributes().hasValues(), is(false));
|
||||
}
|
||||
@@ -139,7 +145,8 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void createsMongoQueryMethodWithMaxExecutionTimeCorrectly() throws Exception {
|
||||
|
||||
MongoQueryMethod method = queryMethod("metaWithMaxExecutionTime");
|
||||
MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithMaxExecutionTime");
|
||||
|
||||
assertThat(method.hasQueryMetaAttributes(), is(true));
|
||||
assertThat(method.getQueryMetaAttributes().getMaxTimeMsec(), is(100L));
|
||||
}
|
||||
@@ -150,7 +157,8 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void createsMongoQueryMethodWithMaxScanCorrectly() throws Exception {
|
||||
|
||||
MongoQueryMethod method = queryMethod("metaWithMaxScan");
|
||||
MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithMaxScan");
|
||||
|
||||
assertThat(method.hasQueryMetaAttributes(), is(true));
|
||||
assertThat(method.getQueryMetaAttributes().getMaxScan(), is(10L));
|
||||
}
|
||||
@@ -161,7 +169,8 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void createsMongoQueryMethodWithCommentCorrectly() throws Exception {
|
||||
|
||||
MongoQueryMethod method = queryMethod("metaWithComment");
|
||||
MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithComment");
|
||||
|
||||
assertThat(method.hasQueryMetaAttributes(), is(true));
|
||||
assertThat(method.getQueryMetaAttributes().getComment(), is("foo bar"));
|
||||
}
|
||||
@@ -172,7 +181,8 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void createsMongoQueryMethodWithSnapshotCorrectly() throws Exception {
|
||||
|
||||
MongoQueryMethod method = queryMethod("metaWithSnapshotUsage");
|
||||
MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithSnapshotUsage");
|
||||
|
||||
assertThat(method.hasQueryMetaAttributes(), is(true));
|
||||
assertThat(method.getQueryMetaAttributes().getSnapshot(), is(true));
|
||||
}
|
||||
@@ -183,14 +193,16 @@ public class MongoQueryMethodUnitTests {
|
||||
@Test
|
||||
public void fallsBackToRepositoryDomainTypeIfMethodDoesNotReturnADomainType() throws Exception {
|
||||
|
||||
MongoQueryMethod method = queryMethod("deleteByUserName", String.class);
|
||||
MongoQueryMethod method = queryMethod(PersonRepository.class, "deleteByUserName", String.class);
|
||||
|
||||
assertThat(method.getEntityInformation().getJavaType(), is(typeCompatibleWith(User.class)));
|
||||
}
|
||||
|
||||
private MongoQueryMethod queryMethod(String name, Class<?>... parameters) throws Exception {
|
||||
Method method = PersonRepository.class.getMethod(name, parameters);
|
||||
return new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class), context);
|
||||
private MongoQueryMethod queryMethod(Class<?> repository, String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
Method method = repository.getMethod(name, parameters);
|
||||
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||
return new MongoQueryMethod(method, new DefaultRepositoryMetadata(repository), factory, context);
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<User, Long> {
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
@@ -42,9 +41,12 @@ import org.springframework.data.mongodb.core.query.TextCriteria;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.Person;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSONParseException;
|
||||
|
||||
/**
|
||||
@@ -57,7 +59,6 @@ import com.mongodb.util.JSONParseException;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PartTreeMongoQueryUnitTests {
|
||||
|
||||
@Mock RepositoryMetadata metadataMock;
|
||||
@Mock MongoOperations mongoOperationsMock;
|
||||
|
||||
MongoMappingContext mappingContext;
|
||||
@@ -65,11 +66,8 @@ public class PartTreeMongoQueryUnitTests {
|
||||
public @Rule ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setUp() {
|
||||
|
||||
when(metadataMock.getDomainType()).thenReturn((Class) Person.class);
|
||||
when(metadataMock.getReturnedDomainClass(Matchers.any(Method.class))).thenReturn((Class) Person.class);
|
||||
mappingContext = new MongoMappingContext();
|
||||
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDbFactory.class));
|
||||
MongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
|
||||
@@ -149,7 +147,52 @@ public class PartTreeMongoQueryUnitTests {
|
||||
deriveQueryFromMethod("findByAge", new Object[] { 1 });
|
||||
}
|
||||
|
||||
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object[] args) {
|
||||
/**
|
||||
* @see DATAMONGO-1345
|
||||
*/
|
||||
@Test
|
||||
public void doesNotDeriveFieldSpecForNormalDomainType() {
|
||||
assertThat(deriveQueryFromMethod("findPersonBy", new Object[0]).getFieldsObject(), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1345
|
||||
*/
|
||||
@Test
|
||||
public void restrictsQueryToFieldsRequiredForProjection() {
|
||||
|
||||
DBObject fieldsObject = deriveQueryFromMethod("findPersonProjectedBy", new Object[0]).getFieldsObject();
|
||||
|
||||
assertThat(fieldsObject.get("firstname"), is((Object) 1));
|
||||
assertThat(fieldsObject.get("lastname"), is((Object) 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1345
|
||||
*/
|
||||
@Test
|
||||
public void restrictsQueryToFieldsRequiredForDto() {
|
||||
|
||||
DBObject fieldsObject = deriveQueryFromMethod("findPersonDtoBy", new Object[0]).getFieldsObject();
|
||||
|
||||
assertThat(fieldsObject.get("firstname"), is((Object) 1));
|
||||
assertThat(fieldsObject.get("lastname"), is((Object) 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1345
|
||||
*/
|
||||
@Test
|
||||
public void usesDynamicProjection() {
|
||||
|
||||
DBObject fields = deriveQueryFromMethod("findDynamicallyProjectedBy", ExtendedProjection.class).getFieldsObject();
|
||||
|
||||
assertThat(fields.get("firstname"), is((Object) 1));
|
||||
assertThat(fields.get("lastname"), is((Object) 1));
|
||||
assertThat(fields.get("age"), is((Object) 1));
|
||||
}
|
||||
|
||||
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object... args) {
|
||||
|
||||
Class<?>[] types = new Class<?>[args.length];
|
||||
|
||||
@@ -168,7 +211,9 @@ public class PartTreeMongoQueryUnitTests {
|
||||
try {
|
||||
|
||||
Method method = Repo.class.getMethod(methodName, paramTypes);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadataMock, mappingContext);
|
||||
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(Repo.class), factory,
|
||||
mappingContext);
|
||||
|
||||
return new PartTreeMongoQuery(queryMethod, mongoOperationsMock);
|
||||
} catch (NoSuchMethodException e) {
|
||||
@@ -196,5 +241,36 @@ public class PartTreeMongoQueryUnitTests {
|
||||
|
||||
@Query(fields = "{ 'firstname }")
|
||||
Person findByAge(Integer age);
|
||||
|
||||
Person findPersonBy();
|
||||
|
||||
PersonProjection findPersonProjectedBy();
|
||||
|
||||
PersonDto findPersonDtoBy();
|
||||
|
||||
<T> T findDynamicallyProjectedBy(Class<T> type);
|
||||
}
|
||||
|
||||
interface PersonProjection {
|
||||
|
||||
String getFirstname();
|
||||
|
||||
String getLastname();
|
||||
}
|
||||
|
||||
interface ExtendedProjection extends PersonProjection {
|
||||
|
||||
int getAge();
|
||||
}
|
||||
|
||||
static class PersonDto {
|
||||
|
||||
public String firstname, lastname;
|
||||
|
||||
public PersonDto(String firstname, String lastname) {
|
||||
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,10 @@ import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.repository.Address;
|
||||
import org.springframework.data.mongodb.repository.Person;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
@@ -65,7 +68,6 @@ public class StringBasedMongoQueryUnitTests {
|
||||
SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
@Mock MongoOperations operations;
|
||||
@Mock RepositoryMetadata metadata;
|
||||
@Mock DbRefResolver factory;
|
||||
|
||||
MongoConverter converter;
|
||||
@@ -81,10 +83,7 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Test
|
||||
public void bindsSimplePropertyCorrectly() throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod("findByLastname", String.class);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, converter.getMappingContext());
|
||||
StringBasedMongoQuery mongoQuery = new StringBasedMongoQuery(queryMethod, operations, PARSER,
|
||||
DefaultEvaluationContextProvider.INSTANCE);
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
@@ -366,11 +365,13 @@ public class StringBasedMongoQueryUnitTests {
|
||||
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod(name, parameters);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, converter.getMappingContext());
|
||||
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
|
||||
factory, converter.getMappingContext());
|
||||
return new StringBasedMongoQuery(queryMethod, operations, PARSER, DefaultEvaluationContextProvider.INSTANCE);
|
||||
}
|
||||
|
||||
private interface SampleRepository {
|
||||
private interface SampleRepository extends Repository<Person, Long> {
|
||||
|
||||
@Query("{ 'lastname' : ?0 }")
|
||||
Person findByLastname(String lastname);
|
||||
|
||||
@@ -130,7 +130,7 @@ class StubParameterAccessor implements MongoParameterAccessor {
|
||||
public TextCriteria getFullText() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getValues()
|
||||
*/
|
||||
@@ -138,4 +138,13 @@ class StubParameterAccessor implements MongoParameterAccessor {
|
||||
public Object[] getValues() {
|
||||
return this.values;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.ParameterAccessor#getDynamicProjection()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getDynamicProjection() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user