activecube 0.1.25 → 0.1.30

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94f1e4575a041108004194789f85b9c030c6f7f94083677dee253de3cc878966
4
- data.tar.gz: b182640a584166cefc17b7b26e7add652600b0c937f08b83689a02f819bb2bb9
3
+ metadata.gz: 663ae546f63257569c7692d59478ffef3c954f2c9a74f53c78ae6ee31bdf093c
4
+ data.tar.gz: df61b6a95415e4879d62b0eada8abcf732d9d7aeca882c72fa28ca929c567db1
5
5
  SHA512:
6
- metadata.gz: ab68fd59fdbffe5e0b3d4b8f0523d351b5a6c2840090328af0e78ed10c7fc23e402335f99f05d114da175c57d0b12f0d4fa02195a89ab52108af451b2dae0445
7
- data.tar.gz: ace9d8f215cb59e930cf66249211405408c2bce905166fbe59370708dea17502d40d2416611297a1d6c06d0f6930ee3e73460775cfe1ad32895030db22a49c65
6
+ metadata.gz: e25d8a4f05d9880c4c3acbf6854393556902e0339976701949ab5b56bc3d627f20a9790144c19f00903786fa0abd947c2cb866bdc5d41856483cd8e030a281d1
7
+ data.tar.gz: 418d88e3f5d6249de2781faa1f27cfc42d219538eac7c2d68611735771cf8d2413ac2a1e626559d7b8f0d0248852358a58ea5400af2a561248cb1a591126e49a
@@ -9,7 +9,7 @@ GIT
9
9
  PATH
10
10
  remote: .
11
11
  specs:
12
- activecube (0.1.24)
12
+ activecube (0.1.29)
13
13
  activerecord (>= 5.2)
14
14
 
15
15
  GEM
data/README.md CHANGED
@@ -121,6 +121,12 @@ index ['currency_id'], cardinality: 4
121
121
  index ['currency_id','date'], cardinality: 6
