mondrian-olap 0.1.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/.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
|