spring-projects / spring-data-cassandra

Provides support to increase developer productivity in Java when using Apache Cassandra. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
https://spring.io/projects/spring-data-cassandra/
Apache License 2.0
374 stars 310 forks source link

Cannot create a Cassandra page request for an indexed page other than the first page (0) #1357

Closed prafullazee closed 1 year ago

prafullazee commented 1 year ago

Java: 19

Springboot: 3.0

Dependency:

<dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-cassandra</artifactId>
</dependency>

Repository

PersonRepository extends ReactiveCassandraRepository<Person, Integer> {
    Mono<Slice<Person>> findAll(Pageable page);
    Mono<Slice<Person>> findAllByType(String type, Pageable page);
}

Service:

public Mono<Slice<Person>> findPaginatedPerson(int limit, int offset) {

    int page = offset / limit;
    //only working with page = 0
    Pageable pageRequest = CassandraPageRequest.of(page,limit); 
    Mono<Slice<Person>> persons = personRepository.findAll(pageRequest);

    //working for first page only
   //Mono<Slice<Person>> persons = personRepository.findAll(CassandraPageRequest.first(1));
    return persons;
  }

Controller:

 @GetMapping("/persons")
  public List<Person> findPaginated(@RequestParam int limit, @RequestParam int offset) {
    Mono<Slice<Person>> personPage = personService.findPaginatedPerson(limit, offset);
    return personPage.block().getContent();
  }

I tried below changes

 public List<Person> findFoo(int limit, int offset) {
    int page = offset / limit;
    int currpage = 0, size = 2;
    Pageable pageRequest = CassandraPageRequest.of(size, limit);
    Slice< Person > persons = personRepository.findAllByAssetType(pageRequest);
    while(persons.hasNext() && currpage < page) {
      persons = personRepository.findAllByAssetType(persons.nextPageable());
      currpage++;
    }
    return persons.getContent();
  }

and created an workaround which is super inefficient

public List<Person> findPaginatedPerson(int limit, int offset) {
    int page =0;
    int noOfrecords = offset * limit;

    Pageable pageRequest = CassandraPageRequest.of(page, noOfrecords);
    Mono<Slice<Person>> persons = personRepository.findAll(pageRequest);
    return persons.block().getContent().stream().skip(noOfrecords-limit).collect(Collectors.toList());
}

would like to know, the best way to implement it .

From this PR https://github.com/spring-projects/spring-data-cassandra/pull/128 allow slice queries using reactive repositories. it seems it is possible, there is definitely I am missing something.

mp911de commented 1 year ago

The pagination in Cassandra pagination is a bit misleading because Cassandra is actually scrolling through results. The Cassandra paging uses a forward-only cursor and there is no possibility to address individual pages. Rather, you start fetching the first query results and from that first query you can then continue to the next page only.

That being said, that is the reason why you cannot construct a page request for pages other than index zero. If you want to paginate across e.g. multiple web requests, then please save the paging state and restore it upon the next request (e.g. using query parameters).

prafullazee commented 1 year ago

@mp911de can you please give me any reference for these implementations? I tried implementing it from some stackoverflow post, but got the same error as above, may be I am missing something and not able to catch it.

mp911de commented 1 year ago

can you please give me any reference for these implementations

I think the code you're looking for should look somewhat along the following example:

Page<Object> foo = …;
CassandraPageRequest nextPageable = (CassandraPageRequest) foo.nextPageable();
ByteBuffer pagingState = nextPageable.getPagingState();
// serialize page size + page number, serialize pagingState

// restore both
CassandraPageRequest.of(Pageable.ofSize(20).withPage(5), pagingState);
prafullazee commented 1 year ago

thank you @mp911de I will try with it.