bmg 0.16.0.pre.rc1 → 0.16.0.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d34f68bbc40a7dd25495cc9964074e20567c8d66
4
- data.tar.gz: 9f6beb3a44b9da0a89e1bf523e9b02543e0ea136
3
+ metadata.gz: 218f9f80891f162695252fbfa56a2e9c2bb7d5f7
4
+ data.tar.gz: 7c2cf33b9fbc68a20ac08c788fc520b345d8193e
5
5
  SHA512:
6
- metadata.gz: cd37f8ad39cd7c05e7a725dfb305b8f8a3d74364d5abab1771f4a598c70d1b643b0b7046907f15fba7788645bfab5e302a4f48285b8ae1f6a7f3b7994a6f727b
7
- data.tar.gz: ecd95510bde8bc27ed111a917a3e38add365db53faaf62df6f21d2ea2703f8e6d60ffb8428819af31862971cda7fa99112efbe04a610b61d590db1d8dca5c3ef
6
+ metadata.gz: 83bd84d4505c42a08df4b618f746f39c1548f9aa942ec37682fc95ef383bd8b46c86cb5654f6a858f1b06a1a32176a4b017bde63d2fded55e4ffcdc0157c13cd
7
+ data.tar.gz: 75adc3b09a8264cf0d56346137688e82bfac9264402fe1c586b42071c470fe0582132a672cd6aa0f8c4699cf8cabfa5728c5e574d54f48121cf16aaeaf9c8064
data/lib/bmg.rb CHANGED
@@ -32,6 +32,7 @@ module Bmg
32
32
  require_relative 'bmg/support'
33
33
  require_relative 'bmg/algebra'
34
34
  require_relative 'bmg/type'
35
+ require_relative 'bmg/summarizer'
35
36
  require_relative 'bmg/relation'
36
37
  require_relative 'bmg/operator'
37
38
 
data/lib/bmg/algebra.rb CHANGED
@@ -71,10 +71,15 @@ module Bmg
71
71
  end
72
72
 
73
73
  def _join(type, right, on)
74
- Operator::Join.new(type, self, right, on)
74
+ right.send(:_joined_with, type, self, on)
75
75
  end
76
76
  protected :_join
77
77
 
78
+ def _joined_with(type, right, on)
79
+ Operator::Join.new(type, right, self, on)
80
+ end
81
+ protected :_joined_with
82
+
78
83
  def matching(right, on = [])
79
84
  _matching self.type.matching(right.type, on), right, on
80
85
  end
@@ -112,6 +117,8 @@ module Bmg
112
117
  protected :_project
113
118
 
114
119
  def rename(renaming = {})
120
+ renaming = renaming.reject{|k,v| k==v }
121
+ return self if renaming.empty?
115
122
  _rename self.type.rename(renaming), renaming
116
123
  end
117
124
 
@@ -143,6 +150,15 @@ module Bmg
143
150
  end
144
151
  protected :_restrict
145
152
 
153
+ def summarize(by, summarization = {})
154
+ _summarize self.type.summarize(by, summarization), by, summarization
155
+ end
156
+
157
+ def _summarize(type, by, summarization)
158
+ Operator::Summarize.new(type, self, by, summarization)
159
+ end
160
+ protected :_summarize
161
+
146
162
  def union(other, options = {})
147
163
  return self if other.is_a?(Relation::Empty)
148
164
  _union self.type.union(other.type), other, options
@@ -9,30 +9,46 @@ module Bmg
9
9
  self.restrict(predicate)
10
10
  end
11
11
 
12
- def prefix(prefix)
12
+ def prefix(prefix, options = {})
13
13
  raise "Attrlist must be known to use `prefix`" unless self.type.knows_attrlist?
14
- renaming = self.type.to_attrlist.each_with_object({}){|a,r|
15
- r[a] = :"#{prefix}#{a}"
16
- }
14
+ attrs = self.type.to_attrlist
15
+ attrs = attrs - options[:but] if options[:but]
16
+ renaming = Hash[attrs.map{|a| [a, :"#{prefix}#{a}"] }]
17
17
  self.rename(renaming)
18
18
  end
19
19
 
20
- def suffix(suffix)
20
+ def suffix(suffix, options = {})
21
21
  raise "Attrlist must be known to use `suffix`" unless self.type.knows_attrlist?
