class ParamsReady::Pagination::KeysetPagination

Public Instance Methods

after_page_value(keyset) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 130
def after_page_value(keyset)
  keyset ||= {}
  { limit: limit, direction: :aft, keyset: keyset }
end
before_page_value(keyset) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 125
def before_page_value(keyset)
  keyset ||= {}
  { limit: limit, direction: :bfr, keyset: keyset }
end
cursor() click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 170
def cursor
  return nil unless is_definite?

  keyset = self[:keyset]
  keyset.names.keys.map do |column_name|
    keyset[column_name].unwrap
  end
end
cursor_columns() click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 151
def cursor_columns
  self[:keyset].names.keys
end
cursor_columns_arel(ordering, arel_table, context, columns: cursor_columns) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 155
def cursor_columns_arel(ordering, arel_table, context, columns: cursor_columns)
  columns.map do |name|
    column = ordering.definition.columns[name]
    column.attribute(name, arel_table, context)
  end
end
cursor_predicates(direction, keyset, ordering, arel_table, context) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 112
def cursor_predicates(direction, keyset, ordering, arel_table, context)
  direction = Direction.instance(direction)
  direction.cursor_predicates(keyset, ordering, arel_table, context)
end
direction() click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 143
def direction
  self[:direction].unwrap
end
exists_predicate(subselect, ordering, arel_table) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 70
def exists_predicate(subselect, ordering, arel_table)
  table_alias = self.table_alias(arel_table)
  aliased = arel_table.alias(table_alias)
  select_manager = Arel::SelectManager.new.from(subselect.as(table_alias))
  related = related_clause(arel_table, aliased, ordering.definition.primary_keys)
  select_manager.where(related).project('1').exists
end
first_page_value() click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 117
def first_page_value
  { limit: limit, direction: :aft, keyset: {} }
end
keyset() click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 147
def keyset
  self[:keyset].unwrap
end
keyset_query(query, limit, direction, keyset, ordering, arel_table, context) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 17
def keyset_query(query, limit, direction, keyset, ordering, arel_table, context)
  cursor, grouping = cursor_predicates(direction, keyset, ordering, arel_table, context)
  cte = cursor.cte_for_query(query, arel_table) unless cursor.nil?
  query = query.where(grouping) unless grouping.nil?

  query = query.with(cte) unless cte.nil?
  ordered = query.order(ordering_arel(direction, ordering, arel_table, context))
  ordered.take(limit)
end
keysets_for_relation(relation, limit, direction, keyset, ordering, context) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 27
def keysets_for_relation(relation, limit, direction, keyset, ordering, context)
  arel_table = relation.arel_table

  cursor, predicates = cursor_predicates(direction, keyset, ordering, arel_table, context)
  full_query = relation.where(predicates)
                       .reorder(ordering_arel(direction, ordering, arel_table, context))
                       .limit(limit)
                       .select(*cursor_columns_arel(ordering, arel_table, context))
  full_query = Arel::Nodes::SqlLiteral.new(full_query.to_sql)
  with_cte(relation, full_query, cursor)
end
last_page_value() click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 121
def last_page_value
  { limit: limit, direction: :bfr, keyset: {} }
end
limit() click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 135
def limit
  self[:limit].unwrap
end
limit_key() click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 139
def limit_key
  :limit
end
ordering_arel(direction, ordering, arel_table, context) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 107
def ordering_arel(direction, ordering, arel_table, context)
  inverted = Direction.instance(direction).invert_ordering?
  ordering.to_arel(arel_table, context: context, inverted: inverted)
end
paginate_query(query, ordering, arel_table, context) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 55
def paginate_query(query, ordering, arel_table, context)
  cursor, predicates = cursor_predicates(direction, keyset, ordering, arel_table, context)
  cte = cursor.cte_for_query(query, arel_table) unless cursor.nil?
  subquery = query.deep_dup
  subquery = subquery.where(predicates) unless predicates.nil?
  subquery = subquery.with(cte) unless cte.nil?

  subselect = subquery.order(ordering_arel(direction, ordering, arel_table, context))
    .take(limit)
    .project(primary_keys_arel(ordering, arel_table, context))

  exists = exists_predicate(subselect, ordering, arel_table)
  query.where(exists)
end
paginate_relation(relation, ordering, context) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 39
def paginate_relation(relation, ordering, context)
  arel_table = relation.arel_table
  cursor, predicates = cursor_predicates(direction, keyset, ordering, arel_table, context)

  subselect = relation.where(predicates)
                      .reorder(ordering_arel(direction, ordering, arel_table, context))
                      .limit(limit)
                      .select(primary_keys_arel(ordering, arel_table, context))

  subselect_sql = Arel::Nodes::SqlLiteral.new(subselect.to_sql)
  subselect_sql = with_cte_grouped(relation, subselect_sql, cursor)

  exists = exists_predicate(subselect_sql, ordering, arel_table)
  relation.where(exists)
end
primary_keys_arel(ordering, arel_table, context) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 162
def primary_keys_arel(ordering, arel_table, context)
  columns = cursor_columns.lazy.select do |name|
    ordering.definition.primary_keys.member? name
  end

  cursor_columns_arel(ordering, arel_table, context, columns: columns).force
end
select_keysets(query, limit, direction, keyset, ordering, arel_table, context) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 12
def select_keysets(query, limit, direction, keyset, ordering, arel_table, context)
  query = keyset_query(query, limit, direction, keyset, ordering, arel_table, context)
  query.project(*cursor_columns_arel(ordering, arel_table, context))
end
table_alias(arel_table) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 103
def table_alias(arel_table)
  Helpers::ArelBuilder.safe_name "#{arel_table.name}_#{cursor_columns.join('_')}"
end
with_cte(relation, select, cursor) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 94
def with_cte(relation, select, cursor)
  return select if cursor.nil?

  cte = cursor.cte_for_relation(relation)
  return select if cte.nil?

  Arel::Nodes::SqlLiteral.new([cte.to_sql, select].join(' '))
end
with_cte_grouped(relation, select, cursor) click to toggle source
# File lib/params_ready/pagination/keyset_pagination.rb, line 89
def with_cte_grouped(relation, select, cursor)
  with_cte = with_cte(relation, select, cursor)
  Arel::Nodes::Grouping.new(with_cte)
end