activewarehouse 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +27 -14
- data/Rakefile +16 -5
- data/doc/references.txt +4 -0
- data/generators/bridge/templates/migration.rb +9 -2
- data/generators/bridge/templates/unit_test.rb +8 -0
- data/generators/date_dimension/USAGE +1 -0
- data/generators/date_dimension/date_dimension_generator.rb +16 -0
- data/generators/date_dimension/templates/fixture.yml +5 -0
- data/generators/date_dimension/templates/migration.rb +31 -0
- data/generators/date_dimension/templates/model.rb +3 -0
- data/generators/date_dimension/templates/unit_test.rb +8 -0
- data/generators/dimension/templates/migration.rb +1 -10
- data/generators/dimension_view/dimension_view_generator.rb +2 -2
- data/generators/dimension_view/templates/migration.rb +8 -2
- data/generators/fact/templates/migration.rb +2 -0
- data/generators/time_dimension/USAGE +1 -0
- data/generators/time_dimension/templates/fixture.yml +5 -0
- data/generators/time_dimension/templates/migration.rb +12 -0
- data/generators/time_dimension/templates/model.rb +3 -0
- data/generators/time_dimension/templates/unit_test.rb +8 -0
- data/generators/time_dimension/time_dimension_generator.rb +14 -0
- data/lib/active_warehouse.rb +13 -2
- data/lib/active_warehouse/aggregate.rb +54 -253
- data/lib/active_warehouse/aggregate/dwarf/node.rb +36 -0
- data/lib/active_warehouse/aggregate/dwarf_aggregate.rb +369 -0
- data/lib/active_warehouse/aggregate/dwarf_common.rb +44 -0
- data/lib/active_warehouse/aggregate/dwarf_printer.rb +34 -0
- data/lib/active_warehouse/aggregate/no_aggregate.rb +194 -0
- data/lib/active_warehouse/aggregate/pid_aggregate.rb +29 -0
- data/lib/active_warehouse/aggregate/pipelined_rolap_aggregate.rb +129 -0
- data/lib/active_warehouse/aggregate/rolap_aggregate.rb +181 -0
- data/lib/active_warehouse/aggregate/rolap_common.rb +89 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_1.sql +12 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_10.sql +7166 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_11.sql +14334 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_12.sql +28670 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_13.sql +57342 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_2.sql +26 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_3.sql +54 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_4.sql +110 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_5.sql +222 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_6.sql +446 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_7.sql +894 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_8.sql +1790 -0
- data/lib/active_warehouse/aggregate/templates/pipelined_rollup_9.sql +3582 -0
- data/lib/active_warehouse/aggregate_field.rb +49 -0
- data/lib/active_warehouse/{dimension/bridge.rb → bridge.rb} +7 -3
- data/lib/active_warehouse/bridge/hierarchy_bridge.rb +46 -0
- data/lib/active_warehouse/builder.rb +2 -1
- data/lib/active_warehouse/builder/date_dimension_builder.rb +5 -2
- data/lib/active_warehouse/builder/generator/generator.rb +13 -0
- data/lib/active_warehouse/builder/generator/name_generator.rb +20 -0
- data/lib/active_warehouse/builder/generator/paragraph_generator.rb +11 -0
- data/lib/active_warehouse/builder/random_data_builder.rb +21 -11
- data/lib/active_warehouse/builder/test_data_builder.rb +54 -0
- data/lib/active_warehouse/calculated_field.rb +27 -0
- data/lib/active_warehouse/compat/compat.rb +4 -4
- data/lib/active_warehouse/cube.rb +126 -225
- data/lib/active_warehouse/cube_query_result.rb +69 -0
- data/lib/active_warehouse/dimension.rb +64 -29
- data/lib/active_warehouse/dimension/date_dimension.rb +15 -0
- data/lib/active_warehouse/dimension/dimension_reflection.rb +21 -0
- data/lib/active_warehouse/dimension/dimension_view.rb +17 -2
- data/lib/active_warehouse/dimension/hierarchical_dimension.rb +43 -5
- data/lib/active_warehouse/dimension/slowly_changing_dimension.rb +22 -12
- data/lib/active_warehouse/fact.rb +119 -40
- data/lib/active_warehouse/field.rb +74 -0
- data/lib/active_warehouse/ordered_hash.rb +34 -0
- data/lib/active_warehouse/prejoin_fact.rb +97 -0
- data/lib/active_warehouse/report/abstract_report.rb +40 -14
- data/lib/active_warehouse/report/chart_report.rb +3 -3
- data/lib/active_warehouse/report/table_report.rb +8 -3
- data/lib/active_warehouse/version.rb +1 -1
- data/lib/active_warehouse/view/report_helper.rb +144 -34
- data/tasks/active_warehouse_tasks.rake +28 -10
- metadata +107 -30
@@ -1,16 +1,15 @@
|
|
1
1
|
module ActiveWarehouse #:nodoc
|
2
|
-
|
3
|
-
#
|
2
|
+
|
3
|
+
# Facts represent business measures. A row in a fact table corresponds to set
|
4
|
+
# of measurements in a particular
|
5
|
+
# granularity along with the foreign keys connecting the fact to various
|
6
|
+
# dimensions. All measurements in a fact
|
4
7
|
# table must be at the same grain.
|
5
8
|
class Fact < ActiveRecord::Base
|
6
9
|
class << self
|
7
|
-
# Array of
|
10
|
+
# Array of AggregateField instances
|
8
11
|
attr_accessor :aggregate_fields
|
9
12
|
|
10
|
-
# Hash of aggregate field options, where the key is the field name
|
11
|
-
# and the value is the Hash of options for that aggregate.
|
12
|
-
attr_accessor :aggregate_field_options
|
13
|
-
|
14
13
|
# Array of calculated field names
|
15
14
|
attr_accessor :calculated_fields
|
16
15
|
|
@@ -18,6 +17,42 @@ module ActiveWarehouse #:nodoc
|
|
18
17
|
# and the value is the Hash of options for that calculated field.
|
19
18
|
attr_accessor :calculated_field_options
|
20
19
|
|
20
|
+
# Array of belongs_to +Reflection+ instances that represent the
|
21
|
+
# dimensions for this fact.
|
22
|
+
attr_accessor :dimension_relationships
|
23
|
+
|
24
|
+
# Acts as an alias for +belongs_to+, yet marks this relationship
|
25
|
+
# as a dimension. You must call +dimension+ instead of +belongs_to+.
|
26
|
+
# Accepts same options as +belongs_to+.
|
27
|
+
def dimension(association_id, options = {})
|
28
|
+
options[:class_name] ||= "#{association_id}Dimension".classify
|
29
|
+
options[:foreign_key] ||= "#{association_id}_id"
|
30
|
+
slowly_changing_over = options.delete(:slowly_changing)
|
31
|
+
belongs_to association_id, options
|
32
|
+
dimension_relationship = reflections[association_id]
|
33
|
+
|
34
|
+
if slowly_changing_over
|
35
|
+
if !dimensions.include?(slowly_changing_over)
|
36
|
+
raise "No dimension specified with name '#{slowly_changing_over}' in fact '#{self.name}', specify it first with dimension macro"
|
37
|
+
end
|
38
|
+
dimension_relationship.slowly_changing_over = dimension_relationships[slowly_changing_over]
|
39
|
+
end
|
40
|
+
|
41
|
+
dimension_relationships[association_id] = dimension_relationship
|
42
|
+
end
|
43
|
+
|
44
|
+
# returns the dimension name (as specified in the dimension macro)
|
45
|
+
# which the specified +dimension_name+ is slowly changing over
|
46
|
+
def slowly_changes_over_name(dimension_name)
|
47
|
+
dimension_relationships[dimension_name].slowly_changing_over.name
|
48
|
+
end
|
49
|
+
|
50
|
+
# returns the Class for the dimension which the specified
|
51
|
+
# +dimension_name+ is slowly changing over
|
52
|
+
def slowly_changes_over_class(dimension_name)
|
53
|
+
dimension_class(slowly_changes_over_name(dimension_name))
|
54
|
+
end
|
55
|
+
|
21
56
|
# Return a list of dimensions for this fact.
|
22
57
|
#
|
23
58
|
# Example:
|
@@ -30,27 +65,13 @@ module ActiveWarehouse #:nodoc
|
|
30
65
|
#
|
31
66
|
# Calling SalesFact.dimensions would return the list: [:date, :region]
|
32
67
|
def dimensions
|
33
|
-
|
68
|
+
dimension_relationships.collect { |k,v| k }
|
34
69
|
end
|
35
70
|
|
36
|
-
#
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
# sales_fact
|
41
|
-
# date_id
|
42
|
-
# region_id
|
43
|
-
# sales_amount
|
44
|
-
# number_items_sold
|
45
|
-
#
|
46
|
-
# Calling SalesFact.foreign_key_columns would return a list of column objects containing
|
47
|
-
# the date column and the region column.
|
48
|
-
def foreign_key_columns
|
49
|
-
fk_columns = []
|
50
|
-
columns.each do |column|
|
51
|
-
fk_columns << column if column.name =~ /(.*)_id/
|
52
|
-
end
|
53
|
-
fk_columns
|
71
|
+
# Returns the dimension class, given a dimension name from this fact.
|
72
|
+
# Must appear as a registered dimension relationship.
|
73
|
+
def dimension_class(dimension_name)
|
74
|
+
dimension_relationships[dimension_name.to_sym].class_name.constantize
|
54
75
|
end
|
55
76
|
|
56
77
|
# Get the time when the fact source file was last modified
|
@@ -79,21 +100,41 @@ module ActiveWarehouse #:nodoc
|
|
79
100
|
|
80
101
|
# Get the fact class for the specified value. The fact parameter may be a class,
|
81
102
|
# String or Symbol.
|
82
|
-
def to_fact(
|
83
|
-
return
|
84
|
-
return class_for_name(
|
103
|
+
def to_fact(fact_name)
|
104
|
+
return fact_name if fact_name.is_a?(Class) and fact_name.superclass == Fact
|
105
|
+
return class_for_name(fact_name)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Return the foreign key that the fact uses to relate back to the specified
|
109
|
+
# dimension. This is found using the dimension_relationships hash.
|
110
|
+
def foreign_key_for(dimension_name)
|
111
|
+
dimension_relationships[dimension_name].primary_key_name
|
85
112
|
end
|
86
113
|
|
87
114
|
# Define an aggregate. Also aliased from aggregate()
|
88
115
|
# * <tt>field</tt>: The field name
|
89
116
|
# * <tt>options</tt>: A hash of options for the aggregate
|
90
117
|
def define_aggregate(field, options={})
|
91
|
-
|
118
|
+
if columns_hash[field.to_s].nil?
|
119
|
+
raise ArgumentError, "Field #{field} does not exist in table #{table_name}"
|
120
|
+
end
|
92
121
|
options[:type] ||= :sum
|
93
|
-
|
122
|
+
|
123
|
+
aggregate_field = AggregateField.new(self, columns_hash[field.to_s],
|
124
|
+
options[:type], options)
|
125
|
+
aggregate_fields << aggregate_field
|
94
126
|
end
|
95
127
|
alias :aggregate :define_aggregate
|
96
128
|
|
129
|
+
# Define prejoined fields from a dimension of the fact. Also aliased
|
130
|
+
# from prejoin()
|
131
|
+
# * <tt>field</tt>: A hash with the key of dimension and an array
|
132
|
+
# of attributes from the dimension as value
|
133
|
+
def define_prejoin(field)
|
134
|
+
prejoined_fields.merge!(field)
|
135
|
+
end
|
136
|
+
alias :prejoin :define_prejoin
|
137
|
+
|
97
138
|
# Define a calculated field
|
98
139
|
# * <tt>field</tt>: The field name
|
99
140
|
# * <tt>options</tt>: An options hash
|
@@ -102,9 +143,16 @@ module ActiveWarehouse #:nodoc
|
|
102
143
|
#
|
103
144
|
# Example: calculated_field (:gross_margin) { |r| r.gross_profit_dollar_amount / r.sales_dollar_amount}
|
104
145
|
def calculated_field(field, options={}, &block)
|
105
|
-
calculated_fields << field
|
106
|
-
|
107
|
-
|
146
|
+
calculated_fields << CalculatedField.new(self, field, options[:type], options, &block)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns true if this fact has at least one fact that is semiadditive,
|
150
|
+
# or false
|
151
|
+
def has_semiadditive_fact?
|
152
|
+
aggregate_fields.each do |field|
|
153
|
+
return true if field.is_semiadditive?
|
154
|
+
end
|
155
|
+
return false
|
108
156
|
end
|
109
157
|
|
110
158
|
# Get a list of all calculated fields
|
@@ -112,9 +160,9 @@ module ActiveWarehouse #:nodoc
|
|
112
160
|
@calculated_field ||= []
|
113
161
|
end
|
114
162
|
|
115
|
-
# Get
|
116
|
-
def
|
117
|
-
|
163
|
+
# Get the CalculatedField instance for the specified name
|
164
|
+
def calculated_field_for_name(name)
|
165
|
+
calculated_fields.find {|f| f.name.to_s == name.to_s}
|
118
166
|
end
|
119
167
|
|
120
168
|
# Get a list of all aggregate fields
|
@@ -122,10 +170,41 @@ module ActiveWarehouse #:nodoc
|
|
122
170
|
@aggregate_fields ||= []
|
123
171
|
end
|
124
172
|
|
125
|
-
# Get
|
126
|
-
def
|
127
|
-
|
173
|
+
# Get the AggregateField instance for the specified name.
|
174
|
+
def aggregate_field_for_name(name)
|
175
|
+
aggregate_fields.find {|f| f.name.to_s == name.to_s}
|
176
|
+
end
|
177
|
+
|
178
|
+
# Get the field instance for the specified name. Looks in aggregate fields first, then
|
179
|
+
# calculated fields
|
180
|
+
def field_for_name(name)
|
181
|
+
field = aggregate_fields.find {|f| f.name.to_s == name.to_s}
|
182
|
+
field = calculated_fields.find {|f| f.name.to_s == name.to_s} unless field
|
183
|
+
field
|
184
|
+
end
|
185
|
+
|
186
|
+
# The table name to use for the prejoined fact table
|
187
|
+
def prejoined_table_name
|
188
|
+
"prejoined_#{table_name}"
|
189
|
+
end
|
190
|
+
|
191
|
+
# Get the hash of all prejoined fields
|
192
|
+
def prejoined_fields
|
193
|
+
@prejoined_fields ||= {}
|
194
|
+
end
|
195
|
+
|
196
|
+
def dimension_relationships
|
197
|
+
@dimension_relationships ||= OrderedHash.new
|
198
|
+
end
|
199
|
+
|
200
|
+
def prejoin_fact
|
201
|
+
@prejoin_fact ||= ActiveWarehouse::PrejoinFact.new(self)
|
202
|
+
end
|
203
|
+
|
204
|
+
def populate
|
205
|
+
prejoin_fact.populate
|
128
206
|
end
|
207
|
+
|
129
208
|
end
|
130
209
|
end
|
131
210
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ActiveWarehouse #:nodoc:
|
2
|
+
# Encapsulates a field.
|
3
|
+
class Field
|
4
|
+
# The owning class which is either a Fact or Dimension
|
5
|
+
attr_reader :owning_class
|
6
|
+
|
7
|
+
# The field name
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
# The field type
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
attr_accessor :limit
|
14
|
+
attr_accessor :scale
|
15
|
+
attr_accessor :precision
|
16
|
+
|
17
|
+
# A Hash of options for the field
|
18
|
+
attr_reader :field_options
|
19
|
+
|
20
|
+
# +owning_class+ is the class of the table, either Fact or Dimension, that
|
21
|
+
# this field is found in. Must somehow subclass ActiveRecord::Base
|
22
|
+
# +name+ is the name of this field.
|
23
|
+
# +field_options+ is a hash of raw options from the original definition.
|
24
|
+
# Options can include :label => a column alias or label for this field,
|
25
|
+
# :table_alias for a table alias (useful for building queries)
|
26
|
+
def initialize(owning_class, name, type, field_options = {})
|
27
|
+
@owning_class = owning_class
|
28
|
+
@name = name
|
29
|
+
@type = type
|
30
|
+
@field_options = field_options
|
31
|
+
@label = field_options[:label]
|
32
|
+
@table_alias = field_options[:table_alias]
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns the :label set in field_options, or from_table_name+'_'+name.
|
36
|
+
# Unless you have table_alias specified, then label will return table_alias+'_'+name.
|
37
|
+
# The default label can exceed database limits, so use :label to override.
|
38
|
+
def label
|
39
|
+
@label ? @label : "#{table_alias || from_table_name}_#{name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# returns name of this field, matches name of the column
|
43
|
+
def name
|
44
|
+
@name
|
45
|
+
end
|
46
|
+
|
47
|
+
# returns rails specific column type, e.g. :float or :string
|
48
|
+
def column_type
|
49
|
+
@type
|
50
|
+
end
|
51
|
+
|
52
|
+
# convert the label into something we can use in a table.
|
53
|
+
# i.e., 'Sum of Transactions' becomes 'sum_of_transactions'
|
54
|
+
def label_for_table
|
55
|
+
label.gsub(/ /, '_').downcase
|
56
|
+
end
|
57
|
+
|
58
|
+
# returns the table name that has this fact column
|
59
|
+
def from_table_name
|
60
|
+
owning_class.table_name
|
61
|
+
end
|
62
|
+
|
63
|
+
# returns a table alias or if none set just the table name
|
64
|
+
def table_alias
|
65
|
+
@table_alias || from_table_name
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get a display string for the field. Delegates to label.
|
69
|
+
def to_s
|
70
|
+
label
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActiveWarehouse
|
2
|
+
# Simple ordered hash implementation
|
3
|
+
class OrderedHash < Hash
|
4
|
+
alias_method :store, :[]=
|
5
|
+
alias_method :each_pair, :each
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@keys = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, val)
|
12
|
+
@keys << key
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete(key)
|
17
|
+
@keys.delete(key)
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
@keys.each { |k| yield k, self[k] }
|
23
|
+
end
|
24
|
+
|
25
|
+
def each_key
|
26
|
+
@keys.each { |k| yield k }
|
27
|
+
end
|
28
|
+
|
29
|
+
def each_value
|
30
|
+
@keys.each { |k| yield self[k] }
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module ActiveWarehouse #:nodoc:
|
2
|
+
# Class that supports prejoining a fact table with dimensions. This is useful if you need
|
3
|
+
# to list facts along with some or all of their detail information.
|
4
|
+
class PrejoinFact
|
5
|
+
# The fact class that this engine instance is connected to
|
6
|
+
attr_accessor :fact_class
|
7
|
+
|
8
|
+
delegate :prejoined_table_name,
|
9
|
+
:connection,
|
10
|
+
:prejoined_fields,
|
11
|
+
:dimension_relationships,
|
12
|
+
:dimension_class,
|
13
|
+
:table_name,
|
14
|
+
:columns, :to => :fact_class
|
15
|
+
|
16
|
+
# Initialize the engine instance
|
17
|
+
def initialize(fact_class)
|
18
|
+
@fact_class = fact_class
|
19
|
+
end
|
20
|
+
|
21
|
+
# Populate the prejoined fact table.
|
22
|
+
def populate(options={})
|
23
|
+
populate_prejoined_fact_table(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
# Drop the storage table
|
28
|
+
def drop_prejoin_fact_table
|
29
|
+
connection.drop_table(prejoined_table_name) if connection.tables.include?(prejoined_table_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get foreign key names that are excluded.
|
33
|
+
def excluded_foreign_key_names
|
34
|
+
excluded_dimension_relations = prejoined_fields.keys.collect {|k| dimension_relationships[k]}
|
35
|
+
excluded_dimension_relations.collect {|r| r.primary_key_name}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Construct the prejoined fact table.
|
39
|
+
def create_prejoined_fact_table(options={})
|
40
|
+
connection.transaction {
|
41
|
+
drop_prejoin_fact_table
|
42
|
+
|
43
|
+
connection.create_table(prejoined_table_name, :id => false) do |t|
|
44
|
+
# get all columns except the foreign_key columns for prejoined dimensions
|
45
|
+
columns.each do |c|
|
46
|
+
t.column(c.name, c.type) unless excluded_foreign_key_names.include?(c.name)
|
47
|
+
end
|
48
|
+
#prejoined_columns
|
49
|
+
prejoined_fields.each_pair do |key, value|
|
50
|
+
dclass = dimension_class(key)
|
51
|
+
dclass.columns.each do |c|
|
52
|
+
t.column(c.name, c.type) if value.include?(c.name.to_sym)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Populate the prejoined fact table.
|
60
|
+
def populate_prejoined_fact_table(options={})
|
61
|
+
fact_columns_string = columns.collect {|c|
|
62
|
+
"#{table_name}." + c.name unless excluded_foreign_key_names.include?(c.name)
|
63
|
+
}.compact.join(",\n")
|
64
|
+
|
65
|
+
prejoined_columns = []
|
66
|
+
|
67
|
+
tables_and_joins = "#{table_name}"
|
68
|
+
|
69
|
+
prejoined_fields.each_pair do |key, value|
|
70
|
+
dimension = dimension_class(key)
|
71
|
+
tables_and_joins += "\nJOIN #{dimension.table_name} as #{key}"
|
72
|
+
tables_and_joins += "\n ON #{table_name}.#{dimension_relationships[key].primary_key_name} = "
|
73
|
+
tables_and_joins += "#{key}.#{dimension.primary_key}"
|
74
|
+
prejoined_columns << value.collect {|v| "#{key}." + v.to_s}
|
75
|
+
end
|
76
|
+
|
77
|
+
if connection.support_select_into_table?
|
78
|
+
drop_prejoin_fact_table
|
79
|
+
sql = <<-SQL
|
80
|
+
SELECT #{fact_columns_string},
|
81
|
+
#{prejoined_columns.join(",\n")}
|
82
|
+
INTO #{prejoined_table_name}
|
83
|
+
FROM #{tables_and_joins}
|
84
|
+
SQL
|
85
|
+
else
|
86
|
+
create_prejoined_fact_table(options)
|
87
|
+
sql = <<-SQL
|
88
|
+
INSERT INTO #{prejoined_table_name}
|
89
|
+
SELECT #{fact_columns_string},
|
90
|
+
#{prejoined_columns.join(",\n")}
|
91
|
+
FROM #{tables_and_joins}
|
92
|
+
SQL
|
93
|
+
end
|
94
|
+
connection.transaction { connection.execute(sql) }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -1,12 +1,25 @@
|
|
1
|
-
module ActiveWarehouse
|
2
|
-
module Report
|
1
|
+
module ActiveWarehouse #:nodoc:
|
2
|
+
module Report #:nodoc:
|
3
|
+
# Base module for reports.
|
3
4
|
module AbstractReport
|
4
5
|
|
6
|
+
# Array of parameters which will be passed
|
5
7
|
attr_accessor :pass_params
|
8
|
+
|
9
|
+
# A Hash of level names mapped to a method that is used to filter the available
|
10
|
+
# column values
|
11
|
+
attr_accessor :column_filters
|
12
|
+
|
13
|
+
# A Hash of level names mapped to a method that is used to filter the available
|
14
|
+
# row values
|
15
|
+
attr_accessor :row_filters
|
16
|
+
|
17
|
+
# An optional conditions String
|
18
|
+
attr_accessor :conditions
|
6
19
|
|
7
20
|
# Set the cube name
|
8
21
|
def cube_name=(name)
|
9
|
-
|
22
|
+
write_attribute(:cube_name, name)
|
10
23
|
@cube = nil
|
11
24
|
end
|
12
25
|
|
@@ -29,12 +42,19 @@ module ActiveWarehouse
|
|
29
42
|
@column_dimension_class ||= ActiveWarehouse::Dimension.class_name(self.column_dimension_name).constantize
|
30
43
|
end
|
31
44
|
|
45
|
+
# Get the column hierarchy. Uses the first hierarchy in the column dimension if not specified
|
32
46
|
def column_hierarchy
|
33
|
-
|
47
|
+
ch = read_attribute(:column_hierarchy)
|
48
|
+
if ch.nil? || ch == 'NULL'
|
49
|
+
column_dimension_class.hierarchies.first
|
50
|
+
else
|
51
|
+
ch
|
52
|
+
end
|
34
53
|
end
|
35
54
|
|
55
|
+
# Get the column prefix. Returns 'c' if not specified.
|
36
56
|
def column_param_prefix
|
37
|
-
|
57
|
+
read_attribute(:column_param_prefix) || 'c'
|
38
58
|
end
|
39
59
|
|
40
60
|
# Get the row dimension class
|
@@ -42,28 +62,34 @@ module ActiveWarehouse
|
|
42
62
|
@row_dimension_class ||= ActiveWarehouse::Dimension.class_name(self.row_dimension_name).constantize
|
43
63
|
end
|
44
64
|
|
65
|
+
# Get the row hierarchy. Uses the first hierarchy in the row dimension if not specified
|
45
66
|
def row_hierarchy
|
46
|
-
|
67
|
+
read_attribute(:row_hierarchy) || row_dimension_class.hierarchies.first
|
47
68
|
end
|
48
69
|
|
70
|
+
# Get the row parameter prefix. Returns 'r' if not specified.
|
49
71
|
def row_param_prefix
|
50
|
-
|
72
|
+
read_attribute(:row_param_prefix) || 'r'
|
51
73
|
end
|
52
74
|
|
53
75
|
# Get the list of displayed fact attributes. If this value is not specified then all aggregate and calculated
|
54
76
|
# fields will be displayed
|
55
77
|
def fact_attributes
|
56
|
-
return
|
78
|
+
return read_attribute(:fact_attributes) if read_attribute(:fact_attributes)
|
57
79
|
fa = []
|
58
|
-
fact_class.aggregate_fields.each
|
59
|
-
|
60
|
-
end
|
61
|
-
fact_class.calculated_fields.each do |name|
|
62
|
-
fa << name
|
63
|
-
end
|
80
|
+
fact_class.aggregate_fields.each { |field| fa << field }
|
81
|
+
fact_class.calculated_fields.each { |field| fa << field }
|
64
82
|
fa
|
65
83
|
end
|
66
84
|
|
85
|
+
def column_filters
|
86
|
+
@column_filters ||= {}
|
87
|
+
end
|
88
|
+
|
89
|
+
def row_filters
|
90
|
+
@row_filters ||= {}
|
91
|
+
end
|
92
|
+
|
67
93
|
def pass_params
|
68
94
|
@pass_params ||= []
|
69
95
|
end
|