Browse Source

DATAMONGO-1738 - Move repository query execution to fluent operations API.

We now use the fluent FindOperations API in AbstractMongoQuery and MongoQueryExecution instead of the MongoOperations. This allows us to eagerly resolve some general execution coordinates (which collection to query etc.) and thus simplify the eventual execution.

Got rid of a couple of very simple QueryExecution implementations that can be replace by a simple lambda. Removed the need to read into a partially filled domain object and then map to the projection DTO as we can now tell the operations to read into the DTO directly.

Adapted unit tests.

Original pull request: #484.
pull/484/merge
Oliver Gierke 9 years ago committed by Mark Paluch
parent
commit
dd8fc1a591
  1. 66
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
  2. 224
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java
  3. 68
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
  4. 63
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java
  5. 6
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
  6. 5
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java

66
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java

@ -15,25 +15,20 @@ @@ -15,25 +15,20 @@
*/
package org.springframework.data.mongodb.repository.query;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithProjection;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithQuery;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.CollectionExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.CountExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.DeleteExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.ExistsExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.GeoNearExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagedExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagingGeoNearExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.ResultProcessingConverter;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.ResultProcessingExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.SingleEntityExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.SlicedExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.StreamExecution;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType;
import org.springframework.util.Assert;
/**
@ -48,7 +43,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -48,7 +43,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
private final MongoQueryMethod method;
private final MongoOperations operations;
private final EntityInstantiators instantiators;
private final FindOperationWithProjection<?> findOperationWithProjection;
/**
* Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
@ -63,7 +58,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -63,7 +58,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
this.method = method;
this.operations = operations;
this.instantiators = new EntityInstantiators();
ReturnedType returnedType = method.getResultProcessor().getReturnedType();
this.findOperationWithProjection = operations//
.query(returnedType.getDomainType())//
.inCollection(method.getEntityInformation().getCollectionName());
}
/*
@ -86,52 +86,36 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -86,52 +86,36 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
applyQueryMetaAttributesWhenPresent(query);
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor);
String collection = method.getEntityInformation().getCollectionName();
MongoQueryExecution execution = getExecution(query, accessor,
new ResultProcessingConverter(processor, operations, instantiators));
return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
}
/**
* Returns the execution instance to use.
*
* @param query must not be {@literal null}.
* @param parameters must not be {@literal null}.
* @param accessor must not be {@literal null}.
* @return
*/
private MongoQueryExecution getExecution(Query query, MongoParameterAccessor accessor,
Converter<Object, Object> resultProcessing) {
ReturnedType returnedType = processor.getReturnedType();
FindOperationWithQuery<?> find = findOperationWithProjection.as(returnedType.getTypeToRead());
if (method.isStreamQuery()) {
return new StreamExecution(operations, resultProcessing);
}
MongoQueryExecution execution = getExecution(accessor, find);
return new ResultProcessingExecution(getExecutionToWrap(query, accessor), resultProcessing);
return processor.processResult(execution.execute(query));
}
private MongoQueryExecution getExecutionToWrap(Query query, MongoParameterAccessor accessor) {
private MongoQueryExecution getExecution(MongoParameterAccessor accessor, FindOperationWithQuery<?> operation) {
if (isDeleteQuery()) {
return new DeleteExecution(operations, method);
} else if (method.isGeoNearQuery() && method.isPageQuery()) {
return new PagingGeoNearExecution(operations, accessor, method.getReturnType(), this);
return new PagingGeoNearExecution(operations, method, accessor, this);
} else if (method.isGeoNearQuery()) {
return new GeoNearExecution(operations, accessor, method.getReturnType());
return new GeoNearExecution(operations, method, accessor);
} else if (method.isSliceQuery()) {
return new SlicedExecution(operations, accessor.getPageable());
return new SlicedExecution(operation, accessor.getPageable());
} else if (method.isStreamQuery()) {
return new StreamExecution(operation);
} else if (method.isCollectionQuery()) {
return new CollectionExecution(operations, accessor.getPageable());
return q -> operation.matching(q.with(accessor.getPageable())).all();
} else if (method.isPageQuery()) {
return new PagedExecution(operations, accessor.getPageable());
return new PagedExecution(operation, accessor.getPageable());
} else if (isCountQuery()) {
return new CountExecution(operations);
return q -> operation.matching(q).count();
} else if (isExistsQuery()) {
return new ExistsExecution(operations);
return q -> operation.matching(q).exists();
} else {
return new SingleEntityExecution(operations);
return q -> operation.matching(q).oneValue();
}
}

224
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java

@ -19,11 +19,7 @@ import lombok.NonNull; @@ -19,11 +19,7 @@ import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range;
@ -34,16 +30,16 @@ import org.springframework.data.geo.GeoPage; @@ -34,16 +30,16 @@ import org.springframework.data.geo.GeoPage;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.ExecutableFindOperation;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithQuery;
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFindOperation;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.StreamUtils;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.ClassUtils;
import com.mongodb.client.result.DeleteResult;
@ -58,7 +54,7 @@ import com.mongodb.client.result.DeleteResult; @@ -58,7 +54,7 @@ import com.mongodb.client.result.DeleteResult;
*/
interface MongoQueryExecution {
Object execute(Query query, Class<?> type, String collection);
Object execute(Query query);
/**
* {@link MongoQueryExecution} for collection returning queries.
@ -68,16 +64,16 @@ interface MongoQueryExecution { @@ -68,16 +64,16 @@ interface MongoQueryExecution {
@RequiredArgsConstructor
final class CollectionExecution implements MongoQueryExecution {
private final @NonNull MongoOperations operations;
private final Pageable pageable;
private final @NonNull ExecutableFindOperation.FindOperationWithQuery<?> find;
private final @NonNull Pageable pageable;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
* @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query)
*/
@Override
public Object execute(Query query, Class<?> type, String collection) {
return operations.find(query.with(pageable), type, collection);
public Object execute(Query query) {
return find.matching(query.with(pageable)).all();
}
}
@ -89,24 +85,24 @@ interface MongoQueryExecution { @@ -89,24 +85,24 @@ interface MongoQueryExecution {
* @since 1.5
*/
@RequiredArgsConstructor
final class SlicedExecution implements MongoQueryExecution {
static final class SlicedExecution implements MongoQueryExecution {
private final @NonNull MongoOperations operations;
private final @NonNull FindOperationWithQuery<?> find;
private final @NonNull Pageable pageable;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
* @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query)
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object execute(Query query, Class<?> type, String collection) {
public Object execute(Query query) {
int pageSize = pageable.getPageSize();
// Apply Pageable but tweak limit to peek into next page
Query modifiedQuery = query.with(pageable).limit(pageSize + 1);
List result = operations.find(modifiedQuery, type, collection);
List result = find.matching(modifiedQuery).all();
boolean hasNext = result.size() > pageSize;
@ -121,19 +117,21 @@ interface MongoQueryExecution { @@ -121,19 +117,21 @@ interface MongoQueryExecution {
* @author Mark Paluch
*/
@RequiredArgsConstructor
final class PagedExecution implements MongoQueryExecution {
static final class PagedExecution implements MongoQueryExecution {
private final @NonNull MongoOperations operations;
private final @NonNull FindOperationWithQuery<?> operation;
private final @NonNull Pageable pageable;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
* @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query)
*/
@Override
public Object execute(final Query query, final Class<?> type, final String collection) {
public Object execute(Query query) {
int overallLimit = query.getLimit();
final int overallLimit = query.getLimit();
TerminatingFindOperation<?> matching = operation.matching(query);
// Apply raw pagination
query.with(pageable);
@ -143,35 +141,14 @@ interface MongoQueryExecution { @@ -143,35 +141,14 @@ interface MongoQueryExecution {
query.limit((int) (overallLimit - pageable.getOffset()));
}
return PageableExecutionUtils.getPage(operations.find(query, type, collection), pageable, () -> {
return PageableExecutionUtils.getPage(matching.all(), pageable, () -> {
long count = operations.count(query, type, collection);
long count = matching.count();
return overallLimit != 0 ? Math.min(count, overallLimit) : count;
});
}
}
/**
* {@link MongoQueryExecution} to return a single entity.
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
final class SingleEntityExecution implements MongoQueryExecution {
private final MongoOperations operations;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
*/
@Override
public Object execute(Query query, Class<?> type, String collection) {
return operations.findOne(query, type, collection);
}
}
/**
* {@link MongoQueryExecution} to perform a count projection.
*
@ -182,36 +159,15 @@ interface MongoQueryExecution { @@ -182,36 +159,15 @@ interface MongoQueryExecution {
@RequiredArgsConstructor
static final class CountExecution implements MongoQueryExecution {
private final MongoOperations operations;
private final @NonNull FindOperationWithQuery<?> operation;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
* @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query)
*/
@Override
public Object execute(Query query, Class<?> type, String collection) {
return operations.count(query, type, collection);
}
}
/**
* {@link MongoQueryExecution} to perform an exists projection.
*
* @author Mark Paluch
* @since 1.10
*/
@RequiredArgsConstructor
static final class ExistsExecution implements MongoQueryExecution {
private final MongoOperations operations;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
*/
@Override
public Object execute(Query query, Class<?> type, String collection) {
return operations.exists(query, type, collection);
public Object execute(Query query) {
return operation.count();
}
}
@ -221,25 +177,28 @@ interface MongoQueryExecution { @@ -221,25 +177,28 @@ interface MongoQueryExecution {
* @author Oliver Gierke
*/
@RequiredArgsConstructor
class GeoNearExecution implements MongoQueryExecution {
static class GeoNearExecution implements MongoQueryExecution {
private final MongoOperations operations;
private final MongoQueryMethod method;
private final MongoParameterAccessor accessor;
private final TypeInformation<?> returnType;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
*/
@Override
public Object execute(Query query, Class<?> type, String collection) {
public Object execute(Query query) {
GeoResults<?> results = doExecuteQuery(query, type, collection);
return isListOfGeoResult() ? results.getContent() : results;
GeoResults<?> results = doExecuteQuery(query);
return isListOfGeoResult(method.getReturnType()) ? results.getContent() : results;
}
@SuppressWarnings("unchecked")
protected GeoResults<Object> doExecuteQuery(Query query, Class<?> type, String collection) {
protected GeoResults<Object> doExecuteQuery(Query query) {
Class<?> type = method.getReturnedObjectType();
String collection = method.getEntityInformation().getCollectionName();
Point nearLocation = accessor.getGeoNearLocation();
NearQuery nearQuery = NearQuery.near(nearLocation);
@ -261,7 +220,7 @@ interface MongoQueryExecution { @@ -261,7 +220,7 @@ interface MongoQueryExecution {
return (GeoResults<Object>) operations.geoNear(nearQuery, type, collection);
}
private boolean isListOfGeoResult() {
private static boolean isListOfGeoResult(TypeInformation<?> returnType) {
if (!returnType.getType().equals(List.class)) {
return false;
@ -278,20 +237,22 @@ interface MongoQueryExecution { @@ -278,20 +237,22 @@ interface MongoQueryExecution {
* @author Oliver Gierke
* @author Mark Paluch
*/
final class PagingGeoNearExecution extends GeoNearExecution {
static final class PagingGeoNearExecution extends GeoNearExecution {
private final MongoOperations operations;
private final MongoQueryMethod method;
private final MongoParameterAccessor accessor;
private final AbstractMongoQuery mongoQuery;
public PagingGeoNearExecution(MongoOperations operations, MongoParameterAccessor accessor,
TypeInformation<?> returnType, AbstractMongoQuery query) {
public PagingGeoNearExecution(MongoOperations operations, MongoQueryMethod method, MongoParameterAccessor accessor,
AbstractMongoQuery query) {
super(operations, accessor, returnType);
super(operations, method, accessor);
this.accessor = accessor;
this.operations = operations;
this.mongoQuery = query;
this.method = method;
}
/*
@ -299,19 +260,20 @@ interface MongoQueryExecution { @@ -299,19 +260,20 @@ interface MongoQueryExecution {
* @see org.springframework.data.mongodb.repository.query.MongoQueryExecution.GeoNearExecution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
*/
@Override
public Object execute(Query query, Class<?> type, final String collection) {
public Object execute(Query query) {
String collectionName = method.getEntityInformation().getCollectionName();
GeoResults<Object> geoResults = doExecuteQuery(query, type, collection);
GeoResults<Object> geoResults = doExecuteQuery(query);
Page<GeoResult<Object>> page = PageableExecutionUtils.getPage(geoResults.getContent(), accessor.getPageable(),
() -> {
ConvertingParameterAccessor parameterAccessor = new ConvertingParameterAccessor(operations.getConverter(),
accessor);
Query countQuery = mongoQuery
.applyQueryMetaAttributesWhenPresent(mongoQuery.createCountQuery(parameterAccessor));
.createCountQuery(new ConvertingParameterAccessor(operations.getConverter(), accessor));
countQuery = mongoQuery.applyQueryMetaAttributesWhenPresent(countQuery);
return operations.count(countQuery, collection);
return operations.count(countQuery, collectionName);
});
@ -326,23 +288,26 @@ interface MongoQueryExecution { @@ -326,23 +288,26 @@ interface MongoQueryExecution {
* @since 1.5
*/
@RequiredArgsConstructor
final class DeleteExecution implements MongoQueryExecution {
static final class DeleteExecution implements MongoQueryExecution {
private final MongoOperations operations;
private final MongoQueryMethod method;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
* @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query)
*/
@Override
public Object execute(Query query, Class<?> type, String collection) {
public Object execute(Query query) {
String collectionName = method.getEntityInformation().getCollectionName();
Class<?> type = method.getEntityInformation().getJavaType();
if (method.isCollectionQuery()) {
return operations.findAllAndRemove(query, type, collection);
return operations.findAllAndRemove(query, type, collectionName);
}
DeleteResult writeResult = operations.remove(query, type, collection);
DeleteResult writeResult = operations.remove(query, type, collectionName);
return writeResult != null ? writeResult.getDeletedCount() : 0L;
}
}
@ -352,82 +317,21 @@ interface MongoQueryExecution { @@ -352,82 +317,21 @@ interface MongoQueryExecution {
* @since 1.7
*/
@RequiredArgsConstructor
final class StreamExecution implements MongoQueryExecution {
static final class StreamExecution implements MongoQueryExecution {
private final @NonNull MongoOperations operations;
private final @NonNull Converter<Object, Object> resultProcessing;
private final @NonNull FindOperationWithQuery<?> operation;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
* @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query)
*/
@Override
@SuppressWarnings("unchecked")
public Object execute(Query query, Class<?> type, String collection) {
return StreamUtils.createStreamFromIterator((CloseableIterator<Object>) operations.stream(query, type))
.map(new Function<Object, Object>() {
@Override
public Object apply(Object t) {
return resultProcessing.convert(t);
}
});
}
}
/**
* An {@link MongoQueryExecution} that wraps the results of the given delegate with the given result processing.
*
* @author Oliver Gierke
* @since 1.9
*/
@RequiredArgsConstructor
final class ResultProcessingExecution implements MongoQueryExecution {
private final @NonNull MongoQueryExecution delegate;
private final @NonNull Converter<Object, Object> converter;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
*/
@Override
public Object execute(Query query, Class<?> type, String collection) {
return converter.convert(delegate.execute(query, type, collection));
}
}
/**
* A {@link Converter} to post-process all source objects using the given {@link ResultProcessor}.
*
* @author Oliver Gierke
* @since 1.9
*/
@RequiredArgsConstructor
final class ResultProcessingConverter implements Converter<Object, Object> {
private final @NonNull ResultProcessor processor;
private final @NonNull MongoOperations operations;
private final @NonNull EntityInstantiators instantiators;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public Object convert(Object source) {
ReturnedType returnedType = processor.getReturnedType();
if (ClassUtils.isPrimitiveOrWrapper(returnedType.getReturnedType())) {
return source;
}
public Object execute(Query query) {
Converter<Object, Object> converter = new DtoInstantiatingConverter(returnedType.getReturnedType(),
operations.getConverter().getMappingContext(), instantiators);
TerminatingFindOperation<?> matching = operation.matching(query);
return processor.processResult(source, converter);
return StreamUtils.createStreamFromIterator((CloseableIterator<Object>) matching.stream());
}
}
}

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

@ -17,6 +17,8 @@ package org.springframework.data.mongodb.repository.query; @@ -17,6 +17,8 @@ package org.springframework.data.mongodb.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
@ -24,12 +26,10 @@ import java.util.List; @@ -24,12 +26,10 @@ import java.util.List;
import java.util.Optional;
import org.bson.Document;
import org.hamcrest.core.Is;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@ -39,6 +39,9 @@ import org.springframework.data.domain.Pageable; @@ -39,6 +39,9 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperation;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithProjection;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithQuery;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
@ -69,6 +72,9 @@ import com.mongodb.client.result.DeleteResult; @@ -69,6 +72,9 @@ import com.mongodb.client.result.DeleteResult;
public class AbstractMongoQueryUnitTests {
@Mock MongoOperations mongoOperationsMock;
@Mock FindOperation<?> findOperationMock;
@Mock FindOperationWithProjection<?> withProjectionMock;
@Mock FindOperationWithQuery<?> withQueryMock;
@Mock BasicMongoPersistentEntity<?> persitentEntityMock;
@Mock MongoMappingContext mappingContextMock;
@Mock WriteResult writeResultMock;
@ -87,26 +93,27 @@ public class AbstractMongoQueryUnitTests { @@ -87,26 +93,27 @@ public class AbstractMongoQueryUnitTests {
converter.afterPropertiesSet();
doReturn(converter).when(mongoOperationsMock).getConverter();
doReturn(findOperationMock).when(mongoOperationsMock).query(any());
doReturn(withProjectionMock).when(findOperationMock).inCollection(any());
doReturn(withQueryMock).when(withProjectionMock).as(any());
doReturn(withQueryMock).when(withQueryMock).matching(any());
}
@SuppressWarnings("unchecked")
@Test // DATAMONGO-566
public void testDeleteExecutionCallsRemoveCorreclty() {
createQueryForMethod("deletePersonByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" });
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());
verify(mongoOperationsMock, times(1)).remove(any(), eq(Person.class), eq("persons"));
verify(mongoOperationsMock, times(0)).find(any(), any(), any());
}
@SuppressWarnings("unchecked")
@Test // DATAMONGO-566, DATAMONGO-1040
public void testDeleteExecutionLoadsListOfRemovedDocumentsWhenReturnTypeIsCollectionLike() {
createQueryForMethod("deleteByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" });
verify(mongoOperationsMock, times(1)).findAllAndRemove(Mockito.any(Query.class), eq(Person.class), eq("persons"));
verify(mongoOperationsMock, times(1)).findAllAndRemove(any(), eq(Person.class), eq("persons"));
}
@Test // DATAMONGO-566
@ -115,21 +122,20 @@ public class AbstractMongoQueryUnitTests { @@ -115,21 +122,20 @@ public class AbstractMongoQueryUnitTests {
MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class);
query.setDeleteQuery(true);
assertThat(query.execute(new Object[] { "fake" }), Is.<Object> is(0L));
assertThat(query.execute(new Object[] { "fake" }), is(0L));
}
@Test // DATAMONGO-566, DATAMONGO-978
public void testDeleteExecutionReturnsNrDocumentsDeletedFromWriteResult() {
when(deleteResultMock.getDeletedCount()).thenReturn(100L);
when(mongoOperationsMock.remove(Matchers.any(Query.class), eq(Person.class), eq("persons")))
.thenReturn(deleteResultMock);
when(mongoOperationsMock.remove(any(), eq(Person.class), eq("persons"))).thenReturn(deleteResultMock);
MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class);
query.setDeleteQuery(true);
assertThat(query.execute(new Object[] { "fake" }), is((Object) 100L));
verify(mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons"));
verify(mongoOperationsMock, times(1)).remove(any(), eq(Person.class), eq("persons"));
}
@Test // DATAMONGO-957
@ -140,7 +146,9 @@ public class AbstractMongoQueryUnitTests { @@ -140,7 +146,9 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(withProjectionMock).as(Person.class);
verify(withQueryMock).matching(captor.capture());
verify(findOperationMock).inCollection("persons");
assertThat(captor.getValue().getMeta().getComment(), nullValue());
}
@ -153,7 +161,10 @@ public class AbstractMongoQueryUnitTests { @@ -153,7 +161,10 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(withProjectionMock).as(Person.class);
verify(withQueryMock).matching(captor.capture());
verify(findOperationMock).inCollection("persons");
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
}
@ -165,7 +176,10 @@ public class AbstractMongoQueryUnitTests { @@ -165,7 +176,10 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(mongoOperationsMock, times(1)).count(captor.capture(), eq(Person.class), eq("persons"));
verify(withProjectionMock).as(Person.class);
verify(withQueryMock).matching(captor.capture());
verify(findOperationMock).inCollection("persons");
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
}
@ -177,7 +191,10 @@ public class AbstractMongoQueryUnitTests { @@ -177,7 +191,10 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(withProjectionMock).as(Person.class);
verify(withQueryMock).matching(captor.capture());
verify(findOperationMock).inCollection("persons");
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
}
@ -193,7 +210,9 @@ public class AbstractMongoQueryUnitTests { @@ -193,7 +210,9 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(withProjectionMock, times(2)).as(Person.class);
verify(withQueryMock, times(2)).matching(captor.capture());
verify(findOperationMock).inCollection("persons");
assertThat(captor.getAllValues().get(0).getSkip(), is(0L));
assertThat(captor.getAllValues().get(1).getSkip(), is(10L));
@ -211,7 +230,9 @@ public class AbstractMongoQueryUnitTests { @@ -211,7 +230,9 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(withProjectionMock, times(2)).as(Person.class);
verify(withQueryMock, times(2)).matching(captor.capture());
verify(findOperationMock).inCollection("persons");
assertThat(captor.getAllValues().get(0).getLimit(), is(11));
assertThat(captor.getAllValues().get(1).getLimit(), is(11));
@ -229,7 +250,9 @@ public class AbstractMongoQueryUnitTests { @@ -229,7 +250,9 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(withProjectionMock, times(2)).as(Person.class);
verify(withQueryMock, times(2)).matching(captor.capture());
verify(findOperationMock).inCollection("persons");
Document expectedSortObject = new Document().append("bar", -1);
assertThat(captor.getAllValues().get(0).getSortObject(), is(expectedSortObject));
@ -240,8 +263,8 @@ public class AbstractMongoQueryUnitTests { @@ -240,8 +263,8 @@ public class AbstractMongoQueryUnitTests {
public void doesNotTryToPostProcessQueryResultIntoWrapperType() {
Person reference = new Person();
when(mongoOperationsMock.findOne(Mockito.any(Query.class), eq(Person.class), eq("persons"))).//
thenReturn(reference);
doReturn(reference).when(withQueryMock).oneValue();
AbstractMongoQuery query = createQueryForMethod("findByLastname", String.class);
@ -269,7 +292,6 @@ public class AbstractMongoQueryUnitTests { @@ -269,7 +292,6 @@ public class AbstractMongoQueryUnitTests {
private static class MongoQueryFake extends AbstractMongoQuery {
private boolean isCountQuery;
private boolean isExistsQuery;
private boolean isDeleteQuery;
public MongoQueryFake(MongoQueryMethod method, MongoOperations operations) {
@ -310,7 +332,7 @@ public class AbstractMongoQueryUnitTests { @@ -310,7 +332,7 @@ public class AbstractMongoQueryUnitTests {
List<Person> findByFirstname(String firstname);
@Meta(comment = "comment", flags = {org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT})
@Meta(comment = "comment", flags = { org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT })
Page<Person> findByFirstname(String firstnanme, Pageable pageable);
@Meta(comment = "comment")

63
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java

@ -15,8 +15,7 @@ @@ -15,8 +15,7 @@
*/
package org.springframework.data.mongodb.repository.query;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
@ -36,6 +35,9 @@ import org.springframework.data.geo.GeoResult; @@ -36,6 +35,9 @@ import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperation;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithQuery;
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFindOperation;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
@ -50,7 +52,6 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -50,7 +52,6 @@ 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;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.util.ReflectionUtils;
/**
@ -64,6 +65,8 @@ import org.springframework.util.ReflectionUtils; @@ -64,6 +65,8 @@ import org.springframework.util.ReflectionUtils;
public class MongoQueryExecutionUnitTests {
@Mock MongoOperations mongoOperationsMock;
@Mock FindOperation<?> findOperationMock;
@Mock FindOperationWithQuery<?> operationMock;
@Mock DbRefResolver dbRefResolver;
Point POINT = new Point(10, 20);
@ -79,46 +82,54 @@ public class MongoQueryExecutionUnitTests { @@ -79,46 +82,54 @@ public class MongoQueryExecutionUnitTests {
public void setUp() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, context);
when(mongoOperationsMock.getConverter()).thenReturn(converter);
when(mongoOperationsMock.query(any(Class.class))).thenReturn(findOperationMock);
}
@Test // DATAMONGO-1464
public void pagedExecutionShouldNotGenerateCountQueryIfQueryReportedNoResults() {
when(mongoOperationsMock.find(any(Query.class), eq(Person.class), eq("person")))
.thenReturn(Collections.<Person> emptyList());
TerminatingFindOperation<Object> terminating = mock(TerminatingFindOperation.class);
doReturn(terminating).when(operationMock).matching(any(Query.class));
doReturn(Collections.emptyList()).when(terminating).all();
PagedExecution execution = new PagedExecution(mongoOperationsMock, PageRequest.of(0, 10));
execution.execute(new Query(), Person.class, "person");
PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(0, 10));
execution.execute(new Query());
verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person"));
verify(mongoOperationsMock, never()).count(any(Query.class), eq(Person.class), eq("person"));
verify(terminating).all();
verify(terminating, never()).count();
}
@Test // DATAMONGO-1464
public void pagedExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSize() {
when(mongoOperationsMock.find(any(Query.class), eq(Person.class), eq("person")))
.thenReturn(Arrays.asList(new Person(), new Person(), new Person(), new Person()));
TerminatingFindOperation<Object> terminating = mock(TerminatingFindOperation.class);
PagedExecution execution = new PagedExecution(mongoOperationsMock, PageRequest.of(0, 10));
execution.execute(new Query(), Person.class, "person");
doReturn(terminating).when(operationMock).matching(any(Query.class));
doReturn(Arrays.asList(new Person(), new Person(), new Person(), new Person())).when(terminating).all();
verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person"));
verify(mongoOperationsMock, never()).count(any(Query.class), eq(Person.class), eq("person"));
PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(0, 10));
execution.execute(new Query());
verify(terminating).all();
verify(terminating, never()).count();
}
@Test // DATAMONGO-1464
public void pagedExecutionRetrievesObjectsForPageableOutOfRange() throws Exception {
when(mongoOperationsMock.find(any(Query.class), eq(Person.class), eq("person")))
.thenReturn(Collections.<Person> emptyList());
TerminatingFindOperation<Object> terminating = mock(TerminatingFindOperation.class);
doReturn(terminating).when(operationMock).matching(any(Query.class));
doReturn(Collections.emptyList()).when(terminating).all();
PagedExecution execution = new PagedExecution(mongoOperationsMock, PageRequest.of(2, 10));
execution.execute(new Query(), Person.class, "person");
PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(2, 10));
execution.execute(new Query());
verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person"));
verify(mongoOperationsMock).count(any(Query.class), eq(Person.class), eq("person"));
verify(terminating).all();
verify(terminating).count();
}
@Test // DATAMONGO-1464
@ -133,9 +144,8 @@ public class MongoQueryExecutionUnitTests { @@ -133,9 +144,8 @@ public class MongoQueryExecutionUnitTests {
when(mongoOperationsMock.geoNear(any(NearQuery.class), eq(Person.class), eq("person")))
.thenReturn(new GeoResults<Person>(Arrays.asList(result, result, result, result)));
PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, accessor,
ClassTypeInformation.fromReturnTypeOf(method), query);
execution.execute(new Query(), Person.class, "person");
PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, queryMethod, accessor, query);
execution.execute(new Query());
verify(mongoOperationsMock).geoNear(any(NearQuery.class), eq(Person.class), eq("person"));
verify(mongoOperationsMock, never()).count(any(Query.class), eq("person"));
@ -152,9 +162,8 @@ public class MongoQueryExecutionUnitTests { @@ -152,9 +162,8 @@ public class MongoQueryExecutionUnitTests {
when(mongoOperationsMock.geoNear(any(NearQuery.class), eq(Person.class), eq("person")))
.thenReturn(new GeoResults<Person>(Collections.<GeoResult<Person>> emptyList()));
PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, accessor,
ClassTypeInformation.fromReturnTypeOf(method), query);
execution.execute(new Query(), Person.class, "person");
PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, queryMethod, accessor, query);
execution.execute(new Query());
verify(mongoOperationsMock).geoNear(any(NearQuery.class), eq(Person.class), eq("person"));
verify(mongoOperationsMock).count(any(Query.class), eq("person"));

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

@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query; @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.core.query.IsTextQuery.*;
@ -33,6 +34,7 @@ import org.mockito.Mock; @@ -33,6 +34,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperation;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
@ -63,6 +65,7 @@ import com.mongodb.util.JSONParseException; @@ -63,6 +65,7 @@ import com.mongodb.util.JSONParseException;
public class PartTreeMongoQueryUnitTests {
@Mock MongoOperations mongoOperationsMock;
@Mock FindOperation<?> findOperationMock;
MongoMappingContext mappingContext;
@ -75,7 +78,8 @@ public class PartTreeMongoQueryUnitTests { @@ -75,7 +78,8 @@ public class PartTreeMongoQueryUnitTests {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDbFactory.class));
MongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
when(mongoOperationsMock.getConverter()).thenReturn(converter);
doReturn(converter).when(mongoOperationsMock).getConverter();
doReturn(findOperationMock).when(mongoOperationsMock).query(any());
}
@Test // DATAMOGO-952

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

@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query; @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
@ -36,6 +37,7 @@ import org.junit.runner.RunWith; @@ -36,6 +37,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.mongodb.core.DocumentTestUtils;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperation;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
@ -67,6 +69,7 @@ public class StringBasedMongoQueryUnitTests { @@ -67,6 +69,7 @@ public class StringBasedMongoQueryUnitTests {
SpelExpressionParser PARSER = new SpelExpressionParser();
@Mock MongoOperations operations;
@Mock FindOperation<Object> findOperation;
@Mock DbRefResolver factory;
MongoConverter converter;
@ -75,6 +78,8 @@ public class StringBasedMongoQueryUnitTests { @@ -75,6 +78,8 @@ public class StringBasedMongoQueryUnitTests {
public void setUp() {
this.converter = new MappingMongoConverter(factory, new MongoMappingContext());
doReturn(findOperation).when(operations).query(any());
}
@Test

Loading…
Cancel
Save