bmg 0.19.1 → 0.19.3
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 +5 -5
- data/README.md +1 -2
- data/lib/bmg/operator/extend.rb +6 -1
- data/lib/bmg/reader/csv.rb +3 -1
- data/lib/bmg/sequel/translator.rb +1 -0
- data/lib/bmg/sql/processor/extend.rb +33 -0
- data/lib/bmg/sql/processor/reorder.rb +1 -1
- data/lib/bmg/sql/processor/where.rb +2 -1
- data/lib/bmg/sql/processor.rb +1 -0
- data/lib/bmg/sql/relation.rb +17 -0
- data/lib/bmg/support/ordering.rb +11 -1
- data/lib/bmg/support/output_preferences.rb +27 -1
- data/lib/bmg/version.rb +1 -1
- data/lib/bmg/writer/csv.rb +3 -1
- data/lib/bmg/writer/xlsx.rb +9 -7
- data/lib/bmg/writer.rb +11 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 773942a08ee8480d73b6d455ef2560c1136002223c86b38ff549beaf280e2760
|
4
|
+
data.tar.gz: 22043853a3d11acde72865db0ad870f2fe033a71e9474ffb1451cc4d69a58d30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96dc776f81d2b15093372b843e4bb20c4207e0e59a44842130cb79035fd06ab32fb633f34479c2022b7ad5c958ade798fdc10f07cdcf8db60848d382554067eb
|
7
|
+
data.tar.gz: fa08f7f03dc62e03302e82e68ba0b32505c1fce2f8a78c498c40894de37e1e486e60104e18a7e84761ea56b947bf6c585825ce2462a702b0e85d30601a138ac5
|
data/README.md
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# Bmg, a relational algebra (Alf's successor)!
|
2
2
|
|
3
|
-
[](https://travis-ci.com/enspirit/bmg)
|
4
|
-
|
5
3
|
Bmg is a relational algebra implemented as a ruby library. It implements the
|
6
4
|
[Relation as First-Class Citizen](http://www.try-alf.org/blog/2013-10-21-relations-as-first-class-citizen)
|
7
5
|
paradigm contributed with [Alf](http://www.try-alf.org/) a few years ago.
|
@@ -210,6 +208,7 @@ r.autowrap(split: '_') # structure a flat relation, split:
|
|
210
208
|
r.autosummarize([:a, :b, ...], x: :sum) # (experimental) usual summarizers supported
|
211
209
|
r.constants(x: 12, ...) # add constant attributes (sometimes useful in unions)
|
212
210
|
r.extend(x: ->(t){ ... }, ...) # add computed attributes
|
211
|
+
r.extend(x: :y) # shortcut for r.extend(x: ->(t){ t[:y] })
|
213
212
|
r.exclude(predicate) # shortcut for restrict(!predicate)
|
214
213
|
r.group([:a, :b, ...], :x) # relation-valued attribute from attributes
|
215
214
|
r.image(right, :x, [:a, :b, ...]) # relation-valued attribute from another relation
|
data/lib/bmg/operator/extend.rb
CHANGED
data/lib/bmg/reader/csv.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Bmg
|
2
|
+
module Sql
|
3
|
+
class Processor
|
4
|
+
class Extend < Processor
|
5
|
+
|
6
|
+
def initialize(extension, builder)
|
7
|
+
super(builder)
|
8
|
+
@extension = extension
|
9
|
+
end
|
10
|
+
attr_reader :extension
|
11
|
+
|
12
|
+
def on_set_operator(sexpr)
|
13
|
+
apply(builder.from_self(sexpr))
|
14
|
+
end
|
15
|
+
alias :on_union :on_set_operator
|
16
|
+
alias :on_except :on_set_operator
|
17
|
+
alias :on_intersect :on_set_operator
|
18
|
+
|
19
|
+
def on_select_star(sexpr)
|
20
|
+
raise NotImplementedError, "Extend on * is not supported"
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_select_list(sexpr)
|
24
|
+
sexpr + extension.each_pair.map{|(k,v)|
|
25
|
+
desaliased = sexpr.desaliaser[v]
|
26
|
+
[:select_item, desaliased, [:column_name, k] ]
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
end # class Extend
|
31
|
+
end # class Processor
|
32
|
+
end # module Sql
|
33
|
+
end # module Bmg
|
@@ -39,9 +39,10 @@ module Bmg
|
|
39
39
|
def falsy?(sexpr)
|
40
40
|
return false unless sexpr.respond_to?(:predicate)
|
41
41
|
return false if sexpr.predicate.nil?
|
42
|
+
|
42
43
|
left = Predicate.new(Predicate::Grammar.sexpr(sexpr.predicate)).unqualify
|
43
44
|
right = Predicate.new(Predicate::Grammar.sexpr(@predicate.sexpr)).unqualify
|
44
|
-
|
45
|
+
(left & right).contradiction?
|
45
46
|
end
|
46
47
|
|
47
48
|
end # class Where
|
data/lib/bmg/sql/processor.rb
CHANGED
@@ -71,6 +71,7 @@ require_relative 'processor/distinct'
|
|
71
71
|
require_relative 'processor/all'
|
72
72
|
require_relative 'processor/clip'
|
73
73
|
require_relative 'processor/constants'
|
74
|
+
require_relative 'processor/extend'
|
74
75
|
require_relative 'processor/star'
|
75
76
|
require_relative 'processor/rename'
|
76
77
|
require_relative 'processor/order_by'
|
data/lib/bmg/sql/relation.rb
CHANGED
@@ -57,6 +57,23 @@ module Bmg
|
|
57
57
|
_instance(type, builder, expr)
|
58
58
|
end
|
59
59
|
|
60
|
+
def _extend(type, extension)
|
61
|
+
supported, unsupported = {}, {}
|
62
|
+
extension.each_pair do |k,v|
|
63
|
+
(v.is_a?(Symbol) ? supported : unsupported)[k] = v
|
64
|
+
end
|
65
|
+
if supported.empty?
|
66
|
+
Operator::Extend.new(type, self, extension)
|
67
|
+
elsif unsupported.empty?
|
68
|
+
expr = Processor::Extend.new(supported, builder).call(self.expr)
|
69
|
+
_instance(type, builder, expr)
|
70
|
+
else
|
71
|
+
expr = Processor::Extend.new(supported, builder).call(self.expr)
|
72
|
+
operand = _instance(type.allbut(unsupported.keys), builder, expr)
|
73
|
+
Operator::Extend.new(type, operand, unsupported)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
60
77
|
def _join(type, right, on)
|
61
78
|
if right_expr = extract_compatible_sexpr(right)
|
62
79
|
right_expr = Processor::Requalify.new(builder).call(right_expr)
|
data/lib/bmg/support/ordering.rb
CHANGED
@@ -2,7 +2,13 @@ module Bmg
|
|
2
2
|
class Ordering
|
3
3
|
|
4
4
|
def initialize(attrs)
|
5
|
-
@attrs = attrs
|
5
|
+
@attrs = if attrs.empty?
|
6
|
+
[]
|
7
|
+
elsif attrs.first.is_a?(Symbol)
|
8
|
+
attrs.map{|a| [a, :asc] }
|
9
|
+
else
|
10
|
+
attrs
|
11
|
+
end
|
6
12
|
end
|
7
13
|
attr_reader :attrs
|
8
14
|
|
@@ -33,5 +39,9 @@ module Bmg
|
|
33
39
|
0
|
34
40
|
end
|
35
41
|
|
42
|
+
def to_a
|
43
|
+
attrs.to_a
|
44
|
+
end
|
45
|
+
|
36
46
|
end # class Ordering
|
37
47
|
end # module Bmg
|
@@ -3,6 +3,7 @@ module Bmg
|
|
3
3
|
|
4
4
|
DEFAULT_PREFS = {
|
5
5
|
attributes_ordering: nil,
|
6
|
+
tuple_ordering: nil,
|
6
7
|
extra_attributes: :after
|
7
8
|
}
|
8
9
|
|
@@ -17,6 +18,12 @@ module Bmg
|
|
17
18
|
new(arg)
|
18
19
|
end
|
19
20
|
|
21
|
+
def tuple_ordering
|
22
|
+
return nil unless to = options[:tuple_ordering]
|
23
|
+
|
24
|
+
@tuple_ordering = Ordering.new(to)
|
25
|
+
end
|
26
|
+
|
20
27
|
def attributes_ordering
|
21
28
|
options[:attributes_ordering]
|
22
29
|
end
|
@@ -25,10 +32,29 @@ module Bmg
|
|
25
32
|
options[:extra_attributes]
|
26
33
|
end
|
27
34
|
|
35
|
+
def grouping_attributes
|
36
|
+
options[:grouping_attributes]
|
37
|
+
end
|
38
|
+
|
39
|
+
def erase_redundance_in_group(before, current)
|
40
|
+
return [nil, current] unless ga = grouping_attributes
|
41
|
+
return [current, current] unless before
|
42
|
+
|
43
|
+
new_before, new_current = current.dup, current.dup
|
44
|
+
ga.each do |attr|
|
45
|
+
return [new_before, new_current] unless before[attr] == current[attr]
|
46
|
+
new_current[attr] = nil
|
47
|
+
end
|
48
|
+
[new_before, new_current]
|
49
|
+
end
|
50
|
+
|
28
51
|
def order_attrlist(attrlist)
|
29
52
|
return attrlist if attributes_ordering.nil?
|
53
|
+
|
30
54
|
index = Hash[attributes_ordering.each_with_index.to_a]
|
31
|
-
attrlist
|
55
|
+
base = attrlist
|
56
|
+
base = attrlist & attributes_ordering if extra_attributes == :ignored
|
57
|
+
base.sort{|a,b|
|
32
58
|
ai, bi = index[a], index[b]
|
33
59
|
if ai && bi
|
34
60
|
ai <=> bi
|
data/lib/bmg/version.rb
CHANGED
data/lib/bmg/writer/csv.rb
CHANGED
@@ -16,12 +16,14 @@ module Bmg
|
|
16
16
|
require 'csv'
|
17
17
|
string_or_io, to_s = string_or_io.nil? ? [StringIO.new, true] : [string_or_io, false]
|
18
18
|
headers, csv = infer_headers(relation.type), nil
|
19
|
-
|
19
|
+
previous = nil
|
20
|
+
each_tuple(relation) do |tuple,i|
|
20
21
|
if csv.nil?
|
21
22
|
headers = infer_headers(tuple) if headers.nil?
|
22
23
|
csv_opts = csv_options.merge(headers: headers)
|
23
24
|
csv = CSV.new(string_or_io, **csv_opts)
|
24
25
|
end
|
26
|
+
previous, tuple = output_preferences.erase_redundance_in_group(previous, tuple)
|
25
27
|
csv << headers.map{|h| tuple[h] }
|
26
28
|
end
|
27
29
|
to_s ? string_or_io.string : string_or_io
|
data/lib/bmg/writer/xlsx.rb
CHANGED
@@ -6,11 +6,11 @@ module Bmg
|
|
6
6
|
DEFAULT_OPTIONS = {
|
7
7
|
}
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@
|
9
|
+
def initialize(xlsx_options, output_preferences = nil)
|
10
|
+
@xlsx_options = DEFAULT_OPTIONS.merge(xlsx_options)
|
11
11
|
@output_preferences = OutputPreferences.dress(output_preferences)
|
12
12
|
end
|
13
|
-
attr_reader :
|
13
|
+
attr_reader :xlsx_options, :output_preferences
|
14
14
|
|
15
15
|
def call(relation, path)
|
16
16
|
require 'write_xlsx'
|
@@ -21,22 +21,24 @@ module Bmg
|
|
21
21
|
attr_reader :workbook, :worksheet
|
22
22
|
|
23
23
|
def _call(relation, path)
|
24
|
-
@workbook = WriteXLSX.new(path)
|
25
|
-
@worksheet = workbook.add_worksheet
|
24
|
+
@workbook = xlsx_options[:workbook] || WriteXLSX.new(path)
|
25
|
+
@worksheet = xlsx_options[:worksheet] || workbook.add_worksheet
|
26
26
|
|
27
27
|
headers = infer_headers(relation.type)
|
28
|
-
|
28
|
+
before = nil
|
29
|
+
each_tuple(relation) do |tuple,i|
|
29
30
|
headers = infer_headers(tuple) if headers.nil?
|
30
31
|
headers.each_with_index do |h,i|
|
31
32
|
worksheet.write_string(0, i, h)
|
32
33
|
end if i == 0
|
34
|
+
before, tuple = output_preferences.erase_redundance_in_group(before, tuple)
|
33
35
|
headers.each_with_index do |h,j|
|
34
36
|
meth, *args = write_pair(tuple[h])
|
35
37
|
worksheet.send(meth, 1+i, j, *args)
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
workbook.close
|
41
|
+
workbook.close unless xlsx_options[:workbook]
|
40
42
|
path
|
41
43
|
end
|
42
44
|
|
data/lib/bmg/writer.rb
CHANGED
@@ -12,6 +12,17 @@ module Bmg
|
|
12
12
|
attrlist ? output_preferences.order_attrlist(attrlist) : nil
|
13
13
|
end
|
14
14
|
|
15
|
+
def each_tuple(relation, &bl)
|
16
|
+
if ordering = output_preferences.tuple_ordering
|
17
|
+
relation
|
18
|
+
.to_a
|
19
|
+
.sort{|t1,t2| ordering.compare_attrs(t1, t2) }
|
20
|
+
.each_with_index(&bl)
|
21
|
+
else
|
22
|
+
relation.each_with_index(&bl)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
15
26
|
end # module Writer
|
16
27
|
end # module Bmg
|
17
28
|
require_relative 'writer/csv'
|
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.19.
|
4
|
+
version: 0.19.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernard Lambeau
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: predicate
|
@@ -248,6 +248,7 @@ files:
|
|
248
248
|
- lib/bmg/sql/processor/clip.rb
|
249
249
|
- lib/bmg/sql/processor/constants.rb
|
250
250
|
- lib/bmg/sql/processor/distinct.rb
|
251
|
+
- lib/bmg/sql/processor/extend.rb
|
251
252
|
- lib/bmg/sql/processor/flatten.rb
|
252
253
|
- lib/bmg/sql/processor/from_self.rb
|
253
254
|
- lib/bmg/sql/processor/join.rb
|
@@ -317,8 +318,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
317
318
|
- !ruby/object:Gem::Version
|
318
319
|
version: '0'
|
319
320
|
requirements: []
|
320
|
-
|
321
|
-
rubygems_version: 2.6.14.4
|
321
|
+
rubygems_version: 3.1.6
|
322
322
|
signing_key:
|
323
323
|
specification_version: 4
|
324
324
|
summary: Bmg is Alf's successor.
|