Class: OrderQuery::WhereBuilder
- Inherits:
-
Object
- Object
- OrderQuery::WhereBuilder
- Defined in:
- lib/order_query/where_builder.rb
Overview
Build where clause for searching around a record in an order space
Constant Summary collapse
- WHERE_IDENTITY =
[''.freeze, [].freeze].freeze
- WHERE_NONE =
['∅'.freeze, [].freeze].freeze
Class Attribute Summary collapse
-
.wrap_top_level_or ⇒ Object
Returns the value of attribute wrap_top_level_or.
Instance Attribute Summary collapse
- #order ⇒ OrderQuery::OrderSpace readonly
- #record ⇒ ActiveRecord::Base readonly
Instance Method Summary collapse
- #attr_value(cond) ⇒ Object
-
#build_query(mode) ⇒ query, parameters
Conditions that exclude all elements not before / after the current one.
-
#group_operators(term_pairs) ⇒ query, parameters
Join condition pairs internally with OR, and nested within each other with AND Since x matches order criteria with values that come before / after the current record, and y matches order criteria with values equal to the current record’s value (for resolving ties), the resulting condition matches just the elements that come before / after the record.
-
#initialize(record, order_space) ⇒ WhereBuilder
constructor
A new instance of WhereBuilder.
-
#join_terms(op, *terms) ⇒ query, parameters
joins terms with an operator.
- #where_eq(cond, value = attr_value(cond)) ⇒ Object
- #where_in(cond, values) ⇒ Object
- #where_ray(cond, from, mode, strict = true) ⇒ Object
-
#where_relative(cond, mode, strict = true, skip_complete = true) ⇒ query, params
Return query conditions for attribute values before / after the current one.
- #wrap_parens(t) ⇒ Object
Constructor Details
#initialize(record, order_space) ⇒ WhereBuilder
Returns a new instance of WhereBuilder.
12 13 14 15 |
# File 'lib/order_query/where_builder.rb', line 12 def initialize(record, order_space) @order = order_space @record = record end |
Class Attribute Details
.wrap_top_level_or ⇒ Object
Returns the value of attribute wrap_top_level_or.
120 121 122 |
# File 'lib/order_query/where_builder.rb', line 120 def wrap_top_level_or @wrap_top_level_or end |
Instance Attribute Details
#order ⇒ OrderQuery::OrderSpace (readonly)
8 9 10 |
# File 'lib/order_query/where_builder.rb', line 8 def order @order end |
#record ⇒ ActiveRecord::Base (readonly)
6 7 8 |
# File 'lib/order_query/where_builder.rb', line 6 def record @record end |
Instance Method Details
#attr_value(cond) ⇒ Object
115 116 117 |
# File 'lib/order_query/where_builder.rb', line 115 def attr_value(cond) record.send cond.name end |
#build_query(mode) ⇒ query, parameters
Returns conditions that exclude all elements not before / after the current one.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/order_query/where_builder.rb', line 19 def build_query(mode) # pairs of [x0, y0] pairs = order.conditions.map { |cond| [where_relative(cond, mode, true), (where_eq(cond) unless cond.unique?)].reject { |x| x.nil? || x == WHERE_IDENTITY || x == WHERE_NONE }.compact } query = group_operators pairs return query unless self.class.wrap_top_level_or # Wrap top level OR clause for performance, see https://github.com/glebm/order_query/issues/3 top_pair_idx = pairs.index(&:present?) if top_pair_idx && pairs[top_pair_idx].length == 2 && (top_level_cond = order.conditions[top_pair_idx]) join_terms 'AND'.freeze, where_relative(top_level_cond, mode, false), wrap_parens(query) else query end end |
#group_operators(term_pairs) ⇒ query, parameters
Join condition pairs internally with OR, and nested within each other with AND Since x matches order criteria with values that come before / after the current record, and y matches order criteria with values equal to the current record’s value (for resolving ties), the resulting condition matches just the elements that come before / after the record
49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/order_query/where_builder.rb', line 49 def group_operators(term_pairs) # create "x OR y" string disjunctive = join_terms 'OR'.freeze, *term_pairs[0] rest = term_pairs.from(1) if rest.present? # nest the remaining pairs recursively, appending them with " AND " rest_grouped = group_operators rest join_terms 'AND'.freeze, disjunctive, (rest.length == 1 ? rest_grouped : wrap_parens(rest_grouped)) else disjunctive end end |
#join_terms(op, *terms) ⇒ query, parameters
joins terms with an operator
68 69 70 71 |
# File 'lib/order_query/where_builder.rb', line 68 def join_terms(op, *terms) [terms.map { |t| t.first.presence }.compact.join(" #{op} "), terms.map(&:second).reduce(:+) || []] end |
#where_eq(cond, value = attr_value(cond)) ⇒ Object
101 102 103 |
# File 'lib/order_query/where_builder.rb', line 101 def where_eq(cond, value = attr_value(cond)) [%Q(#{cond.col_name_sql} = ?).freeze, [value]] end |
#where_in(cond, values) ⇒ Object
90 91 92 93 94 95 96 97 98 99 |
# File 'lib/order_query/where_builder.rb', line 90 def where_in(cond, values) case values.length when 0 WHERE_NONE when 1 where_eq cond, values[0] else ["#{cond.col_name_sql} IN (?)".freeze, [values]] end end |
#where_ray(cond, from, mode, strict = true) ⇒ Object
105 106 107 108 109 110 |
# File 'lib/order_query/where_builder.rb', line 105 def where_ray(cond, from, mode, strict = true) ops = %w(< >) ops = ops.reverse if mode == :after op = {asc: ops[0], desc: ops[1]}[cond.order || :asc] ["#{cond.col_name_sql} #{op}#{'=' unless strict} ?".freeze, [from]] end |
#where_relative(cond, mode, strict = true, skip_complete = true) ⇒ query, params
Return query conditions for attribute values before / after the current one
75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/order_query/where_builder.rb', line 75 def where_relative(cond, mode, strict = true, skip_complete = true) value = attr_value cond if cond.list? values = cond.filter_values(value, mode, strict) if cond.complete? && values.length == cond.order.length WHERE_IDENTITY else where_in cond, values end else where_ray cond, value, mode, strict end end |
#wrap_parens(t) ⇒ Object
62 63 64 |
# File 'lib/order_query/where_builder.rb', line 62 def wrap_parens(t) ["(#{t[0]})", t[1]] end |