bmg 0.21.5 → 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/lib/bmg/algebra.rb +10 -0
- data/lib/bmg/error.rb +4 -0
- data/lib/bmg/operator/autowrap.rb +1 -1
- data/lib/bmg/operator/constants.rb +3 -1
- data/lib/bmg/operator/extend.rb +2 -0
- data/lib/bmg/operator/image.rb +2 -0
- data/lib/bmg/operator/minus.rb +49 -0
- data/lib/bmg/operator/rename.rb +2 -0
- data/lib/bmg/operator.rb +1 -0
- data/lib/bmg/sql/builder.rb +1 -1
- data/lib/bmg/sql/relation.rb +14 -1
- data/lib/bmg/sql.rb +1 -1
- data/lib/bmg/support/ordering.rb +70 -30
- data/lib/bmg/type.rb +21 -6
- data/lib/bmg/version.rb +2 -2
- data/lib/bmg/writer.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be420623e04afe03570a1756d1607a202eb96481
|
4
|
+
data.tar.gz: 150b4f3f4daced5825e661765ed4530455263422
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b2d86803d1bfedfa5605f158a34c744e8b0fa77ed25314e38a28dac6bdef1512bc51a3398cfcd55df42fb60354880b4378b49d339682724cd3da42ef282ef27
|
7
|
+
data.tar.gz: 7321a2b290d2852ff4e2fe52b915e190d3fb13143807e8ac062928c8745a330d70c43fcbbf4abb545507cc8d2b9a05167502c44c7b6d5310c24b2e781920f827
|
data/README.md
CHANGED
@@ -260,6 +260,7 @@ r.left_join(right, [:a, :b, ...], {...}) # left join with optional default r
|
|
260
260
|
r.left_join(right, {:a => :x, ...}, {...}) # left join after right reversed renaming
|
261
261
|
r.matching(right, [:a, :b, ...]) # semi join, aka where exists
|
262
262
|
r.matching(right, :a => :x, :b => :y, ...) # semi join, after right reversed renaming
|
263
|
+
r.minus(right) # set difference
|
263
264
|
r.not_matching(right, [:a, :b, ...]) # inverse semi join, aka where not exists
|
264
265
|
r.not_matching(right, :a => :x, ...) # inverse semi join, after right reversed renaming
|
265
266
|
r.page([[:a, :asc], ...], 12, page_size: 10) # paging, using an explicit ordering
|
@@ -276,7 +277,7 @@ r.transform(:foo => :upcase, ...) # specific-attrs tranformation
|
|
276
277
|
r.transform([:to_s, :upcase]) # chain-transformation
|
277
278
|
r.ungroup([:a, :b, ...]) # ungroup relation-valued attributes within parent tuple
|
278
279
|
r.ungroup(:a) # shortcut over ungroup([:a])
|
279
|
-
r.union(right) #
|
280
|
+
r.union(right) # set union
|
280
281
|
r.unwrap([:a, :b, ...]) # merge tuple-valued attributes within parent tuple
|
281
282
|
r.unwrap(:a) # shortcut over unwrap([:a])
|
282
283
|
r.where(predicate) # alias for restrict(predicate)
|
data/lib/bmg/algebra.rb
CHANGED
@@ -203,6 +203,16 @@ module Bmg
|
|
203
203
|
end
|
204
204
|
protected :_union
|
205
205
|
|
206
|
+
def minus(other)
|
207
|
+
return self if other.is_a?(Relation::Empty)
|
208
|
+
_minus self.type.minus(other.type), other
|
209
|
+
end
|
210
|
+
|
211
|
+
def _minus(type, other)
|
212
|
+
Operator::Minus.new(type, [self, other])
|
213
|
+
end
|
214
|
+
protected :_minus
|
215
|
+
|
206
216
|
def unwrap(attrs)
|
207
217
|
_unwrap self.type.unwrap(attrs), attrs
|
208
218
|
end
|
data/lib/bmg/error.rb
CHANGED
@@ -64,7 +64,7 @@ module Bmg
|
|
64
64
|
protected ### optimization
|
65
65
|
|
66
66
|
def _page(type, ordering, page_index, options)
|
67
|
-
attrs = ordering.map{|(k,v)| k}
|
67
|
+
attrs = ordering.map{|(k,v)| k }
|
68
68
|
cs_attrs = the_constants.keys
|
69
69
|
if (attrs & cs_attrs).empty?
|
70
70
|
operand
|
@@ -73,6 +73,8 @@ module Bmg
|
|
73
73
|
else
|
74
74
|
super
|
75
75
|
end
|
76
|
+
rescue UnsupportedError
|
77
|
+
super
|
76
78
|
end
|
77
79
|
|
78
80
|
def _restrict(type, predicate)
|
data/lib/bmg/operator/extend.rb
CHANGED
data/lib/bmg/operator/image.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Bmg
|
2
|
+
module Operator
|
3
|
+
#
|
4
|
+
# Minus operator.
|
5
|
+
#
|
6
|
+
# Returns all tuples which are in the left operand but not in the right
|
7
|
+
# operand.
|
8
|
+
#
|
9
|
+
# This implementation is actually a NAry-Minus, since it handles
|
10
|
+
# an arbitrary number of operands.
|
11
|
+
#
|
12
|
+
class Minus
|
13
|
+
include Operator::Nary
|
14
|
+
|
15
|
+
def initialize(type, operands)
|
16
|
+
@type = type
|
17
|
+
@operands = operands
|
18
|
+
end
|
19
|
+
|
20
|
+
public
|
21
|
+
|
22
|
+
def each(&bl)
|
23
|
+
return to_enum unless block_given?
|
24
|
+
initial = operands[0].to_set
|
25
|
+
tuples = operands.drop(1).inject(initial) do |agg, op|
|
26
|
+
agg - op.to_set
|
27
|
+
end
|
28
|
+
tuples.each(&bl)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_ast
|
32
|
+
[ :minus ] + operands.map(&:to_ast)
|
33
|
+
end
|
34
|
+
|
35
|
+
protected ### optimization
|
36
|
+
|
37
|
+
def _minus(type, other)
|
38
|
+
return self if other.is_a?(Relation::Empty)
|
39
|
+
case other
|
40
|
+
when Minus
|
41
|
+
Minus.new(type, operands + other.operands)
|
42
|
+
else
|
43
|
+
Minus.new(type, operands + [other])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end # class Union
|
48
|
+
end # module Operator
|
49
|
+
end # module Bmg
|
data/lib/bmg/operator/rename.rb
CHANGED
data/lib/bmg/operator.rb
CHANGED
@@ -39,6 +39,7 @@ require_relative 'operator/group'
|
|
39
39
|
require_relative 'operator/image'
|
40
40
|
require_relative 'operator/join'
|
41
41
|
require_relative 'operator/matching'
|
42
|
+
require_relative 'operator/minus'
|
42
43
|
require_relative 'operator/not_matching'
|
43
44
|
require_relative 'operator/page'
|
44
45
|
require_relative 'operator/project'
|
data/lib/bmg/sql/builder.rb
CHANGED
@@ -157,7 +157,7 @@ module Bmg
|
|
157
157
|
builder :group_by_clause
|
158
158
|
|
159
159
|
def order_by_clause(ordering, &desaliaser)
|
160
|
-
ordering.
|
160
|
+
ordering.map{|(name,direction)|
|
161
161
|
name = name.to_s
|
162
162
|
name = (desaliaser && desaliaser[name]) || column_name(name)
|
163
163
|
[:order_by_term, name, direction ? direction.to_s : "asc"]
|
data/lib/bmg/sql/relation.rb
CHANGED
@@ -120,12 +120,15 @@ module Bmg
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def _page(type, ordering, page_index, options)
|
123
|
+
pairs = Ordering.new(ordering).to_pairs
|
123
124
|
limit = options[:page_size] || Operator::Page::DEFAULT_OPTIONS[:page_size]
|
124
125
|
offset = (page_index - 1) * limit
|
125
126
|
expr = before_use(self.expr)
|
126
|
-
expr = Processor::OrderBy.new(
|
127
|
+
expr = Processor::OrderBy.new(pairs, builder).call(expr)
|
127
128
|
expr = Processor::LimitOffset.new(limit, offset, builder).call(expr)
|
128
129
|
_instance(type, builder, expr)
|
130
|
+
rescue Bmg::NotSupportedError
|
131
|
+
super
|
129
132
|
end
|
130
133
|
|
131
134
|
def _project(type, attrlist)
|
@@ -191,6 +194,16 @@ module Bmg
|
|
191
194
|
end
|
192
195
|
end
|
193
196
|
|
197
|
+
def _minus(type, right)
|
198
|
+
if right_expr = extract_compatible_sexpr(right)
|
199
|
+
expr = before_use(self.expr)
|
200
|
+
expr = Processor::Merge.new(:except, false, right_expr, builder).call(expr)
|
201
|
+
_instance(type, builder, expr)
|
202
|
+
else
|
203
|
+
super
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
194
207
|
# Build a new relation instance for some new type & expression
|
195
208
|
#
|
196
209
|
# This method can be overriden by subclasses to provide their
|
data/lib/bmg/sql.rb
CHANGED
data/lib/bmg/support/ordering.rb
CHANGED
@@ -1,47 +1,87 @@
|
|
1
1
|
module Bmg
|
2
|
-
|
2
|
+
module Ordering
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
# Factory over subclasses
|
5
|
+
def self.new(arg)
|
6
|
+
case arg
|
7
|
+
when Ordering
|
8
|
+
arg
|
9
|
+
when Proc
|
10
|
+
Native.new(arg)
|
9
11
|
else
|
10
|
-
|
12
|
+
Attributes.new(arg)
|
11
13
|
end
|
12
14
|
end
|
13
|
-
attr_reader :attrs
|
14
15
|
|
15
16
|
def call(t1, t2)
|
16
17
|
comparator.call(t1, t2)
|
17
18
|
end
|
18
19
|
|
19
|
-
def
|
20
|
-
|
20
|
+
def to_a
|
21
|
+
to_pairs
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
24
|
+
def map(*args, &bl)
|
25
|
+
to_pairs.map(*args, &bl)
|
26
|
+
end
|
27
|
+
|
28
|
+
class Native
|
29
|
+
include Ordering
|
30
|
+
|
31
|
+
def initialize(comparator)
|
32
|
+
@comparator = comparator
|
33
|
+
end
|
34
|
+
attr_reader :comparator
|
35
|
+
|
36
|
+
def to_pairs
|
37
|
+
raise Bmg::NotSupportedError
|
38
|
+
end
|
39
|
+
|
40
|
+
end # class Native
|
41
|
+
|
42
|
+
class Attributes
|
43
|
+
include Ordering
|
44
|
+
|
45
|
+
def initialize(attrs)
|
46
|
+
@attrs = if attrs.empty?
|
47
|
+
[]
|
48
|
+
elsif attrs.first.is_a?(Symbol)
|
49
|
+
attrs.map{|a| [a, :asc] }
|
50
|
+
else
|
51
|
+
attrs
|
37
52
|
end
|
38
53
|
end
|
39
|
-
|
40
|
-
end
|
54
|
+
attr_reader :attrs
|
41
55
|
|
42
|
-
|
43
|
-
|
44
|
-
|
56
|
+
def comparator
|
57
|
+
@comparator ||= ->(t1, t2) { compare_attrs(t1, t2) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_pairs
|
61
|
+
attrs.to_a
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def compare_attrs(t1, t2)
|
67
|
+
attrs.each do |(attr,direction)|
|
68
|
+
a1, a2 = t1[attr], t2[attr]
|
69
|
+
if a1.nil? && a2.nil?
|
70
|
+
0
|
71
|
+
elsif a1.nil?
|
72
|
+
return direction == :desc ? -1 : 1
|
73
|
+
elsif a2.nil?
|
74
|
+
return direction == :desc ? 1 : -1
|
75
|
+
elsif a1.respond_to?(:<=>)
|
76
|
+
c = a1 <=> a2
|
77
|
+
unless c.nil? || c==0
|
78
|
+
return direction == :desc ? -c : c
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
0
|
83
|
+
end
|
45
84
|
|
46
|
-
|
85
|
+
end # class Attributes
|
86
|
+
end # module Ordering
|
47
87
|
end # module Bmg
|
data/lib/bmg/type.rb
CHANGED
@@ -288,12 +288,7 @@ module Bmg
|
|
288
288
|
end
|
289
289
|
|
290
290
|
def union(other)
|
291
|
-
|
292
|
-
missing = self.attrlist - other.attrlist
|
293
|
-
raise TypeError, "Union incompatible: missing right attributes #{missing.join(', ')}" unless missing.empty?
|
294
|
-
extra = other.attrlist - self.attrlist
|
295
|
-
raise TypeError, "Union incompatible: missing left attributes #{extra.join(', ')}" unless extra.empty?
|
296
|
-
end
|
291
|
+
union_compatible!(other, "Union")
|
297
292
|
dup.tap{|x|
|
298
293
|
### attrlist stays the same
|
299
294
|
x.predicate = self.predicate | predicate
|
@@ -301,6 +296,17 @@ module Bmg
|
|
301
296
|
}
|
302
297
|
end
|
303
298
|
|
299
|
+
def minus(other)
|
300
|
+
union_compatible!(other, "Minus")
|
301
|
+
dup.tap{|x|
|
302
|
+
# 1. attributes do not change
|
303
|
+
# 2. predicate does not change, we can't strengthen it in any way but it
|
304
|
+
# does not become weaker either
|
305
|
+
# 3. we can safely keep all existing keys, but it's not obvious we can
|
306
|
+
# gain some new ones
|
307
|
+
}
|
308
|
+
end
|
309
|
+
|
304
310
|
def unwrap(attrlist)
|
305
311
|
known_attributes!(attrlist) if typechecked? && knows_attrlist?
|
306
312
|
dup.tap{|x|
|
@@ -338,5 +344,14 @@ module Bmg
|
|
338
344
|
end
|
339
345
|
end
|
340
346
|
|
347
|
+
def union_compatible!(other, opname)
|
348
|
+
if typechecked? && knows_attrlist? && other.knows_attrlist?
|
349
|
+
missing = self.attrlist - other.attrlist
|
350
|
+
raise TypeError, "#{opname} requires compatible attribute lists, but the right operand is missing the following attributes: #{missing.join(', ')}" unless missing.empty?
|
351
|
+
extra = other.attrlist - self.attrlist
|
352
|
+
raise TypeError, "#{opname} requires compatible attribute lists, but the left operand is missing the following attributes: #{extra.join(', ')}" unless extra.empty?
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
341
356
|
end # class Type
|
342
357
|
end # module Bmg
|
data/lib/bmg/version.rb
CHANGED
data/lib/bmg/writer.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bmg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernard Lambeau
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: predicate
|
@@ -153,6 +153,7 @@ files:
|
|
153
153
|
- lib/bmg/operator/image.rb
|
154
154
|
- lib/bmg/operator/join.rb
|
155
155
|
- lib/bmg/operator/matching.rb
|
156
|
+
- lib/bmg/operator/minus.rb
|
156
157
|
- lib/bmg/operator/not_matching.rb
|
157
158
|
- lib/bmg/operator/page.rb
|
158
159
|
- lib/bmg/operator/project.rb
|