Implementing pagination and sorting on a ReactiveMongoRepository with a dynamic query
Asked Answered
F

1

5

I know pagination is somewhat against reactive principles, but due to requirements I have to make it work somehow. I'm using Spring Data 2.1.6 and I can't upgrade so ReactiveQuerydslSpecification for the dynamic query is out of the question. I figured I could use ReactiveMongoTemplate so I came up with this:

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);*/
    }

I've tried to return a Page<Person> (assuming for once this single method could be non-reactive, for once) and it fails with the following error while running testing (Spring context does not load successfully due to: InvalidDataAccessApiUsageException: 'IDocumentFilterRepository.findAllByCustomerIdAndCreatedDateBetween' must not use sliced or paged execution. Please use Flux.buffer(size, skip). I've also tried returning Mono<Page<Person>> and then fails with "Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: 'IDocumentFilterRepository.findAllByCustomerIdAndCreatedDateBetween', so I guess my only option is returning a Flux, according to Example 133, snippet 3

Fissi answered 15/4, 2020 at 4:12 Comment(0)
F
7

Turns out you can just add the following to the query object:

query.with(pageable);

reactiveMongoTemplate.find(query, Person.class);

Return Flux<T> and it will work out of the box.

Fissi answered 22/4, 2020 at 16:59 Comment(2)
thanks for the answer. How do you get the total elements though?Angwantibo
@Angwantibo you can use the template itself with count(query) to get the total. Not sure if it would apply correctly before adding the pageable parameter to the query, I didn't have the need back then to do that.Fluoridate

© 2022 - 2024 — McMap. All rights reserved.