Skip to content

Commit 49db28d

Browse files
Polishing.
Move SliceUtils to another package for reuse. Slightly alter its API, add tests and update javadoc. Original Pull Request: #4890
1 parent 63783e6 commit 49db28d

File tree

7 files changed

+122
-25
lines changed

7 files changed

+122
-25
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.data.mongodb.core.query.NearQuery;
3737
import org.springframework.data.mongodb.core.query.Query;
3838
import org.springframework.data.mongodb.core.query.UpdateDefinition;
39+
import org.springframework.data.mongodb.repository.util.SliceUtils;
3940
import org.springframework.data.support.PageableExecutionUtils;
4041
import org.springframework.data.util.TypeInformation;
4142
import org.springframework.lang.Nullable;
@@ -87,7 +88,7 @@ public Object execute(Query query) {
8788
int pageSize = pageable.getPageSize();
8889

8990
// Apply Pageable but tweak limit to peek into next page
90-
Query modifiedQuery = query.with(pageable).limit(pageSize + 1);
91+
Query modifiedQuery = SliceUtils.limitResult(query, pageable).with(pageable.getSort());
9192
List result = find.matching(modifiedQuery).all();
9293

9394
boolean hasNext = result.size() > pageSize;

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.mongodb.repository.support;
1717

18+
import org.springframework.data.mongodb.repository.util.SliceUtils;
1819
import reactor.core.publisher.Flux;
1920
import reactor.core.publisher.Mono;
2021

@@ -113,14 +114,16 @@ Mono<Page<K>> fetchPage(Pageable pageable) {
113114
/**
114115
* Fetch all matching query results as Slice.
115116
*
117+
* @param pageable defines range and sort of requested slice
116118
* @return {@link Mono} emitting the requested Slice.
119+
* @since 4.5
117120
*/
118121
Mono<Slice<K>> fetchSlice(Pageable pageable) {
119122

120-
Mono<List<K>> content = createQuery().map(it -> SliceUtils.getQuery(it, pageable))
123+
Mono<List<K>> content = createQuery().map(it -> SliceUtils.limitResult(it, pageable).with(pageable.getSort()))
121124
.flatMapMany(it -> find.matching(it).all()).collectList();
122125

123-
return content.map(it -> SliceUtils.getSlice(it, pageable));
126+
return content.map(it -> SliceUtils.sliceResult(it, pageable));
124127
}
125128

126129
/**

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.data.mongodb.core.query.Query;
4444
import org.springframework.data.mongodb.repository.MongoRepository;
4545
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
46+
import org.springframework.data.mongodb.repository.util.SliceUtils;
4647
import org.springframework.data.support.PageableExecutionUtils;
4748
import org.springframework.data.util.StreamUtils;
4849
import org.springframework.data.util.Streamable;
@@ -460,9 +461,9 @@ public Slice<T> slice(Pageable pageable) {
460461

461462
Assert.notNull(pageable, "Pageable must not be null");
462463

463-
List<T> resultList = createQuery(q -> SliceUtils.getQuery(q, pageable)).all();
464+
List<T> resultList = createQuery(q -> SliceUtils.limitResult(q, pageable).with(pageable.getSort())).all();
464465

465-
return SliceUtils.getSlice(resultList, pageable);
466+
return SliceUtils.sliceResult(resultList, pageable);
466467
}
467468

468469
@Override

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static org.springframework.data.mongodb.core.query.Criteria.*;
1919

20+
import org.springframework.data.mongodb.repository.util.SliceUtils;
2021
import reactor.core.publisher.Flux;
2122
import reactor.core.publisher.Mono;
2223

@@ -596,8 +597,8 @@ public Mono<Page<T>> page(Pageable pageable) {
596597
@Override
597598
public Mono<Slice<T>> slice(Pageable pageable) {
598599

599-
return createQuery(q -> SliceUtils.getQuery(q, pageable)).all().collectList()
600-
.map(it -> SliceUtils.getSlice(it, pageable));
600+
return createQuery(q -> SliceUtils.limitResult(q, pageable).with(pageable.getSort())).all().collectList()
601+
.map(it -> SliceUtils.sliceResult(it, pageable));
601602
}
602603

603604
@Override

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.data.mongodb.core.mapping.FieldName;
3535
import org.springframework.data.mongodb.core.query.BasicQuery;
3636
import org.springframework.data.mongodb.core.query.Query;
37+
import org.springframework.data.mongodb.repository.util.SliceUtils;
3738
import org.springframework.data.support.PageableExecutionUtils;
3839
import org.springframework.lang.Nullable;
3940

@@ -187,14 +188,15 @@ public Page<T> fetchPage(Pageable pageable) {
187188
/**
188189
* Fetch a {@link Slice}.
189190
*
190-
* @param pageable
191-
* @return
191+
* @param pageable defines range and sort of requested slice
192+
* @return new instance of {@link Slice} containing matching results within range.
193+
* @since 4.5
192194
*/
193195
public Slice<T> fetchSlice(Pageable pageable) {
194196

195-
List<T> content = find.matching(SliceUtils.getQuery(createQuery(), pageable)).all();
197+
List<T> content = find.matching(SliceUtils.limitResult(createQuery(), pageable).with(pageable.getSort())).all();
196198

197-
return SliceUtils.getSlice(content, pageable);
199+
return SliceUtils.sliceResult(content, pageable);
198200
}
199201

200202
@Override

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SliceUtils.java renamed to spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/util/SliceUtils.java

+23-14
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.mongodb.repository.support;
16+
package org.springframework.data.mongodb.repository.util;
1717

1818
import java.util.List;
1919

@@ -26,19 +26,21 @@
2626
* Utility methods for {@link Slice} handling.
2727
*
2828
* @author Mark Paluch
29+
* @author Christoph Strobl
2930
* @since 4.5
3031
*/
31-
class SliceUtils {
32+
public class SliceUtils {
3233

3334
/**
3435
* Creates a {@link Slice} given {@link Pageable} and {@link List} of results.
3536
*
36-
* @param <T>
37-
* @param resultList
38-
* @param pageable
39-
* @return
37+
* @param <T> the element type.
38+
* @param resultList the source list holding the result of the request. If the result list contains more elements
39+
* (indicating a next slice is available) it is trimmed to the {@link Pageable#getPageSize() page size}.
40+
* @param pageable the source pageable.
41+
* @return new instance of {@link Slice}.
4042
*/
41-
public static <T> Slice<T> getSlice(List<T> resultList, Pageable pageable) {
43+
public static <T> Slice<T> sliceResult(List<T> resultList, Pageable pageable) {
4244

4345
boolean hasNext = resultList.size() > pageable.getPageSize();
4446

@@ -50,16 +52,23 @@ public static <T> Slice<T> getSlice(List<T> resultList, Pageable pageable) {
5052
}
5153

5254
/**
53-
* Customize query for slice retrieval.
55+
* Customize query for {@link #sliceResult sliced result} retrieval. If {@link Pageable#isPaged() paged} the
56+
* {@link Query#limit(int) limit} is set to {@code pagesize + 1} in order to determine if more data is available.
5457
*
55-
* @param query
56-
* @param pageable
57-
* @return
58+
* @param query the source query
59+
* @param pageable paging to apply.
60+
* @return new instance of {@link Query} if either {@link Pageable#isPaged() paged}, the source query otherwise.
5861
*/
59-
public static Query getQuery(Query query, Pageable pageable) {
62+
public static Query limitResult(Query query, Pageable pageable) {
6063

61-
query.with(pageable);
64+
if (pageable.isUnpaged()) {
65+
return query;
66+
}
67+
68+
Query target = Query.of(query);
69+
target.skip(pageable.getOffset());
70+
target.limit(pageable.getPageSize() + 1);
6271

63-
return pageable.isPaged() ? query.limit(pageable.getPageSize() + 1) : query;
72+
return target;
6473
}
6574
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.repository.util;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.Mockito.spy;
20+
import static org.mockito.Mockito.verifyNoInteractions;
21+
22+
import java.util.stream.Stream;
23+
24+
import org.bson.Document;
25+
import org.junit.jupiter.params.ParameterizedTest;
26+
import org.junit.jupiter.params.provider.Arguments;
27+
import org.junit.jupiter.params.provider.MethodSource;
28+
import org.springframework.data.domain.PageRequest;
29+
import org.springframework.data.domain.Pageable;
30+
import org.springframework.data.domain.Sort;
31+
import org.springframework.data.domain.Sort.Direction;
32+
import org.springframework.data.mongodb.core.query.BasicQuery;
33+
import org.springframework.data.mongodb.core.query.Query;
34+
35+
/**
36+
* Unit test for {@link SliceUtils}.
37+
*
38+
* @author Christoph Strobl
39+
*/
40+
class SliceUtilsUnitTests {
41+
42+
@ParameterizedTest // GH-4889
43+
@MethodSource("paged")
44+
void pagedPageableModifiesQuery(Pageable page) {
45+
46+
Query source = new BasicQuery(Document.parse("{ 'spring' : 'data' }"));
47+
48+
Query target = SliceUtils.limitResult(source, page);
49+
50+
assertThat(target.getQueryObject()).isEqualTo(source.getQueryObject());
51+
assertThat(target).isNotSameAs(source);
52+
assertThat(target.isLimited()).isTrue();
53+
assertThat(target.getSkip()).isEqualTo(page.getOffset());
54+
assertThat(target.getLimit()).isEqualTo(page.toLimit().max() + 1);
55+
assertThat(target.getSortObject()).isEqualTo(source.getSortObject());
56+
}
57+
58+
@ParameterizedTest // GH-4889
59+
@MethodSource("unpaged")
60+
void unpagedPageableDoesNotModifyQuery(Pageable page) {
61+
62+
Query source = spy(new BasicQuery(Document.parse("{ 'spring' : 'data' }")));
63+
64+
Query target = SliceUtils.limitResult(source, page);
65+
66+
verifyNoInteractions(source);
67+
68+
assertThat(target).isSameAs(source);
69+
assertThat(target.isLimited()).isFalse();
70+
}
71+
72+
public static Stream<Arguments> paged() {
73+
return Stream.of(Arguments.of(Pageable.ofSize(1)), Arguments.of(PageRequest.of(0, 10)),
74+
Arguments.of(PageRequest.of(0, 10, Direction.ASC, "name")));
75+
}
76+
77+
public static Stream<Arguments> unpaged() {
78+
return Stream.of(Arguments.of(Pageable.unpaged()), Arguments.of(Pageable.unpaged(Sort.by("name"))));
79+
}
80+
}

0 commit comments

Comments
 (0)