Skip to content

Default ScrollSubrange is not applied correctly for backwards scrolling in QueryDSL and QBE #900

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

Closed
mspiess opened this issue Feb 7, 2024 · 8 comments
Assignees
Labels
status: superseded Issue is superseded by another type: bug A general bug

Comments

@mspiess
Copy link

mspiess commented Feb 7, 2024

Example:
There is a Entity called Document.
Schema:

type Query {
  documents(first: Int, last: Int): DocumentConnection
}

A query for document returns the same data, regardless of whether first or last is provided.

The culprit:
QuerydslDataFetcher#ScrollableEntityFetcher.getResult uses the limit from the subrange that is build out of the arguments. If a cursor is provided using before or after it also uses the built subrange's position, which has the intended direction. However in the case where there is no cursor provided, it uses the default subrange's position, overriding the provided direction.

@SuppressWarnings("OptionalGetWithoutIsPresent")
@Override
protected Iterable<R> getResult(FetchableFluentQuery<R> queryToUse, DataFetchingEnvironment env) {
	ScrollSubrange subrange = buildScrollSubrange(env);
	int limit = subrange.count().orElse(this.defaultSubrange.count().getAsInt());
	ScrollPosition position = subrange.position().orElse(this.defaultSubrange.position().get());
	return queryToUse.limit(limit).scroll(position);
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 7, 2024
@rstoyanchev rstoyanchev self-assigned this Feb 13, 2024
@rstoyanchev rstoyanchev added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 13, 2024
@rstoyanchev
Copy link
Contributor

Thanks for the report. ScrollSubrange.create always uses forward pagination for offset scrolling, switching fro backward to forward if necessary. So we need to apply defaults earlier, before the call to create.

@rstoyanchev rstoyanchev added this to the 1.2.5 milestone Feb 13, 2024
@rstoyanchev
Copy link
Contributor

@mspiess thanks for the report again. There is now a fix available in 1.2.5-SNAPSHOT if you'd like to give that a try.

@mspiess
Copy link
Author

mspiess commented Feb 19, 2024

Hi, @rstoyanchev,
I would like to re-open this issue. @jokuni tried the snapshot and found it didn't fix the issue.
This tracks with my confusion over

Shouldn't that line assert forward to be false?

@jokuni
Copy link

jokuni commented Feb 19, 2024

For example, given following Entity:

Todo

id content
1 Todo 1
2 Todo 2

Associated query:

query {
   todos(last: 1) {
      ...
   }
}

Actual response:

{
   "data": {
      "todos": {
         "edges": [
            {
               "node": {
                  "id": 1,
                  "content": "Todo 1"
               }
            }
         ]
      }
   }
}

Expected response:

{
   "data": {
      "todos": {
         "edges": [
            {
               "node": {
                  "id": 2,
                  "content": "Todo 2"
               }
            }
         ]
      }
   }
}

@rstoyanchev
Copy link
Contributor

Offset Scrolling works by getting results beginning at an offset, and so there is no concept of going back. Instead we we advance the index back and turn backward to forward.

For example, given request for the last 5 relative to 50, we create OffsetScrollPosition with offset 44, count 5, and forward=true. That's the last 5 items before 50. This is why forward=true in all test cases.

On further thought, I don't see a straight forward way to support last N requests without a before cursor as offset scrolling can only return results forward from a specific offset. There was a real improvement made for this issue to apply correctly default ScrollSubrange but that has to have a position and by default it is 20, from the beginning.

Thinking about what could be done, perhaps we could reverse the Sort order if defined so that the first N from the beginning is effectively the last N. What do you think? /cc @mp911de

@rstoyanchev rstoyanchev changed the title Direction is not respected when not providing a cursor using querydsl Default ScrollSubrange is not applied correctly for QueryDSL for backwards scrolling Feb 19, 2024
@rstoyanchev rstoyanchev changed the title Default ScrollSubrange is not applied correctly for QueryDSL for backwards scrolling Default ScrollSubrange is not applied correctly for backwards scrolling in QueryDSL and QBE Feb 19, 2024
@mp911de
Copy link
Member

mp911de commented Feb 20, 2024

Requesting the last n items without providing a cursor could work in the sense of returning the last n items from the end of the result. That would require us to fetch all query results and then return a sublist of result.sublist(result.size() - last, result.size()).

In other words, reversing the sort order (if a sort order is provided), fetching the first n and then reversing the result list would be an optimization.

@mspiess
Copy link
Author

mspiess commented Feb 20, 2024

What about Keyset Scrolling? The issue also applies when a default scroll subrange with a keyset ScrollPosition is set using QuerydslBuilderCustomizer.

@rstoyanchev
Copy link
Contributor

@mspiess thanks for your feedback. First, it's worth mentioning a related update under #916. Second, I've updated how default scrolling is configured under #917.

@rstoyanchev rstoyanchev closed this as not planned Won't fix, can't repro, duplicate, stale Feb 20, 2024
@rstoyanchev rstoyanchev removed this from the 1.2.5 milestone Feb 20, 2024
@rstoyanchev rstoyanchev added the status: superseded Issue is superseded by another label Feb 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: superseded Issue is superseded by another type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants