Skip to content

Commit 63783e6

Browse files
mp911dechristophstrobl
authored andcommitted
Add support for findBy(…) using predicate that returns a Slice.
Closes: #4889 Original Pull Request: #4890
1 parent fee8485 commit 63783e6

11 files changed

+222
-4
lines changed

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

+9
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.data.domain.Page;
2828
import org.springframework.data.domain.Pageable;
2929
import org.springframework.data.domain.ScrollPosition;
30+
import org.springframework.data.domain.Slice;
3031
import org.springframework.data.domain.Sort;
3132
import org.springframework.data.domain.Window;
3233
import org.springframework.data.mongodb.core.MongoOperations;
@@ -271,6 +272,14 @@ public Page<T> page(Pageable pageable) {
271272
return createQuery().fetchPage(pageable);
272273
}
273274

275+
@Override
276+
public Slice<T> slice(Pageable pageable) {
277+
278+
Assert.notNull(pageable, "Pageable must not be null");
279+
280+
return createQuery().fetchSlice(pageable);
281+
}
282+
274283
@Override
275284
public Stream<T> stream() {
276285
return createQuery().stream();

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424

2525
import org.bson.Document;
2626
import org.reactivestreams.Publisher;
27+
2728
import org.springframework.data.domain.Page;
2829
import org.springframework.data.domain.Pageable;
29-
import org.springframework.data.domain.Window;
3030
import org.springframework.data.domain.ScrollPosition;
31+
import org.springframework.data.domain.Slice;
3132
import org.springframework.data.domain.Sort;
33+
import org.springframework.data.domain.Window;
3234
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
3335
import org.springframework.data.mongodb.core.query.BasicQuery;
3436
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
@@ -239,6 +241,14 @@ public Mono<Page<T>> page(Pageable pageable) {
239241
return createQuery().fetchPage(pageable);
240242
}
241243

244+
@Override
245+
public Mono<Slice<T>> slice(Pageable pageable) {
246+
247+
Assert.notNull(pageable, "Pageable must not be null");
248+
249+
return createQuery().fetchSlice(pageable);
250+
}
251+
242252
@Override
243253
public Mono<Long> count() {
244254
return createQuery().fetchCount();

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

+14
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.data.domain.Page;
2828
import org.springframework.data.domain.Pageable;
2929
import org.springframework.data.domain.ScrollPosition;
30+
import org.springframework.data.domain.Slice;
3031
import org.springframework.data.domain.Window;
3132
import org.springframework.data.mongodb.core.MongoOperations;
3233
import org.springframework.data.mongodb.core.ReactiveFindOperation;
@@ -109,6 +110,19 @@ Mono<Page<K>> fetchPage(Pageable pageable) {
109110
return content.flatMap(it -> ReactivePageableExecutionUtils.getPage(it, pageable, fetchCount()));
110111
}
111112

113+
/**
114+
* Fetch all matching query results as Slice.
115+
*
116+
* @return {@link Mono} emitting the requested Slice.
117+
*/
118+
Mono<Slice<K>> fetchSlice(Pageable pageable) {
119+
120+
Mono<List<K>> content = createQuery().map(it -> SliceUtils.getQuery(it, pageable))
121+
.flatMapMany(it -> find.matching(it).all()).collectList();
122+
123+
return content.map(it -> SliceUtils.getSlice(it, pageable));
124+
}
125+
112126
/**
113127
* Fetch the one matching query result.
114128
*

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.data.domain.PageImpl;
3434
import org.springframework.data.domain.Pageable;
3535
import org.springframework.data.domain.ScrollPosition;
36+
import org.springframework.data.domain.Slice;
3637
import org.springframework.data.domain.Sort;
3738
import org.springframework.data.domain.Window;
3839
import org.springframework.data.mongodb.core.ExecutableFindOperation;
@@ -136,8 +137,7 @@ public boolean existsById(ID id) {
136137
Query query = getIdQuery(id);
137138
getReadPreference().ifPresent(query::withReadPreference);
138139

139-
return mongoOperations.exists(query, entityInformation.getJavaType(),
140-
entityInformation.getCollectionName());
140+
return mongoOperations.exists(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
141141
}
142142

143143
@Override
@@ -455,6 +455,16 @@ public Page<T> page(Pageable pageable) {
455455
return PageableExecutionUtils.getPage(list, pageable, this::count);
456456
}
457457

458+
@Override
459+
public Slice<T> slice(Pageable pageable) {
460+
461+
Assert.notNull(pageable, "Pageable must not be null");
462+
463+
List<T> resultList = createQuery(q -> SliceUtils.getQuery(q, pageable)).all();
464+
465+
return SliceUtils.getSlice(resultList, pageable);
466+
}
467+
458468
@Override
459469
public Stream<T> stream() {
460470
return createQuery().stream();

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

+8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.data.domain.Page;
3838
import org.springframework.data.domain.Pageable;
3939
import org.springframework.data.domain.ScrollPosition;
40+
import org.springframework.data.domain.Slice;
4041
import org.springframework.data.domain.Sort;
4142
import org.springframework.data.domain.Window;
4243
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -592,6 +593,13 @@ public Mono<Page<T>> page(Pageable pageable) {
592593
return items.flatMap(content -> ReactivePageableExecutionUtils.getPage(content, pageable, this.count()));
593594
}
594595

596+
@Override
597+
public Mono<Slice<T>> slice(Pageable pageable) {
598+
599+
return createQuery(q -> SliceUtils.getQuery(q, pageable)).all().collectList()
600+
.map(it -> SliceUtils.getSlice(it, pageable));
601+
}
602+
595603
@Override
596604
public Mono<Long> count() {
597605
return createQuery().count();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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.support;
17+
18+
import java.util.List;
19+
20+
import org.springframework.data.domain.Pageable;
21+
import org.springframework.data.domain.Slice;
22+
import org.springframework.data.domain.SliceImpl;
23+
import org.springframework.data.mongodb.core.query.Query;
24+
25+
/**
26+
* Utility methods for {@link Slice} handling.
27+
*
28+
* @author Mark Paluch
29+
* @since 4.5
30+
*/
31+
class SliceUtils {
32+
33+
/**
34+
* Creates a {@link Slice} given {@link Pageable} and {@link List} of results.
35+
*
36+
* @param <T>
37+
* @param resultList
38+
* @param pageable
39+
* @return
40+
*/
41+
public static <T> Slice<T> getSlice(List<T> resultList, Pageable pageable) {
42+
43+
boolean hasNext = resultList.size() > pageable.getPageSize();
44+
45+
if (hasNext) {
46+
resultList = resultList.subList(0, pageable.getPageSize());
47+
}
48+
49+
return new SliceImpl<>(resultList, pageable, hasNext);
50+
}
51+
52+
/**
53+
* Customize query for slice retrieval.
54+
*
55+
* @param query
56+
* @param pageable
57+
* @return
58+
*/
59+
public static Query getQuery(Query query, Pageable pageable) {
60+
61+
query.with(pageable);
62+
63+
return pageable.isPaged() ? query.limit(pageable.getPageSize() + 1) : query;
64+
}
65+
}

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

+15
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
import java.util.stream.Stream;
2323

2424
import org.bson.Document;
25+
2526
import org.springframework.data.domain.Page;
2627
import org.springframework.data.domain.PageImpl;
2728
import org.springframework.data.domain.Pageable;
2829
import org.springframework.data.domain.ScrollPosition;
30+
import org.springframework.data.domain.Slice;
2931
import org.springframework.data.domain.Window;
3032
import org.springframework.data.mongodb.core.ExecutableFindOperation;
3133
import org.springframework.data.mongodb.core.MongoOperations;
@@ -182,6 +184,19 @@ public Page<T> fetchPage(Pageable pageable) {
182184
}
183185
}
184186

187+
/**
188+
* Fetch a {@link Slice}.
189+
*
190+
* @param pageable
191+
* @return
192+
*/
193+
public Slice<T> fetchSlice(Pageable pageable) {
194+
195+
List<T> content = find.matching(SliceUtils.getQuery(createQuery(), pageable)).all();
196+
197+
return SliceUtils.getSlice(content, pageable);
198+
}
199+
185200
@Override
186201
public T fetchFirst() {
187202
try {

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java

+27
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,33 @@ void findByShouldApplyPagination() {
607607
}).verifyComplete();
608608
}
609609

610+
@Test // GH-4889
611+
void findByShouldApplySlice() {
612+
613+
ReactivePerson probe = new ReactivePerson();
614+
probe.setLastname(oliver.getLastname());
615+
616+
repository
617+
.findBy(Example.of(probe, matching().withIgnorePaths("age")),
618+
it -> it.slice(PageRequest.of(0, 1, Sort.by("firstname")))) //
619+
.as(StepVerifier::create) //
620+
.assertNext(it -> {
621+
622+
assertThat(it.hasNext()).isTrue();
623+
assertThat(it.getContent()).contains(dave);
624+
}).verifyComplete();
625+
626+
repository
627+
.findBy(Example.of(probe, matching().withIgnorePaths("age")),
628+
it -> it.slice(PageRequest.of(1, 1, Sort.by("firstname")))) //
629+
.as(StepVerifier::create) //
630+
.assertNext(it -> {
631+
632+
assertThat(it.hasNext()).isFalse();
633+
assertThat(it.getContent()).contains(oliver);
634+
}).verifyComplete();
635+
}
636+
610637
@Test // GH-3757
611638
void findByShouldCount() {
612639

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java

+18
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.data.domain.Page;
3232
import org.springframework.data.domain.PageRequest;
3333
import org.springframework.data.domain.Pageable;
34+
import org.springframework.data.domain.Slice;
3435
import org.springframework.data.domain.Sort;
3536
import org.springframework.data.domain.Sort.Direction;
3637
import org.springframework.data.mongodb.MongoDatabaseFactory;
@@ -314,6 +315,7 @@ public void findByShouldApplyPagination() {
314315

315316
Page<Person> first = repository.findBy(person.lastname.eq(oliver.getLastname()),
316317
it -> it.page(PageRequest.of(0, 1, Sort.by("firstname"))));
318+
317319
assertThat(first.getTotalElements()).isEqualTo(2);
318320
assertThat(first.getContent()).contains(dave);
319321

@@ -324,6 +326,22 @@ public void findByShouldApplyPagination() {
324326
assertThat(next.getContent()).contains(oliver);
325327
}
326328

329+
@Test // GH-4889
330+
public void findByShouldApplySlice() {
331+
332+
Slice<Person> first = repository.findBy(person.lastname.eq(oliver.getLastname()),
333+
it -> it.slice(PageRequest.of(0, 1, Sort.by("firstname"))));
334+
335+
assertThat(first.hasNext()).isTrue();
336+
assertThat(first.getContent()).contains(dave);
337+
338+
Slice<Person> next = repository.findBy(person.lastname.eq(oliver.getLastname()),
339+
it -> it.slice(PageRequest.of(1, 1, Sort.by("firstname"))));
340+
341+
assertThat(next.hasNext()).isFalse();
342+
assertThat(next.getContent()).contains(oliver);
343+
}
344+
327345
@Test // GH-3757
328346
public void findByShouldCount() {
329347

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.junit.BeforeClass;
3232
import org.junit.Test;
3333
import org.junit.runner.RunWith;
34+
3435
import org.springframework.beans.factory.annotation.Autowired;
3536
import org.springframework.context.annotation.Configuration;
3637
import org.springframework.dao.IncorrectResultSizeDataAccessException;
@@ -50,8 +51,8 @@
5051
import org.springframework.data.mongodb.repository.User;
5152
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
5253
import org.springframework.data.mongodb.test.util.MongoTestUtils;
53-
import org.springframework.data.repository.query.FluentQuery;
5454
import org.springframework.data.mongodb.test.util.ReactiveMongoClientClosingTestConfiguration;
55+
import org.springframework.data.repository.query.FluentQuery;
5556
import org.springframework.test.context.ContextConfiguration;
5657
import org.springframework.test.context.junit4.SpringRunner;
5758

@@ -414,6 +415,28 @@ public void findByShouldApplyPagination() {
414415
}).verifyComplete();
415416
}
416417

418+
@Test // GH-4889
419+
public void findByShouldApplySlice() {
420+
421+
repository
422+
.findBy(person.lastname.eq(oliver.getLastname()), it -> it.slice(PageRequest.of(0, 1, Sort.by("firstname")))) //
423+
.as(StepVerifier::create) //
424+
.assertNext(it -> {
425+
426+
assertThat(it.hasNext()).isTrue();
427+
assertThat(it.getContent()).containsOnly(dave);
428+
}).verifyComplete();
429+
430+
repository
431+
.findBy(person.lastname.eq(oliver.getLastname()), it -> it.slice(PageRequest.of(1, 1, Sort.by("firstname")))) //
432+
.as(StepVerifier::create) //
433+
.assertNext(it -> {
434+
435+
assertThat(it.hasNext()).isFalse();
436+
assertThat(it.getContent()).containsOnly(oliver);
437+
}).verifyComplete();
438+
}
439+
417440
@Test // GH-3757
418441
public void findByShouldCount() {
419442

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java

+19
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.data.domain.Page;
3939
import org.springframework.data.domain.PageRequest;
4040
import org.springframework.data.domain.Pageable;
41+
import org.springframework.data.domain.Slice;
4142
import org.springframework.data.domain.Sort;
4243
import org.springframework.data.geo.Point;
4344
import org.springframework.data.mongodb.MongoTransactionManager;
@@ -579,6 +580,24 @@ void findByShouldApplyPagination() {
579580
assertThat(next.getContent()).contains(oliver);
580581
}
581582

583+
@Test // GH-4889
584+
void findByShouldApplySlice() {
585+
586+
Person probe = new Person();
587+
probe.setLastname(oliver.getLastname());
588+
589+
Slice<Person> first = repository.findBy(Example.of(probe, getMatcher()),
590+
it -> it.slice(PageRequest.of(0, 1, Sort.by("firstname"))));
591+
assertThat(first.hasNext()).isTrue();
592+
assertThat(first.getContent()).contains(dave);
593+
594+
Slice<Person> next = repository.findBy(Example.of(probe, getMatcher()),
595+
it -> it.slice(PageRequest.of(1, 1, Sort.by("firstname"))));
596+
597+
assertThat(next.hasNext()).isFalse();
598+
assertThat(next.getContent()).contains(oliver);
599+
}
600+
582601
@Test // GH-3757
583602
void findByShouldCount() {
584603

0 commit comments

Comments
 (0)