UNPKG

@tanstack/db-ivm

Version:

Incremental View Maintenance for TanStack DB based on Differential Dataflow

118 lines (102 loc) 3.32 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<K, V> { #inner: DefaultMap<K, Map<V, number>> constructor() { this.#inner = new DefaultMap<K, Map<V, number>>(() => new Map<V, number>()) // #inner is a map of: // { // [key]: Map<V, number> // Direct value-to-multiplicity mapping // } } toString(indent = false): string { return `Index(${JSON.stringify( [...this.#inner].map(([k, valueMap]) => [k, [...valueMap]]), undefined, indent ? ` ` : undefined )})` } get(key: K): Array<[V, number]> { const valueMap = this.#inner.get(key) return [...valueMap.entries()] } getMultiplicity(key: K, value: V): number { const valueMap = this.#inner.get(key) return valueMap.get(value) ?? 0 } entries() { return this.#inner.entries() } keys() { return this.#inner.keys() } has(key: K): boolean { return this.#inner.has(key) } get size(): number { return this.#inner.size } addValue(key: K, value: [V, number]): void { 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: Index<K, V>): void { 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<V2>(other: Index<K, V2>): MultiSet<[K, [V, V2]]> { const result: Array<[[K, [V, V2]], number]> = [] // 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) } }