22
- renaming = self.type.to_attrlist.each_with_object({}){|a,r|
23
- r[a] = :"#{a}#{suffix}"
24
- }
22
+ attrs = self.type.to_attrlist
23
+ attrs = attrs - options[:but] if options[:but]
24
+ renaming = Hash[attrs.map{|a| [a, :"#{a}#{suffix}"] }]
25
25
  self.rename(renaming)
26
26
  end
27
27
 
28
+ def image(right, as = :image, on = [], options = {})
29
+ return super unless on.is_a?(Hash)
30
+ renaming = Hash[on.map{|k,v| [v,k] }]
31
+ self.image(right.rename(renaming), as, on.keys, options)
32
+ end
33
+
28
34
  def join(right, on = [])
29
35
  return super unless on.is_a?(Hash)
30
- renaming = on.each_pair.inject({}){|r, (k,v)|
31
- r.merge(v => k)
32
- }
36
+ renaming = Hash[on.map{|k,v| [v,k] }]
33
37
  self.join(right.rename(renaming), on.keys)
34
38
  end
35
39
 
40
+ def matching(right, on = [])
41
+ return super unless on.is_a?(Hash)
42
+ renaming = Hash[on.map{|k,v| [v,k] }]
43
+ self.matching(right.rename(renaming), on.keys)
44
+ end
45
+
46
+ def not_matching(right, on = [])
47
+ return super unless on.is_a?(Hash)
48
+ renaming = Hash[on.map{|k,v| [v,k] }]
49
+ self.not_matching(right.rename(renaming), on.keys)
50
+ end
51
+
36
52
  end # module Shortcuts
37
53
  end # module Algebra
38
54
  end # module Bmg
data/lib/bmg/error.rb CHANGED
@@ -12,4 +12,8 @@ module Bmg
12
12
  # Raised when violating types
13
13
  class TypeError < Error; end
14
14
 
15
+ # Raised by a type when trying to access attribute list
16
+ # while unknown
17
+ class UnknownAttributesError < Error; end
18
+
15
19
  end
data/lib/bmg/operator.rb CHANGED
@@ -24,51 +24,12 @@ module Bmg
24
24
  str
25
25
  end
26
26
 
27
- module Unary
28
- include Operator
27
+ end # module Operator
28
+ end # module Bmg
29
+ require_relative 'operator/shared/unary'
30
+ require_relative 'operator/shared/binary'
31
+ require_relative 'operator/shared/nary'
29
32
 
30
- attr_reader :operand
31
-
32
- def _visit(parent, visitor)
33
- visitor.call(self, parent)
34
- operand._visit(self, visitor)
35
- end
36
-
37
- def operands
38
- [operand]
39
- end
40
-
41
- end
42
-
43
- module Binary
44
- include Operator
45
-
46
- attr_reader :left, :right
47
-
48
- def _visit(parent, visitor)
49
- visitor.call(self, parent)
50
- left._visit(self, visitor)
51
- right._visit(self, visitor)
52
- end
53
-
54
- def operands
55
- [left, right]
56
- end
57
- end
58
-
59
- module Nary
60
- include Operator
61
-
62
- attr_reader :operands
63
-
64
- def _visit(parent, visitor)
65
- visitor.call(self, parent)
66
- operands.each{|op| op._visit(self, visitor) }
67
- end
68
- end
69
-
70
- end
71
- end
72
33
  require_relative 'operator/allbut'
73
34
  require_relative 'operator/autosummarize'
74
35
  require_relative 'operator/autowrap'
@@ -84,4 +45,5 @@ require_relative 'operator/project'
84
45
  require_relative 'operator/rename'
85
46
  require_relative 'operator/restrict'
86
47
  require_relative 'operator/rxmatch'
48
+ require_relative 'operator/summarize'
87
49
  require_relative 'operator/union'
@@ -29,8 +29,7 @@ module Bmg
29
29
  @type = type
30
30
  @operand = operand
31
31
  @original_options = options
32
- @options = DEFAULT_OPTIONS.merge(options)
33
- @options[:postprocessor] = NoLeftJoinNoise.new(@options[:postprocessor])
32
+ @options = normalize_options(options)
34
33
  end
35
34
 
36
35
  private
@@ -39,6 +38,10 @@ module Bmg
39
38
 
40
39
  public
41
40
 
41
+ def same_options?(opts)
42
+ normalize_options(opts) == options
43
+ end
44
+
42
45
  def each
43
46
  @operand.each do |tuple|
44
47
  yield autowrap_tuple(tuple)
@@ -51,26 +54,71 @@ module Bmg
51
54
 
52
55
  protected ### optimization
53
56
 
