123456789_123456789_123456789_123456789_123456789_

Module: Mongoid::Criteria::Queryable::Storable Private

Do not use. This module is for internal use only.
Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Included In:
Defined in: lib/mongoid/criteria/queryable/storable.rb

Overview

This module encapsulates methods that write query expressions into the Criteria’s selector.

The query expressions must have already been expanded as necessary. The methods of this module do not perform processing on expression values.

Methods in this module do not handle negation - if negation is needed, it must have already been handled upstream of these methods.

Instance Method Summary

Instance Method Details

#add_field_expression(field, value) ⇒ Storable

Adds a field expression to the query.

field must be a field name, and it must be a string. The upstream code must have converted other field/key types to the simple string form by the time this method is invoked.

value can be of any type, it is written into the selector unchanged.

This method performs no processing on the provided field value.

Mutates the receiver.

Parameters:

  • field (String)

    The field name.

  • value (Object)

    The field value.

Returns:

  • (Storable)

    self.

[ GitHub ]

  
# File 'lib/mongoid/criteria/queryable/storable.rb', line 36

def add_field_expression(field, value)
  unless field.is_a?(String)
    raise ArgumentError, "Field must be a string: #{field}"
  end

  if field.start_with?('$')
    raise ArgumentError, "Field cannot be an operator (i.e. begin with $): #{field}"
  end

  if selector[field]
    # We already have a restriction by the field we are trying
    # to restrict, combine the restrictions.
    if value.is_a?(Hash) && selector[field].is_a?(Hash) &&
      value.keys.all? { |key|
        key_s = key.to_s
        key_s.start_with?('$') && !selector[field].keys.map(&:to_s).include?(key_s)
      }
    then
      # Multiple operators can be combined on the same field by
      # adding them to the existing hash.
      new_value = selector[field].merge(value)
      selector.store(field, new_value)
    elsif selector[field] != value
      add_operator_expression('$and', [{field => value}])
    end
  else
    selector.store(field, value)
  end

  self
end

#add_logical_operator_expression(operator, op_expr) ⇒ Storable

Adds a logical operator expression to the selector.

This method only handles logical operators ($and, $nor and $or). It raises ArgumentError if called with another operator. Note that in MQL, $not is a field-level operator and not a query-level one, and therefore $not is not handled by this method.

This method takes the operator and the operator value expression separately for callers’ convenience. It can be considered to handle storing the hash {operator => op_expr}.

If the selector consists of a single condition which is the specified operator (on the top level), the new condition given in op_expr is added to the existing conditions for the specified operator. For example, if the selector is currently:

{'$or' => [{'hello' => 'world'}]}

… and operator is ‘$or’ and op_expr is [{‘test’ => 123’}], the resulting selector will be:

{'$or' => [{'hello' => 'world'}, {'test' => 123}]}

This method always adds the new conditions as additional requirements; in other words, it does not implement the ActiveRecord or/nor behavior where the receiver becomes one of the operands. It is expected that code upstream of this method implements such behavior.

This method does not simplify values (i.e. if the selector is currently empty and operator is $and, op_expr is written to the selector with $and even if the $and can in principle be elided). Such simplification is also expected to have already been performed by the upstream code.

This method mutates the receiver.

Parameters:

  • operator (String)

    The operator to add.

  • op_expr (Array<Hash>)

    Operator value to add.

Returns:

  • (Storable)

    self.

[ GitHub ]

  
# File 'lib/mongoid/criteria/queryable/storable.rb', line 108

def add_logical_operator_expression(operator, op_expr)
  unless operator.is_a?(String)
    raise ArgumentError, "Operator must be a string: #{operator}"
  end

  unless %w($and $nor $or).include?(operator)
    raise ArgumentError, "This method only handles logical operators ($and, $nor, $or). Operator given: #{operator}"
  end

  unless op_expr.is_a?(Array)
    raise Errors::InvalidQuery, "#{operator} argument must be an array: #{Errors::InvalidQuery.truncate_expr(op_expr)}"
  end

  if selector.length == 1 && selector.keys.first == operator
    new_value = selector.values.first + op_expr
    selector.store(operator, new_value)
  elsif operator == '$and' || selector.empty?
    # $and can always be added to top level and it will be combined
    # with whatever other conditions exist.
    if current_value = selector[operator]
      new_value = current_value + op_expr
      selector.store(operator, new_value)
    else
      selector.store(operator, op_expr)
    end
  else
    # Other operators need to be added separately
    if selector[operator]
      add_logical_operator_expression('$and', [operator => op_expr])
    else
      selector.store(operator, op_expr)
    end
  end

  self
end

#add_one_expression(field, value) ⇒ Storable

Adds an arbitrary expression to the query.

Field can either be a field name or an operator.

Mutates the receiver.

Parameters:

  • field (String)

    Field name or operator name.

  • value (Object)

    Field value or operator expression.

Returns:

  • (Storable)

    self.

[ GitHub ]

  
# File 'lib/mongoid/criteria/queryable/storable.rb', line 223

def add_one_expression(field, value)
  unless field.is_a?(String)
    raise ArgumentError, "Field must be a string: #{field}"
  end

  if field.start_with?('$')
    add_operator_expression(field, value)
  else
    add_field_expression(field, value)
  end
end

#add_operator_expression(operator, op_expr) ⇒ Storable

Adds an operator expression to the selector.

This method takes the operator and the operator value expression separately for callers’ convenience. It can be considered to handle storing the hash {operator => op_expr}.

The operator value can be of any type.

If the selector already has the specified operator in it (on the top level), the new condition given in op_expr is added to the existing conditions for the specified operator. This is straightforward for $and; for other logical operators, the behavior of this method is to add the new conditions to the existing operator. For example, if the selector is currently:

{'foo' => 'bar', '$or' => [{'hello' => 'world'}]}

… and operator is ‘$or’ and op_expr is {‘test’ => 123’}, the resulting selector will be:

{'foo' => 'bar', '$or' => [{'hello' => 'world'}, {'test' => 123}]}

This does not implement an OR between the existing selector and the new operator expression - handling this is the job of upstream methods. This method simply stores op_expr into the selector on the assumption that the existing selector is the correct left hand side of the operation already.

For non-logical query-level operators like $where and $text, if there already is a top-level operator with the same name, the op_expr is added to the selector via a top-level $and operator, thus producing a selector having both operator values.

This method does not simplify values (i.e. if the selector is currently empty and operator is $and, op_expr is written to the selector with $and even if the $and can in principle be elided).

This method mutates the receiver.

Parameters:

  • operator (String)

    The operator to add.

  • op_expr (Object)

    Operator value to add.

Returns:

  • (Storable)

    self.

[ GitHub ]

  
# File 'lib/mongoid/criteria/queryable/storable.rb', line 188

def add_operator_expression(operator, op_expr)
  unless operator.is_a?(String)
    raise ArgumentError, "Operator must be a string: #{operator}"
  end

  unless operator.start_with?('$')
    raise ArgumentError, "Operator must begin with $: #{operator}"
  end

  if %w($and $nor $or).include?(operator)
    return add_logical_operator_expression(operator, op_expr)
  end

  # For other operators, if the operator already exists in the
  # query, add the new condition with $and, otherwise add the
  # new condition to the top level.
  if selector[operator]
    add_logical_operator_expression('$and', [{operator => op_expr}])
  else
    selector.store(operator, op_expr)
  end

  self
end