diff options
Diffstat (limited to 'lib/gitlab/graphql/pagination/keyset/connection.rb')
-rw-r--r-- | lib/gitlab/graphql/pagination/keyset/connection.rb | 97 |
1 files changed, 26 insertions, 71 deletions
diff --git a/lib/gitlab/graphql/pagination/keyset/connection.rb b/lib/gitlab/graphql/pagination/keyset/connection.rb index 3e119a39e6d..b074c273996 100644 --- a/lib/gitlab/graphql/pagination/keyset/connection.rb +++ b/lib/gitlab/graphql/pagination/keyset/connection.rb @@ -29,7 +29,6 @@ module Gitlab include Gitlab::Utils::StrongMemoize include ::Gitlab::Graphql::ConnectionCollectionMethods prepend ::Gitlab::Graphql::ConnectionRedaction - prepend GenericKeysetPagination # rubocop: disable Naming/PredicateName # https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo.Fields @@ -58,19 +57,13 @@ module Gitlab def has_next_page strong_memoize(:has_next_page) do if before - # If `before` is specified, that points to a specific record, - # even if it's the last one. Since we're asking for `before`, - # then the specific record we're pointing to is in the - # next page true elsif first case sliced_nodes when Array sliced_nodes.size > limit_value else - # If we count the number of requested items plus one (`limit_value + 1`), - # then if we get `limit_value + 1` then we know there is a next page - relation_count(set_limit(sliced_nodes, limit_value + 1)) == limit_value + 1 + sliced_nodes.limit(1).offset(limit_value).exists? # rubocop: disable CodeReuse/ActiveRecord end else false @@ -80,20 +73,15 @@ module Gitlab # rubocop: enable Naming/PredicateName def cursor_for(node) - encoded_json_from_ordering(node) + order = Gitlab::Pagination::Keyset::Order.extract_keyset_order_object(items) + encode(order.cursor_attributes_for_node(node).to_json) end def sliced_nodes - @sliced_nodes ||= - begin - OrderInfo.validate_ordering(ordered_items, order_list) unless loaded?(ordered_items) - - sliced = ordered_items - sliced = slice_nodes(sliced, before, :before) if before.present? - sliced = slice_nodes(sliced, after, :after) if after.present? - - sliced - end + sliced = ordered_items + sliced = slice_nodes(sliced, before, :before) if before.present? + sliced = slice_nodes(sliced, after, :after) if after.present? + sliced end def nodes @@ -104,6 +92,20 @@ module Gitlab @nodes ||= limited_nodes.to_a end + def items + original_items = super + return original_items if Gitlab::Pagination::Keyset::Order.keyset_aware?(original_items) + + strong_memoize(:keyset_pagination_items) do + rebuilt_items_with_keyset_order, success = + Gitlab::Pagination::Keyset::SimpleOrderBuilder.build(original_items) + + raise(Gitlab::Pagination::Keyset::UnsupportedScopeOrder) unless success + + rebuilt_items_with_keyset_order + end + end + private # Apply `first` and `last` to `sliced_nodes` @@ -129,11 +131,11 @@ module Gitlab # rubocop: disable CodeReuse/ActiveRecord def slice_nodes(sliced, encoded_cursor, before_or_after) - decoded_cursor = ordering_from_encoded_json(encoded_cursor) - builder = QueryBuilder.new(arel_table, order_list, decoded_cursor, before_or_after) - ordering = builder.conditions + order = Gitlab::Pagination::Keyset::Order.extract_keyset_order_object(sliced) + order = order.reversed_order if before_or_after == :before - sliced.where(*ordering).where.not(id: decoded_cursor['id']) + decoded_cursor = ordering_from_encoded_json(encoded_cursor) + order.apply_cursor_conditions(sliced, decoded_cursor) end # rubocop: enable CodeReuse/ActiveRecord @@ -157,57 +159,10 @@ module Gitlab raise ArgumentError, 'Relation must have a primary key' end - list = OrderInfo.build_order_list(items) - - if loaded?(items) && !before.present? && !after.present? - @order_list = list.presence || [OrderInfo.new(items.primary_key)] - - # already sorted, or trivially sorted - next items if list.present? || items.size <= 1 - - pkey = items.primary_key.to_sym - next items.sort_by { |item| item[pkey] }.reverse - end - - # ensure there is a primary key ordering - if list&.last&.attribute_name != items.primary_key - items.order(arel_table[items.primary_key].desc) # rubocop: disable CodeReuse/ActiveRecord - else - items - end - end - end - - def order_list - strong_memoize(:order_list) do - OrderInfo.build_order_list(ordered_items) + items end end - def arel_table - items.arel_table - end - - # Storing the current order values in the cursor allows us to - # make an intelligent decision on handling NULL values. - # Otherwise we would either need to fetch the record first, - # or fetch it in the SQL, significantly complicating it. - def encoded_json_from_ordering(node) - ordering = { 'id' => node[:id].to_s } - - order_list.each do |field| - field_name = field.try(:attribute_name) || field - field_value = node[field_name] - ordering[field_name] = if field_value.is_a?(Time) - field_value.to_s(:inspect) - else - field_value.to_s - end - end - - encode(ordering.to_json) - end - def ordering_from_encoded_json(cursor) Gitlab::Json.parse(decode(cursor)) rescue JSON::ParserError |