-
-
Notifications
You must be signed in to change notification settings - Fork 438
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
Use an actual cursor for relay style pagination #311
Comments
Hi @spawnia I found the same issue when I was working on #368. I came up with numbers of ideas a while ago. for the relation directives, it's rather easy to give a generic solution something like this:
for
(or instead of the anyway, I think both of above cases can be done with just a little tweaking of the existing code. |
What is the best way to integrate lampager and lighthouse? |
Create an open source package or add a PR to Lighthouse. You will have to put in some effort, but doing it in the open means you get valuable community feedback and contributions. |
What if I have data that is not ordered in a predictable way? Like a table of rows sorted by a column that changes over time. It sounds like neither offset-based or cursor-based options would work here. |
Thinking about this some more, what if the paginator's cursor was a base64-encoded array of the IDs of results already returned (e.g. 1, 2, 3), then when the user requests the next page with the cursor, the paginator can essentially make the query Any thoughts on potential pitfalls with this? |
@dbbk that would make the client side state handling much harder, it would have to keep track of all pages that were seen. If the goal is to have the clients see every possibly entry over a somewhat extended period of time, that might be a correct approach. I doubt it is a common need and probably way to complicated for most use cases. |
Wouldn't the cursor be able to generate itself completely server side? For example, first query of 10 results it is just the array of those 10 IDs. Then, on the next query for the second page, it takes the existing cursor and appends the new set of 10 IDs so now it has 20. So it shouldn't theoretically require any client side computation? |
That said, there are some merits to your idea. The issues might be solvable. I think the best way forward would be for you to try your hands on an implementation. If it is simple enough and works for the general case, we can further discuss inclusion or integration with Lighthouse. |
Sounds good! I'll give it a go, thanks for the tips. I don't think there is currently a plug-in way to provide new paginator types to Lighthouse, so I'll have to fork, right? |
Cursor pagination has landed in Laravel |
We have to keep in mind that cursor pagination in Laravel is subject to some limitations which our current pseudo-cursor implementation does not have. This means that we cannot simply switch out the implementation without making breaking changes. I think we should try and make the upgrade path as smooth as possible, preferrably not coupling the change to a major release of Lighthouse. We could offer the Laravel native actual cursor pagination as an opt-in configuration option, and make it the default in the next major version of Lighthouse. |
@spawnia I'm interested in taking up this work as I have a new project with a requirement for real cursors. Could you expand a bit on what limitations I'd need to look out for? |
For anyone stumbling across the same I just wrote a directive which I think works as far as I can see: It can even navigate using "before" and "after". It's mostly a copy of the $first = $args['first'] ?? $this->defaultCount();
$last = $args['last'] ?? $this->defaultCount();
$after = rescue(
fn() => Cursor::fromEncoded($args['after'] ?? ''),
null,
false
);
$before = rescue(
fn() => Cursor::fromEncoded($args['before'] ?? ''),
null,
false
);
...
// Retrieve result
if ($before) {
$paginator = $builder->cursorPaginate(
$last,
['*'],
'cursor',
new Cursor(
collect($before->toArray())
->forget('__pointsToNextItems')
->toArray(),
false
)
);
} else {
$paginator = $builder->cursorPaginate($first, ['*'], 'cursor', $after);
} Also, it returns the builder itself: return [
'builder' => $builder,
'paginator' => $paginator,
]; which lets me later lazily retrieve the total count: 'total' => fn () => $builder->count(), Unfortunately, I am limited in time right now to work on a proper PR but it would be really great to see it incorporated in the library itself. @dbbk maybe you could give it a try? |
While the implementation for Relay/Connection pagination is compliant with the Relay spec, it does not actually implement cursor based pagination but rather offset-based pagination in disguise. Read about the difference here https://www.sitepoint.com/paginating-real-time-data-cursor-based-pagination/
This is a hard problem to solve, since there is no easy way to reliable get the next page after a cursor, unless we are basing the sort order on an ordered index.
https://stackoverflow.com/questions/26188519/handling-paging-with-changing-sort-orders
Since i do not think we can find a generalized solution that fits all use cases, we might
limit the scope of Lighthouse to providing just a basic API for the user to implement their own, cursor-based pagination resolver. We can take care of providing helpers for encoding/decoding the cursor, but apart from that, the use cases can be so vastly different we can not possibly cover them all.
Maybe something like https://github.com/lampager/lampager-laravel might provide us with a nice framework to build cursor pagination.
The text was updated successfully, but these errors were encountered: