Commit db2c101c authored by Brett Walker's avatar Brett Walker

Small refactor of keyset pagination

in prep for MR https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24011
parent a90a79ff
...@@ -6,8 +6,8 @@ module Gitlab ...@@ -6,8 +6,8 @@ module Gitlab
module Keyset module Keyset
module Conditions module Conditions
class BaseCondition class BaseCondition
def initialize(arel_table, names, values, operator, before_or_after) def initialize(arel_table, order_list, values, operators, before_or_after)
@arel_table, @names, @values, @operator, @before_or_after = arel_table, names, values, operator, before_or_after @arel_table, @order_list, @values, @operators, @before_or_after = arel_table, order_list, values, operators, before_or_after
end end
def build def build
...@@ -16,7 +16,7 @@ module Gitlab ...@@ -16,7 +16,7 @@ module Gitlab
private private
attr_reader :arel_table, :names, :values, :operator, :before_or_after attr_reader :arel_table, :order_list, :values, :operators, :before_or_after
def table_condition(attribute, value, operator) def table_condition(attribute, value, operator)
case operator case operator
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
# If there is only one order field, we can assume it # If there is only one order field, we can assume it
# does not contain NULLs, and don't need additional # does not contain NULLs, and don't need additional
# conditions # conditions
unless names.count == 1 unless order_list.count == 1
conditions << [second_attribute_condition, final_condition] conditions << [second_attribute_condition, final_condition]
end end
...@@ -24,7 +24,7 @@ module Gitlab ...@@ -24,7 +24,7 @@ module Gitlab
# ex: "(relative_position > 23)" # ex: "(relative_position > 23)"
def first_attribute_condition def first_attribute_condition
<<~SQL <<~SQL
(#{table_condition(names.first, values.first, operator.first).to_sql}) (#{table_condition(order_list.first, values.first, operators.first).to_sql})
SQL SQL
end end
...@@ -32,9 +32,9 @@ module Gitlab ...@@ -32,9 +32,9 @@ module Gitlab
def second_attribute_condition def second_attribute_condition
condition = <<~SQL condition = <<~SQL
OR ( OR (
#{table_condition(names.first, values.first, '=').to_sql} #{table_condition(order_list.first, values.first, '=').to_sql}
AND AND
#{table_condition(names[1], values[1], operator[1]).to_sql} #{table_condition(order_list[1], values[1], operators[1]).to_sql}
) )
SQL SQL
...@@ -45,7 +45,7 @@ module Gitlab ...@@ -45,7 +45,7 @@ module Gitlab
def final_condition def final_condition
if before_or_after == :after if before_or_after == :after
<<~SQL <<~SQL
OR (#{table_condition(names.first, nil, 'is_null').to_sql}) OR (#{table_condition(order_list.first, nil, 'is_null').to_sql})
SQL SQL
end end
end end
......
...@@ -16,9 +16,9 @@ module Gitlab ...@@ -16,9 +16,9 @@ module Gitlab
def first_attribute_condition def first_attribute_condition
condition = <<~SQL condition = <<~SQL
( (
#{table_condition(names.first, nil, 'is_null').to_sql} #{table_condition(order_list.first, nil, 'is_null').to_sql}
AND AND
#{table_condition(names[1], values[1], operator[1]).to_sql} #{table_condition(order_list[1], values[1], operators[1]).to_sql}
) )
SQL SQL
...@@ -29,7 +29,7 @@ module Gitlab ...@@ -29,7 +29,7 @@ module Gitlab
def final_condition def final_condition
if before_or_after == :before if before_or_after == :before
<<~SQL <<~SQL
OR (#{table_condition(names.first, nil, 'is_not_null').to_sql}) OR (#{table_condition(order_list.first, nil, 'is_not_null').to_sql})
SQL SQL
end end
end end
......
...@@ -8,12 +8,12 @@ module Gitlab ...@@ -8,12 +8,12 @@ module Gitlab
attr_reader :attribute_name, :sort_direction attr_reader :attribute_name, :sort_direction
def initialize(order_value) def initialize(order_value)
if order_value.is_a?(String) @attribute_name, @sort_direction =
@attribute_name, @sort_direction = extract_nulls_last_order(order_value) if order_value.is_a?(String)
else extract_nulls_last_order(order_value)
@attribute_name = order_value.expr.name else
@sort_direction = order_value.direction extract_attribute_values(order_value)
end end
end end
def operator_for(before_or_after) def operator_for(before_or_after)
...@@ -71,6 +71,10 @@ module Gitlab ...@@ -71,6 +71,10 @@ module Gitlab
[tokens.first, (tokens[1] == 'asc' ? :asc : :desc)] [tokens.first, (tokens[1] == 'asc' ? :asc : :desc)]
end end
def extract_attribute_values(order_value)
[order_value.expr.name, order_value.direction]
end
end end
end end
end end
......
...@@ -4,10 +4,15 @@ require 'spec_helper' ...@@ -4,10 +4,15 @@ require 'spec_helper'
describe Gitlab::Graphql::Connections::Keyset::Conditions::NotNullCondition do describe Gitlab::Graphql::Connections::Keyset::Conditions::NotNullCondition do
describe '#build' do describe '#build' do
let(:condition) { described_class.new(Issue.arel_table, %w(relative_position id), [1500, 500], ['>', '>'], before_or_after) } let(:operators) { ['>', '>'] }
let(:before_or_after) { :after }
let(:condition) { described_class.new(arel_table, order_list, values, operators, before_or_after) }
context 'when there is only one ordering field' do context 'when there is only one ordering field' do
let(:condition) { described_class.new(Issue.arel_table, ['id'], [500], ['>'], :after) } let(:arel_table) { Issue.arel_table }
let(:order_list) { ['id'] }
let(:values) { [500] }
let(:operators) { ['>'] }
it 'generates a single condition sql' do it 'generates a single condition sql' do
expected_sql = <<~SQL expected_sql = <<~SQL
...@@ -18,38 +23,42 @@ describe Gitlab::Graphql::Connections::Keyset::Conditions::NotNullCondition do ...@@ -18,38 +23,42 @@ describe Gitlab::Graphql::Connections::Keyset::Conditions::NotNullCondition do
end end
end end
context 'when :after' do context 'when ordering by a column attribute' do
let(:before_or_after) { :after } let(:arel_table) { Issue.arel_table }
let(:order_list) { %w(relative_position id) }
let(:values) { [1500, 500] }
it 'generates :after sql' do context 'when :after' do
expected_sql = <<~SQL it 'generates :after sql' do
("issues"."relative_position" > 1500) expected_sql = <<~SQL
OR ( ("issues"."relative_position" > 1500)
"issues"."relative_position" = 1500 OR (
AND "issues"."relative_position" = 1500
"issues"."id" > 500 AND
) "issues"."id" > 500
OR ("issues"."relative_position" IS NULL) )
SQL OR ("issues"."relative_position" IS NULL)
SQL
expect(condition.build.squish).to eq expected_sql.squish expect(condition.build.squish).to eq expected_sql.squish
end
end end
end
context 'when :before' do context 'when :before' do
let(:before_or_after) { :before } let(:before_or_after) { :before }
it 'generates :before sql' do it 'generates :before sql' do
expected_sql = <<~SQL expected_sql = <<~SQL
("issues"."relative_position" > 1500) ("issues"."relative_position" > 1500)
OR ( OR (
"issues"."relative_position" = 1500 "issues"."relative_position" = 1500
AND AND
"issues"."id" > 500 "issues"."id" > 500
) )
SQL SQL
expect(condition.build.squish).to eq expected_sql.squish expect(condition.build.squish).to eq expected_sql.squish
end
end end
end end
end end
......
...@@ -4,38 +4,44 @@ require 'spec_helper' ...@@ -4,38 +4,44 @@ require 'spec_helper'
describe Gitlab::Graphql::Connections::Keyset::Conditions::NullCondition do describe Gitlab::Graphql::Connections::Keyset::Conditions::NullCondition do
describe '#build' do describe '#build' do
let(:condition) { described_class.new(Issue.arel_table, %w(relative_position id), [nil, 500], [nil, '>'], before_or_after) } let(:values) { [nil, 500] }
let(:operators) { [nil, '>'] }
context 'when :after' do let(:before_or_after) { :after }
let(:before_or_after) { :after } let(:condition) { described_class.new(arel_table, order_list, values, operators, before_or_after) }
it 'generates sql' do context 'when ordering by a column attribute' do
expected_sql = <<~SQL let(:arel_table) { Issue.arel_table }
( let(:order_list) { %w(relative_position id) }
"issues"."relative_position" IS NULL
AND context 'when :after' do
"issues"."id" > 500 it 'generates sql' do
) expected_sql = <<~SQL
SQL
expect(condition.build.squish).to eq expected_sql.squish
end
end
context 'when :before' do
let(:before_or_after) { :before }
it 'generates :before sql' do
expected_sql = <<~SQL
( (
"issues"."relative_position" IS NULL "issues"."relative_position" IS NULL
AND AND
"issues"."id" > 500 "issues"."id" > 500
) )
OR ("issues"."relative_position" IS NOT NULL) SQL
SQL
expect(condition.build.squish).to eq expected_sql.squish
end
end
expect(condition.build.squish).to eq expected_sql.squish context 'when :before' do
let(:before_or_after) { :before }
it 'generates :before sql' do
expected_sql = <<~SQL
(
"issues"."relative_position" IS NULL
AND
"issues"."id" > 500
)
OR ("issues"."relative_position" IS NOT NULL)
SQL
expect(condition.build.squish).to eq expected_sql.squish
end
end end
end end
end end
......
...@@ -13,6 +13,7 @@ describe Gitlab::Graphql::Connections::Keyset::QueryBuilder do ...@@ -13,6 +13,7 @@ describe Gitlab::Graphql::Connections::Keyset::QueryBuilder do
describe '#conditions' do describe '#conditions' do
let(:relation) { Issue.order(relative_position: :desc).order(:id) } let(:relation) { Issue.order(relative_position: :desc).order(:id) }
let(:order_list) { Gitlab::Graphql::Connections::Keyset::OrderInfo.build_order_list(relation) } let(:order_list) { Gitlab::Graphql::Connections::Keyset::OrderInfo.build_order_list(relation) }
let(:arel_table) { Issue.arel_table }
let(:builder) { described_class.new(arel_table, order_list, decoded_cursor, before_or_after) } let(:builder) { described_class.new(arel_table, order_list, decoded_cursor, before_or_after) }
let(:before_or_after) { :after } let(:before_or_after) { :after }
...@@ -101,8 +102,4 @@ describe Gitlab::Graphql::Connections::Keyset::QueryBuilder do ...@@ -101,8 +102,4 @@ describe Gitlab::Graphql::Connections::Keyset::QueryBuilder do
end end
end end
end end
def arel_table
Issue.arel_table
end
end end
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