Add support for findBy(…) using predicate that returns a Slice.
Closes: #4889 Original Pull Request: #4890
This commit is contained in:
committed by
Christoph Strobl
parent
fee8485988
commit
63783e6d09
@@ -27,6 +27,7 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
@@ -271,6 +272,14 @@ public class QuerydslMongoPredicateExecutor<T> extends QuerydslPredicateExecutor
|
||||
return createQuery().fetchPage(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<T> slice(Pageable pageable) {
|
||||
|
||||
Assert.notNull(pageable, "Pageable must not be null");
|
||||
|
||||
return createQuery().fetchSlice(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
return createQuery().stream();
|
||||
|
||||
@@ -24,11 +24,13 @@ import java.util.function.Function;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
|
||||
@@ -239,6 +241,14 @@ public class ReactiveQuerydslMongoPredicateExecutor<T> extends QuerydslPredicate
|
||||
return createQuery().fetchPage(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Slice<T>> slice(Pageable pageable) {
|
||||
|
||||
Assert.notNull(pageable, "Pageable must not be null");
|
||||
|
||||
return createQuery().fetchSlice(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> count() {
|
||||
return createQuery().fetchCount();
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.bson.Document;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.ReactiveFindOperation;
|
||||
@@ -109,6 +110,19 @@ class ReactiveSpringDataMongodbQuery<K> extends SpringDataMongodbQuerySupport<Re
|
||||
return content.flatMap(it -> ReactivePageableExecutionUtils.getPage(it, pageable, fetchCount()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all matching query results as Slice.
|
||||
*
|
||||
* @return {@link Mono} emitting the requested Slice.
|
||||
*/
|
||||
Mono<Slice<K>> fetchSlice(Pageable pageable) {
|
||||
|
||||
Mono<List<K>> content = createQuery().map(it -> SliceUtils.getQuery(it, pageable))
|
||||
.flatMapMany(it -> find.matching(it).all()).collectList();
|
||||
|
||||
return content.map(it -> SliceUtils.getSlice(it, pageable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the one matching query result.
|
||||
*
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.mongodb.core.ExecutableFindOperation;
|
||||
@@ -136,8 +137,7 @@ public class SimpleMongoRepository<T, ID> implements MongoRepository<T, ID> {
|
||||
Query query = getIdQuery(id);
|
||||
getReadPreference().ifPresent(query::withReadPreference);
|
||||
|
||||
return mongoOperations.exists(query, entityInformation.getJavaType(),
|
||||
entityInformation.getCollectionName());
|
||||
return mongoOperations.exists(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -455,6 +455,16 @@ public class SimpleMongoRepository<T, ID> implements MongoRepository<T, ID> {
|
||||
return PageableExecutionUtils.getPage(list, pageable, this::count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<T> slice(Pageable pageable) {
|
||||
|
||||
Assert.notNull(pageable, "Pageable must not be null");
|
||||
|
||||
List<T> resultList = createQuery(q -> SliceUtils.getQuery(q, pageable)).all();
|
||||
|
||||
return SliceUtils.getSlice(resultList, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
return createQuery().stream();
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
@@ -592,6 +593,13 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
|
||||
return items.flatMap(content -> ReactivePageableExecutionUtils.getPage(content, pageable, this.count()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Slice<T>> slice(Pageable pageable) {
|
||||
|
||||
return createQuery(q -> SliceUtils.getQuery(q, pageable)).all().collectList()
|
||||
.map(it -> SliceUtils.getSlice(it, pageable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> count() {
|
||||
return createQuery().count();
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
/**
|
||||
* Utility methods for {@link Slice} handling.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.5
|
||||
*/
|
||||
class SliceUtils {
|
||||
|
||||
/**
|
||||
* Creates a {@link Slice} given {@link Pageable} and {@link List} of results.
|
||||
*
|
||||
* @param <T>
|
||||
* @param resultList
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
public static <T> Slice<T> getSlice(List<T> resultList, Pageable pageable) {
|
||||
|
||||
boolean hasNext = resultList.size() > pageable.getPageSize();
|
||||
|
||||
if (hasNext) {
|
||||
resultList = resultList.subList(0, pageable.getPageSize());
|
||||
}
|
||||
|
||||
return new SliceImpl<>(resultList, pageable, hasNext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize query for slice retrieval.
|
||||
*
|
||||
* @param query
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
public static Query getQuery(Query query, Pageable pageable) {
|
||||
|
||||
query.with(pageable);
|
||||
|
||||
return pageable.isPaged() ? query.limit(pageable.getPageSize() + 1) : query;
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,12 @@ import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.mongodb.core.ExecutableFindOperation;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
@@ -182,6 +184,19 @@ public class SpringDataMongodbQuery<T> extends SpringDataMongodbQuerySupport<Spr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a {@link Slice}.
|
||||
*
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
public Slice<T> fetchSlice(Pageable pageable) {
|
||||
|
||||
List<T> content = find.matching(SliceUtils.getQuery(createQuery(), pageable)).all();
|
||||
|
||||
return SliceUtils.getSlice(content, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fetchFirst() {
|
||||
try {
|
||||
|
||||
@@ -607,6 +607,33 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // GH-4889
|
||||
void findByShouldApplySlice() {
|
||||
|
||||
ReactivePerson probe = new ReactivePerson();
|
||||
probe.setLastname(oliver.getLastname());
|
||||
|
||||
repository
|
||||
.findBy(Example.of(probe, matching().withIgnorePaths("age")),
|
||||
it -> it.slice(PageRequest.of(0, 1, Sort.by("firstname")))) //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(it -> {
|
||||
|
||||
assertThat(it.hasNext()).isTrue();
|
||||
assertThat(it.getContent()).contains(dave);
|
||||
}).verifyComplete();
|
||||
|
||||
repository
|
||||
.findBy(Example.of(probe, matching().withIgnorePaths("age")),
|
||||
it -> it.slice(PageRequest.of(1, 1, Sort.by("firstname")))) //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(it -> {
|
||||
|
||||
assertThat(it.hasNext()).isFalse();
|
||||
assertThat(it.getContent()).contains(oliver);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // GH-3757
|
||||
void findByShouldCount() {
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.dao.PermissionDeniedDataAccessException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.MongoDatabaseFactory;
|
||||
@@ -314,6 +315,7 @@ public class QuerydslMongoPredicateExecutorIntegrationTests {
|
||||
|
||||
Page<Person> first = repository.findBy(person.lastname.eq(oliver.getLastname()),
|
||||
it -> it.page(PageRequest.of(0, 1, Sort.by("firstname"))));
|
||||
|
||||
assertThat(first.getTotalElements()).isEqualTo(2);
|
||||
assertThat(first.getContent()).contains(dave);
|
||||
|
||||
@@ -324,6 +326,22 @@ public class QuerydslMongoPredicateExecutorIntegrationTests {
|
||||
assertThat(next.getContent()).contains(oliver);
|
||||
}
|
||||
|
||||
@Test // GH-4889
|
||||
public void findByShouldApplySlice() {
|
||||
|
||||
Slice<Person> first = repository.findBy(person.lastname.eq(oliver.getLastname()),
|
||||
it -> it.slice(PageRequest.of(0, 1, Sort.by("firstname"))));
|
||||
|
||||
assertThat(first.hasNext()).isTrue();
|
||||
assertThat(first.getContent()).contains(dave);
|
||||
|
||||
Slice<Person> next = repository.findBy(person.lastname.eq(oliver.getLastname()),
|
||||
it -> it.slice(PageRequest.of(1, 1, Sort.by("firstname"))));
|
||||
|
||||
assertThat(next.hasNext()).isFalse();
|
||||
assertThat(next.getContent()).contains(oliver);
|
||||
}
|
||||
|
||||
@Test // GH-3757
|
||||
public void findByShouldCount() {
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
@@ -50,8 +51,8 @@ import org.springframework.data.mongodb.repository.QUser;
|
||||
import org.springframework.data.mongodb.repository.User;
|
||||
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
|
||||
import org.springframework.data.mongodb.test.util.MongoTestUtils;
|
||||
import org.springframework.data.repository.query.FluentQuery;
|
||||
import org.springframework.data.mongodb.test.util.ReactiveMongoClientClosingTestConfiguration;
|
||||
import org.springframework.data.repository.query.FluentQuery;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@@ -414,6 +415,28 @@ public class ReactiveQuerydslMongoPredicateExecutorTests {
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // GH-4889
|
||||
public void findByShouldApplySlice() {
|
||||
|
||||
repository
|
||||
.findBy(person.lastname.eq(oliver.getLastname()), it -> it.slice(PageRequest.of(0, 1, Sort.by("firstname")))) //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(it -> {
|
||||
|
||||
assertThat(it.hasNext()).isTrue();
|
||||
assertThat(it.getContent()).containsOnly(dave);
|
||||
}).verifyComplete();
|
||||
|
||||
repository
|
||||
.findBy(person.lastname.eq(oliver.getLastname()), it -> it.slice(PageRequest.of(1, 1, Sort.by("firstname")))) //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(it -> {
|
||||
|
||||
assertThat(it.hasNext()).isFalse();
|
||||
assertThat(it.getContent()).containsOnly(oliver);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // GH-3757
|
||||
public void findByShouldCount() {
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.data.domain.ExampleMatcher;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.MongoTransactionManager;
|
||||
@@ -579,6 +580,24 @@ class SimpleMongoRepositoryTests implements StateFunctions {
|
||||
assertThat(next.getContent()).contains(oliver);
|
||||
}
|
||||
|
||||
@Test // GH-4889
|
||||
void findByShouldApplySlice() {
|
||||
|
||||
Person probe = new Person();
|
||||
probe.setLastname(oliver.getLastname());
|
||||
|
||||
Slice<Person> first = repository.findBy(Example.of(probe, getMatcher()),
|
||||
it -> it.slice(PageRequest.of(0, 1, Sort.by("firstname"))));
|
||||
assertThat(first.hasNext()).isTrue();
|
||||
assertThat(first.getContent()).contains(dave);
|
||||
|
||||
Slice<Person> next = repository.findBy(Example.of(probe, getMatcher()),
|
||||
it -> it.slice(PageRequest.of(1, 1, Sort.by("firstname"))));
|
||||
|
||||
assertThat(next.hasNext()).isFalse();
|
||||
assertThat(next.getContent()).contains(oliver);
|
||||
}
|
||||
|
||||
@Test // GH-3757
|
||||
void findByShouldCount() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user