Commit 1cd17020 authored by Sean McGivern's avatar Sean McGivern

Merge branch '63778-graphql-keyset-is-null' into 'master'

Graphql: Keyset::Connection to support "attribute ASC NULLS LAST"

See merge request gitlab-org/gitlab!18916
parents ab270f88 77a171a4
...@@ -8,10 +8,15 @@ ...@@ -8,10 +8,15 @@
# https://coderwall.com/p/lkcaag/pagination-you-re-probably-doing-it-wrong # https://coderwall.com/p/lkcaag/pagination-you-re-probably-doing-it-wrong
# #
# It currently supports sorting on two columns, but the last column must # It currently supports sorting on two columns, but the last column must
# be the primary key. For example # be the primary key. If it's not already included, an order on the
# primary key will be added automatically, like `order(id: :desc)`
# #
# Issue.order(created_at: :asc).order(:id) # Issue.order(created_at: :asc).order(:id)
# Issue.order(due_date: :asc).order(:id) # Issue.order(due_date: :asc)
#
# You can also use `Gitlab::Database.nulls_last_order`:
#
# Issue.reorder(::Gitlab::Database.nulls_last_order('due_date', 'DESC'))
# #
# It will tolerate non-attribute ordering, but only attributes determine the cursor. # It will tolerate non-attribute ordering, but only attributes determine the cursor.
# For example, this is legitimate: # For example, this is legitimate:
......
...@@ -5,12 +5,15 @@ module Gitlab ...@@ -5,12 +5,15 @@ module Gitlab
module Connections module Connections
module Keyset module Keyset
class OrderInfo class OrderInfo
attr_reader :attribute_name, :sort_direction
def initialize(order_value) def initialize(order_value)
@order_value = order_value if order_value.is_a?(String)
@attribute_name, @sort_direction = extract_nulls_last_order(order_value)
else
@attribute_name = order_value.expr.name
@sort_direction = order_value.direction
end end
def attribute_name
order_value.expr.name
end end
def operator_for(before_or_after) def operator_for(before_or_after)
...@@ -22,10 +25,10 @@ module Gitlab ...@@ -22,10 +25,10 @@ module Gitlab
end end
end end
# Only allow specific node types. For example ignore String nodes # Only allow specific node types
def self.build_order_list(relation) def self.build_order_list(relation)
order_list = relation.order_values.select do |value| order_list = relation.order_values.select do |value|
value.is_a?(Arel::Nodes::Ascending) || value.is_a?(Arel::Nodes::Descending) supported_order_value?(value)
end end
order_list.map { |info| OrderInfo.new(info) } order_list.map { |info| OrderInfo.new(info) }
...@@ -52,12 +55,21 @@ module Gitlab ...@@ -52,12 +55,21 @@ module Gitlab
end end
end end
def self.supported_order_value?(order_value)
return true if order_value.is_a?(Arel::Nodes::Ascending) || order_value.is_a?(Arel::Nodes::Descending)
return false unless order_value.is_a?(String)
tokens = order_value.downcase.split
tokens.last(2) == %w(nulls last) && tokens.count == 4
end
private private
attr_reader :order_value def extract_nulls_last_order(order_value)
tokens = order_value.downcase.split
def sort_direction [tokens.first, (tokens[1] == 'asc' ? :asc : :desc)]
order_value.direction
end end
end end
end end
......
...@@ -17,6 +17,26 @@ describe Gitlab::Graphql::Connections::Keyset::OrderInfo do ...@@ -17,6 +17,26 @@ describe Gitlab::Graphql::Connections::Keyset::OrderInfo do
expect(order_list.last.operator_for(:after)).to eq '>' expect(order_list.last.operator_for(:after)).to eq '>'
end end
end end
context 'when order contains NULLS LAST' do
let(:relation) { Project.order(Arel.sql('projects.updated_at Asc Nulls Last')).order(:id) }
it 'does not ignore the SQL order' do
expect(order_list.count).to eq 2
expect(order_list.first.attribute_name).to eq 'projects.updated_at'
expect(order_list.first.operator_for(:after)).to eq '>'
expect(order_list.last.attribute_name).to eq 'id'
expect(order_list.last.operator_for(:after)).to eq '>'
end
end
context 'when order contains invalid formatted NULLS LAST ' do
let(:relation) { Project.order(Arel.sql('projects.updated_at created_at Asc Nulls Last')).order(:id) }
it 'ignores the SQL order' do
expect(order_list.count).to eq 1
end
end
end end
describe '#validate_ordering' do describe '#validate_ordering' do
......
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