Class: NoBrainer::Criteria::Where::IndexFinder
- Inherits:
-
Struct
- Object
- Struct
- NoBrainer::Criteria::Where::IndexFinder
- Defined in:
- lib/no_brainer/criteria/where.rb
Defined Under Namespace
Classes: IndexStrategy, Strategy
Instance Method Summary collapse
- #find_strategy ⇒ Object
- #find_strategy! ⇒ Object
- #find_strategy_canonical ⇒ Object
- #find_strategy_compound ⇒ Object
- #find_strategy_compound_partial ⇒ Object
- #find_strategy_hidden_between ⇒ Object
- #find_strategy_union ⇒ Object
- #get_candidate_clauses(*types) ⇒ Object
- #get_usable_indexes(options = {}) ⇒ Object
Instance Method Details
#find_strategy ⇒ Object
530 531 532 533 534 535 536 |
# 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! ⇒ Object
538 539 540 |
# File 'lib/no_brainer/criteria/where.rb', line 538 def find_strategy! self.strategy = find_strategy end |
#find_strategy_canonical ⇒ Object
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# 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 = clause.value.dup circle = .delete(:circle) .delete(:max_results) if [:max_results].nil? [:max_dist] = circle.radius [:get_nearest, circle.center.to_rql, circle..merge()] 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 ⇒ Object
438 439 440 441 442 443 444 445 446 447 448 |
# 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 ⇒ Object
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
# 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_hidden_between ⇒ Object
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 |
# File 'lib/no_brainer/criteria/where.rb', line 484 def find_strategy_hidden_between clauses = get_candidate_clauses(:gt, :ge, :lt, :le).group_by(&:key_path) return nil unless clauses.present? get_usable_indexes(:geo => false).each do |index| matched_clauses = clauses[[index.name]].try(:select) { |c| c.compatible_with_index?(index) } next unless matched_clauses.present? op_clauses = Hash[matched_clauses.map { |c| [c.op, c] }] left_bound = op_clauses[:gt] || op_clauses[:ge] right_bound = op_clauses[:lt] || op_clauses[:le] # XXX we must keep only one bound when using `any', otherwise we get different semantics. right_bound = nil if index.multi && left_bound && right_bound = {} [:left_bound] = {:gt => :open, :ge => :closed}[left_bound.op] if left_bound [:right_bound] = {:lt => :open, :le => :closed}[right_bound.op] if right_bound return IndexStrategy.new(self, ast, [left_bound, right_bound].compact, index, :between, [left_bound ? left_bound.try(:value) : RethinkDB::RQL.new.minval, right_bound ? right_bound.try(:value) : RethinkDB::RQL.new.maxval], ) end return nil end |
#find_strategy_union ⇒ Object
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 |
# 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) ⇒ Object
393 394 395 |
# 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 = {}) ⇒ Object
397 398 399 400 401 402 403 404 |
# File 'lib/no_brainer/criteria/where.rb', line 397 def get_usable_indexes(={}) indexes = criteria.model.indexes.values .each { |k,v| indexes = indexes.select { |i| v == i.__send__(k) } } if criteria.[:use_index] && criteria.[:use_index] != true indexes = indexes.select { |i| i.name == criteria.[:use_index].to_sym } end indexes end |