Class: Gitlab::Pagination::Keyset::ColumnConditionBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/gitlab/pagination/keyset/column_condition_builder.rb

Instance Method Summary collapse

Constructor Details

#initialize(column, value) ⇒ ColumnConditionBuilder

This class builds the WHERE conditions for the keyset pagination library. It produces WHERE conditions for one column at a time.

Requisite 1: Only the last column (columns.last) is non-nullable and distinct. Requisite 2: Only one column is distinct and non-nullable.

Scenario: We want to order by columns named X, Y and Z and build the conditions

       used in the WHERE clause of a pagination query using a set of cursor values.
X is the column definition for a nullable column
Y is the column definition for a non-nullable but not distinct column
Z is the column definition for a distinct, non-nullable column used as a tie breaker.

Then the method is initially invoked with these arguments:
  columns = [ColumnDefinition for X, ColumnDefinition for Y, ColumnDefinition for Z]
  values = { X: x, Y: y, Z: z } => these represent cursor values for pagination
                                   (x could be nil since X is nullable)
  current_conditions is initialized to [] to store the result during the iteration calls
  invoked within the Order#build_where_values method.

The elements of current_conditions are instances of Arel::Nodes and -
 will be concatenated using OR or UNION to be used in the WHERE clause.

Example: Let's say we want to build WHERE clause conditions for
  ORDER BY X DESC NULLS LAST, Y ASC, Z DESC

  Iteration 1:
    columns = [X, Y, Z]
    At the end, current_conditions should be:
      [(Z < z)]

  Iteration 2:
    columns = [X, Y]
    At the end, current_conditions should be:
      [(Y > y) OR (Y = y AND Z < z)]

  Iteration 3:
    columns = [X]
    At the end, current_conditions should be:
      [((X IS NOT NULL AND Y > y) OR (X IS NOT NULL AND Y = y AND Z < z))
        OR
       ((x IS NULL) OR (X IS NULL))]

Parameters:

- columns: instance of ColumnOrderDefinition
- value: cursor value for the column


53
54
55
56
# File 'lib/gitlab/pagination/keyset/column_condition_builder.rb', line 53

def initialize(column, value)
  @column = column
  @value = value
end

Instance Method Details

#where_conditions(current_conditions) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/gitlab/pagination/keyset/column_condition_builder.rb', line 58

def where_conditions(current_conditions)
  return not_nullable_conditions(current_conditions) if column.not_nullable?
  return nulls_first_conditions(current_conditions) if column.nulls_first?

  # Here we are dealing with the case of column_definition.nulls_last?
  # Suppose ORDER BY X DESC NULLS FIRST, Y ASC, Z DESC is the ordering clause
  # and we already have built the conditions for columns Y and Z.
  #
  # We first need a set of conditions to use when x (the value for X) is NULL:
  #   null_conds = [
  #     (x IS NULL AND X IS NULL AND Y<y),
  #     (x IS NULL AND X IS NULL AND Y=y AND Z<z),
  null_conds = current_conditions.map do |conditional|
    Arel::Nodes::And.new([value_is_null, column_is_null, conditional])
  end

  # We then need a set of conditions to use when m has an actual value:
  #   non_null_conds = [
  #     (x IS NOT NULL AND X IS NULL),
  #     (x IS NOT NULL AND X < x)
  #     (x IS NOT NULL AND X = x AND Y > y),
  #     (x IS NOT NULL AND X = x AND Y = y AND Z < z),
  tie_breaking_conds = current_conditions.map do |conditional|
    Arel::Nodes::And.new([column_equals_to_value, conditional])
  end

  non_null_conds = [column_is_null, compare_column_with_value, *tie_breaking_conds].map do |conditional|
    Arel::Nodes::And.new([value_is_not_null, conditional])
  end

  [*null_conds, *non_null_conds]
end