Class: ActiveWarehouse::Dimension
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- ActiveWarehouse::Dimension
- Includes:
- HierarchicalDimension, SlowlyChangingDimension
- Defined in:
- lib/active_warehouse/dimension.rb
Overview
Dimension tables contain the textual descriptors of the business. Dimensions provide the filters which are applied to facts. Dimensions are the primary source of query constraints, groupings and report labels.
Direct Known Subclasses
Defined Under Namespace
Classes: Node
Class Attribute Summary collapse
-
.level_orders ⇒ Object
readonly
Get the level orders map.
-
.order ⇒ Object
Alternate order by, to be used rather than the current level being queried.
Class Method Summary collapse
-
.available_child_values(hierarchy_name, parent_values) ⇒ Object
(also: available_children_values)
Get an array of child values for a particular parent in the hierachy For example, given a DateDimension with data from 2002 to 2004:.
-
.available_values(level) ⇒ Object
Get an array of the available values for a particular hierarchy level For example, given a DateDimension with data from 2002 to 2004:.
-
.available_values_tree(hierarchy_name) ⇒ Object
Get a tree of Node objects for all of the values in the specified hierarchy.
-
.class_for_name(name) ⇒ Object
Get a class for the specified named dimension.
-
.class_name(name) ⇒ Object
Convert the given name into a dimension class name.
-
.define_hierarchy(name, levels) ⇒ Object
Define a named attribute hierarchy in the dimension.
-
.denominator_count(hierarchy_name, level, denominator_level = nil) ⇒ Object
Returns a hash of all of the values at the specified hierarchy level mapped to the count at that level.
-
.expire_value_tree_cache ⇒ Object
Expire the value tree cache.
-
.foreign_key ⇒ Object
Get the foreign key for this dimension which is used in Fact tables.
-
.hierarchies ⇒ Object
Get the ordered hierarchy names.
-
.hierarchy(name) ⇒ Object
Get the named attribute hierarchy.
-
.hierarchy_levels ⇒ Object
Get the hierarchy levels hash.
-
.last_modified ⇒ Object
Return the time when the underlying dimension source file was last modified.
-
.set_level_order(level, name) ⇒ Object
Define a column to order by for a specific level.
-
.set_order(name) ⇒ Object
Define a column to order by.
-
.sym ⇒ Object
Return a symbol used when referring to this dimension.
-
.table_name ⇒ Object
Get the table name.
-
.to_dimension(dimension) ⇒ Object
Get the dimension class for the specified dimension parameter.
Instance Method Summary collapse
-
#expire_value_tree_cache ⇒ Object
Expire the value tree cache.
Methods included from SlowlyChangingDimension
Methods included from HierarchicalDimension
Class Attribute Details
.level_orders ⇒ Object (readonly)
Get the level orders map
23 24 25 |
# File 'lib/active_warehouse/dimension.rb', line 23 def level_orders @level_orders end |
.order ⇒ Object
Alternate order by, to be used rather than the current level being queried
20 21 22 |
# File 'lib/active_warehouse/dimension.rb', line 20 def order @order end |
Class Method Details
.available_child_values(hierarchy_name, parent_values) ⇒ Object Also known as: available_children_values
Get an array of child values for a particular parent in the hierachy For example, given a DateDimension with data from 2002 to 2004:
available_child_values(:cy, [2002, ‘Q1’]) returns
- ‘January’, ‘Feburary’, ‘March’, ‘April’
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/active_warehouse/dimension.rb', line 213 def available_child_values(hierarchy_name, parent_values) if hierarchy_levels[hierarchy_name].nil? raise ArgumentError, "The hierarchy '#{hierarchy_name}' does not exist in your dimension #{name}" end levels = hierarchy_levels[hierarchy_name] if levels.length <= parent_values.length raise ArgumentError, "The parent_values '#{parent_values.to_yaml}' exceeds the hierarchy depth #{levels.to_yaml}" end child_level = levels[parent_values.length].to_s # Create the conditions array. Will work with 1.1.6. conditions_parts = [] conditions_values = [] parent_values.each_with_index do |value, index| conditions_parts << "#{levels[index]} = ?" conditions_values << value end conditions = [conditions_parts.join(' AND ')] + conditions_values unless conditions_parts.empty? child_level_method = child_level.to_sym child_level = connection.quote_column_name(child_level) order = level_orders[child_level] || self.order || child_level select_sql = "distinct #{child_level}" select_sql += ", #{order}" unless order == child_level = {:select => select_sql, :order => order} [:conditions] = conditions unless conditions.nil? values = [] find(:all, ).each do |dim| value = dim.send(child_level_method) values << dim.send(child_level_method) unless values.include?(value) end values.to_a end |
.available_values(level) ⇒ Object
Get an array of the available values for a particular hierarchy level For example, given a DateDimension with data from 2002 to 2004:
available_values('calendar_year') returns ['2002','2003','2004']
194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/active_warehouse/dimension.rb', line 194 def available_values(level) level_method = level.to_sym level = connection.quote_column_name(level.to_s) order = level_orders[level] || self.order || level = {:select => "distinct #{order.to_s == level.to_s ? '' : order.to_s+','} #{level}", :order => order} values = [] find(:all, ).each do |dim| value = dim.send(level_method) values << dim.send(level_method) unless values.include?(value) end values.to_a end |
.available_values_tree(hierarchy_name) ⇒ Object
Get a tree of Node objects for all of the values in the specified hierarchy.
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/active_warehouse/dimension.rb', line 253 def available_values_tree(hierarchy_name) root = value_tree_cache[hierarchy_name] if root.nil? root = Node.new('All', '__ROOT__') levels = hierarchy(hierarchy_name) nodes = {nil => root} level_list = levels.collect{|level| connection.quote_column_name(level) }.join(',') order = self.order || level_list find(:all, :select => level_list, :group => level_list, :order => order).each do |dim| parent_node = root levels.each do |level| node_value = dim.send(level) child_node = parent_node.optionally_add_child(node_value, level) parent_node = child_node end end value_tree_cache[hierarchy_name] = root end root end |
.class_for_name(name) ⇒ Object
Get a class for the specified named dimension
104 105 106 |
# File 'lib/active_warehouse/dimension.rb', line 104 def class_for_name(name) class_name(name).constantize end |
.class_name(name) ⇒ Object
Convert the given name into a dimension class name
97 98 99 100 101 |
# File 'lib/active_warehouse/dimension.rb', line 97 def class_name(name) dimension_name = name.to_s dimension_name = "#{dimension_name}_dimension" unless dimension_name =~ /_dimension$/ dimension_name.classify end |
.define_hierarchy(name, levels) ⇒ Object
Define a named attribute hierarchy in the dimension.
Example: define_hierarchy(:fiscal_calendar, [:fiscal_year, :fiscal_quarter, :fiscal_month])
This would indicate that one of the drill down paths for this dimension is: Fiscal Year -> Fiscal Quarter -> Fiscal Month
Internally the hierarchies are stored in order. The first hierarchy defined will be used as the default if no hierarchy is specified when rendering a cube.
55 56 57 58 |
# File 'lib/active_warehouse/dimension.rb', line 55 def define_hierarchy(name, levels) hierarchies << name hierarchy_levels[name] = levels end |
.denominator_count(hierarchy_name, level, denominator_level = nil) ⇒ Object
Returns a hash of all of the values at the specified hierarchy level mapped to the count at that level. For example, given a date dimension with years from 2002 to 2004 and a hierarchy defined with:
hierarchy :cy, [:calendar_year, :calendar_quarter, :calendar_month_name]
…then…
DateDimension.denominator_count(:cy, :calendar_year, :calendar_quarter)
returns {'2002' => 4, '2003' => 4, '2004' => 4}
If the denominator_level parameter is omitted or nil then:
DateDimension.denominator_count(:cy, :calendar_year) returns
{'2003' => 365, '2003' => 365, '2004' => 366}
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/active_warehouse/dimension.rb', line 138 def denominator_count(hierarchy_name, level, denominator_level=nil) if hierarchy_levels[hierarchy_name].nil? raise ArgumentError, "The hierarchy '#{hierarchy_name}' does not exist in your dimension #{name}" end q = nil # If the denominator_level is specified and it is not the last element # in the hierarchy then do a distinct count. If # the denominator level is less than the current level then raise an # ArgumentError. In other words, if the current level is # calendar month then passing in calendar year as the denominator level # would raise an ArgumentErro. # # If the denominator_level is not specified then assume the finest grain # possible (in the context of a date dimension this would be each day) # and use the id to count. if denominator_level && hierarchy_levels[hierarchy_name].last != denominator_level level_index = hierarchy_levels[hierarchy_name].index(level) denominator_index = hierarchy_levels[hierarchy_name].index(denominator_level) if level_index.nil? raise ArgumentError, "The level '#{level}' does not appear to exist" end if denominator_index.nil? raise ArgumentError, "The denominator level '#{denominator_level}' does not appear to exist" end if hierarchy_levels[hierarchy_name].index(denominator_level) < hierarchy_levels[hierarchy_name].index(level) raise ArgumentError, "The index of the denominator level '#{denominator_level}' in the hierarchy '#{hierarchy_name}' must be greater than or equal to the level '#{level}'" end q = "select #{level} as level, count(distinct(#{denominator_level})) as level_count from #{table_name} group by #{level}" else q = "select #{level} as level, count(id) as level_count from #{table_name} group by #{level}" end denominators = {} connection.select_all(q).each do |row| denominators[row['level']] = row['level_count'].to_i end denominators end |
.expire_value_tree_cache ⇒ Object
Expire the value tree cache. This should be called if the dimension
316 317 318 |
# File 'lib/active_warehouse/dimension.rb', line 316 def expire_value_tree_cache @value_tree_cache = nil end |
.foreign_key ⇒ Object
Get the foreign key for this dimension which is used in Fact tables.
Example: DateDimension would have a foreign key of date_id
The actual foreign key may be different and depends on the fact class. You may specify the foreign key to use for a specific fact using the Fact#set_dimension_options method.
186 187 188 |
# File 'lib/active_warehouse/dimension.rb', line 186 def foreign_key table_name.sub(/_dimension/,'') + '_id' end |
.hierarchies ⇒ Object
Get the ordered hierarchy names
68 69 70 |
# File 'lib/active_warehouse/dimension.rb', line 68 def hierarchies @hierarchies ||= [] end |
.hierarchy(name) ⇒ Object
Get the named attribute hierarchy. Returns an array of column names.
Example: hierarchy(:fiscal_calendar) might return [:fiscal_year, :fiscal_quarter, :fiscal_month]
63 64 65 |
# File 'lib/active_warehouse/dimension.rb', line 63 def hierarchy(name) hierarchy_levels[name] end |
.hierarchy_levels ⇒ Object
Get the hierarchy levels hash
73 74 75 |
# File 'lib/active_warehouse/dimension.rb', line 73 def hierarchy_levels @hierarchy_levels ||= {} end |
.last_modified ⇒ Object
Return the time when the underlying dimension source file was last modified. This is used to determine if a cube structure rebuild is required
111 112 113 |
# File 'lib/active_warehouse/dimension.rb', line 111 def last_modified File.new(__FILE__).mtime end |
.set_level_order(level, name) ⇒ Object
Define a column to order by for a specific level.
36 37 38 |
# File 'lib/active_warehouse/dimension.rb', line 36 def set_level_order(level, name) level_orders[level] = name end |
.set_order(name) ⇒ Object
Define a column to order by. If this value is specified then it will be used rather than the actual level being queried in the following method calls:
-
available_values
-
available_child_values
-
available_values_tree
31 32 33 |
# File 'lib/active_warehouse/dimension.rb', line 31 def set_order(name) @order = name end |
.sym ⇒ Object
Return a symbol used when referring to this dimension. The symbol is calculated by demodulizing and underscoring the dimension’s class name and then removing the trailing _dimension.
Example: DateDimension will return a symbol :date
82 83 84 |
# File 'lib/active_warehouse/dimension.rb', line 82 def sym self.name.demodulize.underscore.gsub(/_dimension/, '').to_sym end |
.table_name ⇒ Object
Get the table name. By default the table name will be the name of the dimension in singular form.
Example: DateDimension will have a table called date_dimension
90 91 92 93 94 |
# File 'lib/active_warehouse/dimension.rb', line 90 def table_name name = self.name.demodulize.underscore set_table_name(name) name end |
.to_dimension(dimension) ⇒ Object
Get the dimension class for the specified dimension parameter. The dimension parameter may be a class, String or Symbol.
117 118 119 120 |
# File 'lib/active_warehouse/dimension.rb', line 117 def to_dimension(dimension) return dimension if dimension.is_a?(Class) and dimension.ancestors.include?(Dimension) return class_for_name(dimension) end |
Instance Method Details
#expire_value_tree_cache ⇒ Object
Expire the value tree cache
323 324 325 |
# File 'lib/active_warehouse/dimension.rb', line 323 def expire_value_tree_cache self.class.expire_value_tree_cache end |