activewarehouse 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|