Skip to content

Instantly share code, notes, and snippets.

@galiminus
Created October 24, 2019 10:14
Show Gist options
  • Save galiminus/0eaf536e6d607fa8b19f39a57accf84a to your computer and use it in GitHub Desktop.
Save galiminus/0eaf536e6d607fa8b19f39a57accf84a to your computer and use it in GitHub Desktop.
Faster next page check for GraphQL-ruby
# frozen_string_literal: true
module GraphQL
module Relay
# A connection implementation to expose SQL collection objects.
# It works for:
# - `ActiveRecord::Relation`
# - `Sequel::Dataset`
class SmartRelationConnection < BaseConnection
def cursor_from_node(item)
item_index = paged_nodes.index(item)
if item_index.nil?
raise("Can't generate cursor, item not found in connection: #{item}")
else
offset = item_index + 1 + ((paged_nodes_offset || 0) - (relation_offset(sliced_nodes) || 0))
if after
offset += offset_from_cursor(after)
end
encode(offset.to_s)
end
end
def has_next_page
if first
@has_next_page
else
false
end
end
def first
return @first if defined? @first
@first = get_limited_arg(:first)
@first = max_page_size if !first || (@first && max_page_size && @first > max_page_size)
@first
end
private
# apply first limit results
# @return [Array]
def paged_nodes
return @paged_nodes if defined? @paged_nodes
items = sliced_nodes.limit(first + 1)
items_array = items.to_a
if items_array.count > first
@has_next_page = true
items_array = items_array[0..-2]
else
@has_next_page = false
end
# Store this here so we can convert the relation to an Array
# (this avoids an extra DB call on Sequel)
@paged_nodes_offset = relation_offset(items)
@paged_nodes = items_array
end
def paged_nodes_offset
paged_nodes && @paged_nodes_offset
end
def relation_offset(relation)
if relation.respond_to?(:offset_value)
relation.offset_value
else
relation.opts[:offset]
end
end
# Apply cursors to edges
def sliced_nodes
return @sliced_nodes if defined? @sliced_nodes
@sliced_nodes = nodes
if after
offset = (relation_offset(@sliced_nodes) || 0) + offset_from_cursor(after)
@sliced_nodes = @sliced_nodes.offset(offset)
end
@sliced_nodes
end
def offset_from_cursor(cursor)
decode(cursor).to_i
end
end
if defined?(ActiveRecord::Relation)
BaseConnection.register_connection_implementation(ActiveRecord::Relation, SmartRelationConnection)
end
if defined?(Sequel::Dataset)
BaseConnection.register_connection_implementation(Sequel::Dataset, SmartRelationConnection)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment