@tanstack/db-ivm
Version:
Incremental View Maintenance for TanStack DB based on Differential Dataflow
1 lines • 9.52 kB
Source Map (JSON)
{"version":3,"file":"indexes.cjs","sources":["../../src/indexes.ts"],"sourcesContent":["import { MultiSet } from \"./multiset.js\"\nimport { HashIndex } from \"./hashIndex.js\"\nimport { ValueIndex } from \"./valueIndex.js\"\nimport { concatIterable, mapIterable } from \"./utils.js\"\n\n/**\n * A map from a difference collection trace's keys -> (value, multiplicities) that changed.\n * Used in operations like join and reduce where the operation needs to\n * exploit the key-value structure of the data to run efficiently.\n */\nexport class Index<K, V> {\n /*\n * This is a hybrid Index that composes a ValueIndex and a HashIndex.\n * Keys that have only one value are stored in the ValueIndex.\n * Keys that have multiple values are stored in the HashIndex, the hash distinguishes between the values.\n * This reduces the amount of hashes we need to compute since often times only a small portion of the keys are updated\n * so we don't have to hash the keys that are never updated.\n *\n * Note: The `valueIndex` and `hashIndex` have disjoint keys.\n * When a key that has only one value gets a new distinct value,\n * it is added to the `hashIndex` and removed from the `valueIndex` and vice versa.\n */\n #valueIndex: ValueIndex<K, V>\n #hashIndex: HashIndex<K, V>\n\n constructor() {\n this.#valueIndex = new ValueIndex<K, V>()\n this.#hashIndex = new HashIndex<K, V>()\n }\n\n toString(indent = false): string {\n return `Index(\\n ${this.#valueIndex.toString(indent)},\\n ${this.#hashIndex.toString(indent)}\\n)`\n }\n\n get(key: K): Array<[V, number]> {\n if (this.#valueIndex.has(key)) {\n return [this.#valueIndex.get(key)!]\n }\n return this.#hashIndex.get(key)\n }\n\n getMultiplicity(key: K, value: V): number {\n if (this.#valueIndex.has(key)) {\n return this.#valueIndex.getMultiplicity(key)\n }\n return this.#hashIndex.getMultiplicity(key, value)\n }\n\n /**\n * This returns an iterator that iterates over all key-value pairs.\n * @returns An iterable of all key-value pairs (and their multiplicities) in the index.\n */\n #entries(): Iterable<[K, [V, number]]> {\n return concatIterable(\n this.#valueIndex.entries(),\n this.#hashIndex.entriesIterator()\n )\n }\n\n /**\n * This method only iterates over the keys and not over the values.\n * Hence, it is more efficient than the `#entries` method.\n * It returns an iterator that you can use if you need to iterate over the values for a given key.\n * @returns An iterator of all *keys* in the index and their corresponding value iterator.\n */\n *#entriesIterators(): Iterable<[K, Iterable<[V, number]>]> {\n for (const [key, [value, multiplicity]] of this.#valueIndex.entries()) {\n yield [key, new Map<V, number>([[value, multiplicity]])]\n }\n for (const [key, valueMap] of this.#hashIndex.entries()) {\n yield [\n key,\n mapIterable(valueMap, ([_hash, [value, multiplicity]]) => [\n value,\n multiplicity,\n ]),\n ]\n }\n }\n\n has(key: K): boolean {\n return this.#valueIndex.has(key) || this.#hashIndex.has(key)\n }\n\n get size(): number {\n return this.#valueIndex.size + this.#hashIndex.size\n }\n\n addValue(key: K, value: [V, number]): void {\n const containedInValueIndex = this.#valueIndex.has(key)\n const containedInHashIndex = this.#hashIndex.has(key)\n\n if (containedInHashIndex && containedInValueIndex) {\n throw new Error(\n `Key ${key} is contained in both the value index and the hash index. This should never happen because they should have disjoint keysets.`\n )\n }\n\n if (!containedInValueIndex && !containedInHashIndex) {\n // This is the first time we see the key\n // Add it to the value index\n this.#valueIndex.addValue(key, value)\n return\n }\n\n if (containedInValueIndex) {\n // This key is already in the value index\n // It could be that it's the same value or a different one\n // If it's a different value we will need to remove the key from the value index\n // and add the key and its two values to the hash index\n try {\n this.#valueIndex.addValue(key, value)\n } catch {\n // This is a different value, need to move the key to the hash index\n const existingValue = this.#valueIndex.get(key)!\n this.#valueIndex.delete(key)\n this.#hashIndex.addValue(key, existingValue)\n this.#hashIndex.addValue(key, value)\n }\n return\n }\n\n if (containedInHashIndex) {\n // This key is already in the hash index so it already has two or more values.\n // However, this new value and multiplicity could cause an existing value to be removed\n // and lead to the key having only a single value in which case we need to move it back to the value index\n const singleRemainingValue = this.#hashIndex.addValue(key, value)\n if (singleRemainingValue) {\n // The key only has a single remaining value so we need to move it back to the value index\n this.#hashIndex.delete(key)\n this.#valueIndex.addValue(key, singleRemainingValue)\n }\n return\n }\n }\n\n append(other: Index<K, V>): void {\n for (const [key, value] of other.#entries()) {\n this.addValue(key, value)\n }\n }\n\n join<V2>(other: Index<K, V2>): MultiSet<[K, [V, V2]]> {\n const result: Array<[[K, [V, V2]], number]> = []\n\n // We want to iterate over the smaller of the two indexes to reduce the\n // number of operations we need to do.\n if (this.size <= other.size) {\n for (const [key, valueIt] of this.#entriesIterators()) {\n if (!other.has(key)) continue\n const otherValues = other.get(key)\n for (const [val1, mul1] of valueIt) {\n for (const [val2, mul2] of otherValues) {\n if (mul1 !== 0 && mul2 !== 0) {\n result.push([[key, [val1, val2]], mul1 * mul2])\n }\n }\n }\n }\n } else {\n for (const [key, otherValueIt] of other.#entriesIterators()) {\n if (!this.has(key)) continue\n const values = this.get(key)\n for (const [val2, mul2] of otherValueIt) {\n for (const [val1, mul1] of values) {\n if (mul1 !== 0 && mul2 !== 0) {\n result.push([[key, [val1, val2]], mul1 * mul2])\n }\n }\n }\n }\n }\n\n return new MultiSet(result)\n }\n}\n"],"names":["ValueIndex","HashIndex","MultiSet","concatIterable","mapIterable"],"mappings":"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"}