class NoSE::Search::Results

A container for results from a schema search

Attributes

by_id_graph[RW]
command[RW]
cost_model[R]
enumerated_indexes[RW]
indexes[RW]
plans[RW]
revision[RW]
time[RW]
total_cost[RW]
total_size[RW]
update_plans[RW]
workload[RW]

Public Class Methods

new(problem = nil, by_id_graph = false) click to toggle source
# File lib/nose/search/results.rb, line 12
def initialize(problem = nil, by_id_graph = false)
  @problem = problem
  return if problem.nil?
  @by_id_graph = by_id_graph

  # Find the indexes the ILP says the query should use
  @query_indexes = Hash.new { |h, k| h[k] = Set.new }
  @problem.query_vars.each do |index, query_vars|
    query_vars.each do |query, var|
      next unless var.value
      @query_indexes[query].add index
    end
  end
end

Public Instance Methods

cost_model=(new_cost_model) click to toggle source

After setting the cost model, recalculate the cost @return [void]

# File lib/nose/search/results.rb, line 45
def cost_model=(new_cost_model)
  recalculate_cost new_cost_model
  @cost_model = new_cost_model
end
model() click to toggle source

Provide access to the underlying model in the workload @return [Model]

# File lib/nose/search/results.rb, line 29
def model
  @workload.nil? ? @model : @workload.model
end
model=(model) click to toggle source

Assign the model to the workload if it exists, otherwise store it @return [void]

# File lib/nose/search/results.rb, line 35
def model=(model)
  if @workload.nil?
    @model = model
  else
    @workload.instance_variable_set :@model, model
  end
end
plans_from_trees(trees) click to toggle source

Set the query plans which should be used based on the entire tree @return [void]

# File lib/nose/search/results.rb, line 95
def plans_from_trees(trees)
  @plans = trees.map do |tree|
    # Exclude support queries since they will be in update plans
    query = tree.query
    next if query.is_a?(SupportQuery)

    select_plan tree
  end.compact
end
recalculate_cost(new_cost_model = nil) click to toggle source

After setting the cost model, recalculate the cost @return [void]

# File lib/nose/search/results.rb, line 52
def recalculate_cost(new_cost_model = nil)
  new_cost_model = @cost_model if new_cost_model.nil?

  (@plans || []).each do |plan|
    plan.each { |s| s.calculate_cost new_cost_model }
  end
  (@update_plans || []).each do |plan|
    plan.update_steps.each { |s| s.calculate_cost new_cost_model }
    plan.query_plans.each do |query_plan|
      query_plan.each { |s| s.calculate_cost new_cost_model }
    end
  end

  # Recalculate the total
  query_cost = (@plans || []).sum_by do |plan|
    plan.cost * @workload.statement_weights[plan.query]
  end
  update_cost = (@update_plans || []).sum_by do |plan|
    plan.cost * @workload.statement_weights[plan.statement]
  end
  @total_cost = query_cost + update_cost
end
select_plan(tree) click to toggle source

Select the single query plan from a tree of plans @return [Plans::QueryPlan] @raise [InvalidResultsException]

# File lib/nose/search/results.rb, line 108
def select_plan(tree)
  query = tree.query
  plan = tree.find do |tree_plan|
    tree_plan.indexes.to_set == @query_indexes[query]
  end
  plan.instance_variable_set :@workload, @workload

  fail InvalidResultsException if plan.nil?
  plan
end
validate() click to toggle source

Validate that the results of the search are consistent @return [void]

# File lib/nose/search/results.rb, line 77
def validate
  validate_indexes
  validate_query_indexes @plans
  validate_update_indexes

  planned_queries = plans.map(&:query).to_set
  fail InvalidResultsException unless \
    (@workload.queries.to_set - planned_queries).empty?
  validate_query_plans @plans

  validate_update_plans
  validate_objective

  freeze
end

Private Instance Methods

validate_indexes() click to toggle source

Check that the indexes selected were actually enumerated @return [void]

# File lib/nose/search/results.rb, line 123
def validate_indexes
  # We may not have enumerated ID graphs
  check_indexes = @indexes.dup
  @indexes.each do |index|
    check_indexes.delete index.to_id_graph
  end if @by_id_graph

  fail InvalidResultsException unless \
    (check_indexes - @enumerated_indexes).empty?
end
validate_objective() click to toggle source

Check that the objective function has the expected value @return [void]

# File lib/nose/search/results.rb, line 146
def validate_objective
  if @problem.objective_type == Objective::COST
    query_cost = @plans.reduce 0 do |sum, plan|
      sum + @workload.statement_weights[plan.query] * plan.cost
    end
    update_cost = @update_plans.reduce 0 do |sum, plan|
      sum + @workload.statement_weights[plan.statement] * plan.cost
    end
    cost = query_cost + update_cost

    fail InvalidResultsException unless (cost - @total_cost).abs < 0.001
  elsif @problem.objective_type == Objective::SPACE
    size = @indexes.sum_by(&:size)
    fail InvalidResultsException unless (size - @total_size).abs < 0.001
  end
end
validate_query_indexes(plans) click to toggle source

Ensure that all the query plans use valid indexes @return [void]

# File lib/nose/search/results.rb, line 165
def validate_query_indexes(plans)
  plans.each do |plan|
    plan.each do |step|
      valid_plan = !step.is_a?(Plans::IndexLookupPlanStep) ||
                   @indexes.include?(step.index)
      fail InvalidResultsException unless valid_plan
    end
  end
end
validate_query_plans(plans) click to toggle source

Validate the query plans from the original workload @return [void]

# File lib/nose/search/results.rb, line 177
def validate_query_plans(plans)
  # Check that these indexes are actually used by the query
  plans.each do |plan|
    fail InvalidResultsException unless \
      plan.indexes.to_set == @query_indexes[plan.query]
  end
end
validate_update_indexes() click to toggle source

Ensure we only have necessary update plans which use available indexes @return [void]

# File lib/nose/search/results.rb, line 136
def validate_update_indexes
  @update_plans.each do |plan|
    validate_query_indexes plan.query_plans
    valid_plan = @indexes.include?(plan.index)
    fail InvalidResultsException unless valid_plan
  end
end
validate_update_plans() click to toggle source

Validate the support query plans for each update @return [void]

# File lib/nose/search/results.rb, line 187
def validate_update_plans
  @update_plans.each do |plan|
    plan.instance_variable_set :@workload, @workload

    validate_query_plans plan.query_plans
  end
end