Class: Mochigome::Query

Inherits:
Object
  • Object
show all
Defined in:
lib/query.rb

Instance Method Summary collapse

Constructor Details

#initialize(layers, options = {}) ⇒ Query

Returns a new instance of Query.



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/query.rb', line 3

def initialize(layers, options = {})
  @name = options.delete(:root_name).try(:to_s) || "report"
  access_filter = options.delete(:access_filter) || lambda {|cls| {}}
  aggregate_sources = options.delete(:aggregate_sources) || {}
  @fieldsets = options.delete(:fieldsets) || {}
  unless options.empty?
    raise QueryError.new("Unknown options: #{options.keys.inspect}")
  end

  if layers.is_a? Array
    layer_paths = [layers]
  else
    unless layers.is_a?(Hash) &&
    layers.size == 1 &&
    layers.keys.first == :report
      raise QueryError.new("Invalid layer tree")
    end
    layer_paths = Query.tree_root_to_leaf_paths(layers.values.first)
  end
  layer_paths = [[]] if layer_paths.empty? # Create at least one QueryLine
  @lines = layer_paths.map{ |path| QueryLine.new(path, access_filter) }

  # Given a simple array of aggregates, just apply to every point
  if aggregate_sources.is_a?(Array)
    as_hash = ActiveSupport::OrderedHash.new
    aggregate_sources.each do |a|
      layer_paths.each do |path|
        (0..path.size).each do |len|
          key = [:report] + path.take(len)
          as_hash[key] ||= []
          as_hash[key] << a unless as_hash[key].include?(a)
        end
      end
    end
    aggregate_sources = as_hash
  end

  agg_only_lines = []
  aggregate_sources.each do |path, aggs|
    models_path = path.drop(1) # Remove the :report symbol at the start
    tgt_line = @lines.find do |line|
      line.layer_path.take(models_path.size) == models_path
    end
    unless tgt_line
      raise QueryError.new("Cannot find path #{path.inspect} for agg")
    end
    aggs.each do |a|
      tgt_line.add_aggregate_source(models_path.size, a)
      if tgt_line.layer_path != models_path
        # FIXME Copy&paste from add_aggregate_source
        if a.is_a?(Array) then
          focus_model = a.select{|e| e.is_a?(Class)}.first
          data_model = a.select{|e| e.is_a?(Class)}.last
        else
          focus_model = data_model = a
        end

        line = QueryLine.new(models_path, access_filter)
        line.ids_rel.join_on_path_thru([focus_model, data_model])
        line.ids_rel.apply_condition data_model.arel_primary_key.not_eq(nil)
        agg_only_lines << line
      end
    end
  end
  @lines += agg_only_lines
end

Instance Method Details

#run(cond = nil) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/query.rb', line 70

def run(cond = nil)
  model_ids = {}
  parental_seqs = {}
  @lines.each do |line|
    tbl = line.build_id_table(cond)
    parent_models = []
    line.layer_path.each do |model|
      tbl.each do |ids_row|
        i = ids_row["#{model.name}_id"]
        if i
          (model_ids[model] ||= Set.new).add(i)
          parental_seq_key = parent_models.zip(
            parent_models.map{|pm| ids_row["#{pm}_id"]}
          )
          (parental_seqs[parental_seq_key] ||= Set.new).add([model.name, i])
        end
      end
      parent_models << model.name
    end
  end

  model_datanodes = generate_datanodes(model_ids)
  root = create_root_node
  add_datanode_children([], root, model_datanodes, parental_seqs)
  @lines.each do |line|
    line.load_aggregate_data(root, cond)
  end
  return root
end