From 353b836a77f5b94af25d50f9c3ed24f95711742f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 21 Jul 2016 12:39:27 +0200 Subject: [PATCH] DATAMONGO-1464 - Optimize query execution for pagination queries. We execute paged queries now in an optimized way. The data is obtained for each paged execution but the count query is deferred. We determine the total from the pageable and the results in which we don't hit the page size bounds (i.e. results are less than a full page without offset or results are greater 0 and less than a full page with offset). In all other cases we issue an additional count query. Original pull request: #379. --- .../repository/query/MongoQueryExecution.java | 63 +++--- .../support/QueryDslMongoRepository.java | 28 ++- .../support/SimpleMongoRepository.java | 20 +- ...tractPersonRepositoryIntegrationTests.java | 12 +- .../query/AbstractMongoQueryUnitTests.java | 5 +- .../query/MongoQueryExecutionUnitTests.java | 182 ++++++++++++++++++ .../support/SimpleMongoRepositoryTests.java | 23 ++- 7 files changed, 284 insertions(+), 49 deletions(-) create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java index c4274977b..31bdb45a4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java @@ -18,13 +18,12 @@ package org.springframework.data.mongodb.repository.query; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import java.util.Collections; import java.util.List; import java.util.function.Function; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.EntityInstantiators; -import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Range; import org.springframework.data.domain.Slice; @@ -39,6 +38,8 @@ 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.repository.support.PageableExecutionUtils.TotalSupplier; import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.StreamUtils; import org.springframework.data.util.TypeInformation; @@ -108,6 +109,7 @@ interface MongoQueryExecution { * {@link MongoQueryExecution} for pagination queries. * * @author Oliver Gierke + * @author Mark Paluch */ @RequiredArgsConstructor static final class PagedExecution implements MongoQueryExecution { @@ -120,29 +122,27 @@ interface MongoQueryExecution { * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) */ @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Object execute(Query query, Class type, String collection) { - - int overallLimit = query.getLimit(); - long count = operations.count(query, type, collection); - count = overallLimit != 0 ? Math.min(count, query.getLimit()) : count; + public Object execute(final Query query, final Class type, final String collection) { - boolean pageableOutOfScope = pageable.getOffset() > count; - - if (pageableOutOfScope) { - return new PageImpl(Collections.emptyList(), pageable, count); - } + final int overallLimit = query.getLimit(); // Apply raw pagination - query = query.with(pageable); + query.with(pageable); // Adjust limit if page would exceed the overall limit if (overallLimit != 0 && pageable.getOffset() + pageable.getPageSize() > overallLimit) { query.limit(overallLimit - pageable.getOffset()); } - List result = operations.find(query, type, collection); - return new PageImpl(result, pageable, count); + return PageableExecutionUtils.getPage(operations.find(query, type, collection), pageable, new TotalSupplier() { + + @Override + public long get() { + + long count = operations.count(query, type, collection); + return overallLimit != 0 ? Math.min(count, overallLimit) : count; + } + }); } } @@ -233,6 +233,12 @@ interface MongoQueryExecution { } } + /** + * {@link MongoQueryExecution} to execute geo-near queries with paging. + * + * @author Oliver Gierke + * @author Mark Paluch + */ static final class PagingGeoNearExecution extends GeoNearExecution { private final MongoOperations operations; @@ -257,14 +263,27 @@ interface MongoQueryExecution { * @return */ @Override - public Object execute(Query query, Class type, String collection) { + public Object execute(Query query, Class type, final String collection) { + + GeoResults geoResults = doExecuteQuery(query, type, collection); - ConvertingParameterAccessor parameterAccessor = new ConvertingParameterAccessor(operations.getConverter(), - accessor); - Query countQuery = mongoQuery.applyQueryMetaAttributesWhenPresent(mongoQuery.createCountQuery(parameterAccessor)); - long count = operations.count(countQuery, collection); + Page> page = PageableExecutionUtils.getPage(geoResults.getContent(), accessor.getPageable(), + new TotalSupplier() { + + @Override + public long get() { + + ConvertingParameterAccessor parameterAccessor = new ConvertingParameterAccessor(operations.getConverter(), + accessor); + Query countQuery = mongoQuery + .applyQueryMetaAttributesWhenPresent(mongoQuery.createCountQuery(parameterAccessor)); + + return operations.count(countQuery, collection); + } + }); - return new GeoPage(doExecuteQuery(query, type, collection), accessor.getPageable(), count); + // transform to GeoPage after applying optimization + return new GeoPage(geoResults, accessor.getPageable(), page.getTotalElements()); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QueryDslMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QueryDslMongoRepository.java index 9f759617c..cb6fbc693 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QueryDslMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QueryDslMongoRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2011-2016 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. @@ -19,7 +19,6 @@ import java.io.Serializable; import java.util.List; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @@ -32,6 +31,8 @@ import org.springframework.data.querydsl.QueryDslPredicateExecutor; import org.springframework.data.querydsl.SimpleEntityPathResolver; import org.springframework.data.repository.core.EntityInformation; import org.springframework.data.repository.core.EntityMetadata; +import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.data.repository.support.PageableExecutionUtils.TotalSupplier; import org.springframework.util.Assert; import com.querydsl.core.types.EntityPath; @@ -46,6 +47,7 @@ import com.querydsl.mongodb.AbstractMongodbQuery; * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ public class QueryDslMongoRepository extends SimpleMongoRepository implements QueryDslPredicateExecutor { @@ -136,13 +138,17 @@ public class QueryDslMongoRepository extends SimpleM * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate, org.springframework.data.domain.Pageable) */ @Override - public Page findAll(Predicate predicate, Pageable pageable) { + public Page findAll(final Predicate predicate, Pageable pageable) { - AbstractMongodbQuery> countQuery = createQueryFor(predicate); AbstractMongodbQuery> query = createQueryFor(predicate); - return new PageImpl(applyPagination(query, pageable).fetchResults().getResults(), pageable, - countQuery.fetchCount()); + return PageableExecutionUtils.getPage(applyPagination(query, pageable).fetchResults().getResults(), pageable, new TotalSupplier() { + + @Override + public long get() { + return createQueryFor(predicate).fetchCount(); + } + }); } /* @@ -152,11 +158,15 @@ public class QueryDslMongoRepository extends SimpleM @Override public Page findAll(Pageable pageable) { - AbstractMongodbQuery> countQuery = createQuery(); AbstractMongodbQuery> query = createQuery(); - return new PageImpl(applyPagination(query, pageable).fetchResults().getResults(), pageable, - countQuery.fetchCount()); + return PageableExecutionUtils.getPage(applyPagination(query, pageable).fetchResults().getResults(), pageable, new TotalSupplier() { + + @Override + public long get() { + return createQuery().fetchCount(); + } + }); } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java index 21af693ba..83665535c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java @@ -36,6 +36,8 @@ import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; +import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.data.repository.support.PageableExecutionUtils.TotalSupplier; import org.springframework.util.Assert; /** @@ -266,20 +268,20 @@ public class SimpleMongoRepository implements MongoR * @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example, org.springframework.data.domain.Pageable) */ @Override - public Page findAll(Example example, Pageable pageable) { + public Page findAll(final Example example, Pageable pageable) { Assert.notNull(example, "Sample must not be null!"); - Query q = new Query(new Criteria().alike(example)).with(pageable); + final Query q = new Query(new Criteria().alike(example)).with(pageable); - long count = mongoOperations.count(q, example.getProbeType(), entityInformation.getCollectionName()); + List list = mongoOperations.find(q, example.getProbeType(), entityInformation.getCollectionName()); + return PageableExecutionUtils.getPage(list, pageable, new TotalSupplier() { - if (count == 0) { - return new PageImpl(Collections. emptyList()); - } - - return new PageImpl(mongoOperations.find(q, example.getProbeType(), entityInformation.getCollectionName()), - pageable, count); + @Override + public long get() { + return mongoOperations.count(q, example.getProbeType(), entityInformation.getCollectionName()); + } + }); } /* diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java index bdd9a1eca..84bad2d14 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java @@ -321,6 +321,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests { assertThat(page.isFirst(), is(true)); assertThat(page.isLast(), is(false)); assertThat(page.getNumberOfElements(), is(2)); + assertThat(page.getTotalElements(), is(4L)); assertThat(page, hasItems(carter, stefan)); } @@ -947,7 +948,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests { } /** - * @see DATAMONGO-950 + * @see DATAMONGO-950, DATAMONGO-1464 */ @Test public void shouldNotLimitPagedQueryWhenPageRequestWithinBounds() { @@ -956,6 +957,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests { new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan"))); Page result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(0, 2)); assertThat(result.getContent().size(), is(2)); + assertThat(result.getTotalElements(), is(3L)); } /** @@ -971,19 +973,20 @@ public abstract class AbstractPersonRepositoryIntegrationTests { } /** - * @see DATAMONGO-950 + * @see DATAMONGO-950, DATAMONGO-1464 */ @Test public void shouldReturnEmptyWhenPageRequestedPageIsTotallyOutOfScopeForLimit() { repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan"))); - Page result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(2, 2)); + Page result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(100, 2)); assertThat(result.getContent().size(), is(0)); + assertThat(result.getTotalElements(), is(3L)); } /** - * @see DATAMONGO-996, DATAMONGO-950 + * @see DATAMONGO-996, DATAMONGO-950, DATAMONGO-1464 */ @Test public void gettingNonFirstPageWorksWithoutLimitBeingSet() { @@ -993,6 +996,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests { assertThat(slice.getContent(), hasSize(1)); assertThat(slice.hasPrevious(), is(true)); assertThat(slice.hasNext(), is(false)); + assertThat(slice.getTotalElements(), is(2L)); } /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java index 34e1f1793..981428dfb 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 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. @@ -67,6 +67,7 @@ import com.mongodb.WriteResult; * @author Christoph Strobl * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) public class AbstractMongoQueryUnitTests { @@ -188,7 +189,7 @@ public class AbstractMongoQueryUnitTests { public void metadataShouldBeAddedToCountQueryCorrectly() { MongoQueryFake query = createQueryForMethod("findByFirstname", String.class, Pageable.class); - query.execute(new Object[] { "fake", new PageRequest(0, 10) }); + query.execute(new Object[] { "fake", new PageRequest(1, 10) }); ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java new file mode 100644 index 000000000..133e910dd --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java @@ -0,0 +1,182 @@ +/* + * Copyright 2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.query; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.geo.Distance; +import org.springframework.data.geo.GeoPage; +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.MongoOperations; +import org.springframework.data.mongodb.core.convert.DbRefResolver; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.query.NearQuery; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.repository.Person; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagedExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagingGeoNearExecution; +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; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.util.ReflectionUtils; + +/** + * Unit tests for {@link MongoQueryExecution}. + * + * @author Mark Paluch + * @soundtrack U Can't Touch This - MC Hammer + */ +@RunWith(MockitoJUnitRunner.class) +@SuppressWarnings("unchecked") +public class MongoQueryExecutionUnitTests { + + @Mock MongoOperations mongoOperationsMock; + @Mock DbRefResolver dbRefResolver; + + Point POINT = new Point(10, 20); + Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class); + MongoMappingContext context = new MongoMappingContext(); + ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); + Method method = ReflectionUtils.findMethod(PersonRepository.class, "findByLocationNear", Point.class, Distance.class, + Pageable.class); + MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); + + @Before + public void setUp() throws Exception { + + MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, context); + when(mongoOperationsMock.getConverter()).thenReturn(converter); + } + + /** + * @see DATAMONGO-1464 + */ + @Test + public void pagedExecutionShouldNotGenerateCountQueryIfQueryReportedNoResults() { + + when(mongoOperationsMock.find(any(Query.class), eq(Person.class), eq("person"))) + .thenReturn(Collections. emptyList()); + + PagedExecution execution = new PagedExecution(mongoOperationsMock, new PageRequest(0, 10)); + execution.execute(new Query(), Person.class, "person"); + + verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person")); + verify(mongoOperationsMock, never()).count(any(Query.class), eq(Person.class), eq("person")); + } + + /** + * @see DATAMONGO-1464 + */ + @Test + 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())); + + PagedExecution execution = new PagedExecution(mongoOperationsMock, new PageRequest(0, 10)); + execution.execute(new Query(), Person.class, "person"); + + verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person")); + verify(mongoOperationsMock, never()).count(any(Query.class), eq(Person.class), eq("person")); + } + + /** + * @see DATAMONGO-1464 + */ + @Test + public void pagedExecutionRetrievesObjectsForPageableOutOfRange() throws Exception { + + when(mongoOperationsMock.find(any(Query.class), eq(Person.class), eq("person"))) + .thenReturn(Collections. emptyList()); + + PagedExecution execution = new PagedExecution(mongoOperationsMock, new PageRequest(2, 10)); + execution.execute(new Query(), Person.class, "person"); + + verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person")); + verify(mongoOperationsMock).count(any(Query.class), eq(Person.class), eq("person")); + } + + /** + * @see DATAMONGO-1464 + */ + @Test + public void pagingGeoExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSize() throws Exception { + + MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, + new Object[] { POINT, DISTANCE, new PageRequest(0, 10) }); + + PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock); + GeoResult result = new GeoResult(new Person(), DISTANCE); + + when(mongoOperationsMock.geoNear(any(NearQuery.class), eq(Person.class), eq("person"))) + .thenReturn(new GeoResults(Arrays.asList(result, result, result, result))); + + PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, accessor, + ClassTypeInformation.fromReturnTypeOf(method), query); + execution.execute(new Query(), Person.class, "person"); + + verify(mongoOperationsMock).geoNear(any(NearQuery.class), eq(Person.class), eq("person")); + verify(mongoOperationsMock, never()).count(any(Query.class), eq("person")); + } + + /** + * @see DATAMONGO-1464 + */ + @Test + public void pagingGeoExecutionRetrievesObjectsForPageableOutOfRange() throws Exception { + + MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, + new Object[] { POINT, DISTANCE, new PageRequest(2, 10) }); + + PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock); + + when(mongoOperationsMock.geoNear(any(NearQuery.class), eq(Person.class), eq("person"))) + .thenReturn(new GeoResults(Collections.> emptyList())); + + PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, accessor, + ClassTypeInformation.fromReturnTypeOf(method), query); + execution.execute(new Query(), Person.class, "person"); + + verify(mongoOperationsMock).geoNear(any(NearQuery.class), eq(Person.class), eq("person")); + verify(mongoOperationsMock).count(any(Query.class), eq("person")); + } + + interface PersonRepository extends Repository { + + GeoPage findByLocationNear(Point point, Distance distance, Pageable pageable); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java index d7221de3f..81f3661f7 100755 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java @@ -33,17 +33,17 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; -import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.ExampleMatcher.*; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.repository.Address; import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.Person.Sex; import org.springframework.data.mongodb.repository.User; +import org.springframework.data.mongodb.repository.Person.Sex; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -174,7 +174,7 @@ public class SimpleMongoRepositoryTests { } /** - * @see DATAMONGO-1245 + * @see DATAMONGO-1245, DATAMONGO-1464 */ @Test public void findByExampleShouldLookUpEntriesCorrectly() { @@ -187,6 +187,23 @@ public class SimpleMongoRepositoryTests { assertThat(result.getContent(), hasItems(dave, oliver)); assertThat(result.getContent(), hasSize(2)); + assertThat(result.getTotalPages(), is(1)); + } + + /** + * @see DATAMONGO-1464 + */ + @Test + public void findByExampleMultiplePagesShouldLookUpEntriesCorrectly() { + + Person sample = new Person(); + sample.setLastname("Matthews"); + trimDomainType(sample, "id", "createdAt", "email"); + + Page result = repository.findAll(Example.of(sample), new PageRequest(0, 1)); + + assertThat(result.getContent(), hasSize(1)); + assertThat(result.getTotalPages(), is(2)); } /**