Browse Source

DATAMONGO-1345 - Added support for projections on repository methods.

Related tickets: DATACMNS-89.
pull/337/merge
Oliver Gierke 10 years ago
parent
commit
727271e68c
  1. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
  2. 12
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
  3. 16
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java
  4. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
  5. 45
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
  6. 35
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java
  7. 16
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java
  8. 66
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java
  9. 92
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
  10. 17
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java
  11. 9
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java

@ -90,6 +90,15 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { @@ -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)

12
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java

@ -1,5 +1,5 @@ @@ -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; @@ -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 { @@ -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!");

16
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java

@ -20,10 +20,13 @@ import org.springframework.data.mongodb.core.MongoOperations; @@ -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 { @@ -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;
}

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java

@ -30,6 +30,7 @@ import org.springframework.data.mongodb.repository.query.MongoEntityInformation; @@ -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;
@ -135,11 +136,13 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { @@ -135,11 +136,13 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
/*
* (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)) {

45
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java

@ -53,7 +53,9 @@ import org.springframework.data.mongodb.core.query.BasicQuery; @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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);

35
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java

@ -30,6 +30,8 @@ import org.springframework.data.geo.Point; @@ -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 @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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();

16
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java

@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query; @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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();

66
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java

@ -36,6 +36,8 @@ import org.springframework.data.mongodb.repository.Address; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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> {

92
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java

@ -27,7 +27,6 @@ import org.junit.Rule; @@ -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; @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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;
}
}
}

17
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java

@ -43,7 +43,10 @@ import org.springframework.data.mongodb.core.query.BasicQuery; @@ -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 { @@ -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 { @@ -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 { @@ -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);

9
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java

@ -138,4 +138,13 @@ class StubParameterAccessor implements MongoParameterAccessor { @@ -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;
}
}

Loading…
Cancel
Save