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 +4 -4
- data/lib/bmg.rb +1 -0
- data/lib/bmg/algebra.rb +17 -1
- data/lib/bmg/algebra/shortcuts.rb +27 -11
- data/lib/bmg/error.rb +4 -0
- data/lib/bmg/operator.rb +6 -44
- data/lib/bmg/operator/autowrap.rb +80 -8
- data/lib/bmg/operator/join.rb +19 -0
- data/lib/bmg/operator/shared/binary.rb +22 -0
- data/lib/bmg/operator/shared/nary.rb +19 -0
- data/lib/bmg/operator/shared/unary.rb +21 -0
- data/lib/bmg/operator/summarize.rb +77 -0
- data/lib/bmg/sequel/translator.rb +16 -1
- data/lib/bmg/sql/builder.rb +8 -0
- data/lib/bmg/sql/grammar.rb +2 -0
- data/lib/bmg/sql/grammar.sexp.yml +11 -0
- data/lib/bmg/sql/nodes/group_by_clause.rb +20 -0
- data/lib/bmg/sql/nodes/select_exp.rb +4 -0
- data/lib/bmg/sql/nodes/summarizer.rb +23 -0
- data/lib/bmg/sql/processor.rb +1 -0
- data/lib/bmg/sql/processor/summarize.rb +48 -0
- data/lib/bmg/sql/processor/where.rb +10 -5
- data/lib/bmg/sql/relation.rb +17 -0
- data/lib/bmg/sql/support/from_clause_orderer.rb +129 -39
- data/lib/bmg/summarizer.rb +132 -0
- data/lib/bmg/summarizer/avg.rb +36 -0
- data/lib/bmg/summarizer/collect.rb +31 -0
- data/lib/bmg/summarizer/concat.rb +42 -0
- data/lib/bmg/summarizer/count.rb +31 -0
- data/lib/bmg/summarizer/max.rb +31 -0
- data/lib/bmg/summarizer/min.rb +31 -0
- data/lib/bmg/summarizer/stddev.rb +26 -0
- data/lib/bmg/summarizer/sum.rb +31 -0
- data/lib/bmg/summarizer/variance.rb +42 -0
- data/lib/bmg/type.rb +11 -0
- data/lib/bmg/version.rb +1 -1
- metadata +34 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 218f9f80891f162695252fbfa56a2e9c2bb7d5f7
|
4
|
+
data.tar.gz: 7c2cf33b9fbc68a20ac08c788fc520b345d8193e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83bd84d4505c42a08df4b618f746f39c1548f9aa942ec37682fc95ef383bd8b46c86cb5654f6a858f1b06a1a32176a4b017bde63d2fded55e4ffcdc0157c13cd
|
7
|
+
data.tar.gz: 75adc3b09a8264cf0d56346137688e82bfac9264402fe1c586b42071c470fe0582132a672cd6aa0f8c4699cf8cabfa5728c5e574d54f48121cf16aaeaf9c8064
|
data/lib/bmg.rb
CHANGED
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
|
-
|
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
|
-
|
15
|
-
|
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
|
-
|
23
|
-
|
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.
|
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
data/lib/bmg/operator.rb
CHANGED
@@ -24,51 +24,12 @@ module Bmg
|
|
24
24
|
str
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
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 =
|
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 (
|
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 (
|
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
|
data/lib/bmg/operator/join.rb
CHANGED
@@ -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
|