mondrian-olap 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -0
- data/Gemfile +15 -0
- data/LICENSE-Mondrian.html +259 -0
- data/LICENSE.txt +22 -0
- data/README.rdoc +219 -0
- data/RUNNING_TESTS.rdoc +41 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/lib/mondrian-olap.rb +1 -0
- data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
- data/lib/mondrian/jars/commons-dbcp-1.2.1.jar +0 -0
- data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
- data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
- data/lib/mondrian/jars/commons-pool-1.2.jar +0 -0
- data/lib/mondrian/jars/commons-vfs-1.0.jar +0 -0
- data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
- data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
- data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
- data/lib/mondrian/jars/javacup.jar +0 -0
- data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
- data/lib/mondrian/jars/log4j.properties +18 -0
- data/lib/mondrian/jars/mondrian.jar +0 -0
- data/lib/mondrian/jars/olap4j.jar +0 -0
- data/lib/mondrian/olap.rb +14 -0
- data/lib/mondrian/olap/connection.rb +122 -0
- data/lib/mondrian/olap/cube.rb +236 -0
- data/lib/mondrian/olap/query.rb +313 -0
- data/lib/mondrian/olap/result.rb +155 -0
- data/lib/mondrian/olap/schema.rb +158 -0
- data/lib/mondrian/olap/schema_element.rb +123 -0
- data/mondrian-olap.gemspec +116 -0
- data/spec/connection_spec.rb +56 -0
- data/spec/cube_spec.rb +259 -0
- data/spec/fixtures/MondrianTest.xml +128 -0
- data/spec/fixtures/MondrianTestOracle.xml +128 -0
- data/spec/query_spec.rb +582 -0
- data/spec/rake_tasks.rb +185 -0
- data/spec/schema_definition_spec.rb +345 -0
- data/spec/spec_helper.rb +67 -0
- data/spec/support/matchers/be_like.rb +24 -0
- metadata +217 -0
data/spec/rake_tasks.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
namespace :db do
|
2
|
+
task :require_spec_helper do
|
3
|
+
require File.expand_path("../spec_helper", __FILE__)
|
4
|
+
end
|
5
|
+
|
6
|
+
desc "Create test database tables"
|
7
|
+
task :create_tables => :require_spec_helper do
|
8
|
+
puts "==> Creating tables for test data"
|
9
|
+
ActiveRecord::Schema.define do
|
10
|
+
|
11
|
+
create_table :time, :force => true do |t|
|
12
|
+
t.datetime :the_date
|
13
|
+
t.string :the_day, :limit => 30
|
14
|
+
t.string :the_month, :limit => 30
|
15
|
+
t.integer :the_year
|
16
|
+
t.integer :day_of_month
|
17
|
+
t.integer :week_of_year
|
18
|
+
t.integer :month_of_year
|
19
|
+
t.string :quarter, :limit => 30
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :products, :force => true do |t|
|
23
|
+
t.integer :product_class_id
|
24
|
+
t.string :brand_name, :limit => 60
|
25
|
+
t.string :product_name, :limit => 60
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table :product_classes, :force => true do |t|
|
29
|
+
t.string :product_subcategory, :limit => 30
|
30
|
+
t.string :product_category, :limit => 30
|
31
|
+
t.string :product_department, :limit => 30
|
32
|
+
t.string :product_family, :limit => 30
|
33
|
+
end
|
34
|
+
|
35
|
+
create_table :customers, :force => true do |t|
|
36
|
+
t.string :country, :limit => 30
|
37
|
+
t.string :state_province, :limit => 30
|
38
|
+
t.string :city, :limit => 30
|
39
|
+
t.string :fname, :limit => 30
|
40
|
+
t.string :lname, :limit => 30
|
41
|
+
t.string :fullname, :limit => 60
|
42
|
+
t.string :gender, :limit => 30
|
43
|
+
end
|
44
|
+
|
45
|
+
create_table :sales, :force => true, :id => false do |t|
|
46
|
+
t.integer :product_id
|
47
|
+
t.integer :time_id
|
48
|
+
t.integer :customer_id
|
49
|
+
t.decimal :store_sales, :precision => 10, :scale => 4
|
50
|
+
t.decimal :store_cost, :precision => 10, :scale => 4
|
51
|
+
t.decimal :unit_sales, :precision => 10, :scale => 4
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
task :define_models => :require_spec_helper do
|
57
|
+
class TimeDimension < ActiveRecord::Base
|
58
|
+
set_table_name "time"
|
59
|
+
validates_presence_of :the_date
|
60
|
+
before_create do
|
61
|
+
self.the_day = the_date.strftime("%A")
|
62
|
+
self.the_month = the_date.strftime("%B")
|
63
|
+
self.the_year = the_date.strftime("%Y").to_i
|
64
|
+
self.day_of_month = the_date.strftime("%d").to_i
|
65
|
+
self.week_of_year = the_date.strftime("%W").to_i
|
66
|
+
self.month_of_year = the_date.strftime("%m").to_i
|
67
|
+
self.quarter = "Q#{(month_of_year-1)/3+1}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
class Product < ActiveRecord::Base
|
71
|
+
belongs_to :product_class
|
72
|
+
end
|
73
|
+
class ProductClass < ActiveRecord::Base
|
74
|
+
end
|
75
|
+
class Customer < ActiveRecord::Base
|
76
|
+
end
|
77
|
+
class Sales < ActiveRecord::Base
|
78
|
+
set_table_name "sales"
|
79
|
+
belongs_to :time_by_day
|
80
|
+
belongs_to :product
|
81
|
+
belongs_to :customer
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
desc "Create test data"
|
86
|
+
task :create_data => [:create_tables, :create_time_data, :create_product_data, :create_customer_data, :create_sales_data]
|
87
|
+
|
88
|
+
task :create_time_data => :define_models do
|
89
|
+
puts "==> Creating time dimension"
|
90
|
+
TimeDimension.delete_all
|
91
|
+
start_time = Time.local(2010,1,1)
|
92
|
+
(2*365).times do |i|
|
93
|
+
TimeDimension.create!(:the_date => start_time + i.day)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
task :create_product_data => :define_models do
|
98
|
+
puts "==> Creating product data"
|
99
|
+
Product.delete_all
|
100
|
+
ProductClass.delete_all
|
101
|
+
families = ["Drink", "Food", "Non-Consumable"]
|
102
|
+
(1..100).each do |i|
|
103
|
+
product_class = ProductClass.create!(
|
104
|
+
:product_family => families[i % 3],
|
105
|
+
:product_department => "Department #{i}",
|
106
|
+
:product_category => "Category #{i}",
|
107
|
+
:product_subcategory => "Subcategory #{i}"
|
108
|
+
)
|
109
|
+
Product.create!(
|
110
|
+
:product_class_id => product_class.id,
|
111
|
+
:brand_name => "Brand #{i}",
|
112
|
+
:product_name => "Product #{i}"
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
task :create_customer_data => :define_models do
|
118
|
+
puts "==> Creating customer data"
|
119
|
+
Customer.delete_all
|
120
|
+
i = 0
|
121
|
+
[
|
122
|
+
["Canada", "BC", "Burnaby"],["Canada", "BC", "Cliffside"],["Canada", "BC", "Haney"],["Canada", "BC", "Ladner"],
|
123
|
+
["Canada", "BC", "Langford"],["Canada", "BC", "Langley"],["Canada", "BC", "Metchosin"],["Canada", "BC", "N. Vancouver"],
|
124
|
+
["Canada", "BC", "Newton"],["Canada", "BC", "Oak Bay"],["Canada", "BC", "Port Hammond"],["Canada", "BC", "Richmond"],
|
125
|
+
["Canada", "BC", "Royal Oak"],["Canada", "BC", "Shawnee"],["Canada", "BC", "Sooke"],["Canada", "BC", "Vancouver"],
|
126
|
+
["Canada", "BC", "Victoria"],["Canada", "BC", "Westminster"],
|
127
|
+
["Mexico", "DF", "San Andres"],["Mexico", "DF", "Santa Anita"],["Mexico", "DF", "Santa Fe"],["Mexico", "DF", "Tixapan"],
|
128
|
+
["Mexico", "Guerrero", "Acapulco"],["Mexico", "Jalisco", "Guadalajara"],["Mexico", "Mexico", "Mexico City"],
|
129
|
+
["Mexico", "Oaxaca", "Tlaxiaco"],["Mexico", "Sinaloa", "La Cruz"],["Mexico", "Veracruz", "Orizaba"],
|
130
|
+
["Mexico", "Yucatan", "Merida"],["Mexico", "Zacatecas", "Camacho"],["Mexico", "Zacatecas", "Hidalgo"],
|
131
|
+
["USA", "CA", "Altadena"],["USA", "CA", "Arcadia"],["USA", "CA", "Bellflower"],["USA", "CA", "Berkeley"],
|
132
|
+
["USA", "CA", "Beverly Hills"],["USA", "CA", "Burbank"],["USA", "CA", "Burlingame"],["USA", "CA", "Chula Vista"],
|
133
|
+
["USA", "CA", "Colma"],["USA", "CA", "Concord"],["USA", "CA", "Coronado"],["USA", "CA", "Daly City"],
|
134
|
+
["USA", "CA", "Downey"],["USA", "CA", "El Cajon"],["USA", "CA", "Fremont"],["USA", "CA", "Glendale"],
|
135
|
+
["USA", "CA", "Grossmont"],["USA", "CA", "Imperial Beach"],["USA", "CA", "La Jolla"],["USA", "CA", "La Mesa"],
|
136
|
+
["USA", "CA", "Lakewood"],["USA", "CA", "Lemon Grove"],["USA", "CA", "Lincoln Acres"],["USA", "CA", "Long Beach"],
|
137
|
+
["USA", "CA", "Los Angeles"],["USA", "CA", "Mill Valley"],["USA", "CA", "National City"],["USA", "CA", "Newport Beach"],
|
138
|
+
["USA", "CA", "Novato"],["USA", "CA", "Oakland"],["USA", "CA", "Palo Alto"],["USA", "CA", "Pomona"],
|
139
|
+
["USA", "CA", "Redwood City"],["USA", "CA", "Richmond"],["USA", "CA", "San Carlos"],["USA", "CA", "San Diego"],
|
140
|
+
["USA", "CA", "San Francisco"],["USA", "CA", "San Gabriel"],["USA", "CA", "San Jose"],["USA", "CA", "Santa Cruz"],
|
141
|
+
["USA", "CA", "Santa Monica"],["USA", "CA", "Spring Valley"],["USA", "CA", "Torrance"],["USA", "CA", "West Covina"],
|
142
|
+
["USA", "CA", "Woodland Hills"],
|
143
|
+
["USA", "OR", "Albany"],["USA", "OR", "Beaverton"],["USA", "OR", "Corvallis"],["USA", "OR", "Lake Oswego"],
|
144
|
+
["USA", "OR", "Lebanon"],["USA", "OR", "Milwaukie"],["USA", "OR", "Oregon City"],["USA", "OR", "Portland"],
|
145
|
+
["USA", "OR", "Salem"],["USA", "OR", "W. Linn"],["USA", "OR", "Woodburn"],
|
146
|
+
["USA", "WA", "Anacortes"],["USA", "WA", "Ballard"],["USA", "WA", "Bellingham"],["USA", "WA", "Bremerton"],
|
147
|
+
["USA", "WA", "Burien"],["USA", "WA", "Edmonds"],["USA", "WA", "Everett"],["USA", "WA", "Issaquah"],
|
148
|
+
["USA", "WA", "Kirkland"],["USA", "WA", "Lynnwood"],["USA", "WA", "Marysville"],["USA", "WA", "Olympia"],
|
149
|
+
["USA", "WA", "Port Orchard"],["USA", "WA", "Puyallup"],["USA", "WA", "Redmond"],["USA", "WA", "Renton"],
|
150
|
+
["USA", "WA", "Seattle"],["USA", "WA", "Sedro Woolley"],["USA", "WA", "Spokane"],["USA", "WA", "Tacoma"],
|
151
|
+
["USA", "WA", "Walla Walla"],["USA", "WA", "Yakima"]
|
152
|
+
].each do |country, state, city|
|
153
|
+
i += 1
|
154
|
+
Customer.create!(
|
155
|
+
:country => country,
|
156
|
+
:state_province => state,
|
157
|
+
:city => city,
|
158
|
+
:fname => "First#{i}",
|
159
|
+
:lname => "Last#{i}",
|
160
|
+
:fullname => "First#{i} Last#{i}",
|
161
|
+
:gender => i % 2 == 0 ? "M" : "F"
|
162
|
+
)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
task :create_sales_data => :define_models do
|
167
|
+
puts "==> Creating sales data"
|
168
|
+
Sales.delete_all
|
169
|
+
count = 100
|
170
|
+
products = Product.order("id").limit(count).all
|
171
|
+
times = TimeDimension.order("id").limit(count).all
|
172
|
+
customers = Customer.order("id").limit(count).all
|
173
|
+
count.times do |i|
|
174
|
+
Sales.create!(
|
175
|
+
:product_id => products[i].id,
|
176
|
+
:time_id => times[i].id,
|
177
|
+
:customer_id => customers[i].id,
|
178
|
+
:store_sales => BigDecimal("2#{i}.12"),
|
179
|
+
:store_cost => BigDecimal("1#{i}.1234"),
|
180
|
+
:unit_sales => i+1
|
181
|
+
)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
@@ -0,0 +1,345 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Schema definition" do
|
4
|
+
|
5
|
+
describe "elements" do
|
6
|
+
before(:each) do
|
7
|
+
@schema = Mondrian::OLAP::Schema.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "root element" do
|
11
|
+
it "should render to XML" do
|
12
|
+
@schema.to_xml.should be_like <<-XML
|
13
|
+
<?xml version="1.0"?>
|
14
|
+
<Schema/>
|
15
|
+
XML
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should render to XML with attributes" do
|
19
|
+
@schema.define('FoodMart') do
|
20
|
+
description 'Demo "FoodMart" schema'
|
21
|
+
end
|
22
|
+
@schema.to_xml.should be_like <<-XML
|
23
|
+
<?xml version="1.0"?>
|
24
|
+
<Schema description="Demo "FoodMart" schema" name="FoodMart"/>
|
25
|
+
XML
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should render to XML using class method" do
|
29
|
+
schema = Mondrian::OLAP::Schema.define('FoodMart')
|
30
|
+
schema.to_xml.should be_like <<-XML
|
31
|
+
<?xml version="1.0"?>
|
32
|
+
<Schema name="FoodMart"/>
|
33
|
+
XML
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "Cube" do
|
38
|
+
it "should render to XML" do
|
39
|
+
@schema.define do
|
40
|
+
cube 'Sales' do
|
41
|
+
default_measure 'Unit Sales'
|
42
|
+
description 'Sales cube'
|
43
|
+
cache false
|
44
|
+
enabled true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@schema.to_xml.should be_like <<-XML
|
48
|
+
<?xml version="1.0"?>
|
49
|
+
<Schema name="default">
|
50
|
+
<Cube cache="false" defaultMeasure="Unit Sales" description="Sales cube" enabled="true" name="Sales"/>
|
51
|
+
</Schema>
|
52
|
+
XML
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should render to XML using options hash" do
|
56
|
+
@schema.define do
|
57
|
+
cube 'Sales', :default_measure => 'Unit Sales',
|
58
|
+
:description => 'Sales cube', :cache => false, :enabled => true
|
59
|
+
end
|
60
|
+
@schema.to_xml.should be_like <<-XML
|
61
|
+
<?xml version="1.0"?>
|
62
|
+
<Schema name="default">
|
63
|
+
<Cube cache="false" defaultMeasure="Unit Sales" description="Sales cube" enabled="true" name="Sales"/>
|
64
|
+
</Schema>
|
65
|
+
XML
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "Table" do
|
70
|
+
it "should render to XML" do
|
71
|
+
@schema.define do
|
72
|
+
cube 'Sales' do
|
73
|
+
table 'sales_fact', :alias => 'sales'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@schema.to_xml.should be_like <<-XML
|
77
|
+
<?xml version="1.0"?>
|
78
|
+
<Schema name="default">
|
79
|
+
<Cube name="Sales">
|
80
|
+
<Table alias="sales" name="sales_fact"/>
|
81
|
+
</Cube>
|
82
|
+
</Schema>
|
83
|
+
XML
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should render table name in uppercase when using Oracle driver" do
|
87
|
+
@schema.define do
|
88
|
+
cube 'Sales' do
|
89
|
+
table 'sales_fact', :alias => 'sales'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
@schema.to_xml(:driver => 'oracle').should be_like <<-XML
|
93
|
+
<?xml version="1.0"?>
|
94
|
+
<Schema name="default">
|
95
|
+
<Cube name="Sales">
|
96
|
+
<Table alias="SALES" name="SALES_FACT"/>
|
97
|
+
</Cube>
|
98
|
+
</Schema>
|
99
|
+
XML
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "Dimension" do
|
104
|
+
it "should render to XML" do
|
105
|
+
@schema.define do
|
106
|
+
cube 'Sales' do
|
107
|
+
dimension 'Gender' do
|
108
|
+
foreign_key 'customer_id'
|
109
|
+
hierarchy do
|
110
|
+
has_all true
|
111
|
+
all_member_name 'All Genders'
|
112
|
+
primary_key 'customer_id'
|
113
|
+
table 'customer'
|
114
|
+
level 'Gender', :column => 'gender', :unique_members => true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
@schema.to_xml.should be_like <<-XML
|
120
|
+
<?xml version="1.0"?>
|
121
|
+
<Schema name="default">
|
122
|
+
<Cube name="Sales">
|
123
|
+
<Dimension foreignKey="customer_id" name="Gender">
|
124
|
+
<Hierarchy allMemberName="All Genders" hasAll="true" primaryKey="customer_id">
|
125
|
+
<Table name="customer"/>
|
126
|
+
<Level column="gender" name="Gender" uniqueMembers="true"/>
|
127
|
+
</Hierarchy>
|
128
|
+
</Dimension>
|
129
|
+
</Cube>
|
130
|
+
</Schema>
|
131
|
+
XML
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should render time dimension" do
|
135
|
+
@schema.define do
|
136
|
+
cube 'Sales' do
|
137
|
+
dimension 'Time' do
|
138
|
+
foreign_key 'time_id'
|
139
|
+
hierarchy do
|
140
|
+
has_all false
|
141
|
+
primary_key 'time_id'
|
142
|
+
table 'time_by_day'
|
143
|
+
level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true
|
144
|
+
level 'Quarter', :column => 'quarter', :unique_members => false
|
145
|
+
level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
@schema.to_xml.should be_like <<-XML
|
151
|
+
<?xml version="1.0"?>
|
152
|
+
<Schema name="default">
|
153
|
+
<Cube name="Sales">
|
154
|
+
<Dimension foreignKey="time_id" name="Time">
|
155
|
+
<Hierarchy hasAll="false" primaryKey="time_id">
|
156
|
+
<Table name="time_by_day"/>
|
157
|
+
<Level column="the_year" name="Year" type="Numeric" uniqueMembers="true"/>
|
158
|
+
<Level column="quarter" name="Quarter" uniqueMembers="false"/>
|
159
|
+
<Level column="month_of_year" name="Month" type="Numeric" uniqueMembers="false"/>
|
160
|
+
</Hierarchy>
|
161
|
+
</Dimension>
|
162
|
+
</Cube>
|
163
|
+
</Schema>
|
164
|
+
XML
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should render dimension hierarchy with join" do
|
168
|
+
@schema.define do
|
169
|
+
cube 'Sales' do
|
170
|
+
dimension 'Products', :foreign_key => 'product_id' do
|
171
|
+
hierarchy :has_all => true, :all_member_name => 'All Products',
|
172
|
+
:primary_key => 'product_id', :primary_key_table => 'product' do
|
173
|
+
join :left_key => 'product_class_id', :right_key => 'product_class_id' do
|
174
|
+
table 'product'
|
175
|
+
table 'product_class'
|
176
|
+
end
|
177
|
+
level 'Product Family', :table => 'product_class', :column => 'product_family', :unique_members => true
|
178
|
+
level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
|
179
|
+
level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
@schema.to_xml.should be_like <<-XML
|
185
|
+
<?xml version="1.0"?>
|
186
|
+
<Schema name="default">
|
187
|
+
<Cube name="Sales">
|
188
|
+
<Dimension foreignKey="product_id" name="Products">
|
189
|
+
<Hierarchy allMemberName="All Products" hasAll="true" primaryKey="product_id" primaryKeyTable="product">
|
190
|
+
<Join leftKey="product_class_id" rightKey="product_class_id">
|
191
|
+
<Table name="product"/>
|
192
|
+
<Table name="product_class"/>
|
193
|
+
</Join>
|
194
|
+
<Level column="product_family" name="Product Family" table="product_class" uniqueMembers="true"/>
|
195
|
+
<Level column="brand_name" name="Brand Name" table="product" uniqueMembers="false"/>
|
196
|
+
<Level column="product_name" name="Product Name" table="product" uniqueMembers="true"/>
|
197
|
+
</Hierarchy>
|
198
|
+
</Dimension>
|
199
|
+
</Cube>
|
200
|
+
</Schema>
|
201
|
+
XML
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should render table and column names in uppercase when using Oracle driver" do
|
205
|
+
@schema.define do
|
206
|
+
cube 'Sales' do
|
207
|
+
dimension 'Products', :foreign_key => 'product_id' do
|
208
|
+
hierarchy :has_all => true, :all_member_name => 'All Products',
|
209
|
+
:primary_key => 'product_id', :primary_key_table => 'product' do
|
210
|
+
join :left_key => 'product_class_id', :right_key => 'product_class_id' do
|
211
|
+
table 'product'
|
212
|
+
table 'product_class'
|
213
|
+
end
|
214
|
+
level 'Product Family', :table => 'product_class', :column => 'product_family', :unique_members => true
|
215
|
+
level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
|
216
|
+
level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
@schema.to_xml(:driver => 'oracle').should be_like <<-XML
|
222
|
+
<?xml version="1.0"?>
|
223
|
+
<Schema name="default">
|
224
|
+
<Cube name="Sales">
|
225
|
+
<Dimension foreignKey="PRODUCT_ID" name="Products">
|
226
|
+
<Hierarchy allMemberName="All Products" hasAll="true" primaryKey="PRODUCT_ID" primaryKeyTable="PRODUCT">
|
227
|
+
<Join leftKey="PRODUCT_CLASS_ID" rightKey="PRODUCT_CLASS_ID">
|
228
|
+
<Table name="PRODUCT"/>
|
229
|
+
<Table name="PRODUCT_CLASS"/>
|
230
|
+
</Join>
|
231
|
+
<Level column="PRODUCT_FAMILY" name="Product Family" table="PRODUCT_CLASS" uniqueMembers="true"/>
|
232
|
+
<Level column="BRAND_NAME" name="Brand Name" table="PRODUCT" uniqueMembers="false"/>
|
233
|
+
<Level column="PRODUCT_NAME" name="Product Name" table="PRODUCT" uniqueMembers="true"/>
|
234
|
+
</Hierarchy>
|
235
|
+
</Dimension>
|
236
|
+
</Cube>
|
237
|
+
</Schema>
|
238
|
+
XML
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "Measure" do
|
244
|
+
it "should render XML" do
|
245
|
+
@schema.define do
|
246
|
+
cube 'Sales' do
|
247
|
+
measure 'Unit Sales' do
|
248
|
+
column 'unit_sales'
|
249
|
+
aggregator 'sum'
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
@schema.to_xml.should be_like <<-XML
|
254
|
+
<?xml version="1.0"?>
|
255
|
+
<Schema name="default">
|
256
|
+
<Cube name="Sales">
|
257
|
+
<Measure aggregator="sum" column="unit_sales" name="Unit Sales"/>
|
258
|
+
</Cube>
|
259
|
+
</Schema>
|
260
|
+
XML
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should render column name in uppercase when using Oracle driver" do
|
264
|
+
@schema.define do
|
265
|
+
cube 'Sales' do
|
266
|
+
measure 'Unit Sales' do
|
267
|
+
column 'unit_sales'
|
268
|
+
aggregator 'sum'
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
@schema.to_xml(:driver => 'oracle').should be_like <<-XML
|
273
|
+
<?xml version="1.0"?>
|
274
|
+
<Schema name="default">
|
275
|
+
<Cube name="Sales">
|
276
|
+
<Measure aggregator="sum" column="UNIT_SALES" name="Unit Sales"/>
|
277
|
+
</Cube>
|
278
|
+
</Schema>
|
279
|
+
XML
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
describe "Calculated Member" do
|
284
|
+
it "should render XML" do
|
285
|
+
@schema.define do
|
286
|
+
cube 'Sales' do
|
287
|
+
calculated_member 'Profit' do
|
288
|
+
dimension 'Measures'
|
289
|
+
formula '[Measures].[Store Sales] - [Measures].[Store Cost]'
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
@schema.to_xml.should be_like <<-XML
|
294
|
+
<?xml version="1.0"?>
|
295
|
+
<Schema name="default">
|
296
|
+
<Cube name="Sales">
|
297
|
+
<CalculatedMember dimension="Measures" formula="[Measures].[Store Sales] - [Measures].[Store Cost]" name="Profit"/>
|
298
|
+
</Cube>
|
299
|
+
</Schema>
|
300
|
+
XML
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "connection with schema" do
|
306
|
+
before(:all) do
|
307
|
+
@schema = Mondrian::OLAP::Schema.define do
|
308
|
+
cube 'Sales' do
|
309
|
+
table 'sales'
|
310
|
+
dimension 'Gender', :foreign_key => 'customer_id' do
|
311
|
+
hierarchy :has_all => true, :primary_key => 'id' do
|
312
|
+
table 'customers'
|
313
|
+
level 'Gender', :column => 'gender', :unique_members => true
|
314
|
+
end
|
315
|
+
end
|
316
|
+
dimension 'Time', :foreign_key => 'time_id' do
|
317
|
+
hierarchy :has_all => false, :primary_key => 'id' do
|
318
|
+
table 'time'
|
319
|
+
level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true
|
320
|
+
level 'Quarter', :column => 'quarter', :unique_members => false
|
321
|
+
level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false
|
322
|
+
end
|
323
|
+
end
|
324
|
+
measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
|
325
|
+
measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
|
326
|
+
end
|
327
|
+
end
|
328
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should connect" do
|
332
|
+
@olap.should be_connected
|
333
|
+
end
|
334
|
+
|
335
|
+
it "should execute query" do
|
336
|
+
@olap.from('Sales').
|
337
|
+
columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
338
|
+
rows('descendants([Time].[2010].[Q1])').
|
339
|
+
where('[Gender].[F]').
|
340
|
+
execute.should be_a(Mondrian::OLAP::Result)
|
341
|
+
end
|
342
|
+
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|