Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pagination with "after" cursor results in ScrollPosition that skips one extra item #925

Closed
tata-dr-dk opened this issue Mar 18, 2024 · 9 comments
Assignees
Labels
type: regression A bug that is also a regression
Milestone

Comments

@tata-dr-dk
Copy link

tata-dr-dk commented Mar 18, 2024

Using Graphql Query 'after' option with a cursor of position N, yields a result-set starting from position N+2 upwards, skipping the document right after the cursor that should have been included (N+1).

Using the latest graphql and mongodb starters

	implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
	implementation 'org.springframework.boot:spring-boot-starter-graphql'

Using graphql @QueryMapping annotation and ScrollSubrange on a controller

    @QueryMapping
    public Window<Customer> customers(@Argument String storeNumber, ScrollSubrange subrange) {
       //...
    }

and a query:

query MyQuery {
  customers(storeNumber: "123", after: "T18x") {
    edges {
      cursor
      node {
        name
        storeNumber
      }
    }
  }
}

where 'T18x' is the cursor of the first document, results in subrange.position() of 2.

Using that position in a mongodb Query:

        var query = Query.query(criteria)
                .with(scrollPosition);

would actually skip 2 documents, and not start from document of position 2 (thus not inclusive).
Seems to be related to #916

A project reproducing the bug can be found here: graphql-bug

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 18, 2024
@JIngrahamTG
Copy link

I've noticed the same issue. Also, the last and before includes the cursor provided as the "before", so last:2 before:"cursor" only includes one node before the cursor as well as the before cursor itself. I am also using the latest released version of spring graphql.

@pax95
Copy link

pax95 commented Mar 19, 2024

It seems to be a regression from #916.
Downgrading to spring-graphql:1.2.4 solves the pagination problem.

@rstoyanchev
Copy link
Contributor

rstoyanchev commented Mar 20, 2024

@tata-dr-dk, thanks for the sample.

Indeed #916 was a breaking change, but it was necessary to fix a problem that had been there from the start, and reverse another that was introduced in 1.2.4. That's pretty confusing I'll admit, but I believe the current behavior is correct.

In the sample, there are 3 customers. Cursor "T18x" decodes to "O_1", which is offset 1. That points to the 2nd customer, and the server correctly returns only the 3rd customer.

If I use cursor "T18w" instead, or "O_0", which is offset 0, that points to the first customer, and the server returns the 2nd and 3rd customers.

only includes one node before the cursor as well as the before cursor itself

If I use last:2 before:"T18x" in same the sample, the resulting ScrollSubrange has offset 0 and count 1 as expected. However, the controller uses 10, in effect ignoring the count, and the server returns all 3 customers. If I change the count to subrange.count().orElse(10), then the server returns only the 1st customer.

@rstoyanchev rstoyanchev added the status: waiting-for-feedback We need additional information before we can continue label Mar 20, 2024
@tata-dr-dk
Copy link
Author

tata-dr-dk commented Mar 20, 2024

Thanks @rstoyanchev for your response

I am totally new to GraphQL, but for me there still seems to be a incompatible behavior. I would expect the cursor X returned for any document to be usable in the after clause, which then would result in the next page starting from the document that exactly follows it, e.g. with cursor Y.

I updated the example unit tests with two cases, one using the first returned document.cursor, the other using the pageInfo.endCursor to query the next page. Both seem to show that there is a missing document, and an extra erroneous skip indeed is happening.

It would surprise me if that is indeed the correct behavior. In that case, requesting the next page would require the client to use the cursor with index "pageSize - 1", and the endCursor would not be of any value

The first document cursor returned is T18x and not T18w

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 20, 2024
@rstoyanchev
Copy link
Contributor

That helps better to demonstrate the issue, thanks.

It looks like OffsetScrollPosition adds 1 to its offset, so when we render cursors starting from 1. However, when the cursor is sent back and used, the repository seems to treat the offset as zero based. I'm not sure if this is expected behavior for Spring Data or not, but I will investigate.

@rstoyanchev rstoyanchev changed the title Bug: Pagination using "after: <cursor>" / ScrollPosition skips one extra document Pagination with "after: <cursor>" / ScrollPosition skips one extra document Mar 26, 2024
@rstoyanchev rstoyanchev changed the title Pagination with "after: <cursor>" / ScrollPosition skips one extra document Pagination with "after" cursor results in ScrollPosition that skips one extra item Mar 26, 2024
@rstoyanchev rstoyanchev added the type: regression A bug that is also a regression label Mar 26, 2024
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Mar 26, 2024

After some discussions with the Spring Data team, there is now a change planned on their side, see spring-projects/spring-data-commons#3070. After that we will see if we need any follow-up changes here.

@rstoyanchev rstoyanchev removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels Mar 26, 2024
@rstoyanchev rstoyanchev added this to the 1.2.6 milestone Mar 26, 2024
@rstoyanchev rstoyanchev self-assigned this Mar 26, 2024
@rstoyanchev
Copy link
Contributor

We've decided to apply a temporary fix in spring-graphql, which allows us to release a fix for this regression without delay, and gives Spring Data a bit more time to work out how to proceed. Once there is a clear direction under spring-projects/spring-data-commons#3070, we'll adapt here as needed.

@tata-dr-dk, @JIngrahamTG, @pax95, if you are able to confirm the fix that's in 1.2.6-SNAPSHOT via https://repo.spring.io/snapshot that would be much appreciated.

@tata-dr-dk
Copy link
Author

@rstoyanchev The fix works - thanks a lot!
Apologies for the late reply - was absent for quite long time and could not check the fix out.

@rstoyanchev
Copy link
Contributor

rstoyanchev commented Apr 16, 2024

Thanks for the confirmation.

For the upcoming 1.3 (RC1 released today, GA on May 21), there are underlying changes in Spring Data 2024 where offset positions start from 0 (rather than 1), and data stores treat offset positions as exclusive. We have adapted accordingly in #946.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

5 participants