@electric-sql/d2mini
Version:
D2Mini is a minimal implementation of Differential Dataflow for performing in-memory incremental view maintenance.
98 lines • 4.13 kB
JavaScript
import { DifferenceStreamWriter, UnaryOperator, } from '../graph.js';
import { StreamBuilder } from '../d2.js';
import { MultiSet } from '../multiset.js';
import { Index } from '../indexes.js';
import { hash } from '../utils.js';
/**
* Base operator for reduction operations (version-free)
*/
export class ReduceOperator extends UnaryOperator {
constructor(id, inputA, output, f) {
super(id, inputA, output);
this.
}
run() {
// Collect all input messages and update the index
const keysTodo = new Set();
for (const message of this.inputMessages()) {
for (const [item, multiplicity] of message.getInner()) {
const [key, value] = item;
this.
keysTodo.add(key);
}
}
// For each key, compute the reduction and delta
const result = [];
for (const key of keysTodo) {
const curr = this.
const currOut = this.
const out = this.
// Create maps for current and previous outputs
const newOutputMap = new Map();
const oldOutputMap = new Map();
// Process new output
for (const [value, multiplicity] of out) {
const valueKey = hash(value);
if (newOutputMap.has(valueKey)) {
newOutputMap.get(valueKey).multiplicity += multiplicity;
}
else {
newOutputMap.set(valueKey, { value, multiplicity });
}
}
// Process previous output
for (const [value, multiplicity] of currOut) {
const valueKey = hash(value);
if (oldOutputMap.has(valueKey)) {
oldOutputMap.get(valueKey).multiplicity += multiplicity;
}
else {
oldOutputMap.set(valueKey, { value, multiplicity });
}
}
// First, emit removals for old values that are no longer present or have changed
for (const [valueKey, { value, multiplicity }] of oldOutputMap) {
const newEntry = newOutputMap.get(valueKey);
if (!newEntry || newEntry.multiplicity !== multiplicity) {
// Remove the old value entirely
result.push([[key, value], -multiplicity]);
this.
}
}
// Then, emit additions for new values that are not present in old or have changed
for (const [valueKey, { value, multiplicity }] of newOutputMap) {
const oldEntry = oldOutputMap.get(valueKey);
if (!oldEntry || oldEntry.multiplicity !== multiplicity) {
// Add the new value only if it has non-zero multiplicity
if (multiplicity !== 0) {
result.push([[key, value], multiplicity]);
this.
}
}
}
}
if (result.length > 0) {
this.output.sendData(new MultiSet(result));
}
// Compact both indexes to consolidate values and remove zero-multiplicity entries
// Only compact changed keys for efficiency
this.
this.
}
}
/**
* Reduces the elements in the stream by key (version-free)
*/
export function reduce(f) {
return (stream) => {
const output = new StreamBuilder(stream.graph, new DifferenceStreamWriter());
const operator = new ReduceOperator(stream.graph.getNextOperatorId(), stream.connectReader(), output.writer, f);
stream.graph.addOperator(operator);
stream.graph.addStream(output.connectReader());
return output;
};
}
//# sourceMappingURL=reduce.js.map