class Activecube::Processor::Composer

Attributes

cube_query[R]
models[R]
query[R]

Public Class Methods

new(cube_query) click to toggle source
# File lib/activecube/processor/composer.rb, line 11
def initialize cube_query
  @cube_query = cube_query
end

Public Instance Methods

build_query() click to toggle source
# File lib/activecube/processor/composer.rb, line 15
def build_query
  @query = compose_queries optimize! ranked_tables
end
connection() click to toggle source
# File lib/activecube/processor/composer.rb, line 19
def connection
  connections = models.map(&:connection).compact.uniq
  raise "No connection found for query" if connections.empty?
  raise "Tables #{models.map(&:name).join(',')} mapped to multiple connections, can not query" if connections.count>1
  connections.first
end

Private Instance Methods

compose_queries(measure_tables) click to toggle source
# File lib/activecube/processor/composer.rb, line 72
def compose_queries measure_tables
  composed_query  = nil
  @models = []
  measures_by_tables = measure_tables.group_by(&:table)
  measures_by_tables.each_pair do |table, list|
    @models << table.model
    reduce_options = measures_by_tables.count==1 ? cube_query.options : []
    reduced = cube_query.reduced list.map(&:measure), reduce_options
    table_query = table.query reduced
    composed_query = composed_query ? table.join(cube_query, composed_query, table_query) : table_query
  end
  composed_query
end
optimize!(measure_tables) click to toggle source
# File lib/activecube/processor/composer.rb, line 28
def optimize! measure_tables

  all_tables = measure_tables.map(&:tables).map(&:keys).flatten.uniq

  cost_matrix = measure_tables.collect do |measure_table|
    all_tables.collect{|table|
      measure_table.tables[table].try(&:cost)
    }
  end

  before = total_cost measure_tables
  Optimizer.new(cost_matrix).optimize.each_with_index do |optimal, index|
    measure_tables[index].selected = measure_tables[index].entries.map(&:table).index(all_tables[optimal])
  end
  after = total_cost measure_tables

  raise "Optimizer made it worse #{before} -> #{after} for #{cost_matrix}" unless after <= before
  measure_tables

end
ranked_tables() click to toggle source
# File lib/activecube/processor/composer.rb, line 53
def ranked_tables
  tables = cube_query.tables.select{|table| table.matches? cube_query, []}
  measures = cube_query.measures.empty? ?
                 [Activecube::Query::MeasureNothing.new(cube_query.cube)] :
                 cube_query.measures
  measures.collect do |measure|
    by = MeasureTables.new measure
    tables.each{|table|
      next unless table.measures? measure
      max_cardinality_index = table.model.activecube_indexes.select{|index|
        index.indexes? cube_query, [measure]
      }.sort_by(&:cardinality).last
      by.add_table table, max_cardinality_index
    }
    raise "Metric #{measure.key} #{measure.definition.try(:name) || measure.class.name} can not be measured by any of tables #{tables.map(&:name).join(',')}" if by.tables.empty?
    by
  end
end
total_cost(measure_tables) click to toggle source
# File lib/activecube/processor/composer.rb, line 49
def total_cost measure_tables
  measure_tables.group_by(&:table).collect{|t| t.second.map(&:entry).map(&:cost).max }.sum
end