Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to implement pagination in spring webflux and spring data reactive

I'm trying to understand reactive part of spring 5. I have created simple rest endpoint for finding all entities using spring web-flux and spring data reactive (mongo) but don't see any way how to implement pagination.

Here is my simple example in Kotlin:

@GetMapping("/posts/")
fun getAllPosts() = postRepository.findAll()

Does it mean that reactive endpoint does not require pagination? Is some way to implement pagination from server side using this stack?

like image 533
Loniks Avatar asked Aug 28 '17 17:08

Loniks


People also ask

Does spring data provides support for pagination and sorting?

Spring Data PagingAndSortingRepository To help us deal with this situation, Spring Data JPA provides way to implement pagination with PagingAndSortingRepository. PagingAndSortingRepository extends CrudRepository to provide additional methods to retrieve entities using the sorting abstraction.

Can I use Springmvc and WebFlux together?

both infrastructure will compete for the same job (for example, serving static resources, the mappings, etc) mixing both runtime models within the same container is not a good idea and is likely to perform badly or just not work at all.

Is Spring WebFlux better than Spring MVC?

Moreover, Spring WebFlux supports reactive backpressure, so we have more control over how we should react to fast producers than both Spring MVC Async and Spring MVC. Spring Flux also has a tangible shift towards functional coding style and declarative API decomposition thanks to Reactor API behind it.


2 Answers

Flux provides skip and take methods to get pagination support, and you also can use filter and sort to filter and sort the result. The filter and sort below is not a good example, but use skip and Pageable as 2nd parameter are no different.

The following codes work for me.

@GetMapping("")
public Flux<Post> all(
//@RequestParam(value = "q", required = false) String q,
                      @RequestParam(value = "page", defaultValue = "0") long page,
                      @RequestParam(value = "size", defaultValue = "10") long size) {
    return this.postRepository.findAll()
        //.filter(p -> Optional.ofNullable(q).map(key -> p.getTitle().contains(key) || p.getContent().contains(key)).orElse(true))//(replace this with query parameters)
        .sort(comparing(Post::getCreatedDate).reversed())
        .skip(page * size).take(size);
}

Update: The underlay drivers should be responsible for handling the result in the reactivestreams way.

And as you see in the answer from Christoph, if using a findByXXX method, Spring Data Mongo Reactive provides a variant to accept a pageable argument, but the findAll(reactive version) does not include such a variant, you have to do skip in the later operations if you really need the pagination feature. When switching to Flux instead of List, imagine the data in Flux as living water in the rivers or oil in the pipes, or the tweets in twitter.com.

I have tried to compare the queries using Pageale and not in the following case.

this.postRepository.findByTitleContains("title")
                .skip(0)
                .limitRequest(10)
                .sort((o1, o2) -> o1.getTitle().compareTo(o2.getTitle()))


this.postRepository.findByTitleContains("title", PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "title")))

When enabling logging for logging.level.org.springframework.data.mongodb.core.ReactiveMongoTemplate=DEBUG and found they print the same log for queries.

 find using query: { "title" : { "$regularExpression" : { "pattern" : ".*title.*", "options" : ""}}} fields: Document{{title=1}} for class: class com.example.demo.Post in collection: post

//other logging...

 find using query: { "title" : { "$regularExpression" : { "pattern" : ".*title.*", "options" : ""}}} fields: Document{{title=1}} for class: class com.example.demo.Post in collection: post

Keep in mind, all these operations should be DELEGATED to the underlay R2dbc drivers which implemented the reactive streams spec and performed on the DB side, NOT in the memory of your application side.

Check the example codes.

The early sample code I provided above maybe is not a good sample of filter and sort operations(MongoDB itself provides great regularexpression operations for it). But pagination in the reactive variant is not a good match with the concept in the reactive stream spec. When embracing Spring reactive stack, most of the time, we just move our work to a new collection of APIs. In my opinion, the realtime update and elastic response scene could be better match Reactive, eg. using it with SSE, Websocket, RSocket, application/stream+json(missing in the new Spring docs) protocols, etc

like image 64
Hantsy Avatar answered Sep 21 '22 05:09

Hantsy


The reactive support in Spring Data does not provide means of a Page return type. Still, the Pageable parameter is supported in method signatures passing on limit and offset to the drivers and therefore the store itself, returning a Flux<T> that emits the range requested.

Flux<Person> findByFirstname(String firstname, Pageable pageable);

For more information please have a look at the current Reference Documentation for 2.0.RC2 and the Spring Data Examples.

like image 39
Christoph Strobl Avatar answered Sep 20 '22 05:09

Christoph Strobl