class NoBrainer::Criteria::Where::IndexFinder
Public Instance Methods
find_strategy()
click to toggle source
# File lib/no_brainer/criteria/where.rb, line 530 def find_strategy return nil unless ast.try(:clauses).present? && !criteria.without_index? case ast.op when :and then find_strategy_compound || find_strategy_compound_partial || find_strategy_canonical || find_strategy_hidden_between when :or then find_strategy_union end end
find_strategy!()
click to toggle source
# File lib/no_brainer/criteria/where.rb, line 538 def find_strategy! self.strategy = find_strategy end
find_strategy_canonical()
click to toggle source
# File lib/no_brainer/criteria/where.rb, line 406 def find_strategy_canonical clauses = get_candidate_clauses(:eq, :in, :between, :near, :intersects, :defined) return nil unless clauses.present? usable_indexes = Hash[get_usable_indexes.map { |i| [[i.name], i] }] clauses.map do |clause| index = usable_indexes[clause.key_path] next unless index && clause.compatible_with_index?(index) next unless index.geo == [:near, :intersects].include?(clause.op) args = case clause.op when :intersects then [:get_intersecting, clause.value.to_rql] when :near options = clause.value.dup circle = options.delete(:circle) options.delete(:max_results) if options[:max_results].nil? options[:max_dist] = circle.radius [:get_nearest, circle.center.to_rql, circle.options.merge(options)] when :eq then [:get_all, [clause.value]] when :in then [:get_all, clause.value] when :defined then next unless clause.value == true next unless clause.key_modifier == :scalar && index.multi == false [:between, [RethinkDB::RQL.new.minval, RethinkDB::RQL.new.maxval], :left_bound => :open, :right_bound => :open] when :between then [:between, [clause.value.min, clause.value.max], :left_bound => :closed, :right_bound => :closed] end IndexStrategy.new(self, ast, [clause], index, *args) end.compact.sort_by { |strat| usable_indexes.values.index(strat.index) }.first end
find_strategy_compound()
click to toggle source
# File lib/no_brainer/criteria/where.rb, line 438 def find_strategy_compound clauses = Hash[get_candidate_clauses(:eq).map { |c| [c.key_path, c] }] return nil unless clauses.present? get_usable_indexes(:kind => :compound, :geo => false, :multi => false).each do |index| indexed_clauses = index.what.map { |field| clauses[[field]] } next unless indexed_clauses.all? { |c| c.try(:compatible_with_index?, index) } return IndexStrategy.new(self, ast, indexed_clauses, index, :get_all, [indexed_clauses.map(&:value)]) end return nil end
find_strategy_compound_partial()
click to toggle source
# File lib/no_brainer/criteria/where.rb, line 450 def find_strategy_compound_partial clauses = get_candidate_clauses(:eq, :between).map { |c| [c.key_path, c] }.to_h return nil unless clauses.present? get_usable_indexes(:kind => :compound, :geo => false, :multi => false).each do |index| indexed_clauses = index.what.map { |field| clauses[[field]] } partial_clauses = indexed_clauses.compact pad = indexed_clauses.length - partial_clauses.length if partial_clauses.any? && partial_clauses.all? { |c| c.try(:compatible_with_index?, index) } # can only use partial compound index if: # * index contains all clause fields next unless (clauses.values & partial_clauses) == clauses.values # * all clause fields come first in the indexed clauses (unused indexed fields are at the end) next unless indexed_clauses.last(pad).all?(&:nil?) # * all clause fields are :eq, except the last (which may be :between) next unless partial_clauses[0..-2].all? { |c| c.op == :eq } # use range query to cover unused index fields left_bound = partial_clauses.map(&:value) right_bound = partial_clauses.map(&:value) if (clause = partial_clauses[-1]).op == :between left_bound[-1] = clause.value.min right_bound[-1] = clause.value.max end if pad > 0 left_bound.append *Array.new(pad, RethinkDB::RQL.new.minval) right_bound.append *Array.new(pad, RethinkDB::RQL.new.maxval) end return IndexStrategy.new(self, ast, partial_clauses, index, :between, [left_bound, right_bound], :left_bound => :closed, :right_bound => :closed) end end nil end
find_strategy_union()
click to toggle source
# File lib/no_brainer/criteria/where.rb, line 510 def find_strategy_union strategies = ast.clauses.map do |inner_ast| inner_ast = MultiOperator.new(:and, [inner_ast]) unless inner_ast.is_a?(MultiOperator) raise 'fatal' unless inner_ast.op == :and self.class.new(criteria, inner_ast).find_strategy end return nil if strategies.any?(&:nil?) rql_proc = lambda do |base_rql| strategies.map do |strategy| rql = strategy.rql_proc.call(base_rql) rql = rql.filter { |doc| strategy.ast.to_rql(doc) } if strategy.ast.try(:clauses).present? rql end.reduce(:union).distinct end Strategy.new(self, :union, strategies.map(&:index), nil, rql_proc) end
get_candidate_clauses(*types)
click to toggle source
# File lib/no_brainer/criteria/where.rb, line 393 def get_candidate_clauses(*types) BinaryOperator.get_candidate_clauses(ast.clauses, *types) end
get_usable_indexes(options={})
click to toggle source
# File lib/no_brainer/criteria/where.rb, line 397 def get_usable_indexes(options={}) indexes = criteria.model.indexes.values options.each { |k,v| indexes = indexes.select { |i| v == i.__send__(k) } } if criteria.options[:use_index] && criteria.options[:use_index] != true indexes = indexes.select { |i| i.name == criteria.options[:use_index].to_sym } end indexes end