From cbb32bd29d09666764f6f381a320c995d9294975 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 18 Jun 2014 09:34:51 +0200 Subject: [PATCH] DATAMONGO-950 - Add support for limiting the query result in the query derivation mechanism. When deriving the query from its method name we check for the limit set on the PartTree to pass this on to the created query. PagedExecution not takes the overall limit into account, skips a query execution entirely (if the Pageable is out of scope completely) or alters the query limits accordingly. Note, that there has been significant rework of this compared to the pull request to avoid new API in Query and extensive changes in MongoTemplate's QueryCursorPreparer. Original pull request: #191. --- .../repository/query/AbstractMongoQuery.java | 19 ++++++- .../repository/query/PartTreeMongoQuery.java | 4 ++ ...tractPersonRepositoryIntegrationTests.java | 49 +++++++++++++++++++ .../mongodb/repository/PersonRepository.java | 11 +++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java index 824bd8e60..5f104634d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.repository.query; +import java.util.Collections; import java.util.List; import org.springframework.core.convert.ConversionService; @@ -254,10 +255,26 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { Object execute(Query query) { MongoEntityMetadata metadata = method.getEntityInformation(); + + int overallLimit = query.getLimit(); long count = operations.count(query, metadata.getCollectionName()); + count = overallLimit != 0 ? Math.min(count, query.getLimit()) : count; - List result = operations.find(query.with(pageable), metadata.getJavaType(), metadata.getCollectionName()); + boolean pageableOutOfScope = pageable.getOffset() > query.getLimit(); + if (pageableOutOfScope) { + return new PageImpl(Collections.emptyList(), pageable, count); + } + + // Apply raw pagination + query = 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, metadata.getJavaType(), metadata.getCollectionName()); return new PageImpl(result, pageable, count); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java index b8a34f118..29eeb2418 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java @@ -73,6 +73,10 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery); Query query = creator.createQuery(); + if (tree.isLimiting()) { + query.limit(tree.getMaxResults()); + } + String fieldSpec = this.getQueryMethod().getFieldSpecification(); if (!StringUtils.hasText(fieldSpec)) { 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 757822de8..28c6aa7c9 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 @@ -917,5 +917,54 @@ public abstract class AbstractPersonRepositoryIntegrationTests { assertThat(result.getContent(), hasSize(2)); assertThat(result.getTotalPages(), is(2)); assertThat(result.getTotalElements(), is(3L)); + + } + + /** + * @see DATAMONGO-950 + */ + @Test + public void shouldLimitCollectionQueryToMaxResultsWhenPresent() { + + 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"))); + List result = repository.findTop3ByLastnameStartingWith("Dylan"); + assertThat(result.size(), is(3)); + } + + /** + * @see DATAMONGO-950 + */ + @Test + public void shouldNotLimitPagedQueryWhenPageRequestWithinBounds() { + + 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(0, 2)); + assertThat(result.getContent().size(), is(2)); + } + + /** + * @see DATAMONGO-950 + */ + @Test + public void shouldLimitPagedQueryWhenPageRequestExceedsUpperBoundary() { + + 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(1, 2)); + assertThat(result.getContent().size(), is(1)); + } + + /** + * @see DATAMONGO-950 + */ + @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)); + assertThat(result.getContent().size(), is(0)); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java index 3cde5acf8..d8ef7643f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java @@ -302,4 +302,15 @@ public interface PersonRepository extends MongoRepository, Query */ @Query("{lastname:?0, address.street:{$in:?1}}") Page findByCustomQueryLastnameAndAddressStreetInList(String lastname, List streetNames, Pageable page); + + /** + * @see DATAMONGO-950 + */ + List findTop3ByLastnameStartingWith(String lastname); + + /** + * @see DATAMONGO-950 + */ + Page findTop3ByLastnameStartingWith(String lastname, Pageable pageRequest); + }