Browse Source

Polishing.

Move SliceUtils to another package for reuse. Slightly alter its API, add tests and update javadoc.

Original Pull Request: #4890
pull/4906/head
Christoph Strobl 10 months ago
parent
commit
49db28dc77
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java
  2. 7
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java
  3. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java
  4. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java
  5. 10
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java
  6. 37
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/util/SliceUtils.java
  7. 80
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/util/SliceUtilsUnitTests.java

3
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java

@ -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.NearQuery;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition; 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.support.PageableExecutionUtils;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -87,7 +88,7 @@ interface MongoQueryExecution {
int pageSize = pageable.getPageSize(); int pageSize = pageable.getPageSize();
// Apply Pageable but tweak limit to peek into next page // 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(); List result = find.matching(modifiedQuery).all();
boolean hasNext = result.size() > pageSize; boolean hasNext = result.size() > pageSize;

7
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java

@ -15,6 +15,7 @@
*/ */
package org.springframework.data.mongodb.repository.support; package org.springframework.data.mongodb.repository.support;
import org.springframework.data.mongodb.repository.util.SliceUtils;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -113,14 +114,16 @@ class ReactiveSpringDataMongodbQuery<K> extends SpringDataMongodbQuerySupport<Re
/** /**
* Fetch all matching query results as Slice. * Fetch all matching query results as Slice.
* *
* @param pageable defines range and sort of requested slice
* @return {@link Mono} emitting the requested Slice. * @return {@link Mono} emitting the requested Slice.
* @since 4.5
*/ */
Mono<Slice<K>> fetchSlice(Pageable pageable) { 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(); .flatMapMany(it -> find.matching(it).all()).collectList();
return content.map(it -> SliceUtils.getSlice(it, pageable)); return content.map(it -> SliceUtils.sliceResult(it, pageable));
} }
/** /**

5
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java

@ -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.core.query.Query;
import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation; 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.support.PageableExecutionUtils;
import org.springframework.data.util.StreamUtils; import org.springframework.data.util.StreamUtils;
import org.springframework.data.util.Streamable; 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"); 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 @Override

5
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java

@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.support;
import static org.springframework.data.mongodb.core.query.Criteria.*; 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.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -596,8 +597,8 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
@Override @Override
public Mono<Slice<T>> slice(Pageable pageable) { public Mono<Slice<T>> slice(Pageable pageable) {
return createQuery(q -> SliceUtils.getQuery(q, pageable)).all().collectList() return createQuery(q -> SliceUtils.limitResult(q, pageable).with(pageable.getSort())).all().collectList()
.map(it -> SliceUtils.getSlice(it, pageable)); .map(it -> SliceUtils.sliceResult(it, pageable));
} }
@Override @Override

10
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java

@ -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.mapping.FieldName;
import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Query; 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.data.support.PageableExecutionUtils;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -187,14 +188,15 @@ public class SpringDataMongodbQuery<T> extends SpringDataMongodbQuerySupport<Spr
/** /**
* Fetch a {@link Slice}. * Fetch a {@link Slice}.
* *
* @param pageable * @param pageable defines range and sort of requested slice
* @return * @return new instance of {@link Slice} containing matching results within range.
* @since 4.5
*/ */
public Slice<T> fetchSlice(Pageable pageable) { 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 @Override

37
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SliceUtils.java → spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/util/SliceUtils.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.data.mongodb.repository.support; package org.springframework.data.mongodb.repository.util;
import java.util.List; import java.util.List;
@ -26,19 +26,21 @@ import org.springframework.data.mongodb.core.query.Query;
* Utility methods for {@link Slice} handling. * Utility methods for {@link Slice} handling.
* *
* @author Mark Paluch * @author Mark Paluch
* @author Christoph Strobl
* @since 4.5 * @since 4.5
*/ */
class SliceUtils { public class SliceUtils {
/** /**
* Creates a {@link Slice} given {@link Pageable} and {@link List} of results. * Creates a {@link Slice} given {@link Pageable} and {@link List} of results.
* *
* @param <T> * @param <T> the element type.
* @param resultList * @param resultList the source list holding the result of the request. If the result list contains more elements
* @param pageable * (indicating a next slice is available) it is trimmed to the {@link Pageable#getPageSize() page size}.
* @return * @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(); 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 query the source query
* @param pageable * @param pageable paging to apply.
* @return * @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;
}
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;
} }
} }

80
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/util/SliceUtilsUnitTests.java

@ -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"))));
}
}
Loading…
Cancel
Save