ash icon indicating copy to clipboard operation
ash copied to clipboard

Analog for more? field for Keyset pagination, but for backward direction

Open dolfinus opened this issue 3 years ago • 5 comments

Is your feature request related to a problem? Please describe. GraphQL Relay specification has 2 fields to describe cursor position: hasNextPage and hasPreviousPage. First returns true if there is a next page, second returns true if there is a previous page.

Ash.Page.Keyset have implementation only for the first field (it is called more?), but not for the second one.

Describe the solution you'd like Add another field to Keyset record which will behave in a similar way with more?, but for the backward paginagion direction.

Describe alternatives you've considered

%Ash.Page.Keyset{count: count, after: after_cursor, before: before_cursor} = page

has_previous_page = count > 0 and (not is_nil(after_cursor) or not is_nil(before_cursor))

But it is possible that it's working only because of some issue (like #333), or can break on the some set of conditions

Express the feature either with a change to resource syntax, or with a change to the resource interface

For example

  %Ash.Page.Keyset{more?: more?, prev?: prev?}

Additional context Discussion of https://github.com/ash-project/ash_graphql/pull/36

dolfinus avatar May 29 '22 20:05 dolfinus

Actually, I think you are correct. Given that after and before doesn't include the record itself, your method of determining has_previous_page is correct. We can just add that into the Keyset struct.

zachdaniel avatar May 30 '22 19:05 zachdaniel

count > 0 is not necessarily enough though, because count is not always selected.

zachdaniel avatar May 30 '22 20:05 zachdaniel

However, it will only be "wrong" in the very rare case that the thing you are asking for has been removed i.e after: 1 when 1 has been deleted and you aren't selecting the count.

zachdaniel avatar May 30 '22 20:05 zachdaniel

with #255 issue resolved actually we can detect this case, because we will look up the record before doing the rest of the request. If it isn't there then we'll know and be able to say that there isn't a previous page.

zachdaniel avatar May 30 '22 20:05 zachdaniel

Alright, so my plan is to fix this after #255 and to make "fetch" style (where we look up the record by its primary key keyset and load any relevant aggregates/calculations) the only method.

zachdaniel avatar May 30 '22 20:05 zachdaniel

Alright, I think perhaps this is all that is left to get https://github.com/ash-project/ash_graphql/pull/36 going?

zachdaniel avatar Sep 11 '22 20:09 zachdaniel

Actually, I didn't get how prev? could be implemented with a https://github.com/ash-project/ash/commit/64b3312cb94869d3ef9b25a11658e056c7191380

dolfinus avatar Sep 11 '22 21:09 dolfinus

🤔 I've just realized two things.

  1. 64b3312 falls apart when there is no record with the provided primary key. I'm not sure if that is a kryptonite for that solution or not. What we would have to do in that case is restart the pagination, which sounds like a pretty bad experience. Another flaw with that solution is that if you are sorting on something and that value changes, then you'd jump around in the page. I think that means that the "fetch" style of keyset pagination actually is unacceptable, and that we instead need to go back to the old way. I have an idea for how we can support "complex" fields the old way. I'll undo the previous commit and go back to the previous way.

  2. As for prev? I think you may be right. I think there is no way to determine if there is a previous page without running a second query.

zachdaniel avatar Sep 11 '22 22:09 zachdaniel

i.e flip before and after and say limit: 1. We can do that only on request via a pagination option, i.e page: [populate_prev_page?: true], and then the graphql extension can do that only when that information is requested.

zachdaniel avatar Sep 11 '22 22:09 zachdaniel

I think for now, the relay specification required for ash_graphql allows for us to just say false when showing hasNextPage combined with before and hasPrevPage when combined with after, meaning we actually have all that we need (for now).

zachdaniel avatar Sep 14 '22 18:09 zachdaniel