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/query_spec.rb
ADDED
@@ -0,0 +1,582 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Query" do
|
4
|
+
before(:all) do
|
5
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS_WITH_CATALOG)
|
6
|
+
@sql = ActiveRecord::Base.connection
|
7
|
+
|
8
|
+
@query_string = <<-SQL
|
9
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
10
|
+
{[Product].children} ON ROWS
|
11
|
+
FROM [Sales]
|
12
|
+
WHERE ([Time].[2010].[Q1], [Customers].[USA].[CA])
|
13
|
+
SQL
|
14
|
+
|
15
|
+
@sql_select = <<-SQL
|
16
|
+
SELECT SUM(unit_sales) unit_sales_sum, SUM(store_sales) store_sales_sum
|
17
|
+
FROM sales
|
18
|
+
LEFT JOIN products ON sales.product_id = products.id
|
19
|
+
LEFT JOIN product_classes ON products.product_class_id = product_classes.id
|
20
|
+
LEFT JOIN time ON sales.time_id = time.id
|
21
|
+
LEFT JOIN customers ON sales.customer_id = customers.id
|
22
|
+
WHERE time.the_year = 2010 AND time.quarter = 'Q1'
|
23
|
+
AND customers.country = 'USA' AND customers.state_province = 'CA'
|
24
|
+
GROUP BY product_classes.product_family
|
25
|
+
ORDER BY product_classes.product_family
|
26
|
+
SQL
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def sql_select_numbers(select_string)
|
31
|
+
@sql.select_rows(select_string).map do |rows|
|
32
|
+
rows.map{|col| BigDecimal(col.to_s)}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "result" do
|
37
|
+
before(:all) do
|
38
|
+
|
39
|
+
# TODO: replace hardcoded expected values with result of SQL query
|
40
|
+
@expected_column_names = ["Unit Sales", "Store Sales"]
|
41
|
+
@expected_column_full_names = ["[Measures].[Unit Sales]", "[Measures].[Store Sales]"]
|
42
|
+
@expected_drillable_columns = [false, false]
|
43
|
+
@expected_row_names = ["Drink", "Food", "Non-Consumable"]
|
44
|
+
@expected_row_full_names = ["[Product].[Drink]", "[Product].[Food]", "[Product].[Non-Consumable]"]
|
45
|
+
@expected_drillable_rows = [true, true, true]
|
46
|
+
|
47
|
+
# AR JDBC driver always returns strings, need to convert to BigDecimal
|
48
|
+
@expected_result_values = sql_select_numbers(@sql_select)
|
49
|
+
|
50
|
+
@expected_result_values_by_columns =
|
51
|
+
[@expected_result_values.map{|row| row[0]}, @expected_result_values.map{|row| row[1]}]
|
52
|
+
|
53
|
+
@result = @olap.execute @query_string
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should return axes" do
|
57
|
+
@result.axes_count.should == 2
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should return column names" do
|
61
|
+
@result.column_names.should == @expected_column_names
|
62
|
+
@result.column_full_names.should == @expected_column_full_names
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should return row names" do
|
66
|
+
@result.row_names.should == @expected_row_names
|
67
|
+
@result.row_full_names.should == @expected_row_full_names
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return axis by index names" do
|
71
|
+
@result.axis_names[0].should == @expected_column_names
|
72
|
+
@result.axis_full_names[0].should == @expected_column_full_names
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should return column members" do
|
76
|
+
@result.column_members.map(&:name).should == @expected_column_names
|
77
|
+
@result.column_members.map(&:full_name).should == @expected_column_full_names
|
78
|
+
@result.column_members.map(&:"drillable?").should == @expected_drillable_columns
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should return row members" do
|
82
|
+
@result.row_members.map(&:name).should == @expected_row_names
|
83
|
+
@result.row_members.map(&:full_name).should == @expected_row_full_names
|
84
|
+
@result.row_members.map(&:"drillable?").should == @expected_drillable_rows
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should return cells" do
|
88
|
+
@result.values.should == @expected_result_values
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should return cells with specified axes number sequence" do
|
92
|
+
@result.values(0, 1).should == @expected_result_values_by_columns
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should return cells with specified axes name sequence" do
|
96
|
+
@result.values(:columns, :rows).should == @expected_result_values_by_columns
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return formatted cells" do
|
100
|
+
@result.formatted_values.map{|r| r.map{|s| BigDecimal.new(s.gsub(',',''))}}.should == @expected_result_values
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "builder" do
|
106
|
+
|
107
|
+
before(:each) do
|
108
|
+
@query = @olap.from('Sales')
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "from cube" do
|
112
|
+
it "should return query" do
|
113
|
+
@query.should be_a(Mondrian::OLAP::Query)
|
114
|
+
@query.cube_name.should == 'Sales'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "columns" do
|
119
|
+
it "should accept list" do
|
120
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').should equal(@query)
|
121
|
+
@query.columns.should == ['[Measures].[Unit Sales]', '[Measures].[Store Sales]']
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should accept list as array" do
|
125
|
+
@query.columns(['[Measures].[Unit Sales]', '[Measures].[Store Sales]'])
|
126
|
+
@query.columns.should == ['[Measures].[Unit Sales]', '[Measures].[Store Sales]']
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should accept with several method calls" do
|
130
|
+
@query.columns('[Measures].[Unit Sales]').columns('[Measures].[Store Sales]')
|
131
|
+
@query.columns.should == ['[Measures].[Unit Sales]', '[Measures].[Store Sales]']
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "other axis" do
|
136
|
+
it "should accept axis with index member list" do
|
137
|
+
@query.axis(0, '[Measures].[Unit Sales]', '[Measures].[Store Sales]')
|
138
|
+
@query.axis(0).should == ['[Measures].[Unit Sales]', '[Measures].[Store Sales]']
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should accept rows list" do
|
142
|
+
@query.rows('[Product].children')
|
143
|
+
@query.rows.should == ['[Product].children']
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should accept pages list" do
|
147
|
+
@query.pages('[Product].children')
|
148
|
+
@query.pages.should == ['[Product].children']
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "crossjoin" do
|
153
|
+
it "should do crossjoin of several dimensions" do
|
154
|
+
@query.rows('[Product].children').crossjoin('[Customers].[Canada]', '[Customers].[USA]')
|
155
|
+
@query.rows.should == [:crossjoin, ['[Product].children'], ['[Customers].[Canada]', '[Customers].[USA]']]
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should do crossjoin passing array as first argument" do
|
159
|
+
@query.rows('[Product].children').crossjoin(['[Customers].[Canada]', '[Customers].[USA]'])
|
160
|
+
@query.rows.should == [:crossjoin, ['[Product].children'], ['[Customers].[Canada]', '[Customers].[USA]']]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "nonempty" do
|
165
|
+
it "should limit to set of members with nonempty values" do
|
166
|
+
@query.rows('[Product].children').nonempty
|
167
|
+
@query.rows.should == [:nonempty, ['[Product].children']]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "order" do
|
172
|
+
it "should order by one measure" do
|
173
|
+
@query.rows('[Product].children').order('[Measures].[Unit Sales]', :bdesc)
|
174
|
+
@query.rows.should == [:order, ['[Product].children'], '[Measures].[Unit Sales]', 'BDESC']
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should order using String order direction" do
|
178
|
+
@query.rows('[Product].children').order('[Measures].[Unit Sales]', 'DESC')
|
179
|
+
@query.rows.should == [:order, ['[Product].children'], '[Measures].[Unit Sales]', 'DESC']
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should order by measure and other member" do
|
183
|
+
@query.rows('[Product].children').order(['[Measures].[Unit Sales]', '[Customers].[USA]'], :basc)
|
184
|
+
@query.rows.should == [:order, ['[Product].children'], ['[Measures].[Unit Sales]', '[Customers].[USA]'], 'BASC']
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
%w(top bottom).each do |extreme|
|
189
|
+
describe extreme do
|
190
|
+
it "should select #{extreme} count rows by measure" do
|
191
|
+
@query.rows('[Product].children').send(:"#{extreme}_count", 5, '[Measures].[Unit Sales]')
|
192
|
+
@query.rows.should == [:"#{extreme}_count", ['[Product].children'], 5, '[Measures].[Unit Sales]']
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should select #{extreme} count rows without measure" do
|
196
|
+
@query.rows('[Product].children').send(:"#{extreme}_count", 5)
|
197
|
+
@query.rows.should == [:"#{extreme}_count", ['[Product].children'], 5]
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should select #{extreme} percent rows by measure" do
|
201
|
+
@query.rows('[Product].children').send(:"#{extreme}_percent", 20, '[Measures].[Unit Sales]')
|
202
|
+
@query.rows.should == [:"#{extreme}_percent", ['[Product].children'], 20, '[Measures].[Unit Sales]']
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should select #{extreme} sum rows by measure" do
|
206
|
+
@query.rows('[Product].children').send(:"#{extreme}_sum", 1000, '[Measures].[Unit Sales]')
|
207
|
+
@query.rows.should == [:"#{extreme}_sum", ['[Product].children'], 1000, '[Measures].[Unit Sales]']
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "hierarchize" do
|
213
|
+
it "should hierarchize simple set" do
|
214
|
+
@query.rows('[Customers].[Country].Members', '[Customers].[City].Members').hierarchize
|
215
|
+
@query.rows.should == [:hierarchize, ['[Customers].[Country].Members', '[Customers].[City].Members']]
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should hierarchize last set of crossjoin" do
|
219
|
+
@query.rows('[Product].children').crossjoin('[Customers].[Country].Members', '[Customers].[City].Members').hierarchize
|
220
|
+
@query.rows.should == [:crossjoin, ['[Product].children'],
|
221
|
+
[:hierarchize, ['[Customers].[Country].Members', '[Customers].[City].Members']]]
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should hierarchize all crossjoin" do
|
225
|
+
@query.rows('[Product].children').crossjoin('[Customers].[Country].Members', '[Customers].[City].Members').hierarchize_all
|
226
|
+
@query.rows.should == [:hierarchize, [:crossjoin, ['[Product].children'],
|
227
|
+
['[Customers].[Country].Members', '[Customers].[City].Members']]]
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should hierarchize with POST" do
|
231
|
+
@query.rows('[Customers].[Country].Members', '[Customers].[City].Members').hierarchize(:post)
|
232
|
+
@query.rows.should == [:hierarchize, ['[Customers].[Country].Members', '[Customers].[City].Members'], 'POST']
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "except" do
|
238
|
+
it "should except one set from other" do
|
239
|
+
@query.rows('[Customers].[Country].Members').except('[Customers].[USA]')
|
240
|
+
@query.rows.should == [:except, ['[Customers].[Country].Members'], ['[Customers].[USA]']]
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should except from last set of crossjoin" do
|
244
|
+
@query.rows('[Product].children').crossjoin('[Customers].[Country].Members').except('[Customers].[USA]')
|
245
|
+
@query.rows.should == [:crossjoin, ['[Product].children'],
|
246
|
+
[:except, ['[Customers].[Country].Members'], ['[Customers].[USA]']]]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "filter" do
|
251
|
+
it "should filter set by condition" do
|
252
|
+
@query.rows('[Customers].[Country].Members').filter('[Measures].[Unit Sales] > 1000')
|
253
|
+
@query.rows.should == [:filter, ['[Customers].[Country].Members'], '[Measures].[Unit Sales] > 1000']
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should filter using set alias" do
|
257
|
+
@query.rows('[Customers].[Country].Members').filter('NOT ISEMPTY(S.CURRENT)', :as => 'S')
|
258
|
+
@query.rows.should == [:filter, ['[Customers].[Country].Members'], 'NOT ISEMPTY(S.CURRENT)', 'S']
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe "where" do
|
263
|
+
it "should accept conditions" do
|
264
|
+
@query.where('[Time].[2010].[Q1]', '[Customers].[USA].[CA]').should equal(@query)
|
265
|
+
@query.where.should == ['[Time].[2010].[Q1]', '[Customers].[USA].[CA]']
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should accept conditions as array" do
|
269
|
+
@query.where(['[Time].[2010].[Q1]', '[Customers].[USA].[CA]'])
|
270
|
+
@query.where.should == ['[Time].[2010].[Q1]', '[Customers].[USA].[CA]']
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should accept conditions with several method calls" do
|
274
|
+
@query.where('[Time].[2010].[Q1]').where('[Customers].[USA].[CA]')
|
275
|
+
@query.where.should == ['[Time].[2010].[Q1]', '[Customers].[USA].[CA]']
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
describe "with member" do
|
280
|
+
it "should accept definition" do
|
281
|
+
@query.with_member('[Measures].[ProfitPct]').
|
282
|
+
as('Val((Measures.[Store Sales] - Measures.[Store Cost]) / Measures.[Store Sales])').
|
283
|
+
should equal(@query)
|
284
|
+
@query.with.should == [
|
285
|
+
[ :member, '[Measures].[ProfitPct]',
|
286
|
+
'Val((Measures.[Store Sales] - Measures.[Store Cost]) / Measures.[Store Sales])'
|
287
|
+
]
|
288
|
+
]
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should accept definition with additional parameters" do
|
292
|
+
@query.with_member('[Measures].[ProfitPct]').
|
293
|
+
as('Val((Measures.[Store Sales] - Measures.[Store Cost]) / Measures.[Store Sales])',
|
294
|
+
:solve_order => 1,
|
295
|
+
:format_string => 'Percent')
|
296
|
+
@query.with.should == [
|
297
|
+
[ :member, '[Measures].[ProfitPct]',
|
298
|
+
'Val((Measures.[Store Sales] - Measures.[Store Cost]) / Measures.[Store Sales])',
|
299
|
+
{:solve_order => 1, :format_string => 'Percent'}
|
300
|
+
]
|
301
|
+
]
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "with set" do
|
306
|
+
it "should accept simple defition" do
|
307
|
+
@query.with_set('SelectedRows').as('[Product].children')
|
308
|
+
@query.with.should == [
|
309
|
+
[ :set, 'SelectedRows',
|
310
|
+
['[Product].children']
|
311
|
+
]
|
312
|
+
]
|
313
|
+
end
|
314
|
+
|
315
|
+
it "should accept definition with crossjoin" do
|
316
|
+
@query.with_set('SelectedRows').as('[Product].children').crossjoin('[Customers].[Canada]', '[Customers].[USA]')
|
317
|
+
@query.with.should == [
|
318
|
+
[ :set, 'SelectedRows',
|
319
|
+
[:crossjoin, ['[Product].children'], ['[Customers].[Canada]', '[Customers].[USA]']]
|
320
|
+
]
|
321
|
+
]
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "to MDX" do
|
326
|
+
it "should return MDX query" do
|
327
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
328
|
+
rows('[Product].children').
|
329
|
+
where('[Time].[2010].[Q1]', '[Customers].[USA].[CA]').
|
330
|
+
to_mdx.should be_like <<-SQL
|
331
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
332
|
+
[Product].children ON ROWS
|
333
|
+
FROM [Sales]
|
334
|
+
WHERE ([Time].[2010].[Q1], [Customers].[USA].[CA])
|
335
|
+
SQL
|
336
|
+
end
|
337
|
+
|
338
|
+
it "should return query with crossjoin" do
|
339
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
340
|
+
rows('[Product].children').crossjoin('[Customers].[Canada]', '[Customers].[USA]').
|
341
|
+
where('[Time].[2010].[Q1]').
|
342
|
+
to_mdx.should be_like <<-SQL
|
343
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
344
|
+
CROSSJOIN([Product].children, {[Customers].[Canada], [Customers].[USA]}) ON ROWS
|
345
|
+
FROM [Sales]
|
346
|
+
WHERE ([Time].[2010].[Q1])
|
347
|
+
SQL
|
348
|
+
end
|
349
|
+
|
350
|
+
it "should return query with several crossjoins" do
|
351
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
352
|
+
rows('[Product].children').crossjoin('[Customers].[Canada]', '[Customers].[USA]').
|
353
|
+
crossjoin('[Time].[2010].[Q1]', '[Time].[2010].[Q2]').
|
354
|
+
to_mdx.should be_like <<-SQL
|
355
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
356
|
+
CROSSJOIN(CROSSJOIN([Product].children, {[Customers].[Canada], [Customers].[USA]}),
|
357
|
+
{[Time].[2010].[Q1], [Time].[2010].[Q2]}) ON ROWS
|
358
|
+
FROM [Sales]
|
359
|
+
SQL
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should return query with crossjoin and nonempty" do
|
363
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
364
|
+
rows('[Product].children').crossjoin('[Customers].[Canada]', '[Customers].[USA]').nonempty.
|
365
|
+
where('[Time].[2010].[Q1]').
|
366
|
+
to_mdx.should be_like <<-SQL
|
367
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
368
|
+
NON EMPTY CROSSJOIN([Product].children, {[Customers].[Canada], [Customers].[USA]}) ON ROWS
|
369
|
+
FROM [Sales]
|
370
|
+
WHERE ([Time].[2010].[Q1])
|
371
|
+
SQL
|
372
|
+
end
|
373
|
+
|
374
|
+
it "should return query with order by one measure" do
|
375
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
376
|
+
rows('[Product].children').order('[Measures].[Unit Sales]', :bdesc).
|
377
|
+
to_mdx.should be_like <<-SQL
|
378
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
379
|
+
ORDER([Product].children, [Measures].[Unit Sales], BDESC) ON ROWS
|
380
|
+
FROM [Sales]
|
381
|
+
SQL
|
382
|
+
end
|
383
|
+
|
384
|
+
it "should return query with order by measure and other member" do
|
385
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
386
|
+
rows('[Product].children').order(['[Measures].[Unit Sales]', '[Customers].[USA]'], :asc).
|
387
|
+
to_mdx.should be_like <<-SQL
|
388
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
389
|
+
ORDER([Product].children, ([Measures].[Unit Sales], [Customers].[USA]), ASC) ON ROWS
|
390
|
+
FROM [Sales]
|
391
|
+
SQL
|
392
|
+
end
|
393
|
+
|
394
|
+
%w(top bottom).each do |extreme|
|
395
|
+
it "should return query with #{extreme} count by one measure" do
|
396
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
397
|
+
rows('[Product].children').send(:"#{extreme}_count", 5, '[Measures].[Unit Sales]').
|
398
|
+
to_mdx.should be_like <<-SQL
|
399
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
400
|
+
#{extreme.upcase}COUNT([Product].children, 5, [Measures].[Unit Sales]) ON ROWS
|
401
|
+
FROM [Sales]
|
402
|
+
SQL
|
403
|
+
end
|
404
|
+
|
405
|
+
it "should return query with #{extreme} count without measure" do
|
406
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
407
|
+
rows('[Product].children').send(:"#{extreme}_count", 5).
|
408
|
+
to_mdx.should be_like <<-SQL
|
409
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
410
|
+
#{extreme.upcase}COUNT([Product].children, 5) ON ROWS
|
411
|
+
FROM [Sales]
|
412
|
+
SQL
|
413
|
+
end
|
414
|
+
|
415
|
+
it "should return query with #{extreme} count by measure and other member" do
|
416
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
417
|
+
rows('[Product].children').send(:"#{extreme}_count", 5, ['[Measures].[Unit Sales]', '[Customers].[USA]']).
|
418
|
+
to_mdx.should be_like <<-SQL
|
419
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
420
|
+
#{extreme.upcase}COUNT([Product].children, 5, ([Measures].[Unit Sales], [Customers].[USA])) ON ROWS
|
421
|
+
FROM [Sales]
|
422
|
+
SQL
|
423
|
+
end
|
424
|
+
|
425
|
+
it "should return query with #{extreme} percent by one measure" do
|
426
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
427
|
+
rows('[Product].children').send(:"#{extreme}_percent", 20, '[Measures].[Unit Sales]').
|
428
|
+
to_mdx.should be_like <<-SQL
|
429
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
430
|
+
#{extreme.upcase}PERCENT([Product].children, 20, [Measures].[Unit Sales]) ON ROWS
|
431
|
+
FROM [Sales]
|
432
|
+
SQL
|
433
|
+
end
|
434
|
+
|
435
|
+
it "should return query with #{extreme} sum by one measure" do
|
436
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
437
|
+
rows('[Product].children').send(:"#{extreme}_sum", 1000, '[Measures].[Unit Sales]').
|
438
|
+
to_mdx.should be_like <<-SQL
|
439
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
440
|
+
#{extreme.upcase}SUM([Product].children, 1000, [Measures].[Unit Sales]) ON ROWS
|
441
|
+
FROM [Sales]
|
442
|
+
SQL
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
it "should return query with hierarchize" do
|
447
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
448
|
+
rows('[Customers].[Country].Members', '[Customers].[City].Members').hierarchize.
|
449
|
+
to_mdx.should be_like <<-SQL
|
450
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
451
|
+
HIERARCHIZE({[Customers].[Country].Members, [Customers].[City].Members}) ON ROWS
|
452
|
+
FROM [Sales]
|
453
|
+
SQL
|
454
|
+
end
|
455
|
+
|
456
|
+
it "should return query with hierarchize and order" do
|
457
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
458
|
+
rows('[Customers].[Country].Members', '[Customers].[City].Members').hierarchize(:post).
|
459
|
+
to_mdx.should be_like <<-SQL
|
460
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
461
|
+
HIERARCHIZE({[Customers].[Country].Members, [Customers].[City].Members}, POST) ON ROWS
|
462
|
+
FROM [Sales]
|
463
|
+
SQL
|
464
|
+
end
|
465
|
+
|
466
|
+
it "should return query with except" do
|
467
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
468
|
+
rows('[Customers].[Country].Members').except('[Customers].[USA]').
|
469
|
+
to_mdx.should be_like <<-SQL
|
470
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
471
|
+
EXCEPT([Customers].[Country].Members, [Customers].[USA]) ON ROWS
|
472
|
+
FROM [Sales]
|
473
|
+
SQL
|
474
|
+
end
|
475
|
+
|
476
|
+
it "should return query with filter" do
|
477
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
478
|
+
rows('[Customers].[Country].Members').filter('[Measures].[Unit Sales] > 1000').
|
479
|
+
to_mdx.should be_like <<-SQL
|
480
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
481
|
+
FILTER([Customers].[Country].Members, [Measures].[Unit Sales] > 1000) ON ROWS
|
482
|
+
FROM [Sales]
|
483
|
+
SQL
|
484
|
+
end
|
485
|
+
|
486
|
+
it "should return query with filter and set alias" do
|
487
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
488
|
+
rows('[Customers].[Country].Members').filter('NOT ISEMPTY(S.CURRENT)', :as => 'S').
|
489
|
+
to_mdx.should be_like <<-SQL
|
490
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
491
|
+
FILTER([Customers].[Country].Members AS S, NOT ISEMPTY(S.CURRENT)) ON ROWS
|
492
|
+
FROM [Sales]
|
493
|
+
SQL
|
494
|
+
end
|
495
|
+
|
496
|
+
it "should return query with filter non-empty" do
|
497
|
+
@query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
498
|
+
rows('[Customers].[Country].Members').filter_nonempty.
|
499
|
+
to_mdx.should be_like <<-SQL
|
500
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
501
|
+
FILTER([Customers].[Country].Members AS S, NOT ISEMPTY(S.CURRENT)) ON ROWS
|
502
|
+
FROM [Sales]
|
503
|
+
SQL
|
504
|
+
end
|
505
|
+
|
506
|
+
it "should return query including WITH MEMBER clause" do
|
507
|
+
@query.
|
508
|
+
with_member('[Measures].[ProfitPct]').
|
509
|
+
as('Val((Measures.[Store Sales] - Measures.[Store Cost]) / Measures.[Store Sales])',
|
510
|
+
:solve_order => 1, :format_string => 'Percent').
|
511
|
+
with_member('[Measures].[ProfitValue]').
|
512
|
+
as('[Measures].[Store Sales] * [Measures].[ProfitPct]',
|
513
|
+
:solve_order => 2, :format_string => 'Currency').
|
514
|
+
columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
515
|
+
rows('[Product].children').
|
516
|
+
where('[Time].[2010].[Q1]', '[Customers].[USA].[CA]').
|
517
|
+
to_mdx.should be_like <<-SQL
|
518
|
+
WITH
|
519
|
+
MEMBER [Measures].[ProfitPct] AS
|
520
|
+
'Val((Measures.[Store Sales] - Measures.[Store Cost]) / Measures.[Store Sales])',
|
521
|
+
SOLVE_ORDER = 1, FORMAT_STRING = 'Percent'
|
522
|
+
MEMBER [Measures].[ProfitValue] AS
|
523
|
+
'[Measures].[Store Sales] * [Measures].[ProfitPct]',
|
524
|
+
SOLVE_ORDER = 2, FORMAT_STRING = 'Currency'
|
525
|
+
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
|
526
|
+
[Product].children ON ROWS
|
527
|
+
FROM [Sales]
|
528
|
+
WHERE ([Time].[2010].[Q1], [Customers].[USA].[CA])
|
529
|
+
SQL
|
530
|
+
end
|
531
|
+
|
532
|
+
it "should return query including WITH SET clause" do
|
533
|
+
@query.with_set('SelectedRows').
|
534
|
+
as('[Product].children').crossjoin('[Customers].[Canada]', '[Customers].[USA]').
|
535
|
+
with_member('[Measures].[Profit]').
|
536
|
+
as('[Measures].[Store Sales] - [Measures].[Store Cost]').
|
537
|
+
columns('[Measures].[Profit]').
|
538
|
+
rows('SelectedRows').
|
539
|
+
to_mdx.should be_like <<-SQL
|
540
|
+
WITH
|
541
|
+
SET SelectedRows AS
|
542
|
+
'CROSSJOIN([Product].children, {[Customers].[Canada], [Customers].[USA]})'
|
543
|
+
MEMBER [Measures].[Profit] AS
|
544
|
+
'[Measures].[Store Sales] - [Measures].[Store Cost]'
|
545
|
+
SELECT [Measures].[Profit] ON COLUMNS,
|
546
|
+
SelectedRows ON ROWS
|
547
|
+
FROM [Sales]
|
548
|
+
SQL
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
describe "execute" do
|
553
|
+
it "should return result" do
|
554
|
+
result = @query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
555
|
+
rows('[Product].children').
|
556
|
+
where('[Time].[2010].[Q1]', '[Customers].[USA].[CA]').
|
557
|
+
execute
|
558
|
+
result.values.should == sql_select_numbers(@sql_select)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
describe "result HTML formatting" do
|
563
|
+
it "should format result" do
|
564
|
+
result = @query.columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
565
|
+
rows('[Product].children').
|
566
|
+
where('[Time].[2010].[Q1]', '[Customers].[USA].[CA]').
|
567
|
+
execute
|
568
|
+
Nokogiri::HTML.fragment(result.to_html).css('tr').size.should == (sql_select_numbers(@sql_select).size + 1)
|
569
|
+
end
|
570
|
+
|
571
|
+
# it "test" do
|
572
|
+
# puts @olap.from('Sales').
|
573
|
+
# columns('[Product].children').
|
574
|
+
# rows('[Customers].[USA].[CA].children').
|
575
|
+
# where('[Time].[2010].[Q1]', '[Measures].[Store Sales]').
|
576
|
+
# execute.to_html
|
577
|
+
# end
|
578
|
+
end
|
579
|
+
|
580
|
+
end
|
581
|
+
|
582
|
+
end
|