Class: OrderQuery::WhereBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/order_query/where_builder.rb

Overview

Build where clause for searching around a record in an order space

Constant Summary collapse

EMPTY_FILTER =
[''.freeze, []]

Class Attribute Summary collapse

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(record, order_space) ⇒ WhereBuilder

Returns a new instance of WhereBuilder.

Parameters:



11
12
13
14
# File 'lib/order_query/where_builder.rb', line 11

def initialize(record, order_space)
  @order  = order_space
  @record = record
end

Class Attribute Details

.wrap_top_level_orObject

Returns the value of attribute wrap_top_level_or.



112
113
114
# File 'lib/order_query/where_builder.rb', line 112

def wrap_top_level_or
  @wrap_top_level_or
end

Instance Attribute Details

#orderOrderQuery::OrderSpace (readonly)



7
8
9
# File 'lib/order_query/where_builder.rb', line 7

def order
  @order
end

#recordActiveRecord::Base (readonly)

Returns:

  • (ActiveRecord::Base)


5
6
7
# File 'lib/order_query/where_builder.rb', line 5

def record
  @record
end

Instance Method Details

#attr_value(cond) ⇒ Object



107
108
109
# File 'lib/order_query/where_builder.rb', line 107

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.

Parameters:

  • mode (:before or :after)

Returns:

  • (query, parameters)

    conditions that exclude all elements not before / after the current one



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/order_query/where_builder.rb', line 18

def build_query(mode)
  conditions = order.conditions
  terms = conditions.map { |cond| [where_mode(cond, mode, true), where_eq(cond)] }
  query = group_operators terms
  # Wrap top level OR clause for performance, see https://github.com/glebm/order_query/issues/3
  if self.class.wrap_top_level_or && !terms[0].include?(EMPTY_FILTER)
    join_terms 'AND'.freeze,
               where_mode(conditions.first, mode, false),
               ["(#{query[0]})", query[1]]
  else
    query
  end
end

#group_operators(term_pairs) ⇒ query, parameters

Join conditions with operators and parenthesis 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

Parameters:

  • term_pairs (Array)

    of query terms [[x0, y0], [x1, y1], …], xi, yi are pairs of [query, parameters]

Returns:

  • (query, parameters)

    x0 OR y0 AND (x1 OR

    y1 AND (x2 OR
            y2 AND x3))
    


44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/order_query/where_builder.rb', line 44

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
    rest_grouped[0] = "(#{rest_grouped[0]})" unless rest.length == 1
    join_terms 'AND'.freeze, disjunctive, rest_grouped
  else
    disjunctive
  end
end

#join_terms(op, *terms) ⇒ query, parameters

joins terms with an operator

Returns:

  • (query, parameters)


60
61
62
63
# File 'lib/order_query/where_builder.rb', line 60

def join_terms(op, *terms)
  [terms.map { |t| t.first.presence }.compact.join(" #{op} "),
   terms.map(&:second).reduce(:+) || []]
end

#where_eq(cond) ⇒ query, params

Returns Unless order attribute is unique, such as id, return [‘WHERE value = ?’, current value].

Returns:

  • (query, params)

    Unless order attribute is unique, such as id, return [‘WHERE value = ?’, current value].



68
69
70
71
72
73
74
# File 'lib/order_query/where_builder.rb', line 68

def where_eq(cond)
  if cond.unique?
    EMPTY_FILTER
  else
    [%Q(#{cond.col_name_sql} = ?).freeze, [attr_value(cond)]]
  end
end

#where_in(cond, values) ⇒ Object



83
84
85
86
87
88
89
90
91
92
# File 'lib/order_query/where_builder.rb', line 83

def where_in(cond, values)
  case values.length
    when 0
      EMPTY_FILTER
    when 1
      ["#{cond.col_name_sql} = ?".freeze, [values]]
    else
      ["#{cond.col_name_sql} IN (?)".freeze, [values]]
  end
end

#where_mode(cond, mode, strict = true) ⇒ query, params

Return query conditions for attribute values before / after the current one

Parameters:

  • mode (:before or :after)

Returns:

  • (query, params)

    return query conditions for attribute values before / after the current one



96
97
98
99
100
101
102
103
104
105
# File 'lib/order_query/where_builder.rb', line 96

def where_mode(cond, mode, strict = true)
  value = attr_value cond
  if cond.ray?
    where_ray cond, value, mode, strict
  else
    # ord is an array of sort values, ordered first to last
    # if current not in result set, do not apply filter
    where_in cond, cond.values_around(value, mode, strict)
  end
end

#where_ray(cond, from, mode, strict = true) ⇒ Object



76
77
78
79
80
81
# File 'lib/order_query/where_builder.rb', line 76

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