Class: Gitlab::Pagination::Keyset::ColumnOrderDefinition
- Inherits:
-
Object
- Object
- Gitlab::Pagination::Keyset::ColumnOrderDefinition
- Defined in:
- lib/gitlab/pagination/keyset/column_order_definition.rb
Overview
This class stores information for one column (or SQL expression) which can be used in an ORDER BY SQL clasue. The goal of this class is to encapsulate all the metadata in one place which are needed to make keyset pagination work in a generalized way.
Arguments
**order expression** (Arel::Nodes::Node | String)
The actual SQL expression for the ORDER BY clause.
Examples:
# Arel column order definition
Project.arel_table[:id].asc # ORDER BY projects.id ASC
# Arel expression, calculated order definition
Arel::Nodes::NamedFunction.new("COALESCE", [Project.arel_table[:issue_count].asc, 0]).asc # ORDER BY COALESCE(projects.issue_count, 0)
# Another Arel expression
Arel::Nodes::Multiplication(Issue.arel_table[:weight], Issue.arel_table[:time_spent]).desc
# Raw string order definition
'issues.type DESC NULLS LAST'
column_expression (Arel::Nodes::Node | String)
Expression for the database column or an expression. This value will be used with logical operations (>, <, =, !=) when building the database query for the next page.
Examples:
# Arel column reference
Issue.arel_table[:title]
# Calculated value
Arel::Nodes::Multiplication(Issue.arel_table[:weight], Issue.arel_table[:time_spent])
attribute_name (String | Symbol)
An attribute on the loaded ActiveRecord model where the value can be obtained.
Examples:
# Simple attribute definition
attribute_name = :title
# Later on this attribute will be used like this:
my_record = Issue.find(x)
value = my_record[attribute_name] # reads data from the title column
# Calculated value based on an Arel or raw SQL expression
attribute_name = :lowercase_title
# `lowercase_title` is not is not a table column therefore we need to make sure it's available in the `SELECT` clause
my_record = Issue.select(:id, 'LOWER(title) as lowercase_title').last
value = my_record[:lowercase_title]
distinct
Boolean value.
Tells us whether the database column contains only distinct values. If the column is covered by a unique index then set to true.
nullable (:not_nullable | :nulls_last | :nulls_first)
Tells us whether the database column is nullable or not. This information can be obtained from the DB schema.
If the column is not nullable, set this attribute to :not_nullable.
If the column is nullable, then additional information is needed. Based on the ordering, the null values will show up at the top or at the bottom of the resultset.
Examples:
# Nulls are showing up at the top (for example: ORDER BY column ASC):
nullable = :nulls_first
# Nulls are showing up at the bottom (for example: ORDER BY column DESC):
nullable = :nulls_last
order_direction
:asc or :desc
Note: this is an optional attribute, the value will be inferred from the order_expression. Sometimes it’s not possible to infer the order automatically. In this case an exception will be raised (when the query is executed). If the reverse order cannot be computed, it must be provided explicitly.
reversed_order_expression
The reversed version of the order_expression.
A ColumnOrderDefinition object is able to reverse itself which is used when paginating backwards. When a complex order_expression is provided (raw string), then reversing the order automatically is not possible. In this case an exception will be raised.
Example:
order_expression = Project.arel_table[:id].asc
reversed_order_expression = Project.arel_table[:id].desc
add_to_projections
Set to true if the column is not part of the queried table. (Not part of SELECT *)
Example:
- When the order is a calculated expression or the column is in another table (JOIN-ed)
If the add_to_projections is true, the query builder will automatically add the column to the SELECT values
**sql_type**
The SQL type of the column or SQL expression. This is an optional field which is only required when using the column with the InOperatorOptimization class.
Example: When the order expression is a calculated SQL expression.
{
attribute_name: 'id_times_count',
order_expression: Arel.sql('(id * count)').asc,
sql_type: 'integer' # the SQL type here must match with the type of the produced data by the order_expression. Putting 'text' here would be incorrect.
}
Constant Summary collapse
- REVERSED_ORDER_DIRECTIONS =
{ asc: :desc, desc: :asc }.freeze
- REVERSED_NULL_POSITIONS =
{ nulls_first: :nulls_last, nulls_last: :nulls_first }.freeze
- AREL_ORDER_CLASSES =
{ Arel::Nodes::Ascending => :asc, Arel::Nodes::Descending => :desc }.freeze
- ALLOWED_NULLABLE_VALUES =
[:not_nullable, :nulls_first, :nulls_last].freeze
Instance Attribute Summary collapse
-
#add_to_projections ⇒ Object
readonly
Returns the value of attribute add_to_projections.
-
#attribute_name ⇒ Object
readonly
Returns the value of attribute attribute_name.
-
#column_expression ⇒ Object
readonly
Returns the value of attribute column_expression.
-
#order_direction ⇒ Object
readonly
Returns the value of attribute order_direction.
-
#order_expression ⇒ Object
readonly
Returns the value of attribute order_expression.
Instance Method Summary collapse
- #ascending_order? ⇒ Boolean
- #descending_order? ⇒ Boolean
- #distinct? ⇒ Boolean
-
#initialize(attribute_name:, order_expression:, column_expression: nil, reversed_order_expression: nil, nullable: :not_nullable, distinct: true, order_direction: nil, sql_type: nil, add_to_projections: false) ⇒ ColumnOrderDefinition
constructor
rubocop: disable Metrics/ParameterLists.
- #not_nullable? ⇒ Boolean
- #nullable? ⇒ Boolean
- #nulls_first? ⇒ Boolean
- #nulls_last? ⇒ Boolean
- #order_direction_as_sql_string ⇒ Object
-
#reverse ⇒ Object
rubocop: enable Metrics/ParameterLists.
- #sql_type ⇒ Object
Constructor Details
#initialize(attribute_name:, order_expression:, column_expression: nil, reversed_order_expression: nil, nullable: :not_nullable, distinct: true, order_direction: nil, sql_type: nil, add_to_projections: false) ⇒ ColumnOrderDefinition
rubocop: disable Metrics/ParameterLists
140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 140 def initialize(attribute_name:, order_expression:, column_expression: nil, reversed_order_expression: nil, nullable: :not_nullable, distinct: true, order_direction: nil, sql_type: nil, add_to_projections: false) @attribute_name = attribute_name @order_expression = order_expression @column_expression = column_expression || calculate_column_expression(order_expression) @distinct = distinct @reversed_order_expression = reversed_order_expression || calculate_reversed_order(order_expression) @nullable = parse_nullable(nullable, distinct) @order_direction = parse_order_direction(order_expression, order_direction) @sql_type = sql_type @add_to_projections = add_to_projections end |
Instance Attribute Details
#add_to_projections ⇒ Object (readonly)
Returns the value of attribute add_to_projections.
137 138 139 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 137 def add_to_projections @add_to_projections end |
#attribute_name ⇒ Object (readonly)
Returns the value of attribute attribute_name.
137 138 139 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 137 def attribute_name @attribute_name end |
#column_expression ⇒ Object (readonly)
Returns the value of attribute column_expression.
137 138 139 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 137 def column_expression @column_expression end |
#order_direction ⇒ Object (readonly)
Returns the value of attribute order_direction.
137 138 139 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 137 def order_direction @order_direction end |
#order_expression ⇒ Object (readonly)
Returns the value of attribute order_expression.
137 138 139 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 137 def order_expression @order_expression end |
Instance Method Details
#ascending_order? ⇒ Boolean
165 166 167 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 165 def ascending_order? order_direction == :asc end |
#descending_order? ⇒ Boolean
169 170 171 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 169 def descending_order? order_direction == :desc end |
#distinct? ⇒ Boolean
189 190 191 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 189 def distinct? distinct end |
#not_nullable? ⇒ Boolean
181 182 183 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 181 def not_nullable? nullable == :not_nullable end |
#nullable? ⇒ Boolean
185 186 187 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 185 def nullable? !not_nullable? end |
#nulls_first? ⇒ Boolean
173 174 175 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 173 def nulls_first? nullable == :nulls_first end |
#nulls_last? ⇒ Boolean
177 178 179 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 177 def nulls_last? nullable == :nulls_last end |
#order_direction_as_sql_string ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 193 def order_direction_as_sql_string sql_string = ascending_order? ? +'ASC' : +'DESC' if nulls_first? sql_string << ' NULLS FIRST' elsif nulls_last? sql_string << ' NULLS LAST' end sql_string end |
#reverse ⇒ Object
rubocop: enable Metrics/ParameterLists
153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 153 def reverse self.class.new( attribute_name: attribute_name, column_expression: column_expression, order_expression: reversed_order_expression, reversed_order_expression: order_expression, nullable: not_nullable? ? :not_nullable : REVERSED_NULL_POSITIONS[nullable], distinct: distinct, order_direction: REVERSED_ORDER_DIRECTIONS[order_direction] ) end |
#sql_type ⇒ Object
205 206 207 208 209 |
# File 'lib/gitlab/pagination/keyset/column_order_definition.rb', line 205 def sql_type raise Gitlab::Pagination::Keyset::SqlTypeMissingError.for_column(self) if @sql_type.nil? @sql_type end |