Commit 28f1221c authored by Eulyeon Ko's avatar Eulyeon Ko Committed by Adam Hegyi

Use keyset pagination for fetching board issues [RUN ALL RSPEC] [RUN AS-IF-FOSS]

parent 7bd32bfe
......@@ -16,7 +16,7 @@ module Resolvers
filter_params = item_filters(args[:filters]).merge(board_id: list.board.id, id: list.id)
service = ::Boards::Issues::ListService.new(list.board.resource_parent, context[:current_user], filter_params)
offset_pagination(service.execute)
service.execute
end
# https://gitlab.com/gitlab-org/gitlab/-/issues/235681
......
......@@ -332,12 +332,15 @@ module Issuable
# When using CTE make sure to select the same columns that are on the group_by clause.
# This prevents errors when ignored columns are present in the database.
issuable_columns = with_cte ? issue_grouping_columns(use_cte: with_cte) : "#{table_name}.*"
group_columns = issue_grouping_columns(use_cte: with_cte) + ["highest_priorities.label_priority"]
extra_select_columns.unshift("(#{highest_priority}) AS highest_priority")
extra_select_columns.unshift("highest_priorities.label_priority as highest_priority")
select(issuable_columns)
.select(extra_select_columns)
.group(issue_grouping_columns(use_cte: with_cte))
.from("#{table_name}")
.joins("JOIN LATERAL(#{highest_priority}) as highest_priorities ON TRUE")
.group(group_columns)
.reorder(Gitlab::Database.nulls_last_order('highest_priority', direction))
end
......@@ -384,7 +387,7 @@ module Issuable
if use_cte
attribute_names.map { |attr| arel_table[attr.to_sym] }
else
arel_table[:id]
[arel_table[:id]]
end
end
......
......@@ -46,7 +46,7 @@ module Sortable
private
def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
query = Label.select(LabelPriority.arel_table[:priority].minimum)
query = Label.select(LabelPriority.arel_table[:priority].minimum.as('label_priority'))
.left_join_priorities
.joins(:label_links)
.where("label_priorities.project_id = #{project_column}")
......
......@@ -268,10 +268,41 @@ class Issue < ApplicationRecord
# `with_cte` argument allows sorting when using CTE queries and prevents
# errors in postgres when using CTE search optimisation
def self.order_by_position_and_priority(with_cte: false)
order = Gitlab::Pagination::Keyset::Order.build([column_order_relative_position, column_order_highest_priority, column_order_id_desc])
order_labels_priority(with_cte: with_cte)
.reorder(Gitlab::Database.nulls_last_order('relative_position', 'ASC'),
Gitlab::Database.nulls_last_order('highest_priority', 'ASC'),
"id DESC")
.reorder(order)
end
def self.column_order_relative_position
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'relative_position',
column_expression: arel_table[:relative_position],
order_expression: Gitlab::Database.nulls_last_order('issues.relative_position', 'ASC'),
reversed_order_expression: Gitlab::Database.nulls_last_order('issues.relative_position', 'DESC'),
order_direction: :asc,
nullable: :nulls_last,
distinct: false
)
end
def self.column_order_highest_priority
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'highest_priority',
column_expression: Arel.sql('highest_priorities.label_priority'),
order_expression: Gitlab::Database.nulls_last_order('highest_priorities.label_priority', 'ASC'),
reversed_order_expression: Gitlab::Database.nulls_last_order('highest_priorities.label_priority', 'DESC'),
order_direction: :asc,
nullable: :nulls_last,
distinct: false
)
end
def self.column_order_id_desc
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: arel_table[:id].desc
)
end
# Temporary disable moving null elements because of performance problems
......
......@@ -4,11 +4,42 @@ module Gitlab
module Graphql
module Pagination
module Keyset
# https://gitlab.com/gitlab-org/gitlab/-/issues/334973
# Use the generic keyset implementation if the given ActiveRecord scope supports it.
# Note: this module is temporary, at some point it will be merged with Keyset::Connection
module GenericKeysetPagination
extend ActiveSupport::Concern
# rubocop: disable Naming/PredicateName
# rubocop: disable CodeReuse/ActiveRecord
def has_next_page
return super unless Gitlab::Pagination::Keyset::Order.keyset_aware?(items)
strong_memoize(:generic_keyset_pagination_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
sliced_nodes.limit(1).offset(limit_value).exists?
# replacing relation count
# relation_count(set_limit(sliced_nodes, limit_value + 1)) == limit_value + 1
end
else
false
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def ordered_items
raise ArgumentError, 'Relation must have a primary key' unless items.primary_key.present?
......
......@@ -355,6 +355,10 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
context 'when primary key is not in original order' do
let(:nodes) { Project.order(last_repository_check_at: :desc) }
before do
stub_feature_flags(new_graphql_keyset_pagination: false)
end
it 'is added to end' do
sliced = subject.sliced_nodes
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment