Class: NoBrainer::Criteria::Where::BinaryOperator

Inherits:
Struct
  • Object
show all
Defined in:
lib/no_brainer/criteria/where.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.get_candidate_clauses(clauses, *types) ⇒ Object



70
71
72
# File 'lib/no_brainer/criteria/where.rb', line 70

def self.get_candidate_clauses(clauses, *types)
  clauses.select { |c| c.is_a?(self) && types.include?(c.op) }
end

.simplify_clauses(op, ast_clauses) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/no_brainer/criteria/where.rb', line 74

def self.simplify_clauses(op, ast_clauses)
  # This code assumes that simplfy() has already been called on all clauses.
  if op == :or
    eq_clauses = get_candidate_clauses(ast_clauses, :in, :eq)
    new_clauses = eq_clauses.group_by { |c| [c.key_path, c.key_modifier] }.map do |(key_path, key_modifier), clauses|
      if key_modifier.in?([:scalar, :any]) && clauses.size > 1
        values = clauses.flat_map { |c| c.op == :in ? c.value : [c.value] }.uniq
        [BinaryOperator.new(key_path, key_modifier, :in, values, clauses.first.model, true)]
      else
        clauses
      end
    end.flatten(1)

    if new_clauses.size != eq_clauses.size
      ast_clauses = ast_clauses - eq_clauses + new_clauses
    end
  end

  ast_clauses
end

Instance Method Details

#compatible_with_index?(index) ⇒ Boolean

Returns:



160
161
162
# File 'lib/no_brainer/criteria/where.rb', line 160

def compatible_with_index?(index)
  [key_modifier, index.multi].in?([[:any, true], [:scalar, false]])
end

#simplifyObject



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/no_brainer/criteria/where.rb', line 95

def simplify
  new_key_path = cast_key_path(key_path)
  new_key_modifier, new_op, new_value = case op
    when :in then
      case value
      when Range then [key_modifier, :between, (cast_value(value.min)..cast_value(value.max))]
      when Array then [key_modifier, :in, value.map(&method(:cast_value)).uniq]
      else raise ArgumentError.new "`in' takes an array/range, not #{value}"
      end
    when :between then [key_modifier, op, (cast_value(value.min)..cast_value(value.max))]
    when :include then ensure_scalar_for(op); [:any, :eq, cast_value(value)]
    when :defined, :undefined then ensure_scalar_for(op); [key_modifier, op, cast_value(value)]
    when :during then [key_modifier, op, [cast_value(value.first), cast_value(value.last)]]
    else [key_modifier, op, cast_value(value)]
  end

  # When key_path relates to a polymorphic associatoin, the new_key_path is
  # an Array containing the foreign_type and then the foreign_key.
  if new_key_path.first.is_a?(Array)
    foreign_type, foreign_key = new_key_path.first

    MultiOperator.new(
      :and,
      [
        BinaryOperator.new([foreign_type], new_key_modifier, new_op, value.class.to_s, model, true),
        BinaryOperator.new([foreign_key], new_key_modifier, new_op, value.__send__(value.class.pk_name), model, true)
      ]
    )
  else
    BinaryOperator.new(new_key_path, new_key_modifier, new_op, new_value, model, true)
  end
end

#to_rql(doc) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/no_brainer/criteria/where.rb', line 128

def to_rql(doc)
  key_path = [model.lookup_field_alias(self.key_path.first), *self.key_path[1..-1]]

  doc = key_path[0..-2].reduce(doc) { |d,k| d[k] }
  key = key_path.last

  case key_modifier
  when :scalar then
    case op
    when :defined   then  value ? doc.has_fields(key) : doc.has_fields(key).not
    when :undefined then !value ? doc.has_fields(key) : doc.has_fields(key).not
    else to_rql_scalar(doc[key])
    end
  when :any then doc[key].map { |lvalue| to_rql_scalar(lvalue) }.contains(true)
  when :all then doc[key].map { |lvalue| to_rql_scalar(lvalue) }.contains(false).not
  end
end

#to_rql_scalar(lvalue) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/no_brainer/criteria/where.rb', line 146

def to_rql_scalar(lvalue)
  case op
  when :between    then (lvalue >= value.min) & (lvalue <= value.max)
  when :in         then RethinkDB::RQL.new.expr(value).contains(lvalue)
  when :intersects then lvalue.intersects(value.to_rql)
  when :during     then lvalue.during(value.first, value.last)
  when :near
    # XXX options[:max_results] is not used, seems to be a workaround of rethinkdb index implementation.
    circle = value[:circle]
    RethinkDB::RQL.new.distance(lvalue, circle.center.to_rql, circle.options) <= circle.radius
  else lvalue.__send__(op, value)
  end
end