mondrian-olap 1.2.0 → 1.3.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.
- 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
|