Class: ElasticGraph::GraphQL::Resolvers::RelayConnection::PageInfo

Inherits:
Object
  • Object
show all
Defined in:
lib/elastic_graph/graphql/resolvers/relay_connection/page_info.rb

Overview

Provides the ‘PageInfo` field values required by the relay spec.

The relay connections spec defines an algorithm behind ‘hasPreviousPage` and `hasNextPage`: facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo.Fields

However, it has a couple bugs as currently written (github.com/facebook/relay/issues/2787), so we have implemented our own algorithm instead. It would be nice to calculate ‘hasPreviousPage` and `hasNextPage` on-demand in a resolver, so we do not spend any effort on it if the client has not requested those fields, but it is quite hard to calculate them after the fact: we need to know whether we removed any leading or trailing items while processing the list to accurately answer the question, “do we have a page before or after the one we are returning?”.

Note: it’s not clear what values ‘hasPreviousPage` and `hasNextPage` should have when we are returning a blank page (the client isn’t being returned any cursors to continue paginating from!). This logic, as written, will normally cause both fields to be ‘true` (our request of `size: size + 1` will get us a list of 1 document, which will then be removed, causing `items.first` and `items.last` to both change to `nil`). However, if the datastore returns an empty list to us than `false` will be returned for one or both fields, based on the presence or absence of the `before`/`after` cursors in the pagination arguments. Regardless, given that it’s not clear what the correct value is, we are just doing the least-effort thing and not putting any special handling for this case in place.

Instance Method Summary collapse

Instance Method Details

#end_cursorObject



49
50
51
# File 'lib/elastic_graph/graphql/resolvers/relay_connection/page_info.rb', line 49

def end_cursor
  edges.last&.cursor
end

#has_next_pageObject



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/elastic_graph/graphql/resolvers/relay_connection/page_info.rb', line 66

def has_next_page
  # If we dropped the last node during truncation then it means we removed some trailing docs, indicating a next page.
  return true if edges.last&.node != before_truncation_nodes.last

  # Nothing exists both before and after the same cursor, and there is therefore no page after that set of results.
  return false if paginator.before == paginator.after

  # If a `before` cursor was passed then there is definitely at least one doc after the page we are
  # returning (the one matching the cursor), assuming the client did not construct a cursor by hand
  # (which we do not support).
  !!paginator.before
end

#has_previous_pageObject



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/elastic_graph/graphql/resolvers/relay_connection/page_info.rb', line 53

def has_previous_page
  # If we dropped the first node during truncation then it means we removed some leading docs, indicating a previous page.
  return true if edges.first&.node != before_truncation_nodes.first

  # Nothing exists both before and after the same cursor, and there is therefore no page before that set of results.
  return false if paginator.before == paginator.after

  # If an `after` cursor was passed then there is definitely at least one doc before the page we are
  # returning (the one matching the cursor), assuming the client did not construct a cursor by hand
  # (which we do not support).
  !!paginator.after
end

#start_cursorObject



45
46
47
# File 'lib/elastic_graph/graphql/resolvers/relay_connection/page_info.rb', line 45

def start_cursor
  edges.first&.cursor
end