module NoBrainer::Criteria::Where

Public Class Methods

merge_where_ast(a, b) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 18
def self.merge_where_ast(a, b)
  (a ? MultiOperator.new(:and, [a, b]) : b).simplify
end

Public Instance Methods

_where(*args, &block) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 14
def _where(*args, &block)
  chain(:where_ast => parse_clause([*args, block].compact, :unsafe => true))
end
where(*args, &block) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 10
def where(*args, &block)
  chain(:where_ast => parse_clause([*args, block].compact))
end
where_index_name() click to toggle source
# File lib/no_brainer/criteria/where.rb, line 30
def where_index_name
  index = where_index_finder.strategy.try(:index)
  index.is_a?(Array) ? index.map(&:name) : index.try(:name)
end
where_index_type() click to toggle source
# File lib/no_brainer/criteria/where.rb, line 35
def where_index_type
  where_index_finder.strategy.try(:rql_op)
end
where_indexed?() click to toggle source
# File lib/no_brainer/criteria/where.rb, line 26
def where_indexed?
  where_index_name.present?
end
where_present?() click to toggle source
# File lib/no_brainer/criteria/where.rb, line 22
def where_present?
  finalized_criteria.options[:where_ast].try(:clauses).present?
end
without_distinct(value = true) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 39
def without_distinct(value = true)
  # helper for delete_all which can't operate on distinct
  chain(:without_distinct => value)
end

Private Instance Methods

compile_rql_pass1() click to toggle source
Calls superclass method
# File lib/no_brainer/criteria/where.rb, line 548
def compile_rql_pass1
  rql = super
  rql = where_index_finder.strategy.rql_proc.call(rql) if where_indexed?
  rql
end
compile_rql_pass2() click to toggle source
Calls superclass method
# File lib/no_brainer/criteria/where.rb, line 554
def compile_rql_pass2
  rql = super
  ast = where_indexed? ? where_index_finder.strategy.ast : @options[:where_ast]
  rql = rql.filter { |doc| ast.to_rql(doc) } if ast.try(:clauses).present?
  rql
end
instantiate_binary_op(key, op, value, options={}) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 351
def instantiate_binary_op(key, op, value, options={})
  op, value = case value
              when Range  then [:between, value]
              when Regexp then [:match, translate_regexp_to_re2_syntax(value)]
              else [:eq, value]
              end if op == :eq

  nested_prefix = options[:nested_prefix] || []

  tail_args = [op, value, self.model, !!options[:unsafe]]

  case key
  when Symbol::Decoration
    raise "Use only one .not, .all or .any modifiers in the query" if key.symbol.is_a?(Symbol::Decoration)
    case key.decorator
      when :any, :all then BinaryOperator.new(nested_prefix + [key.symbol], key.decorator, *tail_args)
      when :not       then UnaryOperator.new(:not, BinaryOperator.new(nested_prefix + [key.symbol], :scalar, *tail_args))
    end
  else BinaryOperator.new(nested_prefix + [key], :scalar, *tail_args)
  end
end
parse_clause(clause, options={}) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 283
def parse_clause(clause, options={})
  clause = sanitize_for_mass_assignment(clause)
  case clause
  when Array then MultiOperator.new(:and, clause.map { |c| parse_clause(c, options) })
  when Hash  then MultiOperator.new(:and, clause.map { |k,v| parse_clause_stub(k, v, options) })
  when Proc  then Lambda.new(clause)
  when Symbol::Decoration
    case clause.args.size
    when 1 then parse_clause_stub(clause, clause.args.first, options)
    when 2 then parse_clause_stub(clause, clause.args, options)
    else raise "Invalid argument: #{clause}"
    end
  else raise "Invalid clause: #{clause}"
  end
end
parse_clause_stub(key, value, options={}) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 299
def parse_clause_stub(key, value, options={})
  case key
  when :and  then parse_multi_value(:and, value, false, options)
  when :or   then parse_multi_value(:or,  value, false, options)
  when :_and then parse_multi_value(:and, value, true, options)
  when :_or  then parse_multi_value(:or,  value, true, options)
  when :not  then UnaryOperator.new(:not, parse_clause(value, options))
  when String, Symbol then
    case value
    when Hash then parse_clause(value, options.merge(:nested_prefix => (options[:nested_prefix] || []) + [key.to_sym]))
    else instantiate_binary_op(key.to_sym, :eq, value, options)
    end
  when Symbol::Decoration then
    # The :eq operator can have only one arg
    if key.decorator == :eq && value.is_a?(Array) && value.size > 1
      raise "Invalid key: #{key}"
    end

    case key.decorator
    when :any, :all, :not then instantiate_binary_op(key, :eq, value, options)
    when :gte then instantiate_binary_op(key.symbol, :ge, value, options)
    when :lte then instantiate_binary_op(key.symbol, :le, value, options)
    else instantiate_binary_op(key.symbol, key.decorator, value, options)
    end
  else raise "Invalid key: #{key}"
  end
end
parse_multi_value(op, value, multi_safe, options={}) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 327
def parse_multi_value(op, value, multi_safe, options={})
  raise "The `#{op}' operator takes an array as argument" unless value.is_a?(Array)
  if value.size == 1 && value.first.is_a?(Hash) && !multi_safe
    raise "The `#{op}' operator was provided an array with a single hash element.\n" +
          "In Ruby, [:a => :b, :c => :d] means [{:a => :b, :c => :d}] which is not the same as [{:a => :b}, {:c => :d}].\n" +
          "To prevent mistakes, the former construct is prohibited as you probably mean the latter.\n" +
          "However, if you know what you are doing, you can use the `_#{op}' operator instead."
  end
  MultiOperator.new(op, value.map { |v| parse_clause(v, options) })
end
translate_regexp_to_re2_syntax(value) click to toggle source
# File lib/no_brainer/criteria/where.rb, line 338
def translate_regexp_to_re2_syntax(value)
  # Ruby always uses what RE2 calls "multiline mode" (the "m" flag),
  # meaning that "foo\nbar" matches /^bar$/.
  #
  # Ruby's /m modifier means that . matches \n and corresponds to RE2's "s" flag.

  flags = "m"
  flags << "s" if value.options & Regexp::MULTILINE != 0
  flags << "i" if value.options & Regexp::IGNORECASE != 0

  "(?#{flags})#{value.source}"
end
where_index_finder() click to toggle source
# File lib/no_brainer/criteria/where.rb, line 543
def where_index_finder
  return finalized_criteria.__send__(:where_index_finder) unless finalized?
  @where_index_finder ||= IndexFinder.new(self, @options[:where_ast]).tap(&:find_strategy!)
end