@enspirit/bmg-js
v1.1.1
Published
Relational algebra for JavaScript/TypeScript - work with arrays of objects using powerful set operations
Readme
Bmg.js - Relational Algebra for JavaScript/TypeScript
A TypeScript/JavaScript implementation of BMG, providing relational algebra operators for working with arrays of objects.
Installation
npm install @enspirit/bmg-jsQuick Start
Using the Relation abstraction:
import { Bmg } from '@enspirit/bmg-js'
const suppliers = Bmg([
{ sid: 'S1', name: 'Smith', status: 20, city: 'London' },
{ sid: 'S2', name: 'Jones', status: 10, city: 'Paris' },
{ sid: 'S3', name: 'Blake', status: 30, city: 'Paris' },
])
// Chain operations fluently
const parisSuppliers = suppliers
.restrict({ city: 'Paris' })
.project(['sid', 'name'])
console.log(parisSuppliers.toArray())
// => [{ sid: 'S2', name: 'Jones' }, { sid: 'S3', name: 'Blake' }]
// Extract a single tuple
const smith = suppliers.restrict({ sid: 'S1' }).one()
// => { sid: 'S1', name: 'Smith', status: 20, city: 'London' }Using standalone operators on plain arrays:
import { restrict, project } from '@enspirit/bmg-js'
const suppliers = [
{ sid: 'S1', name: 'Smith', city: 'London' },
{ sid: 'S2', name: 'Jones', city: 'Paris' },
]
const result = project(restrict(suppliers, { city: 'Paris' }), ['name'])
// => [{ name: 'Jones' }]TypeScript Support
Bmg.js provides full TypeScript support with generic types:
import { Bmg } from '@enspirit/bmg-js'
interface Supplier {
sid: string
name: string
status: number
city: string
}
const suppliers = Bmg<Supplier>([
{ sid: 'S1', name: 'Smith', status: 20, city: 'London' },
])
// Type-safe operations with autocomplete
const projected = suppliers.project(['sid', 'name'])
// Type: Relation<{ sid: string; name: string }>
const one = suppliers.restrict({ sid: 'S1' }).one()
// Type: SupplierSee the full type-safe example for more.
Available Operators
| Category | Operator | Description |
|----------|----------|-------------|
| Filtering | restrict(predicate) | Keep tuples matching predicate |
| | where(predicate) | Alias for restrict |
| | exclude(predicate) | Keep tuples NOT matching predicate |
| | matching(other, keys?) | Semi-join (tuples with match in other) |
| | not_matching(other, keys?) | Anti-join (tuples without match) |
| Projection | project(attrs) | Keep only specified attributes |
| | allbut(attrs) | Keep all attributes except specified |
| Extension | extend(extensions) | Add computed attributes |
| | constants(values) | Add constant attributes |
| Renaming | rename(mapping) | Rename attributes |
| | prefix(pfx, options?) | Add prefix to attribute names |
| | suffix(sfx, options?) | Add suffix to attribute names |
| Set Operations | union(other) | Set union of two relations |
| | minus(other) | Set difference (tuples in left but not right) |
| | intersect(other) | Set intersection (tuples in both) |
| Join Operations | join(other, keys?) | Natural join on common/specified attributes |
| | left_join(other, keys?) | Left outer join |
| | cross_product(other) | Cartesian product |
| | cross_join(other) | Alias for cross_product |
| Nesting & Grouping | image(other, as, keys?) | Nest matching tuples as relation attribute |
| | group(attrs, as) | Group attributes into nested relation |
| | ungroup(attr) | Flatten nested relation |
| | wrap(attrs, as) | Wrap attributes into tuple-valued attribute |
| | unwrap(attr) | Flatten tuple-valued attribute |
| | autowrap(options?) | Auto-wrap by separator pattern |
| Aggregation | summarize(by, aggregators) | Group and aggregate |
| Transformation | transform(transformation) | Transform attribute values |
| Non-Relational | one() | Extract single tuple (throws if not exactly one) |
| | toArray() | Convert relation to array |
| | isEqual(other) | Check set equality |
| | yByX(y, x) | Create { x-value: y-value } mapping |
| | toText(options?) | Render as ASCII table |
| | Bmg.isRelation(value) | Check if value is a Relation (static) |
Built-in aggregators for summarize: count, sum, min, max, avg, collect
Theory
Bmg.js implements relational algebra with these principles:
- No duplicates - Relations are sets; duplicate tuples are automatically removed
- Order independence - Tuple order and attribute order have no semantic meaning
- Set equality - Two relations are equal if they contain the same tuples
Horizon
The aim is to have a language where one can write beautiful functional expressions:
suppliers
|> restrict( _ ~> _.status > 20 )
|> rename(sid: 'id', name: 'lastname')
|> restrict(city: 'Paris')
|> oneThis will be provided by Elo.
Versioning
Bmg.js follows Semantic Versioning (SemVer). See API.md for the public API reference.
- MAJOR (x.0.0) - Breaking changes to the public API
- MINOR (0.x.0) - New operators or features, fully backward-compatible
- PATCH (0.0.x) - Bug fixes and performance improvements, no API changes
For Bmg.js specifically:
- Adding a new operator (e.g.,
sort,page) is a minor release - Fixing incorrect behavior in an existing operator is a patch release
- Changing an operator's signature or renaming it is a major release
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/new-operator) - Add your changes with tests
- Ensure tests pass:
npm run test - Commit following conventional commits (e.g.,
feat: add sort operator) - Open a pull request
When adding a new operator:
- Add the operator implementation
- Add unit tests (use relational comparisons with
isEqual, avoid accessing "first" tuple) - Update the README operators table
- One commit per operator
License
MIT
