使用动态查询在 ReactiveMongoRepository 上实现分页和排序
Implementing pagination and sorting on a ReactiveMongoRepository with a dynamic query
我知道分页有点违背反应原则,但由于要求我必须以某种方式让它工作。我正在使用 Spring Data 2.1.6,但我无法升级,因此 ReactiveQuerydslSpecification
无法进行动态查询。我想我可以使用 ReactiveMongoTemplate
所以我想到了这个:
public interface IPersonRepository extends ReactiveMongoRepository<Person, String>, IPersonFilterRepository {
Flux<Person> findAllByCarId(String carId);
}
public interface IPersonFilterRepository {
Flux<Person> findAllByCarIdAndCreatedDateBetween(String carId, PersonStatus status,
OffsetDateTime from, OffsetDateTime to,
Pageable pageable);
}
@Repository
public class PersonFilterRepository implements IPersonFilterRepository {
@Autowired
private ReactiveMongoTemplate reactiveMongoTemplate;
@Override
public Flux<Person> findAllByCarIdAndCreatedDateBetween(String carId, PersonStatus status,
OffsetDateTime from, OffsetDateTime to,
Pageable pageable) {
Query query = new Query(Criteria.where("carId").is(carId));
if (status != null) {
query.addCriteria(Criteria.where("status").is(status));
}
OffsetDateTime maxLimit = OffsetDateTime.now(ZoneOffset.UTC).minusMonths(3).withDayOfMonth(1); // beginning of month
if (from == null || from.isBefore(maxLimit)) {
from = maxLimit;
}
query.addCriteria(Criteria.where("createdDateTime").gte(from));
if (to == null) {
to = OffsetDateTime.now(ZoneOffset.UTC);
}
query.addCriteria(Criteria.where("createdDateTime").lte(to));
// problem is trying to come up with a decent page-ish behavior compatible with Flux
/*return reactiveMongoTemplate.count(query, Person.class)
.flatMap(count -> reactiveMongoTemplate.find(query, Person.class)
.flatMap(p -> new PageImpl<Person>(p, pageable, count))
.collectList()
.map());*/
/* return reactiveMongoTemplate.find(query, Person.class)
.buffer(pageable.getPageSize(), pageable.getPageNumber() + 1)
//.elementAt(pageable.getPageNumber(), new ArrayList<>())
.thenMany(Flux::from);*/
}
我试过return一个Page<Person>
(假设这个单一的方法一次可能是非反应性的,一次)并且失败并出现以下错误,而运行测试(Spring 上下文未成功加载,原因是:InvalidDataAccessApiUsageException: 'IDocumentFilterRepository.findAllByCustomerIdAndCreatedDateBetween' must not use sliced or paged execution. Please use Flux.buffer(size, skip)
。我也尝试过 returning Mono<Page<Person>>
,然后因 "Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: 'IDocumentFilterRepository.findAllByCustomerIdAndCreatedDateBetween'
而失败,所以我猜根据 Example 133, snippet 3
,我唯一的选择是 returning Flux
事实证明,您只需将以下内容添加到查询对象中即可:
query.with(pageable);
reactiveMongoTemplate.find(query, Person.class);
Return Flux<T>
开箱即用。
我知道分页有点违背反应原则,但由于要求我必须以某种方式让它工作。我正在使用 Spring Data 2.1.6,但我无法升级,因此 ReactiveQuerydslSpecification
无法进行动态查询。我想我可以使用 ReactiveMongoTemplate
所以我想到了这个:
public interface IPersonRepository extends ReactiveMongoRepository<Person, String>, IPersonFilterRepository {
Flux<Person> findAllByCarId(String carId);
}
public interface IPersonFilterRepository {
Flux<Person> findAllByCarIdAndCreatedDateBetween(String carId, PersonStatus status,
OffsetDateTime from, OffsetDateTime to,
Pageable pageable);
}
@Repository
public class PersonFilterRepository implements IPersonFilterRepository {
@Autowired
private ReactiveMongoTemplate reactiveMongoTemplate;
@Override
public Flux<Person> findAllByCarIdAndCreatedDateBetween(String carId, PersonStatus status,
OffsetDateTime from, OffsetDateTime to,
Pageable pageable) {
Query query = new Query(Criteria.where("carId").is(carId));
if (status != null) {
query.addCriteria(Criteria.where("status").is(status));
}
OffsetDateTime maxLimit = OffsetDateTime.now(ZoneOffset.UTC).minusMonths(3).withDayOfMonth(1); // beginning of month
if (from == null || from.isBefore(maxLimit)) {
from = maxLimit;
}
query.addCriteria(Criteria.where("createdDateTime").gte(from));
if (to == null) {
to = OffsetDateTime.now(ZoneOffset.UTC);
}
query.addCriteria(Criteria.where("createdDateTime").lte(to));
// problem is trying to come up with a decent page-ish behavior compatible with Flux
/*return reactiveMongoTemplate.count(query, Person.class)
.flatMap(count -> reactiveMongoTemplate.find(query, Person.class)
.flatMap(p -> new PageImpl<Person>(p, pageable, count))
.collectList()
.map());*/
/* return reactiveMongoTemplate.find(query, Person.class)
.buffer(pageable.getPageSize(), pageable.getPageNumber() + 1)
//.elementAt(pageable.getPageNumber(), new ArrayList<>())
.thenMany(Flux::from);*/
}
我试过return一个Page<Person>
(假设这个单一的方法一次可能是非反应性的,一次)并且失败并出现以下错误,而运行测试(Spring 上下文未成功加载,原因是:InvalidDataAccessApiUsageException: 'IDocumentFilterRepository.findAllByCustomerIdAndCreatedDateBetween' must not use sliced or paged execution. Please use Flux.buffer(size, skip)
。我也尝试过 returning Mono<Page<Person>>
,然后因 "Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: 'IDocumentFilterRepository.findAllByCustomerIdAndCreatedDateBetween'
而失败,所以我猜根据 Example 133, snippet 3
Flux
事实证明,您只需将以下内容添加到查询对象中即可:
query.with(pageable);
reactiveMongoTemplate.find(query, Person.class);
Return Flux<T>
开箱即用。