-
Notifications
You must be signed in to change notification settings - Fork 0
join
Inner join of two or more sorted streams by key. The inputs are walked in lockstep; at each shared key the equal-key groups are combined into output rows. Streaming and single-pass — only the equal-key groups are held in memory, never a whole input.
import join from 'stream-sorting/sorted/join.js';
// `departments` and `employees`, each already sorted by department id
for await (const row of join({
dept: {input: departments, key: d => d.id},
emp: {input: employees, key: e => e.deptId}
})) {
// row === {dept: {...}, emp: {...}} for every employee with a matching department
}Inputs are a named map of descriptors. Each descriptor's key extracts the join key (default: the item itself); the map key names the input both on input and in the result. Every input must be sorted by its key under compareKey. The result is an AsyncIterable<R>; convert at the boundary you need:
import readableFrom from 'stream-chain/utils/readableFrom.js';
readableFrom(join(inputs, opts)).pipe(downstream); // Node Readable
ReadableStream.from(join(inputs, opts)).pipeTo(webDestination); // Web ReadableStreamEach value in the input map is a descriptor:
| Field | Type | Default | Description |
|---|---|---|---|
input |
AsyncIterable<T> | Iterable<T> |
— | The sorted source stream. Required. |
key |
(item: T) => K |
identity |
Extracts the join key from an item. |
optional |
boolean |
false |
When true, the input may be absent at a key and is null-filled instead of suppressing the row (an outer join on it). |
| Option | Type | Default | Description |
|---|---|---|---|
compareKey |
(a, b) => number |
natural order | Orders keys (Array.prototype.sort semantics). Provide this or lessKey. Composite keys need an explicit one. |
lessKey |
(a, b) => boolean |
— | Strict-less key comparator. Provide this or compareKey. |
combine |
(bag) => R |
(bag) => bag |
Builds each output row from the named bag of matched rows. Return undefined to drop that combination. |
maxGroupSize |
number |
— | Throw if any single equal-key group exceeds this many items (guards a Cartesian blow-up). |
- Inner by default. A row emits only when every required input has the key; keys missing from any required input are skipped.
-
Equal keys form a Cartesian product (SQL semantics):
mrows on one side andnon another at the same key emitm × ncombinations. -
combinereceives a named bag keyed by the input names —{dept, emp}above — with absent optional inputs asnull. The default returns the bag unchanged; returningundefineddrops the combination (a join-time filter). - N-way. Any number of inputs; the inner join keeps keys present in all required inputs.
-
Outer joins come from
optional— mark inputs null-fillable per input, or use leftJoin / fullJoin. - Inputs must be sorted by key; an out-of-order input throws at runtime.
Each input has its own key, so joining differently-shaped tables is the norm — dept keys on d.id, emp keys on e.deptId. Keys meet in a shared comparable space ordered by compareKey. For composite keys, return a tuple and supply a matching comparator:
join(
{a: {input: a, key: r => [r.deptId, r.empId]}, b: {input: b, key: r => [r.deptId, r.empId]}},
{compareKey: (x, y) => x[0] - y[0] || x[1] - y[1]}
);