mondrian-olap 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +21 -1
- data/README.md +4 -4
- data/VERSION +1 -1
- data/lib/mondrian/jars/log4j-api-2.17.1.jar +0 -0
- data/lib/mondrian/jars/log4j-core-2.17.1.jar +0 -0
- data/lib/mondrian/jars/log4j2-config.jar +0 -0
- data/lib/mondrian/jars/mondrian-9.3.0.0.jar +0 -0
- data/lib/mondrian/olap/connection.rb +33 -20
- data/lib/mondrian/olap/cube.rb +46 -4
- data/lib/mondrian/olap/error.rb +10 -2
- data/lib/mondrian/olap/query.rb +1 -0
- data/lib/mondrian/olap/result.rb +128 -59
- data/lib/mondrian/olap/schema.rb +9 -3
- data/lib/mondrian/olap/schema_udf.rb +8 -82
- data/lib/mondrian/olap.rb +11 -7
- data/spec/connection_spec.rb +37 -3
- data/spec/cube_cache_control_spec.rb +2 -2
- data/spec/cube_spec.rb +36 -2
- data/spec/fixtures/MondrianTest.xml +40 -0
- data/spec/fixtures/MondrianTestOracle.xml +40 -0
- data/spec/mondrian_spec.rb +132 -0
- data/spec/query_spec.rb +86 -21
- data/spec/rake_tasks.rb +85 -16
- data/spec/schema_definition_spec.rb +0 -235
- data/spec/spec_helper.rb +317 -75
- data/spec/support/data/customers.csv +111 -111
- data/spec/support/data/promotions.csv +11 -0
- data/spec/support/data/sales.csv +101 -101
- data/spec/support/data/warehouse.csv +101 -0
- data/spec/support/matchers/be_like.rb +1 -0
- metadata +36 -73
- data/lib/mondrian/jars/log4j-1.2.17.jar +0 -0
- data/lib/mondrian/jars/log4j.properties +0 -3
- data/lib/mondrian/jars/mondrian-9.1.0.0.jar +0 -0
data/spec/query_spec.rb
CHANGED
@@ -101,7 +101,7 @@ describe "Query" do
|
|
101
101
|
end
|
102
102
|
|
103
103
|
it "should return formatted cells" do
|
104
|
-
@result.formatted_values.map{|r| r.map{|s| BigDecimal
|
104
|
+
@result.formatted_values.map{|r| r.map{|s| BigDecimal(s.gsub(',',''))}}.should == @expected_result_values
|
105
105
|
end
|
106
106
|
|
107
107
|
end
|
@@ -703,6 +703,11 @@ describe "Query" do
|
|
703
703
|
execute
|
704
704
|
result.values.should == sql_select_numbers(@sql_select)
|
705
705
|
end
|
706
|
+
|
707
|
+
it "should not fail without columns" do
|
708
|
+
result = @query.rows('[Product].DefaultMember').execute
|
709
|
+
result.values.should == [[]]
|
710
|
+
end
|
706
711
|
end
|
707
712
|
|
708
713
|
describe "result HTML formatting" do
|
@@ -713,14 +718,6 @@ describe "Query" do
|
|
713
718
|
execute
|
714
719
|
Nokogiri::HTML.fragment(result.to_html).css('tr').size.should == (sql_select_numbers(@sql_select).size + 1)
|
715
720
|
end
|
716
|
-
|
717
|
-
# it "test" do
|
718
|
-
# puts @olap.from('Sales').
|
719
|
-
# columns('[Product].children').
|
720
|
-
# rows('[Customers].[USA].[CA].children').
|
721
|
-
# where('[Time].[2010].[Q1]', '[Measures].[Store Sales]').
|
722
|
-
# execute.to_html
|
723
|
-
# end
|
724
721
|
end
|
725
722
|
|
726
723
|
end
|
@@ -842,15 +839,6 @@ describe "Query" do
|
|
842
839
|
String,
|
843
840
|
BigDecimal
|
844
841
|
]
|
845
|
-
when 'mssql'
|
846
|
-
[
|
847
|
-
Integer, String, Integer, Integer, Integer,
|
848
|
-
String, String, String, String, String, String,
|
849
|
-
# last one can be BigDecimal or Integer, probably depends on MS SQL version
|
850
|
-
String, String, String, Numeric,
|
851
|
-
String,
|
852
|
-
BigDecimal
|
853
|
-
]
|
854
842
|
else
|
855
843
|
[
|
856
844
|
Integer, String, Integer, Integer, Integer,
|
@@ -896,6 +884,25 @@ describe "Query" do
|
|
896
884
|
]
|
897
885
|
end
|
898
886
|
|
887
|
+
it "should return rows also for field dimension that is not present in the report query" do
|
888
|
+
result = @olap.from('Sales').columns('[Measures].[Unit Sales]').rows('[Customers].[Canada].[BC].[Burnaby]').execute
|
889
|
+
drill_through = result.drill_through(row: 0, column: 0, return: ["[Product].[Product Family]"])
|
890
|
+
drill_through.rows.should == @sql.select_rows(<<-SQL)
|
891
|
+
SELECT
|
892
|
+
product_classes.product_family
|
893
|
+
FROM
|
894
|
+
sales,
|
895
|
+
products,
|
896
|
+
product_classes,
|
897
|
+
customers
|
898
|
+
WHERE
|
899
|
+
products.product_class_id = product_classes.id AND
|
900
|
+
sales.product_id = products.id AND
|
901
|
+
sales.customer_id = customers.id AND
|
902
|
+
customers.country = 'Canada' AND customers.state_province = 'BC' AND customers.city = 'Burnaby'
|
903
|
+
SQL
|
904
|
+
end
|
905
|
+
|
899
906
|
it "should return only nonempty measures" do
|
900
907
|
@drill_through = @result.drill_through(:row => 0, :column => 0,
|
901
908
|
:return => "[Measures].[Unit Sales], [Measures].[Store Sales]",
|
@@ -904,7 +911,7 @@ describe "Query" do
|
|
904
911
|
@drill_through.column_labels.should == [
|
905
912
|
"Unit Sales", "Store Sales"
|
906
913
|
]
|
907
|
-
@drill_through.rows.all?{|r| r.any?{|c| c}}.should
|
914
|
+
@drill_through.rows.all?{|r| r.any?{|c| c}}.should == true
|
908
915
|
end
|
909
916
|
|
910
917
|
it "should return member name and property values" do
|
@@ -913,12 +920,12 @@ describe "Query" do
|
|
913
920
|
"Name([Customers].[Name])",
|
914
921
|
"Property([Customers].[Name], 'Gender')",
|
915
922
|
"Property([Customers].[Name], 'Description')",
|
916
|
-
"Property([Customers].[Name], '
|
923
|
+
"Property([Customers].[Name], 'Non-existing property name')"
|
917
924
|
]
|
918
925
|
)
|
919
926
|
@drill_through.column_labels.should == [
|
920
927
|
"Name", "Gender", "Description",
|
921
|
-
"
|
928
|
+
"Non-existing property name"
|
922
929
|
]
|
923
930
|
@drill_through.rows.should == @sql.select_rows(<<-SQL)
|
924
931
|
SELECT
|
@@ -981,6 +988,64 @@ describe "Query" do
|
|
981
988
|
end
|
982
989
|
end
|
983
990
|
|
991
|
+
describe "drill through cell with return and role restrictions" do
|
992
|
+
before(:all) do
|
993
|
+
@olap.role_name = "Canada manager"
|
994
|
+
@query = @olap.from('Sales')
|
995
|
+
@result = @query.columns('[Measures].[Unit Sales]').
|
996
|
+
rows('[Customers].[All Customers]').
|
997
|
+
execute
|
998
|
+
end
|
999
|
+
|
1000
|
+
after(:all) do
|
1001
|
+
@olap.role_name = nil
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
it "should return data according to role restriction" do
|
1005
|
+
@drill_through = @result.drill_through(:row => 0, :column => 0, :return => [
|
1006
|
+
'[Customers].[Country]',
|
1007
|
+
'[Measures].[Unit Sales]'
|
1008
|
+
])
|
1009
|
+
@drill_through.rows.all? { |r| r.first == "Canada" }.should == true
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
describe "drill through virtual cube cell with return" do
|
1014
|
+
before(:all) do
|
1015
|
+
@query = @olap.from('Sales and Warehouse')
|
1016
|
+
@result = @query.columns(
|
1017
|
+
'[Measures].[Unit Sales]', '[Measures].[Store Sales]',
|
1018
|
+
'[Measures].[Units Shipped]', '[Measures].[Products with units shipped]'
|
1019
|
+
).
|
1020
|
+
rows('[Product].children').
|
1021
|
+
where('[Time].[2010].[Q1]', '[Time].[2010].[Q2]').
|
1022
|
+
execute
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
it "should return specified fields from other cubes as empty strings" do
|
1026
|
+
@drill_through = @result.drill_through(:row => 0, :column => 3, :return => [
|
1027
|
+
'[Time].[Month]',
|
1028
|
+
'[Product].[Product Family]',
|
1029
|
+
'[Customers].[City]', # missing in Warehouse cube
|
1030
|
+
'[Measures].[Unit Sales]', # missing in Warehouse cube
|
1031
|
+
'[Measures].[Units Shipped]',
|
1032
|
+
'[Measures].[Products with units shipped]'
|
1033
|
+
])
|
1034
|
+
@drill_through.column_labels.should == [
|
1035
|
+
"Month (Key)",
|
1036
|
+
"Product Family (Key)",
|
1037
|
+
"City (Key)",
|
1038
|
+
"Unit Sales",
|
1039
|
+
"Units Shipped",
|
1040
|
+
"Products with units shipped"
|
1041
|
+
]
|
1042
|
+
# Validate that only City and Unit Sales values are missing
|
1043
|
+
@drill_through.rows.map { |r| r.map(&:present?) }.uniq.should == [
|
1044
|
+
[true, true, false, false, true, true]
|
1045
|
+
]
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
|
984
1049
|
describe "drill through statement" do
|
985
1050
|
before(:all) do
|
986
1051
|
@query = @olap.from('Sales').
|
data/spec/rake_tasks.rb
CHANGED
@@ -5,10 +5,12 @@ namespace :db do
|
|
5
5
|
require File.expand_path("../spec_helper", __FILE__)
|
6
6
|
end
|
7
7
|
|
8
|
+
import_data_drivers = %w(vertica snowflake clickhouse mariadb)
|
9
|
+
|
8
10
|
desc "Create test database tables"
|
9
11
|
task :create_tables => :require_spec_helper do
|
10
12
|
puts "==> Creating tables for test data"
|
11
|
-
ActiveRecord::Schema.
|
13
|
+
ActiveRecord::Schema.instance_eval do
|
12
14
|
|
13
15
|
create_table :time, :force => true do |t|
|
14
16
|
t.datetime :the_date
|
@@ -34,7 +36,10 @@ namespace :db do
|
|
34
36
|
t.string :product_family, :limit => 30
|
35
37
|
end
|
36
38
|
|
37
|
-
|
39
|
+
customers_options = {force: true}
|
40
|
+
customers_options[:id] = false if import_data_drivers.include?(MONDRIAN_DRIVER)
|
41
|
+
create_table :customers, **customers_options do |t|
|
42
|
+
t.integer :id, :limit => 8 if import_data_drivers.include?(MONDRIAN_DRIVER)
|
38
43
|
t.string :country, :limit => 30
|
39
44
|
t.string :state_province, :limit => 30
|
40
45
|
t.string :city, :limit => 30
|
@@ -42,6 +47,7 @@ namespace :db do
|
|
42
47
|
t.string :lname, :limit => 30
|
43
48
|
t.string :fullname, :limit => 60
|
44
49
|
t.string :gender, :limit => 30
|
50
|
+
t.date :birthdate
|
45
51
|
t.integer :promotion_id
|
46
52
|
t.string :related_fullname, :limit => 60
|
47
53
|
# Mondrian does not support properties with Oracle CLOB type
|
@@ -79,7 +85,7 @@ namespace :db do
|
|
79
85
|
execute "ALTER TABLE customers MODIFY COLUMN id BIGINT NOT NULL AUTO_INCREMENT"
|
80
86
|
when /postgresql/
|
81
87
|
execute "ALTER TABLE customers ALTER COLUMN id SET DATA TYPE bigint"
|
82
|
-
when /
|
88
|
+
when /sqlserver/
|
83
89
|
sql = "SELECT name FROM sysobjects WHERE xtype = 'PK' AND parent_obj=OBJECT_ID('customers')"
|
84
90
|
primary_key_constraint = select_value(sql)
|
85
91
|
execute "ALTER TABLE customers DROP CONSTRAINT #{primary_key_constraint}"
|
@@ -90,11 +96,23 @@ namespace :db do
|
|
90
96
|
create_table :sales, :force => true, :id => false do |t|
|
91
97
|
t.integer :product_id
|
92
98
|
t.integer :time_id
|
93
|
-
t.integer :customer_id
|
99
|
+
t.integer :customer_id, limit: MONDRIAN_DRIVER == 'sqlserver' ? nil : 8
|
94
100
|
t.integer :promotion_id
|
95
|
-
t.decimal :store_sales, :
|
96
|
-
t.decimal :store_cost, :
|
97
|
-
t.decimal :unit_sales, :
|
101
|
+
t.decimal :store_sales, precision: 10, scale: 4
|
102
|
+
t.decimal :store_cost, precision: 10, scale: 4
|
103
|
+
t.decimal :unit_sales, precision: 10, scale: 4
|
104
|
+
end
|
105
|
+
|
106
|
+
case MONDRIAN_DRIVER
|
107
|
+
when /sqlserver/
|
108
|
+
execute "ALTER TABLE sales ALTER COLUMN customer_id BIGINT"
|
109
|
+
end
|
110
|
+
|
111
|
+
create_table :warehouse, :force => true, :id => false do |t|
|
112
|
+
t.integer :product_id
|
113
|
+
t.integer :time_id
|
114
|
+
t.integer :units_shipped
|
115
|
+
t.decimal :store_invoice, precision: 10, scale: 4
|
98
116
|
end
|
99
117
|
end
|
100
118
|
end
|
@@ -128,11 +146,17 @@ namespace :db do
|
|
128
146
|
belongs_to :product
|
129
147
|
belongs_to :customer
|
130
148
|
end
|
149
|
+
class Warehouse < ActiveRecord::Base
|
150
|
+
self.table_name = "warehouse"
|
151
|
+
belongs_to :time_by_day
|
152
|
+
belongs_to :product
|
153
|
+
end
|
131
154
|
end
|
132
155
|
|
133
156
|
desc "Create test data"
|
134
|
-
task :create_data => [:create_tables] + (
|
135
|
-
[ :create_time_data, :create_product_data, :create_promotion_data, :create_customer_data, :create_sales_data
|
157
|
+
task :create_data => [:create_tables] + (import_data_drivers.include?(ENV['MONDRIAN_DRIVER']) ? [:import_data] :
|
158
|
+
[ :create_time_data, :create_product_data, :create_promotion_data, :create_customer_data, :create_sales_data,
|
159
|
+
:create_warehouse_data ] )
|
136
160
|
|
137
161
|
task :create_time_data => :define_models do
|
138
162
|
puts "==> Creating time dimension"
|
@@ -217,6 +241,7 @@ namespace :db do
|
|
217
241
|
:lname => "Last#{i}",
|
218
242
|
:fullname => "First#{i} Last#{i}",
|
219
243
|
:gender => i % 2 == 0 ? "M" : "F",
|
244
|
+
:birthdate => Date.new(1970, 1, 1) + i,
|
220
245
|
:promotion_id => promotions[i % 10].id,
|
221
246
|
:related_fullname => "First#{i} Last#{i}",
|
222
247
|
:description => 100.times.map{"1234567890"}.join("\n")
|
@@ -236,10 +261,10 @@ namespace :db do
|
|
236
261
|
:related_fullname => "Big Number"
|
237
262
|
}
|
238
263
|
case MONDRIAN_DRIVER
|
239
|
-
when
|
240
|
-
Customer.connection.
|
241
|
-
|
242
|
-
|
264
|
+
when 'sqlserver'
|
265
|
+
Customer.connection.execute "SET IDENTITY_INSERT customers ON"
|
266
|
+
Customer.create!(attributes)
|
267
|
+
Customer.connection.execute "SET IDENTITY_INSERT customers OFF"
|
243
268
|
else
|
244
269
|
Customer.create!(attributes)
|
245
270
|
end
|
@@ -266,8 +291,24 @@ namespace :db do
|
|
266
291
|
end
|
267
292
|
end
|
268
293
|
|
294
|
+
task :create_warehouse_data => :define_models do
|
295
|
+
puts "==> Creating warehouse data"
|
296
|
+
Warehouse.delete_all
|
297
|
+
count = 100
|
298
|
+
products = Product.order("id").to_a[0...count]
|
299
|
+
times = TimeDimension.order("id").to_a[0...count]
|
300
|
+
count.times do |i|
|
301
|
+
Warehouse.create!(
|
302
|
+
:product_id => products[i].id,
|
303
|
+
:time_id => times[i].id,
|
304
|
+
:units_shipped => i+1,
|
305
|
+
:store_invoice => BigDecimal("1#{i}.1234")
|
306
|
+
)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
269
310
|
export_data_dir = File.expand_path("spec/support/data")
|
270
|
-
table_names = %w(time product_classes products customers sales)
|
311
|
+
table_names = %w(time product_classes products customers promotions sales warehouse)
|
271
312
|
|
272
313
|
desc "Export test data"
|
273
314
|
task :export_data => :create_data do
|
@@ -330,13 +371,41 @@ namespace :db do
|
|
330
371
|
"FILE_FORMAT = (FORMAT_NAME = csv)"
|
331
372
|
puts "==> Loaded #{count} records"
|
332
373
|
end
|
374
|
+
|
375
|
+
when 'clickhouse'
|
376
|
+
table_names.each do |table_name|
|
377
|
+
puts "==> Truncate #{table_name}"
|
378
|
+
conn.execute "TRUNCATE TABLE #{table_name}"
|
379
|
+
puts "==> Copy into #{table_name}"
|
380
|
+
file_path = "#{export_data_dir}/#{table_name}.csv"
|
381
|
+
columns_string = File.open(file_path) { |f| f.gets }.chomp
|
382
|
+
clickhouse_format_class = Java::com.clickhouse.data.ClickHouseFormat rescue Java::com.clickhouse.client.ClickHouseFormat
|
383
|
+
conn.jdbc_connection.createStatement.write.
|
384
|
+
query("INSERT INTO #{table_name}(#{columns_string})").
|
385
|
+
data(file_path).format(clickhouse_format_class::CSVWithNames).execute
|
386
|
+
count = conn.select_value("SELECT COUNT(*) FROM #{table_name}").to_i
|
387
|
+
puts "==> Loaded #{count} records"
|
388
|
+
end
|
389
|
+
|
390
|
+
when 'mariadb'
|
391
|
+
table_names.each do |table_name|
|
392
|
+
puts "==> Truncate #{table_name}"
|
393
|
+
conn.execute "TRUNCATE TABLE `#{table_name}`"
|
394
|
+
puts "==> Copy into #{table_name}"
|
395
|
+
file_path = "#{export_data_dir}/#{table_name}.csv"
|
396
|
+
columns_string = File.open(file_path) { |f| f.gets }.chomp
|
397
|
+
count = conn.execute "LOAD DATA LOCAL INFILE '#{file_path}' INTO TABLE `#{table_name}` CHARACTER SET UTF8 " \
|
398
|
+
"FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 1 LINES (#{columns_string})"
|
399
|
+
puts "==> Loaded #{count} records"
|
400
|
+
end
|
401
|
+
|
333
402
|
end
|
334
403
|
end
|
335
404
|
|
336
405
|
end
|
337
406
|
|
338
407
|
namespace :spec do
|
339
|
-
%w(mysql jdbc_mysql postgresql oracle
|
408
|
+
%w(mysql jdbc_mysql postgresql oracle sqlserver vertica snowflake clickhouse mariadb).each do |driver|
|
340
409
|
desc "Run specs with #{driver} driver"
|
341
410
|
task driver do
|
342
411
|
ENV['MONDRIAN_DRIVER'] = driver
|
@@ -347,7 +416,7 @@ namespace :spec do
|
|
347
416
|
|
348
417
|
desc "Run specs with all primary database drivers"
|
349
418
|
task :all do
|
350
|
-
%w(mysql jdbc_mysql postgresql oracle
|
419
|
+
%w(mysql jdbc_mysql postgresql oracle sqlserver).each do |driver|
|
351
420
|
Rake::Task["spec:#{driver}"].invoke
|
352
421
|
end
|
353
422
|
end
|
@@ -865,241 +865,6 @@ describe "Schema definition" do
|
|
865
865
|
end
|
866
866
|
end
|
867
867
|
|
868
|
-
describe "User defined functions and formatters in JavaScript" do
|
869
|
-
next pending "not supported by Mondrian in Java 8" if ENV_JAVA["java.version"] >= "1.8"
|
870
|
-
|
871
|
-
before(:each) do
|
872
|
-
@schema.define do
|
873
|
-
cube 'Sales' do
|
874
|
-
table 'sales'
|
875
|
-
dimension 'Customers', :foreign_key => 'customer_id' do
|
876
|
-
hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
|
877
|
-
table 'customers'
|
878
|
-
level 'Name', :column => 'fullname' do
|
879
|
-
member_formatter { javascript "return member.getName().toUpperCase();" }
|
880
|
-
property 'City', :column => 'city' do
|
881
|
-
property_formatter { javascript "return propertyValue.toUpperCase();" }
|
882
|
-
end
|
883
|
-
end
|
884
|
-
end
|
885
|
-
end
|
886
|
-
calculated_member 'Factorial' do
|
887
|
-
dimension 'Measures'
|
888
|
-
formula 'Factorial(6)'
|
889
|
-
cell_formatter do
|
890
|
-
javascript <<-JS
|
891
|
-
var s = value.toString();
|
892
|
-
while (s.length < 20) {
|
893
|
-
s = "0" + s;
|
894
|
-
}
|
895
|
-
return s;
|
896
|
-
JS
|
897
|
-
end
|
898
|
-
end
|
899
|
-
calculated_member 'City' do
|
900
|
-
dimension 'Measures'
|
901
|
-
formula "[Customers].CurrentMember.Properties('City')"
|
902
|
-
end
|
903
|
-
end
|
904
|
-
user_defined_function 'Factorial' do
|
905
|
-
javascript <<-JS
|
906
|
-
function getParameterTypes() {
|
907
|
-
return new Array(
|
908
|
-
new mondrian.olap.type.NumericType());
|
909
|
-
}
|
910
|
-
function getReturnType(parameterTypes) {
|
911
|
-
return new mondrian.olap.type.NumericType();
|
912
|
-
}
|
913
|
-
function execute(evaluator, arguments) {
|
914
|
-
var n = arguments[0].evaluateScalar(evaluator);
|
915
|
-
return factorial(n);
|
916
|
-
}
|
917
|
-
function factorial(n) {
|
918
|
-
return n <= 1 ? 1 : n * factorial(n - 1);
|
919
|
-
}
|
920
|
-
JS
|
921
|
-
end
|
922
|
-
end
|
923
|
-
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
924
|
-
end
|
925
|
-
|
926
|
-
it "should render XML" do
|
927
|
-
@schema.to_xml.should be_like <<-XML
|
928
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
929
|
-
<Schema name="default">
|
930
|
-
<Cube name="Sales">
|
931
|
-
<Table name="sales"/>
|
932
|
-
<Dimension foreignKey="customer_id" name="Customers">
|
933
|
-
<Hierarchy allMemberName="All Customers" hasAll="true" primaryKey="id">
|
934
|
-
<Table name="customers"/>
|
935
|
-
<Level column="fullname" name="Name" uniqueMembers="true">
|
936
|
-
<MemberFormatter>
|
937
|
-
<Script language="JavaScript">return member.getName().toUpperCase();</Script>
|
938
|
-
</MemberFormatter>
|
939
|
-
<Property column="city" name="City">
|
940
|
-
<PropertyFormatter>
|
941
|
-
<Script language="JavaScript">return propertyValue.toUpperCase();</Script>
|
942
|
-
</PropertyFormatter>
|
943
|
-
</Property>
|
944
|
-
</Level>
|
945
|
-
</Hierarchy>
|
946
|
-
</Dimension>
|
947
|
-
<CalculatedMember dimension="Measures" name="Factorial">
|
948
|
-
<Formula>Factorial(6)</Formula>
|
949
|
-
<CellFormatter>
|
950
|
-
<Script language="JavaScript">
|
951
|
-
var s = value.toString();
|
952
|
-
while (s.length < 20) {
|
953
|
-
s = "0" + s;
|
954
|
-
}
|
955
|
-
return s;
|
956
|
-
</Script>
|
957
|
-
</CellFormatter>
|
958
|
-
</CalculatedMember>
|
959
|
-
<CalculatedMember dimension="Measures" name="City">
|
960
|
-
<Formula>[Customers].CurrentMember.Properties('City')</Formula>
|
961
|
-
</CalculatedMember>
|
962
|
-
</Cube>
|
963
|
-
<UserDefinedFunction name="Factorial">
|
964
|
-
<Script language="JavaScript">
|
965
|
-
function getParameterTypes() {
|
966
|
-
return new Array(
|
967
|
-
new mondrian.olap.type.NumericType());
|
968
|
-
}
|
969
|
-
function getReturnType(parameterTypes) {
|
970
|
-
return new mondrian.olap.type.NumericType();
|
971
|
-
}
|
972
|
-
function execute(evaluator, arguments) {
|
973
|
-
var n = arguments[0].evaluateScalar(evaluator);
|
974
|
-
return factorial(n);
|
975
|
-
}
|
976
|
-
function factorial(n) {
|
977
|
-
return n <= 1 ? 1 : n * factorial(n - 1);
|
978
|
-
}
|
979
|
-
</Script>
|
980
|
-
</UserDefinedFunction>
|
981
|
-
</Schema>
|
982
|
-
XML
|
983
|
-
end
|
984
|
-
|
985
|
-
it "should execute user defined function" do
|
986
|
-
result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
|
987
|
-
value = 1*2*3*4*5*6
|
988
|
-
result.values.should == [value]
|
989
|
-
result.formatted_values.should == ["%020d" % value]
|
990
|
-
end
|
991
|
-
|
992
|
-
it "should format members and properties" do
|
993
|
-
result = @olap.from('Sales').columns('[Measures].[City]').rows('[Customers].[All Customers].Children').execute
|
994
|
-
result.row_members.each_with_index do |member, i|
|
995
|
-
member.caption.should == member.name.upcase
|
996
|
-
city = member.property_value('City')
|
997
|
-
result.formatted_values[i].first.should == city
|
998
|
-
member.property_formatted_value('City').should == city.upcase
|
999
|
-
end
|
1000
|
-
end
|
1001
|
-
end
|
1002
|
-
|
1003
|
-
describe "User defined functions and formatters in CoffeeScript" do
|
1004
|
-
next pending "not supported by Mondrian in Java 8" if ENV_JAVA["java.version"] >= "1.8"
|
1005
|
-
|
1006
|
-
before(:each) do
|
1007
|
-
@schema.define do
|
1008
|
-
cube 'Sales' do
|
1009
|
-
table 'sales'
|
1010
|
-
dimension 'Customers', :foreign_key => 'customer_id' do
|
1011
|
-
hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
|
1012
|
-
table 'customers'
|
1013
|
-
level 'Name', :column => 'fullname' do
|
1014
|
-
member_formatter { coffeescript "member.getName().toUpperCase()" }
|
1015
|
-
property 'City', :column => 'city' do
|
1016
|
-
property_formatter { coffeescript "propertyValue.toUpperCase()" }
|
1017
|
-
end
|
1018
|
-
end
|
1019
|
-
end
|
1020
|
-
end
|
1021
|
-
calculated_member 'Factorial' do
|
1022
|
-
dimension 'Measures'
|
1023
|
-
formula 'Factorial(6)'
|
1024
|
-
cell_formatter do
|
1025
|
-
coffeescript <<-CS
|
1026
|
-
s = value.toString()
|
1027
|
-
s = "0" + s while s.length < 20
|
1028
|
-
s
|
1029
|
-
CS
|
1030
|
-
end
|
1031
|
-
end
|
1032
|
-
calculated_member 'City' do
|
1033
|
-
dimension 'Measures'
|
1034
|
-
formula "[Customers].CurrentMember.Properties('City')"
|
1035
|
-
end
|
1036
|
-
end
|
1037
|
-
user_defined_function 'Factorial' do
|
1038
|
-
coffeescript <<-CS
|
1039
|
-
parameters: ["Numeric"]
|
1040
|
-
returns: "Numeric"
|
1041
|
-
execute: (n) ->
|
1042
|
-
if n <= 1 then 1 else n * @execute(n - 1)
|
1043
|
-
CS
|
1044
|
-
end
|
1045
|
-
user_defined_function 'UpperName' do
|
1046
|
-
coffeescript <<-CS
|
1047
|
-
parameters: ["Member"]
|
1048
|
-
returns: "String"
|
1049
|
-
syntax: "Property"
|
1050
|
-
execute: (member) ->
|
1051
|
-
member.getName().toUpperCase()
|
1052
|
-
CS
|
1053
|
-
end
|
1054
|
-
user_defined_function 'toUpperName' do
|
1055
|
-
coffeescript <<-CS
|
1056
|
-
parameters: ["Member", "String"]
|
1057
|
-
returns: "String"
|
1058
|
-
syntax: "Method"
|
1059
|
-
execute: (member, dummy) ->
|
1060
|
-
member.getName().toUpperCase()
|
1061
|
-
CS
|
1062
|
-
end
|
1063
|
-
end
|
1064
|
-
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
1065
|
-
end
|
1066
|
-
|
1067
|
-
it "should execute user defined function" do
|
1068
|
-
result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
|
1069
|
-
value = 1*2*3*4*5*6
|
1070
|
-
result.values.should == [value]
|
1071
|
-
result.formatted_values.should == ["%020d" % value]
|
1072
|
-
end
|
1073
|
-
|
1074
|
-
it "should format members and properties" do
|
1075
|
-
result = @olap.from('Sales').columns('[Measures].[City]').rows('[Customers].[All Customers].Children').execute
|
1076
|
-
result.row_members.each_with_index do |member, i|
|
1077
|
-
member.caption.should == member.name.upcase
|
1078
|
-
city = member.property_value('City')
|
1079
|
-
result.formatted_values[i].first.should == city
|
1080
|
-
member.property_formatted_value('City').should == city.upcase
|
1081
|
-
end
|
1082
|
-
end
|
1083
|
-
|
1084
|
-
it "should execute user defined property on member" do
|
1085
|
-
result = @olap.from('Sales').
|
1086
|
-
with_member('[Measures].[Upper Name]').as('[Customers].CurrentMember.UpperName').
|
1087
|
-
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
1088
|
-
result.row_members.each_with_index do |member, i|
|
1089
|
-
result.values[i].should == [member.name.upcase]
|
1090
|
-
end
|
1091
|
-
end
|
1092
|
-
|
1093
|
-
it "should execute user defined method on member" do
|
1094
|
-
result = @olap.from('Sales').
|
1095
|
-
with_member('[Measures].[Upper Name]').as("[Customers].CurrentMember.toUpperName('dummy')").
|
1096
|
-
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
1097
|
-
result.row_members.each_with_index do |member, i|
|
1098
|
-
result.values[i].should == [member.name.upcase]
|
1099
|
-
end
|
1100
|
-
end
|
1101
|
-
end
|
1102
|
-
|
1103
868
|
describe "User defined functions and formatters in Ruby" do
|
1104
869
|
before(:each) do
|
1105
870
|
@schema.define do
|