bmg 0.21.5 → 0.22.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/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
|