Module: Mongoid::Criteria::Queryable::Mergeable
Relationships & Source Files | |
Extension / Inclusion / Inheritance Descendants | |
Included In:
| |
Defined in: | lib/mongoid/criteria/queryable/mergeable.rb |
Overview
Contains behavior for merging existing selection with new selection.
Instance Attribute Summary
Instance Method Summary
-
#and_with_operator(criterion, operator) ⇒ Criteria
Merge criteria with operators using the and operator.
-
#intersect ⇒ Mergeable
Instruct the next mergeable call to use intersection.
-
#override ⇒ Mergeable
Instruct the next mergeable call to use override.
-
#reset_strategies! ⇒ Criteria
Clear the current strategy and negating flag, used after cloning.
-
#union ⇒ Mergeable
Instruct the next mergeable call to use union.
-
#__add__(criterion, operator) ⇒ Mergeable
private
Internal use only
Internal use only
Adds the criterion to the existing selection.
-
#__expanded__(criterion, outer, inner) ⇒ Mergeable
private
Internal use only
Internal use only
Adds the criterion to the existing selection.
-
#__intersect__(criterion, operator) ⇒ Mergeable
private
Internal use only
Internal use only
Adds the criterion to the existing selection.
-
#__merge__(criterion) ⇒ Mergeable
private
Internal use only
Internal use only
Perform a straight merge of the criterion into the selection and let the symbol overrides do all the work.
-
#__multi__(criteria, operator) ⇒ Mergeable
private
Internal use only
Internal use only
Adds $and/$or/$nor criteria to a copy of this selection.
-
#__override__(criterion, operator) ⇒ Mergeable
private
Internal use only
Internal use only
Adds the criterion to the existing selection.
-
#__union__(criterion, operator) ⇒ Mergeable
private
Internal use only
Internal use only
Adds the criterion to the existing selection.
-
#_mongoid_add_top_level_operation(operator, criteria)
private
Internal use only
Internal use only
Combines criteria into a MongoDB selector.
-
#_mongoid_expand_keys(expr) ⇒ BSON::Document
private
Takes a criteria hash and expands
Key
objects into hashes containing MQL corresponding to said key objects. -
#_mongoid_flatten_arrays(array)
private
Calling .flatten on an array which includes a
::Mongoid::Criteria
instance evaluates the criteria, which we do not want. -
#prepare(field, operator, value) ⇒ Object
private
Internal use only
Internal use only
Prepare the value for merging.
-
#use(strategy) ⇒ Mergeable
private
Internal use only
Internal use only
Use the named strategy for the next operation.
-
#with_strategy(strategy, criterion, operator) ⇒ Mergeable
private
Internal use only
Internal use only
Add criterion to the selection with the named strategy.
Instance Attribute Details
#strategy (rw)
[ GitHub ]# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 12
attr_accessor :strategy
#strategy The name of the current strategy.(The name of the current strategy.) (rw)
[ GitHub ]# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 12
attr_accessor :strategy
Instance Method Details
#__add__(criterion, operator) ⇒ Mergeable
(private)
Adds the criterion to the existing selection.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 90
def __add__(criterion, operator) with_strategy(:__add__, criterion, operator) end
#__expanded__(criterion, outer, inner) ⇒ Mergeable
(private)
Adds the criterion to the existing selection.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 106
def (criterion, outer, inner) selection(criterion) do |selector, field, value| selector.store(field, { outer => { inner => value }}) end end
#__intersect__(criterion, operator) ⇒ Mergeable
(private)
Adds the criterion to the existing selection.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 140
def __intersect__(criterion, operator) with_strategy(:__intersect__, criterion, operator) end
#__merge__(criterion) ⇒ Mergeable
(private)
Perform a straight merge of the criterion into the selection and let the symbol overrides do all the work.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 123
def __merge__(criterion) selection(criterion) do |selector, field, value| selector.merge!(field.__expr_part__(value)) end end
#__multi__(criteria, operator) ⇒ Mergeable
(private)
Adds $and/$or/$nor criteria to a copy of this selection.
Each of the criteria can be a ::Hash
of key/value pairs or MongoDB operators (keys beginning with $), or a Selectable
object (which typically will be a ::Mongoid::Criteria
instance).
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 160
def __multi__(criteria, operator) clone.tap do |query| sel = query.selector criteria.flatten.each do |expr| next unless expr result_criteria = sel[operator] || [] if expr.is_a?(Selectable) expr = expr.selector end normalized = (expr) sel.store(operator, result_criteria.push(normalized)) end end end
#__override__(criterion, operator) ⇒ Mergeable
(private)
Adds the criterion to the existing selection.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 343
def __override__(criterion, operator) if criterion.is_a?(Selectable) criterion = criterion.selector end selection(criterion) do |selector, field, value| expression = prepare(field, operator, value) existing = selector[field] if existing.respond_to?(:merge!) selector.store(field, existing.merge!(expression)) else selector.store(field, expression) end end end
#__union__(criterion, operator) ⇒ Mergeable
(private)
Adds the criterion to the existing selection.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 369
def __union__(criterion, operator) with_strategy(:__union__, criterion, operator) end
#_mongoid_add_top_level_operation(operator, criteria) (private)
Combines criteria into a MongoDB selector.
::Mongoid::Criteria
is an array of criterion objects which will be flattened.
Each criterion can be:
-
A hash
-
A Criteria instance
-
nil, in which case it is ignored
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 185
private def _mongoid_add_top_level_operation(operator, criteria) # Flatten the criteria. The idea is that predicates in MongoDB # are always hashes and are never arrays. This method additionally # allows Criteria instances as predicates. # The flattening is existing Mongoid behavior but we could possibly # get rid of it as applications can splat their predicates, or # flatten if needed. clone.tap do |query| sel = query.selector _mongoid_flatten_arrays(criteria).each do |criterion| if criterion.is_a?(Selectable) expr = (criterion.selector) else expr = (criterion) end if sel.empty? sel.store(operator, [expr]) elsif sel.keys == [operator] sel.store(operator, sel[operator] + [expr]) else operands = [sel.dup] + [expr] sel.clear sel.store(operator, operands) end end end end
#_mongoid_expand_keys(expr) ⇒ BSON::Document
(private)
Takes a criteria hash and expands Key
objects into hashes containing MQL corresponding to said key objects. Also converts the input to BSON::Document to permit indifferent access.
The argument must be a hash containing key-value pairs of the following forms:
-
value
-
=> value
-
value
-
=> operator_value_expression
-
=> operator_value_expression
Ruby does not permit multiple symbol operators. For example, => 1,
is collapsed to :foo
.gt => 2=> 2
by the language. Therefore this method never has to deal with multiple identical operators.
Similarly, this method should never need to expand a literal value and an operator at the same time.
This method effectively converts symbol keys to string keys in the input expr
, such that the downstream code can assume that conditions always contain string keys.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 259
private def (expr) unless expr.is_a?(Hash) raise ArgumentError, 'Argument must be a Hash' end result = BSON::Document.new expr.each do |field, value| field.__expr_part__(value., negating?).each do |k, v| if existing = result[k] if existing.is_a?(Hash) # Existing value is an operator. # If new value is also an operator, ensure there are no # conflicts and add if v.is_a?(Hash) # The new value is also an operator. # If there are no conflicts, combine the hashes, otherwise # add new conditions to top level with $and. if (v.keys & existing.keys).empty? existing.update(v) else raise NotImplementedError, 'Ruby does not allow same symbol operator with different values' result['$and'] ||= [] result['$and'] << {k => v} end else # The new value is a simple value. # Transform the implicit equality to either $eq or $regexp # depending on the type of the argument. See # https://www.mongodb.com/docs/manual/reference/operator/query/eq/#std-label-eq-usage-examples # for the description of relevant server behavior. op = case v when Regexp, BSON::Regexp::Raw '$regex' else '$eq' end # If there isn't an $eq/$regex operator already in the # query, transform the new value into an operator # expression and add it to the existing hash. Otherwise # add the new condition with $and to the top level. if existing.key?(op) raise NotImplementedError, 'Ruby does not allow same symbol operator with different values' result['$and'] ||= [] result['$and'] << {k => v} else existing.update(op => v) end end else # Existing value is a simple value. # See the notes above about transformations to $eq/$regex. op = case existing when Regexp, BSON::Regexp::Raw '$regex' else '$eq' end if v.is_a?(Hash) && !v.key?(op) result[k] = {op => existing}.update(v) else raise NotImplementedError, 'Ruby does not allow same symbol operator with different values' result['$and'] ||= [] result['$and'] << {k => v} end end else result[k] = v end end end result end
#_mongoid_flatten_arrays(array) (private)
Calling .flatten on an array which includes a ::Mongoid::Criteria
instance evaluates the criteria, which we do not want. Hence this method explicitly only expands ::Array
objects and ::Array
subclasses.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 216
private def _mongoid_flatten_arrays(array) out = [] pending = array.dup until pending.empty? item = pending.shift if item.nil? # skip elsif item.is_a?(Array) pending += item else out << item end end out end
#and_with_operator(criterion, operator) ⇒ Criteria
Merge criteria with operators using the and operator.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 62
def and_with_operator(criterion, operator) crit = self if criterion criterion.each_pair do |field, value| val = prepare(field, operator, value) # The prepare method already takes the negation into account. We # set negating to false here so that ``and`` doesn't also apply # negation and we have a double negative. crit.negating = false crit = crit.and(field => val) end end crit end
#intersect ⇒ Mergeable
Instruct the next mergeable call to use intersection.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 20
def intersect use(:__intersect__) end
#override ⇒ Mergeable
Instruct the next mergeable call to use override.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 30
def override use(:__override__) end
#prepare(field, operator, value) ⇒ Object
(private)
Prepare the value for merging.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 421
def prepare(field, operator, value) unless operator =~ /exists|type|size/ value = value. field = field.to_s name = aliases[field] || field serializer = serializers[name] value = serializer ? serializer.evolve(value) : value end selection = { operator => value } negating? ? { "$not" => selection } : selection end
#reset_strategies! ⇒ Criteria
Clear the current strategy and negating flag, used after cloning.
# File 'lib/mongoid/criteria/queryable/mergeable.rb', line 50
def reset_strategies! self.strategy = nil self.negating = nil self end
#union ⇒ Mergeable
Instruct the next mergeable call to use union.
#use(strategy) ⇒ Mergeable
(private)
Use the named strategy for the next operation.
#with_strategy(strategy, criterion, operator) ⇒ Mergeable
(private)
Add criterion to the selection with the named strategy.