122
122
  ```
123
123
 
124
+ You can require using index in some cases. If required: true added, the table will be used **only** in case when this field is used
125
+ in query metric, dimension or selector.
126
+ ```ruby
127
+ index ['currency_id'], cardinality: 4, required: true
128
+ ```
129
+
124
130
  ### Query language
125
131
 
126
132
  You use the cube class to create and execute queries.
@@ -2,7 +2,7 @@ module Activecube::Common
2
2
 
3
3
  module Metrics
4
4
 
5
- METHODS = [:count, :minimum,:maximum,:average,:sum,:uniqueExact,:unique,:median,:any,:anyLast]
5
+ METHODS = [:count, :minimum,:maximum,:average,:sum,:uniqueExact,:unique,:median,:medianExact,:any,:anyLast]
6
6
 
7
7
  METHODS.each do |fname|
8
8
 
@@ -31,13 +31,14 @@ module Activecube
31
31
 
32
32
  include DefinitionMethods
33
33
 
34
- attr_reader :identity, :fields
34
+ attr_reader :identity, :identity_expression, :fields
35
35
 
36
36
  private
37
37
 
38
38
  def identity_column *args
39
39
  raise "Identity already defined as #{identity} for #{self.name}" if @identity
40
40
  @identity = args.first
41
+ @identity_expression = args.second
41
42
  end
42
43
 
43
44
  def field *args
@@ -52,7 +53,7 @@ module Activecube
52
53
 
53
54
  include DefinitionMethods
54
55
 
55
- attr_reader :modifiers
56
+ attr_reader :modifiers, :tuple
56
57
 
57
58
  private
58
59
 
@@ -60,6 +61,9 @@ module Activecube
60
61
  (@modifiers ||= {} )[args.first.to_sym] = Modifier.new( *args)
61
62
  end
62
63
 
64
+ def tuple_fields *args
65
+ @tuple = args
66
+ end
63
67
  end
64
68
 
65
69
  end
@@ -2,16 +2,20 @@ module Activecube
2
2
  module Processor
3
3
  class Index
4
4
 
5
- attr_reader :fields, :cardinality
5
+ attr_reader :fields, :cardinality, :required
6
6
  def initialize name, *args
7
7
  @fields = [name].flatten
8
8
  @cardinality = args.first && args.first[:cardinality]
9
+ @required = args.first && args.first[:required]
9
10
  end
10
11
 
11
12
  def indexes? query, measures
12
13
  (fields - query.selector_column_names(measures)).empty?
13
14
  end
14
15
 
16
+ def matches? query, measures
17
+ !required || (fields - query.column_names(measures)).empty?
18
+ end
15
19
  end
16
20
  end
17
21
  end
@@ -18,7 +18,7 @@ module Activecube::Processor
18
18
  @tables_count = cost_matrix.map(&:count).max
19
19
  @metrics_count = cost_matrix.count
20
20
 
21
- tables_count==1 ? [0]*metrics_count : do_optimize
21
+ (tables_count==1 || metrics_count==0) ? [0]*metrics_count : do_optimize
22
22
 
23
23
  end
24
24
 
@@ -27,95 +27,41 @@ module Activecube::Processor
27
27
 
28
28
  private
29
29
 
30
- def do_optimize
30
+ def generate_variants vs, metric_i
31
31
 
32
- @tables_by_metrics = []
32
+ return vs if metric_i==metrics_count
33
33
 
34
- # sort metrics from low min cost to higher min costs ( by all applicable tables )
35
- sort_metrics
34
+ metric_tables = cost_matrix[metric_i].map.with_index do |c, index|
35
+ [index] if c
36
+ end.compact
36
37
 
37
- # fill initial @tables_by_metrics by selecting tables with minimum cost for metrics.
38
- # If there are more than one table with this minimum cost, then select already selected table with maximum cost
39
- select_min_cost_by_metric
38
+ vsnew = if metric_i==0
39
+ metric_tables
40
+ else
41
+ arry = []
42
+ vs.each do |v|
43
+ metric_tables.each{|newv|
44
+ arry << (v + newv)
45
+ }
46
+ end
47
+ arry
48
+ end
40
49
 
41
- # make iterations over @tables_by_metrics ( max MAX_ITERATIONS)
42
- iterates
50
+ generate_variants vsnew, metric_i+1
43
51
 
44
- @tables_by_metrics
45
52
  end
46
53
 
47
- def sort_metrics
48
- @metrics_index_sorted = (0...metrics_count).sort_by{|m_i| cost_matrix[m_i].compact.min || UNLIM_COST }
54
+ def cost_for variant
55
+ variant.each_with_index.group_by(&:first).collect do |table_index, arry|
56
+ arry.map(&:second).map{|metric_index| cost_matrix[metric_index][table_index] }.max
57
+ end.sum
49
58
  end
50
59
 
51
- def select_min_cost_by_metric
52
-
53
- @metrics_index_sorted.collect do |m_i|
54
-
55
- table_index_cost = (0...tables_count).map{|c_i| [c_i,
56
- cost_matrix[m_i][c_i] || UNLIM_COST,
57
- (@tables_by_metrics.include?(c_i) ? -cost_matrix[@tables_by_metrics.index(c_i)][c_i] : 0)
58
- ]}.sort_by(&:third).sort_by(&:second)
59
-
60
- @tables_by_metrics[m_i] = table_index_cost.first.first
61
-
62
- end
63
- end
64
-
65
- def iterates
66
-
67
- steps = [@tables_by_metrics]
68
-
69
- (1..MAX_ITERATIONS).each do |iteration|
70
-
71
- step = []
72
- prev_step = steps.last
73
-
74
- prev_step.each_with_index {|c_i, m_i|
75
-
76
- table_included_times = prev_step.select{|c| c==c_i }.count
77
- old_cost = cost_matrix[m_i][c_i]
78
- new_c_i = (0...tables_count).detect{|c_n|
79
- new_cost = cost_matrix[m_i][c_n]
80
- next if c_i==c_n || new_cost.nil?
81
- new_table_included_times = prev_step.select{|c| c==c_n }.count
82
-
83
- if old_cost.nil?
84
- # if we have non indexed table now
85
- true
86
- elsif table_included_times>1
87
- if new_table_included_times>0
88
- # table to used table if
89
- # cost now > new cost
90
- old_cost > new_cost
91
- else
92
- # table to unused table if
93
- # cost now > new cost + max other cost in table now
94
- old_cost > new_cost + ( prev_step.select.with_index{|c,i| c==c_i && i!=m_i }.map{|c| cost_matrix[m_i][c]}.max || UNLIM_COST )
95
- end
96
- else
97
- if new_table_included_times>0
98
- # unused table to table if
99
- # new cost < cost now + max other cost in new table
100
- old_cost > new_cost - ( prev_step.select{|c| c==c_n }.map{|c| cost_matrix[m_i][c]}.max || UNLIM_COST )
101
- else
102
- # unused to unused
103
- # cost now > new cost
104
- old_cost > new_cost
105
- end
106
- end
107
-
108
- }
109
-
110
- step << (new_c_i || c_i)
111
-
112
- }
113
-
114
- break if steps.include? step
115
- steps << step
116
- end
60
+ def do_optimize
117
61
 
118
- @tables_by_metrics = steps.last
62
+ variants = generate_variants [], 0
63
+ variant_costs = variants.map{|v| cost_for v}
64
+ variants[variant_costs.each_with_index.min.second]
119
65
 
120
66
  end
121
67
 
@@ -12,7 +12,8 @@ module Activecube::Processor
12
12
  end
13
13
 
14
14
  def matches? query, measures = query.measures
15
- (query.column_names(measures)-model.attribute_types.keys).empty?
15
+ (query.column_names(measures)-model.attribute_types.keys).empty? &&
16
+ !model.activecube_indexes.detect{|index| !index.matches?(query, measures) }
16
17
  end
17
18
 
18
19
  def measures? measure
@@ -1,6 +1,7 @@
1
1
  require 'activecube/query/chain_appender'
2
2
  require 'activecube/query/item'
3
3
  require 'activecube/query/limit'
4
+ require 'activecube/query/limit_by'
4
5
  require 'activecube/query/measure'
5
6
  require 'activecube/query/ordering'
6
7
  require 'activecube/query/option'
@@ -82,6 +83,12 @@ module Activecube::Query
82
83
  self
83
84
  end
84
85
 
86
+ def limit_by *args
87
+ clear_sql
88
+ options << LimitBy.new(args)
89
+ self
90
+ end
91
+
85
92
 
86
93
  def query
87
94
  sql = to_query.to_sql
@@ -0,0 +1,19 @@
1
+ module Activecube
2
+ module Query
3
+ class LimitBy
4
+
5
+ attr_reader :each, :limit, :offset
6
+ def initialize arguments
7
+ map = Hash[arguments]
8
+ @each = map[:each]
9
+ @limit = map[:limit]
10
+ @offset = map[:offset] || 0
11
+ end
12
+
13
+ def append_query _model, _cube_query, _table, query
14
+ query.limit_by each, limit, offset
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -68,11 +68,20 @@ module Activecube::Query
68
68
  query = query.project(expr.as(attr_alias))
69
69
 
70
70
  if dimension.class.identity
71
+ expr = dimension.class.identity_expression
71
72
  group_by_columns.each do |column|
72
- if !query.projections.detect{|p| p.kind_of?(::Arel::Attributes::Attribute) && p.name==column}
73
- query = query.project(table[column])
73
+
74
+ node = if column==dimension.class.identity && expr
75
+ Arel.sql(expr).as(column)
76
+ else
77
+ table[column]
78
+ end
79
+
80
+ unless query.projections.include?(node)
81
+ query = query.project(node)
74
82
  end
75
- query = query.group(table[column])
83
+
84
+ query = query.group( expr ? column : table[column])
76
85
  end
77
86
  else
78
87
  query = query.group(attr_alias)
@@ -1,3 +1,3 @@
1
1
  module Activecube
2
- VERSION = "0.1.25"
2
+ VERSION = "0.1.30"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activecube
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.25
4
+ version: 0.1.30
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksey Studnev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-10 00:00:00.000000000 Z
11
+ date: 2020-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -108,6 +108,7 @@ files:
108
108
  - lib/activecube/query/cube_query.rb
109
109
  - lib/activecube/query/item.rb
110
110
  - lib/activecube/query/limit.rb
111
+ - lib/activecube/query/limit_by.rb
111
112
  - lib/activecube/query/measure.rb
112
113
  - lib/activecube/query/measure_nothing.rb
113
114
  - lib/activecube/query/modification.rb