bmg 0.21.2 → 0.21.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -9
- data/lib/bmg/algebra/shortcuts.rb +8 -0
- data/lib/bmg/reader/csv.rb +3 -1
- data/lib/bmg/support/ordering.rb +11 -1
- data/lib/bmg/support/output_preferences.rb +27 -1
- data/lib/bmg/type.rb +8 -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 +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59da4b61f1d6c332de148552eab6277032df750c
|
4
|
+
data.tar.gz: b65877260b6a5eca59fb864f52a08dad26f917f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd85e6e179eb6e1a4d9ffe2f074db3c0d856d239bc65b8d535d6d14cff987a0af1eed75046aed0538726ff87925b9a2a400153a1acec7d7ecc3cc7fdc50d0e87
|
7
|
+
data.tar.gz: 5954aad879e293d34eb175ff54055473e3a983e6a16ec816694eb0b16fd90897cbf9d31bcdf53bfdef664891abb42cb538c7962954d76331c291411e816c956b
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Bmg, a relational algebra (Alf's successor)!
|
2
2
|
|
3
|
-
Bmg is a relational algebra implemented as a
|
3
|
+
Bmg is a relational algebra implemented as a Ruby library. It implements the
|
4
4
|
[Relation as First-Class Citizen](http://www.try-alf.org/blog/2013-10-21-relations-as-first-class-citizen)
|
5
5
|
paradigm contributed with [Alf](http://www.try-alf.org/) a few years ago.
|
6
6
|
|
@@ -15,7 +15,7 @@ further down this README.
|
|
15
15
|
* [Where are base relations coming from?](#where-are-base-relations-coming-from)
|
16
16
|
* [Memory relations](#memory-relations)
|
17
17
|
* [Connecting to SQL databases](#connecting-to-sql-databases)
|
18
|
-
* [Reading files (csv,
|
18
|
+
* [Reading files (csv, Excel, text)](#reading-files-csv-excel-text)
|
19
19
|
* [Connecting to Redis databases](#connecting-to-redis-databases)
|
20
20
|
* [Your own relations](#your-own-relations)
|
21
21
|
* [List of supported operators](#supported-operators)
|
@@ -117,7 +117,7 @@ Bmg.sequel(:suppliers, sequel_db)
|
|
117
117
|
# {:array=>false})
|
118
118
|
```
|
119
119
|
|
120
|
-
### Reading files (csv,
|
120
|
+
### Reading files (csv, Excel, text)
|
121
121
|
|
122
122
|
Bmg provides simple adapters to read files and reach Relationland as soon as
|
123
123
|
possible.
|
@@ -129,7 +129,7 @@ csv_options = { col_sep: ",", quote_char: '"' }
|
|
129
129
|
r = Bmg.csv("path/to/a/file.csv", csv_options)
|
130
130
|
```
|
131
131
|
|
132
|
-
Options are directly transmitted to `::CSV.new`, check
|
132
|
+
Options are directly transmitted to `::CSV.new`, check Ruby's standard
|
133
133
|
library.
|
134
134
|
|
135
135
|
#### Excel files
|
@@ -245,6 +245,7 @@ r.allbut([:a, :b, ...]) # remove specified attributes
|
|
245
245
|
r.autowrap(split: '_') # structure a flat relation, split: '_' is the default
|
246
246
|
r.autosummarize([:a, :b, ...], x: :sum) # (experimental) usual summarizers supported
|
247
247
|
r.constants(x: 12, ...) # add constant attributes (sometimes useful in unions)
|
248
|
+
r.cross_product(right) # cross product, alias `cross_join`
|
248
249
|
r.extend(x: ->(t){ ... }, ...) # add computed attributes
|
249
250
|
r.extend(x: :y) # shortcut for r.extend(x: ->(t){ t[:y] })
|
250
251
|
r.exclude(predicate) # shortcut for restrict(!predicate)
|
@@ -302,7 +303,7 @@ r.where(predicate) # alias for restrict(predicate)
|
|
302
303
|
.where(...)
|
303
304
|
```
|
304
305
|
|
305
|
-
2. Bmg supports in
|
306
|
+
2. Bmg supports in-memory relations, JSON relations, csv relations, SQL
|
306
307
|
relations and so on. It's not tight to SQL generation, and supports
|
307
308
|
queries accross multiple data sources.
|
308
309
|
|
@@ -312,8 +313,8 @@ r.where(predicate) # alias for restrict(predicate)
|
|
312
313
|
4. Bmg supports various *structuring* operators (group, image, autowrap,
|
313
314
|
autosummarize, etc.) and allows building 'non flat' relations.
|
314
315
|
|
315
|
-
5. Bmg can use full
|
316
|
-
WHERE clauses or
|
316
|
+
5. Bmg can use full Ruby power when that helps (e.g. regular expressions in
|
317
|
+
WHERE clauses or Ruby code in EXTEND clauses). This may prevent Bmg from
|
317
318
|
delegating work to underlying data sources (e.g. SQL server) and should
|
318
319
|
therefore be used with care though.
|
319
320
|
|
@@ -323,14 +324,14 @@ If you use Alf (or used it in the past), below are the main differences between
|
|
323
324
|
Bmg and Alf. Bmg has NOT been written to be API-compatible with Alf and will
|
324
325
|
probably never be.
|
325
326
|
|
326
|
-
1. Bmg's implementation is much simpler than Alf and uses no
|
327
|
+
1. Bmg's implementation is much simpler than Alf and uses no Ruby core
|
327
328
|
extention.
|
328
329
|
|
329
330
|
2. We are confident using Bmg in production. Systematic inspection of query
|
330
331
|
plans is advised though. Alf was a bit too experimental to be used on
|
331
332
|
(critical) production systems.
|
332
333
|
|
333
|
-
3. Alf exposes a functional syntax, command
|
334
|
+
3. Alf exposes a functional syntax, command-line tool, restful tools and
|
334
335
|
many more. Bmg is limited to the core algebra, main Relation abstraction
|
335
336
|
and SQL generation.
|
336
337
|
|
@@ -57,6 +57,14 @@ module Bmg
|
|
57
57
|
self.left_join(right.rename(renaming), on.keys, *args)
|
58
58
|
end
|
59
59
|
|
60
|
+
def cross_product(right)
|
61
|
+
return join(right) unless self.type.typechecked? || right.type.typechecked?
|
62
|
+
return join(right) unless self.type.knows_attrlist? && right.type.knows_attrlist?
|
63
|
+
|
64
|
+
self.type.cross_join_compatible!(right)
|
65
|
+
return join(right)
|
66
|
+
end
|
67
|
+
|
60
68
|
def matching(right, on = [])
|
61
69
|
return super unless on.is_a?(Hash)
|
62
70
|
renaming = Hash[on.map{|k,v| [v,k] }]
|
data/lib/bmg/reader/csv.rb
CHANGED
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/type.rb
CHANGED
@@ -310,7 +310,7 @@ module Bmg
|
|
310
310
|
}
|
311
311
|
end
|
312
312
|
|
313
|
-
|
313
|
+
public
|
314
314
|
|
315
315
|
def known_attributes!(attrs)
|
316
316
|
extra = attrs - self.attrlist
|
@@ -331,5 +331,12 @@ module Bmg
|
|
331
331
|
end
|
332
332
|
end
|
333
333
|
|
334
|
+
def cross_join_compatible!(right)
|
335
|
+
shared = self.attrlist & right.type.attrlist
|
336
|
+
unless shared.empty?
|
337
|
+
raise TypeError, "Cross product incompatible — duplicate attribute(s): #{shared.join(', ')}"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
334
341
|
end # class Type
|
335
342
|
end # module Bmg
|
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.21.
|
4
|
+
version: 0.21.4
|
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-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: predicate
|