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?
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.
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.
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.
Flux provides skip
and take
methods to get pagination support, and you also can use The filter and sort below is not a good example, but use filter
and sort
to filter and sort the result.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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With