Module: Snaptime::Harvester

Defined in:
lib/snaptime/harvester.rb

Class Method Summary collapse

Class Method Details

.harvest_for(record) ⇒ Object



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
69
70
71
72
73
# File 'lib/snaptime/harvester.rb', line 3

def self.harvest_for(record)
  # ---------------------------------------------------------------
  # Build individual selects for each combination of table,
  # key fields and values.
  # ---------------------------------------------------------------
  selects = []

  queries = snaptime_queries(record.class, [record.natural_id], nil)

  queries.each do |klass, keys_and_values|
    keys_and_values.each do |key, values|
      selects << select_for(klass, key, values)
    end
  end

  # ---------------------------------------------------------------
  # Build master select that unions all of the above queries
  # TODO: Probably add a separate option for coalesce to avoid
  #   doubling the code for max and min.
  # ---------------------------------------------------------------
  rel = Snaptime.model_class
  rel = rel.from(
    "#{union_selects(*selects)} inner_snaptimes"
  )

  # TODO: Move to DB-specific adapter
  rel = rel.select(Arel.sql("LISTAGG(record_lookups, ';') WITHIN GROUP(ORDER BY record_lookups)").as('record_lookups'))

  Snaptime.consolidation_fields.each do |field, options|
    if options[:aggregate_with].nil?
      rel = rel.select(field)
    elsif options[:aggregate_with] == :max
      rel = rel.select(Arel.sql(field.to_s).maximum.as(field.to_s))
    elsif options[:aggregate_with] == :max_coalesce_0
      rel = rel.select(
        Arel.sql(
          Arel::Nodes::NamedFunction.new('coalesce', [Arel.sql(field.to_s), Arel.sql(0.to_s)]).to_sql
        ).maximum.as(field.to_s)
      )
    elsif options[:aggregate_with] == :min
      rel = rel.select(Arel.sql(field.to_s).minimum.as(field.to_s))
    elsif options[:aggregate_with] == :min_coalesce_0
      rel = rel.select(
        Arel.sql(
          Arel::Nodes::NamedFunction.new('coalesce', [Arel.sql(field.to_s), Arel.sql(0.to_s)]).to_sql
        ).minimum.as(field.to_s)
      )
    elsif options[:aggregate_with] == :sum
      rel = rel.select(Arel.sql(field.to_s).sum.as(field.to_s))
    end
  end

  grouping_fields = Snaptime.consolidation_fields.select { |_k, v| v[:aggregate_with].nil? }.keys.collect(&:to_s)
  # rel = rel.order('inner_snaptimes.valid_from desc')
  rel = rel.order(Arel::Table.new(:inner_snaptimes)[:valid_from].desc)
  rel = rel.group(*grouping_fields)

  # ---------------------------------------------------------------
  # Wrap master select in another select so that outer orders,
  # wheres and counts work out-of-the-box.
  # ---------------------------------------------------------------
  all_keys = [:record_lookups] + Snaptime.consolidation_fields.keys

  all_fields = all_keys.collect do |key|
    Arel::Table.new(:snaptimes)[key]
  end

  outer_rel = Snaptime.model_class.select(all_fields).from("(#{rel.to_sql}) snaptimes")

  return outer_rel
end