Think in relations
Relational algebra gives you a powerful mental model for data manipulation. Chain operations naturally.
Use it in your projects
Works with any data
Arrays, CSV files, JSON, Redis, SQL databases — same operations everywhere.
# In-memory arraypurchases = Bmg::Relation.new([ { product_id: 1, quantity: 5, type: 'sale' }, { product_id: 2, quantity: 3, type: 'sample' },])
# Or load from CSVpurchases = Bmg.csv("purchases.csv")
# Or connect to a SQL databasepurchases = Bmg.sequel(:purchases, db)
# Same operations, any sourcepurchases .restrict(Predicate.neq(:type, 'sample')) .summarize([:product_id], quantity: :sum)// In-memory arrayconst purchases = Bmg([ { product_id: 1, quantity: 5, type: 'sale' }, { product_id: 2, quantity: 3, type: 'sample' },])
// Same operations, full type safetypurchases .exclude({ type: 'sample' }) .summarize(['product_id'], { quantity: { op: 'sum', attr: 'quantity' } })What is it good for?
Think in relations
Relational algebra gives you a powerful mental model for data manipulation. Chain operations naturally.
Query anything
Same operations work on arrays, files, Redis, or SQL. Your code doesn’t change when your data source does.
Compose freely
Extract, reuse, and combine query fragments. Build complex transformations from simple pieces.
Drop in anywhere
Works alongside ActiveRecord, Sequel, or standalone. No framework lock-in.
SQL Compilation
When your data lives in a database, Bmg compiles your queries to efficient SQL. Currently available in Ruby — coming soon to TypeScript.
suppliers .restrict(Predicate.eq(:city, "Paris")) .join(supplies, [:sid]) .project([:name, :qty])
SELECT DISTINCT `t2`.`qty`, `t1`.`name`FROM `suppliers` AS 't1'INNER JOIN `supplies` AS 't2' ON (`t1`.`sid` = `t2`.`sid`)WHERE (`t1`.`city` = 'Paris')See more in our SQL vs. Bmg cheatsheet.
Source Code