Skip to content

Add support for findBy(…) using predicate that returns a Slice #4890

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
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;
Expand Down Expand Up @@ -87,7 +88,7 @@ public Object execute(Query query) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -114,13 +115,14 @@ Mono<Page<K>> fetchPage(Pageable pageable) {
* Fetch all matching query results as 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));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
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;
Expand Down Expand Up @@ -460,9 +461,9 @@ public Slice<T> slice(Pageable pageable) {

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

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;

Expand Down Expand Up @@ -596,8 +597,8 @@ public Mono<Page<T>> page(Pageable pageable) {
@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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;

Expand Down Expand Up @@ -187,14 +188,15 @@ public Page<T> fetchPage(Pageable pageable) {
/**
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -28,7 +28,7 @@
* @author Mark Paluch
* @since 4.5
*/
class SliceUtils {
public class SliceUtils {

/**
* Creates a {@link Slice} given {@link Pageable} and {@link List} of results.
Expand All @@ -38,7 +38,7 @@ class SliceUtils {
* @param pageable
* @return
*/
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();

Expand All @@ -52,14 +52,21 @@ public static <T> Slice<T> getSlice(List<T> resultList, Pageable pageable) {
/**
* Customize query for slice retrieval.
*
* @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} or {@link Pageable#getSort()
* sorted}, the source query otherwise.
*/
public static Query getQuery(Query query, Pageable pageable) {
public static Query limitResult(Query query, Pageable pageable) {

if (pageable.isUnpaged()) {
return query;
}

query.with(pageable);
Query target = Query.of(query);
target.skip(pageable.getOffset());
target.limit(pageable.getPageSize() + 1);

return pageable.isPaged() ? query.limit(pageable.getPageSize() + 1) : query;
return target;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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;

/**
* @author Christoph Strobl
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Care to extend the comment and add ticket numbers to the tests?

*/
class SliceUtilsUnitTests {

@ParameterizedTest
@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
@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"))));
}
}