Polishing.
Move SliceUtils to another package for reuse. Slightly alter its API, add tests and update javadoc. Original Pull Request: #4890
This commit is contained in:
@@ -36,6 +36,7 @@ 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.mongodb.core.query.UpdateDefinition;
|
||||
import org.springframework.data.mongodb.repository.util.SliceUtils;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -87,7 +88,7 @@ interface MongoQueryExecution {
|
||||
int pageSize = pageable.getPageSize();
|
||||
|
||||
// Apply Pageable but tweak limit to peek into next page
|
||||
Query modifiedQuery = query.with(pageable).limit(pageSize + 1);
|
||||
Query modifiedQuery = SliceUtils.limitResult(query, pageable).with(pageable.getSort());
|
||||
List result = find.matching(modifiedQuery).all();
|
||||
|
||||
boolean hasNext = result.size() > pageSize;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import org.springframework.data.mongodb.repository.util.SliceUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -113,14 +114,16 @@ class ReactiveSpringDataMongodbQuery<K> extends SpringDataMongodbQuerySupport<Re
|
||||
/**
|
||||
* Fetch all matching query results as Slice.
|
||||
*
|
||||
* @param pageable defines range and sort of requested slice
|
||||
* @return {@link Mono} emitting the requested Slice.
|
||||
* @since 4.5
|
||||
*/
|
||||
Mono<Slice<K>> fetchSlice(Pageable pageable) {
|
||||
|
||||
Mono<List<K>> content = createQuery().map(it -> SliceUtils.getQuery(it, pageable))
|
||||
Mono<List<K>> content = createQuery().map(it -> SliceUtils.limitResult(it, pageable).with(pageable.getSort()))
|
||||
.flatMapMany(it -> find.matching(it).all()).collectList();
|
||||
|
||||
return content.map(it -> SliceUtils.getSlice(it, pageable));
|
||||
return content.map(it -> SliceUtils.sliceResult(it, pageable));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,7 @@ 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.mongodb.repository.util.SliceUtils;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.data.util.Streamable;
|
||||
@@ -460,9 +461,9 @@ public class SimpleMongoRepository<T, ID> implements MongoRepository<T, ID> {
|
||||
|
||||
Assert.notNull(pageable, "Pageable must not be null");
|
||||
|
||||
List<T> resultList = createQuery(q -> SliceUtils.getQuery(q, pageable)).all();
|
||||
List<T> resultList = createQuery(q -> SliceUtils.limitResult(q, pageable).with(pageable.getSort())).all();
|
||||
|
||||
return SliceUtils.getSlice(resultList, pageable);
|
||||
return SliceUtils.sliceResult(resultList, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
|
||||
import org.springframework.data.mongodb.repository.util.SliceUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -596,8 +597,8 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
|
||||
@Override
|
||||
public Mono<Slice<T>> slice(Pageable pageable) {
|
||||
|
||||
return createQuery(q -> SliceUtils.getQuery(q, pageable)).all().collectList()
|
||||
.map(it -> SliceUtils.getSlice(it, pageable));
|
||||
return createQuery(q -> SliceUtils.limitResult(q, pageable).with(pageable.getSort())).all().collectList()
|
||||
.map(it -> SliceUtils.sliceResult(it, pageable));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.mapping.FieldName;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.repository.util.SliceUtils;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -187,14 +188,15 @@ public class SpringDataMongodbQuery<T> extends SpringDataMongodbQuerySupport<Spr
|
||||
/**
|
||||
* Fetch a {@link Slice}.
|
||||
*
|
||||
* @param pageable
|
||||
* @return
|
||||
* @param pageable defines range and sort of requested slice
|
||||
* @return new instance of {@link Slice} containing matching results within range.
|
||||
* @since 4.5
|
||||
*/
|
||||
public Slice<T> fetchSlice(Pageable pageable) {
|
||||
|
||||
List<T> content = find.matching(SliceUtils.getQuery(createQuery(), pageable)).all();
|
||||
List<T> content = find.matching(SliceUtils.limitResult(createQuery(), pageable).with(pageable.getSort())).all();
|
||||
|
||||
return SliceUtils.getSlice(content, pageable);
|
||||
return SliceUtils.sliceResult(content, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
package org.springframework.data.mongodb.repository.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -26,19 +26,21 @@ import org.springframework.data.mongodb.core.query.Query;
|
||||
* Utility methods for {@link Slice} handling.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 4.5
|
||||
*/
|
||||
class SliceUtils {
|
||||
public class SliceUtils {
|
||||
|
||||
/**
|
||||
* Creates a {@link Slice} given {@link Pageable} and {@link List} of results.
|
||||
*
|
||||
* @param <T>
|
||||
* @param resultList
|
||||
* @param pageable
|
||||
* @return
|
||||
* @param <T> the element type.
|
||||
* @param resultList the source list holding the result of the request. If the result list contains more elements
|
||||
* (indicating a next slice is available) it is trimmed to the {@link Pageable#getPageSize() page size}.
|
||||
* @param pageable the source pageable.
|
||||
* @return new instance of {@link Slice}.
|
||||
*/
|
||||
public static <T> Slice<T> getSlice(List<T> resultList, Pageable pageable) {
|
||||
public static <T> Slice<T> sliceResult(List<T> resultList, Pageable pageable) {
|
||||
|
||||
boolean hasNext = resultList.size() > pageable.getPageSize();
|
||||
|
||||
@@ -50,16 +52,23 @@ class SliceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize query for slice retrieval.
|
||||
* Customize query for {@link #sliceResult sliced result} retrieval. If {@link Pageable#isPaged() paged} the
|
||||
* {@link Query#limit(int) limit} is set to {@code pagesize + 1} in order to determine if more data is available.
|
||||
*
|
||||
* @param query
|
||||
* @param pageable
|
||||
* @return
|
||||
* @param query the source query
|
||||
* @param pageable paging to apply.
|
||||
* @return new instance of {@link Query} if either {@link Pageable#isPaged() paged}, the source query otherwise.
|
||||
*/
|
||||
public static Query getQuery(Query query, Pageable pageable) {
|
||||
public static Query limitResult(Query query, Pageable pageable) {
|
||||
|
||||
query.with(pageable);
|
||||
if (pageable.isUnpaged()) {
|
||||
return query;
|
||||
}
|
||||
|
||||
return pageable.isPaged() ? query.limit(pageable.getPageSize() + 1) : query;
|
||||
Query target = Query.of(query);
|
||||
target.skip(pageable.getOffset());
|
||||
target.limit(pageable.getPageSize() + 1);
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2025 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
|
||||
*
|
||||
* https://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.util;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
/**
|
||||
* Unit test for {@link SliceUtils}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SliceUtilsUnitTests {
|
||||
|
||||
@ParameterizedTest // GH-4889
|
||||
@MethodSource("paged")
|
||||
void pagedPageableModifiesQuery(Pageable page) {
|
||||
|
||||
Query source = new BasicQuery(Document.parse("{ 'spring' : 'data' }"));
|
||||
|
||||
Query target = SliceUtils.limitResult(source, page);
|
||||
|
||||
assertThat(target.getQueryObject()).isEqualTo(source.getQueryObject());
|
||||
assertThat(target).isNotSameAs(source);
|
||||
assertThat(target.isLimited()).isTrue();
|
||||
assertThat(target.getSkip()).isEqualTo(page.getOffset());
|
||||
assertThat(target.getLimit()).isEqualTo(page.toLimit().max() + 1);
|
||||
assertThat(target.getSortObject()).isEqualTo(source.getSortObject());
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-4889
|
||||
@MethodSource("unpaged")
|
||||
void unpagedPageableDoesNotModifyQuery(Pageable page) {
|
||||
|
||||
Query source = spy(new BasicQuery(Document.parse("{ 'spring' : 'data' }")));
|
||||
|
||||
Query target = SliceUtils.limitResult(source, page);
|
||||
|
||||
verifyNoInteractions(source);
|
||||
|
||||
assertThat(target).isSameAs(source);
|
||||
assertThat(target.isLimited()).isFalse();
|
||||
}
|
||||
|
||||
public static Stream<Arguments> paged() {
|
||||
return Stream.of(Arguments.of(Pageable.ofSize(1)), Arguments.of(PageRequest.of(0, 10)),
|
||||
Arguments.of(PageRequest.of(0, 10, Direction.ASC, "name")));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> unpaged() {
|
||||
return Stream.of(Arguments.of(Pageable.unpaged()), Arguments.of(Pageable.unpaged(Sort.by("name"))));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user