57
+ def _autowrap(type, opts)
58
+ if same_options?(opts)
59
+ self
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ def _join(type, right, on)
66
+ if _join_optimizable?(type, right, on)
67
+ operand.join(right, on).autowrap(options)
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ def _joined_with(type, right, on)
74
+ if _join_optimizable?(type, right, on)
75
+ right.join(operand, on).autowrap(options)
76
+ else
77
+ super
78
+ end
79
+ end
80
+
81
+ def _join_optimizable?(type, right, on)
82
+ # 1. Can't optimize if wrapped roots are used in join clause
83
+ # 2. Can't optimize if other attributes would be autowrapped
84
+ (wrapped_roots! & on).empty? && wrapped_roots_of!(right, options).empty?
85
+ rescue UnknownAttributesError
86
+ false
87
+ end
88
+
54
89
  def _page(type, ordering, page_index, opts)
55
- return super unless operand.type.knows_attrlist?
56
- roots = Support.wrapped_roots(operand.type.to_attrlist, options[:split])
57
90
  attrs = ordering.map{|(a,d)| a }
58
- if (roots & attrs).empty?
91
+ if (wrapped_roots! & attrs).empty?
59
92
  operand.page(ordering, page_index, opts).autowrap(options)
60
93
  else
61
94
  super
62
95
  end
96
+ rescue UnknownAttributesError
97
+ super
98
+ end
99
+
100
+ def _rename(type, renaming)
101
+ # 1. Can't optimize if renaming applies to a wrapped one
102
+ return super unless (wrapped_roots! & renaming.keys).empty?
103
+
104
+ # 2. Can't optimize if new attributes would be autowrapped
105
+ new_roots = Support.wrapped_roots(renaming.values, options[:split])
106
+ return super unless new_roots.empty?
107
+
108
+ operand.rename(renaming).autowrap(options)
109
+ rescue UnknownAttributesError
110
+ super
63
111
  end
64
112
 
65
113
  def _restrict(type, predicate)
66
- return super unless operand.type.knows_attrlist?
67
- roots = Support.wrapped_roots(operand.type.to_attrlist, options[:split])
68
114
  vars = predicate.free_variables
69
- if (roots & vars).empty?
115
+ if (wrapped_roots! & vars).empty?
70
116
  operand.restrict(predicate).autowrap(options)
71
117
  else
72
118
  super
73
119
  end
120
+ rescue UnknownAttributesError
121
+ super
74
122
  end
75
123
 
76
124
  protected ### inspect
@@ -81,6 +129,21 @@ module Bmg
81
129
 
82
130
  private
83
131
 
132
+ def wrapped_roots!
133
+ @wrapped_roots ||= wrapped_roots_of!(operand, options)
134
+ end
135
+
136
+ def wrapped_roots_of!(r, opts)
137
+ raise UnknownAttributesError unless r.type.knows_attrlist?
138
+ Support.wrapped_roots(r.type.to_attrlist, opts[:split])
139
+ end
140
+
141
+ def normalize_options(options)
142
+ opts = DEFAULT_OPTIONS.merge(options)
143
+ opts[:postprocessor] = NoLeftJoinNoise.new(opts[:postprocessor])
144
+ opts
145
+ end
146
+
84
147
  def autowrap_tuple(tuple)
85
148
  separator = @options[:split]
