UNPKG

@electric-sql/d2mini

Version:

D2Mini is a minimal implementation of Differential Dataflow for performing in-memory incremental view maintenance.

104 lines 3.66 kB
import { MultiSet } from './multiset.js'; import { DefaultMap } from './utils.js'; /** * A map from a difference collection trace's keys -> (value, multiplicities) that changed. * Used in operations like join and reduce where the operation needs to * exploit the key-value structure of the data to run efficiently. */ export class Index { #inner; constructor() { this.#inner = new DefaultMap(() => new Map()); // #inner is a map of: // { // [key]: Map<V, number> // Direct value-to-multiplicity mapping // } } toString(indent = false) { return `Index(${JSON.stringify([...this.#inner].map(([k, valueMap]) => [k, [...valueMap]]), undefined, indent ? ' ' : undefined)})`; } get(key) { const valueMap = this.#inner.get(key); return [...valueMap.entries()]; } getMultiplicity(key, value) { const valueMap = this.#inner.get(key); return valueMap.get(value) ?? 0; } entries() { return this.#inner.entries(); } keys() { return this.#inner.keys(); } has(key) { return this.#inner.has(key); } get size() { return this.#inner.size; } addValue(key, value) { const [val, multiplicity] = value; const valueMap = this.#inner.get(key); const existingMultiplicity = valueMap.get(val) ?? 0; const newMultiplicity = existingMultiplicity + multiplicity; if (multiplicity !== 0) { if (newMultiplicity === 0) { valueMap.delete(val); } else { valueMap.set(val, newMultiplicity); } } } append(other) { for (const [key, otherValueMap] of other.entries()) { const thisValueMap = this.#inner.get(key); for (const [value, multiplicity] of otherValueMap.entries()) { const existingMultiplicity = thisValueMap.get(value) ?? 0; const newMultiplicity = existingMultiplicity + multiplicity; if (newMultiplicity === 0) { thisValueMap.delete(value); } else { thisValueMap.set(value, newMultiplicity); } } } } join(other) { const result = []; // We want to iterate over the smaller of the two indexes to reduce the // number of operations we need to do. if (this.size <= other.size) { for (const [key, valueMap] of this.entries()) { if (!other.has(key)) continue; const otherValues = other.get(key); for (const [val1, mul1] of valueMap.entries()) { for (const [val2, mul2] of otherValues) { if (mul1 !== 0 && mul2 !== 0) { result.push([[key, [val1, val2]], mul1 * mul2]); } } } } } else { for (const [key, otherValueMap] of other.entries()) { if (!this.has(key)) continue; const values = this.get(key); for (const [val2, mul2] of otherValueMap.entries()) { for (const [val1, mul1] of values) { if (mul1 !== 0 && mul2 !== 0) { result.push([[key, [val1, val2]], mul1 * mul2]); } } } } } return new MultiSet(result); } } //# sourceMappingURL=indexes.js.map