README.md in bmg-0.17.3 vs README.md in bmg-0.17.4
- old
+ new
@@ -12,11 +12,11 @@
object-oriented syntax only (not Alf's functional one). Bmg implementation is
also much simpler, and make its easier to implement user-defined relations.
## Example
-```
+```ruby
require 'bmg'
require 'json'
suppliers = Bmg::Relation.new([
{ sid: "S1", name: "Smith", status: 20, city: "London" },
@@ -30,44 +30,98 @@
.restrict(Predicate.neq(status: 30))
.extend(upname: ->(t){ t[:name].upcase })
.group([:sid, :name, :status], :suppliers_in)
puts JSON.pretty_generate(by_city)
+# [{...},...]
```
## Connecting to a SQL database
Bmg requires `sequel >= 3.0` to connect to SQL databases.
-```
+```ruby
require 'sqlite3'
require 'bmg'
require 'bmg/sequel'
DB = Sequel.connect("sqlite://suppliers-and-parts.db")
suppliers = Bmg.sequel(:suppliers, DB)
-puts suppliers
+big_suppliers = suppliers
.restrict(Predicate.neq(status: 30))
- .to_sql
+puts big_suppliers.to_sql
# SELECT `t1`.`sid`, `t1`.`name`, `t1`.`status`, `t1`.`city` FROM `suppliers` AS 't1' WHERE (`t1`.`status` != 30)
+
+puts JSON.pretty_generate(big_suppliers)
+# [{...},...]
```
+## How is this different from similar libraries?
+
+1. The libraries you probably know (Sequel, Arel, SQLAlchemy, Korma, jOOQ,
+ etc.) do not implement a genuine relational algebra: their support for
+ chaining relational operators is limited (yielding errors or wrong SQL
+ queries). Bmg **always** allows chaining operators. If it does not, it's
+ a bug. In other words, the following query is 100% valid:
+
+ relation
+ .restrict(...) # aka where
+ .union(...)
+ .summarize(...) # aka group by
+ .restrict(...)
+
+2. Bmg supports in memory relations, json relations, csv relations, SQL
+ relations and so on. It's not tight to SQL generation, and supports
+ queries accross multiple data sources.
+
+3. Bmg makes a best effort to optimize queries, simplifying both generated
+ SQL code (low-level accesses to datasources) and in-memory operations.
+
+4. Bmg supports various *structuring* operators (group, image, autowrap,
+ autosummarize, etc.) and allows building 'non flat' relations.
+
+## How is this different from Alf?
+
+1. Bmg's implementation is much simpler than Alf, and uses no ruby core
+ extention.
+
+2. We are confident using Bmg in production. Systematic inspection of query
+ plans is suggested though. Alf was a bit too experimental to be used on
+ (critical) production systems.
+
+2. Alf exposes a functional syntax, command line tool, restful tools and
+ many more. Bmg is limited to the core algebra, main Relation abstraction
+ and SQL generation.
+
+3. Bmg is less strict regarding conformance to relational theory, and
+ may actually expose non relational features (such as support for null,
+ left_join operator, etc.). Sharp tools hurt, use them with great care.
+
+4. Bmg does not yet implement all operators documented on try-alf.org, even
+ if we plan to eventually support them all.
+
+5. Bmg has a few additional operators that prove very useful on real
+ production use cases: prefix, suffix, autowrap, autosummarize, left_join,
+ rxmatch, etc.
+
## Supported operators
-```
+```ruby
r.allbut([:a, :b, ...]) # remove specified attributes
r.autowrap(split: '_') # structure a flat relation, split: '_' is the default
r.autosummarize([:a, :b, ...], x: :sum) # (experimental) usual summarizers supported
r.constants(x: 12, ...) # add constant attributes (sometimes useful in unions)
r.extend(x: ->(t){ ... }, ...) # add computed attributes
r.group([:a, :b, ...], :x) # relation-valued attribute from attributes
r.image(right, :x, [:a, :b, ...]) # relation-valued attribute from another relation
r.join(right, [:a, :b, ...]) # natural join on a join key
r.join(right, :a => :x, :b => :y, ...) # natural join after right reversed renaming
+r.left_join(right, [:a, :b, ...], {...}) # left join with optional default right tuple
+r.left_join(right, {:a => :x, ...}, {...}) # left join after right reversed renaming
r.matching(right, [:a, :b, ...]) # semi join, aka where exists
r.matching(right, :a => :x, :b => :y, ...) # semi join, after right reversed renaming
r.not_matching(right, [:a, :b, ...]) # inverse semi join, aka where not exists
r.not_matching(right, :a => :x, ...) # inverse semi join, after right reversed renaming
r.page([[:a, :asc], ...], 12, page_size: 10) # paging, using an explicit ordering
@@ -78,5 +132,15 @@
r.rxmatch([:a, :b, ...], /xxx/) # regex match kind of restriction
r.summarize([:a, :b, ...], x: :sum) # relational summarization
r.suffix(:_foo, but: [:a, ...]) # suffix kind of renaming
r.union(right) # relational union
```
+
+## Who is behind Bmg?
+
+Bernard Lambeau (bernard@klaro.cards) is Alf & Bmg main engineer & maintainer.
+
+Enspirit (https://enspirit.be) and Klaro App (https://klaro.cards) are both
+actively using and contributing to the library.
+
+Feel free to contact us for help, ideas and/or contributions. Please use github
+issues and pull requests if possible if code is involved.