86
149
  autowrapped = tuple.each_with_object({}){|(k,v),h|
@@ -137,6 +200,7 @@ module Bmg
137
200
  raise "Invalid remover `#{remover}`"
138
201
  end
139
202
  end
203
+ attr_reader :remover
140
204
 
141
205
  def call(tuple)
142
206
  tuple.each_key do |k|
@@ -155,6 +219,14 @@ module Bmg
155
219
  end
156
220
  alias :to_s :inspect
157
221
 
222
+ def hash
223
+ remover.hash
224
+ end
225
+
226
+ def ==(other)
227
+ other.is_a?(NoLeftJoinNoise) && remover.eql?(other.remover)
228
+ end
229
+
158
230
  end # NoLeftJoinNoise
159
231
 
160
232
  module Support
@@ -42,6 +42,25 @@ module Bmg
42
42
  [ :join, left.to_ast, right.to_ast, on ]
43
43
  end
44
44
 
45
+ protected ### optimization
46
+
47
+ def _autowrap(type, options)
48
+ u_left, left_replaced = _unautowrap(left, options)
49
+ u_right, right_replaced = _unautowrap(right, options)
50
+ if (!left_replaced && !right_replaced)
51
+ super
52
+ else
53
+ u_left.join(u_right, on).autowrap(options)
54
+ end
55
+ end
56
+
57
+ def _unautowrap(operand, options)
58
+ return [operand, false] unless operand.is_a?(Operator::Autowrap)
59
+ return [operand, false] unless operand.same_options?(options)
60
+ [operand.send(:operand), true]
61
+ end
62
+ private :_unautowrap
63
+
45
64
  protected ### inspect
46
65
 
47
66
  def args
@@ -0,0 +1,22 @@
1
+ module Bmg
2
+ module Operator
3
+ module Binary
4
+ include Operator
5
+
6
+ protected
7
+
8
+ attr_reader :left, :right
9
+
10
+ def _visit(parent, visitor)
11
+ visitor.call(self, parent)
12
+ left.send(:_visit, self, visitor)
13
+ right.send(:_visit, self, visitor)
14
+ end
15
+
16
+ def operands
17
+ [left, right]
18
+ end
19
+
20
+ end # module Binary
21
+ end # module Operator
22
+ end # module Bmg
@@ -0,0 +1,19 @@
1
+ module Bmg
2
+ module Operator
3
+ module Nary
4
+ include Operator
5
+
6
+ protected
7
+
8
+ attr_reader :operands
9
+
10
+ def _visit(parent, visitor)
11
+ visitor.call(self, parent)
12
+ operands.each{|op|
13
+ op.send(:_visit, self, visitor)
14
+ }
15
+ end
16
+
17
+ end # module Nary
18
+ end # module Operator
19
+ end # module Bmg
@@ -0,0 +1,21 @@
1
+ module Bmg
2
+ module Operator
3
+ module Unary
4
+ include Operator
5
+
6
+ protected
7
+
8
+ attr_reader :operand
9
+
10
+ def _visit(parent, visitor)
11
+ visitor.call(self, parent)
12
+ operand._visit(self, visitor)
13
+ end
14
+
15
+ def operands
16
+ [operand]
17
+ end
18
+
19
+ end # module Unary
20
+ end # module Operator
21
+ end # module Bmg
@@ -0,0 +1,77 @@
1
+ module Bmg
2
+ module Operator
3
+ #
4
+ # Summarize operator.
5
+ #
6
+ # Makes a summarization by some attributes, applying aggregations
7
+ # to the corresponding images.
8
+ #
9
+ class Summarize
10
+ include Operator::Unary
11
+
12
+ def initialize(type, operand, by, summarization)
13
+ @type = type
14
+ @operand = operand
15
+ @by = by
16
+ @summarization = Summarize.compile(summarization)
17
+ end
18
+
19
+ protected
20
+
21
+ attr_reader :by, :summarization
22
+
23
+ public
24
+
25
+ def each
26
+ # summary key => summarization memo, starting with least
27
+ result = Hash.new{|h,k|
28
+ h[k] = Hash[@summarization.map{|k,v|
29
+ [ k, v.least ]
30
+ }]
31
+ }
32
+ # iterate each tuple
33
+ @operand.each do |tuple|
34
+ key = TupleAlgebra.project(tuple, @by)
35
+ # apply them all and create a new memo
36
+ result[key] = Hash[@summarization.map{|k,v|
37
+ [ k, v.happens(result[key][k], tuple) ]
38
+ }]
39
+ end
40
+ # Merge result keys and values
41
+ result.each_pair do |by,sums|
42
+ tuple = Hash[@summarization.map{|k,v|
43
+ [ k, v.finalize(sums[k]) ]
44
+ }].merge(by)
45
+ yield(tuple)
46
+ end
47
+ end
48
+
49
+ def to_ast
50
+ [ :summarize, operand.to_ast, by, summarization ]
51
+ end
52
+
53
+ protected ### inspect
54
+
55
+ def args
56
+ [ by, summarization ]
57
+ end
58
+
59
+ private
60
+
61
+ # Compile a summarization hash so that every value is a Summarizer
62
+ # instance
63
+ def self.compile(summarization)
64
+ Hash[summarization.map{|k,v|
65
+ summarizer = case v
66
+ when Summarizer then v
67
+ when Symbol then Summarizer.send(v, k)
68
+ else
69
+ raise ArgumentError, "Unexpected summarizer #{k} => #{v}"
70
+ end
71
+ [ k, summarizer ]
72
+ }]
73
+ end
74
+
75
+ end # class Summarize
76
+ end # module Operator
77
+ end # module Bmg