@tanstack/db-ivm
Version:
Incremental View Maintenance for TanStack DB based on Differential Dataflow
1 lines • 23.9 kB
Source Map (JSON)
{"version":3,"file":"indexes.cjs","sources":["../../src/indexes.ts"],"sourcesContent":["/**\n * # Optimized Index Data Structure\n *\n * Multi-level index that adapts storage strategy based on data patterns to minimize memory\n * usage, eliminate wasteful lookups, and avoid hashing whenever possible.\n *\n * ## Storage Strategy\n *\n * **Single value**: `IndexMap['key'] → [value, multiplicity]` (no hashing needed)\n *\n * **Multiple unprefixed values**: Direct ValueMap (avoids NO_PREFIX lookup)\n * ```\n * IndexMap['key'] → ValueMap { hash(value1) → [value1, mult1], ... }\n * ```\n *\n * **Values with prefixes**: PrefixMap uses prefix keys directly (no hashing)\n * ```\n * IndexMap['key'] → PrefixMap { 'prefix1' → [value1, mult1], NO_PREFIX → ValueMap{...} }\n * ```\n *\n * **Multiple values per prefix**: ValueMap within PrefixMap (hash only suffixes)\n * ```\n * PrefixMap['prefix'] → ValueMap { hash(suffix1) → [full_value1, mult1], ... }\n * ```\n *\n * ## Dynamic Evolution\n *\n * Structure automatically evolves as data is added:\n * - Single → ValueMap (when both values unprefixed)\n * - Single → PrefixMap (when at least one prefixed)\n * - ValueMap → PrefixMap (adding prefixed value to unprefixed)\n *\n * Prefixes extracted from array values: `['prefix', 'suffix']` → prefix='prefix'\n */\n\nimport { MultiSet } from './multiset.js'\nimport { hash } from './hashing/index.js'\nimport type { Hash } from './hashing/index.js'\n\n// We use a symbol to represent the absence of a prefix, unprefixed values a stored\n// against this key.\nconst NO_PREFIX = Symbol(`NO_PREFIX`)\ntype NO_PREFIX = typeof NO_PREFIX\n\n// A single value is a tuple of the value and the multiplicity.\ntype SingleValue<TValue> = [TValue, number]\n\n// Base map type for the index. Stores single values, prefix maps, or value maps against a key.\ntype IndexMap<TKey, TValue, TPrefix> = Map<\n TKey,\n SingleValue<TValue> | PrefixMap<TValue, TPrefix> | ValueMap<TValue>\n>\n\n// Second level map type for the index, stores single values or value maps against a prefix.\nclass PrefixMap<TValue, TPrefix> extends Map<\n TPrefix | NO_PREFIX,\n SingleValue<TValue> | ValueMap<TValue>\n> {\n /**\n * Add a value to the PrefixMap. Returns true if the map becomes empty after the operation.\n */\n addValue(value: TValue, multiplicity: number): boolean {\n if (multiplicity === 0) return this.size === 0\n\n const prefix = getPrefix<TValue, TPrefix>(value)\n const valueMapOrSingleValue = this.get(prefix)\n\n if (isSingleValue(valueMapOrSingleValue)) {\n const [currentValue, currentMultiplicity] = valueMapOrSingleValue\n const currentPrefix = getPrefix<TValue, TPrefix>(currentValue)\n\n if (currentPrefix !== prefix) {\n throw new Error(`Mismatching prefixes, this should never happen`)\n }\n\n if (currentValue === value || hash(currentValue) === hash(value)) {\n // Same value, update multiplicity\n const newMultiplicity = currentMultiplicity + multiplicity\n if (newMultiplicity === 0) {\n this.delete(prefix)\n } else {\n this.set(prefix, [value, newMultiplicity])\n }\n } else {\n // Different suffixes, need to create ValueMap\n const valueMap = new ValueMap<TValue>()\n valueMap.set(hash(currentValue), valueMapOrSingleValue)\n valueMap.set(hash(value), [value, multiplicity])\n this.set(prefix, valueMap)\n }\n } else if (valueMapOrSingleValue === undefined) {\n // No existing value for this prefix\n this.set(prefix, [value, multiplicity])\n } else {\n // Existing ValueMap\n const isEmpty = valueMapOrSingleValue.addValue(value, multiplicity)\n if (isEmpty) {\n this.delete(prefix)\n }\n }\n\n return this.size === 0\n }\n}\n\n// Third level map type for the index, stores single values or value maps against a hash.\nclass ValueMap<TValue> extends Map<Hash, [TValue, number]> {\n /**\n * Add a value to the ValueMap. Returns true if the map becomes empty after the operation.\n * @param value - The full value to store\n * @param multiplicity - The multiplicity to add\n * @param hashKey - Optional hash key to use instead of hashing the full value (used when in PrefixMap context)\n */\n addValue(value: TValue, multiplicity: number): boolean {\n if (multiplicity === 0) return this.size === 0\n\n const key = hash(value)\n const currentValue = this.get(key)\n\n if (currentValue) {\n const [, currentMultiplicity] = currentValue\n const newMultiplicity = currentMultiplicity + multiplicity\n if (newMultiplicity === 0) {\n this.delete(key)\n } else {\n this.set(key, [value, newMultiplicity])\n }\n } else {\n this.set(key, [value, multiplicity])\n }\n\n return this.size === 0\n }\n}\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<TKey, TValue, TPrefix = any> {\n /*\n * This index maintains a nested map of keys -> (value, multiplicities), where:\n * - initially the values are stored against the key as a single value tuple\n * - when a key gets additional values, the values are stored against the key in a\n * prefix map\n * - the prefix is extract where possible from values that are structured as\n * [rowPrimaryKey, rowValue], as they are in the Tanstack DB query pipeline.\n * - only when there are multiple values for a given prefix do we fall back to a\n * hash to identify identical values, storing them in a third level value map.\n */\n #inner: IndexMap<TKey, TValue, TPrefix>\n #consolidatedMultiplicity: Map<TKey, number> = new Map() // sum of multiplicities per key\n\n constructor() {\n this.#inner = new Map()\n }\n\n /**\n * Create an Index from multiple MultiSet messages.\n * @param messages - Array of MultiSet messages to build the index from.\n * @returns A new Index containing all the data from the messages.\n */\n static fromMultiSets<K, V>(messages: Array<MultiSet<[K, V]>>): Index<K, V> {\n const index = new Index<K, V>()\n\n for (const message of messages) {\n for (const [item, multiplicity] of message.getInner()) {\n const [key, value] = item\n index.addValue(key, [value, multiplicity])\n }\n }\n\n return index\n }\n\n /**\n * This method returns a string representation of the index.\n * @param indent - Whether to indent the string representation.\n * @returns A string representation of the index.\n */\n toString(indent = false): string {\n return `Index(${JSON.stringify(\n [...this.entries()],\n undefined,\n indent ? 2 : undefined,\n )})`\n }\n\n /**\n * The size of the index.\n */\n get size(): number {\n return this.#inner.size\n }\n\n /**\n * This method checks if the index has a given key.\n * @param key - The key to check.\n * @returns True if the index has the key, false otherwise.\n */\n has(key: TKey): boolean {\n return this.#inner.has(key)\n }\n\n /**\n * Check if a key has presence (non-zero consolidated multiplicity).\n * @param key - The key to check.\n * @returns True if the key has non-zero consolidated multiplicity, false otherwise.\n */\n hasPresence(key: TKey): boolean {\n return (this.#consolidatedMultiplicity.get(key) || 0) !== 0\n }\n\n /**\n * Get the consolidated multiplicity (sum of multiplicities) for a key.\n * @param key - The key to get the consolidated multiplicity for.\n * @returns The consolidated multiplicity for the key.\n */\n getConsolidatedMultiplicity(key: TKey): number {\n return this.#consolidatedMultiplicity.get(key) || 0\n }\n\n /**\n * Get all keys that have presence (non-zero consolidated multiplicity).\n * @returns An iterator of keys with non-zero consolidated multiplicity.\n */\n getPresenceKeys(): Iterable<TKey> {\n return this.#consolidatedMultiplicity.keys()\n }\n\n /**\n * This method returns all values for a given key.\n * @param key - The key to get the values for.\n * @returns An array of value tuples [value, multiplicity].\n */\n get(key: TKey): Array<[TValue, number]> {\n return [...this.getIterator(key)]\n }\n\n /**\n * This method returns an iterator over all values for a given key.\n * @param key - The key to get the values for.\n * @returns An iterator of value tuples [value, multiplicity].\n */\n *getIterator(key: TKey): Iterable<[TValue, number]> {\n const mapOrSingleValue = this.#inner.get(key)\n if (isSingleValue(mapOrSingleValue)) {\n yield mapOrSingleValue\n } else if (mapOrSingleValue === undefined) {\n return\n } else if (mapOrSingleValue instanceof ValueMap) {\n // Direct ValueMap - all values have NO_PREFIX\n for (const valueTuple of mapOrSingleValue.values()) {\n yield valueTuple\n }\n } else {\n // PrefixMap - iterate through all prefixes\n for (const singleValueOrValueMap of mapOrSingleValue.values()) {\n if (isSingleValue(singleValueOrValueMap)) {\n yield singleValueOrValueMap\n } else {\n for (const valueTuple of singleValueOrValueMap.values()) {\n yield valueTuple\n }\n }\n }\n }\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<[TKey, [TValue, number]]> {\n for (const key of this.#inner.keys()) {\n for (const valueTuple of this.getIterator(key)) {\n yield [key, valueTuple]\n }\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<[TKey, Iterable<[TValue, number]>]> {\n for (const key of this.#inner.keys()) {\n yield [key, this.getIterator(key)]\n }\n }\n\n /**\n * This method adds a value to the index.\n * @param key - The key to add the value to.\n * @param valueTuple - The value tuple [value, multiplicity] to add to the index.\n */\n addValue(key: TKey, valueTuple: SingleValue<TValue>) {\n const [value, multiplicity] = valueTuple\n // If the multiplicity is 0, do nothing\n if (multiplicity === 0) return\n\n // Update consolidated multiplicity tracking\n const newConsolidatedMultiplicity =\n (this.#consolidatedMultiplicity.get(key) || 0) + multiplicity\n if (newConsolidatedMultiplicity === 0) {\n this.#consolidatedMultiplicity.delete(key)\n } else {\n this.#consolidatedMultiplicity.set(key, newConsolidatedMultiplicity)\n }\n\n const mapOrSingleValue = this.#inner.get(key)\n\n if (mapOrSingleValue === undefined) {\n // First value for this key\n this.#inner.set(key, valueTuple)\n return\n }\n\n if (isSingleValue(mapOrSingleValue)) {\n // Handle transition from single value to map\n this.#handleSingleValueTransition(\n key,\n mapOrSingleValue,\n value,\n multiplicity,\n )\n return\n }\n\n if (mapOrSingleValue instanceof ValueMap) {\n // Handle existing ValueMap\n const prefix = getPrefix<TValue, TPrefix>(value)\n if (prefix !== NO_PREFIX) {\n // Convert ValueMap to PrefixMap since we have a prefixed value\n const prefixMap = new PrefixMap<TValue, TPrefix>()\n prefixMap.set(NO_PREFIX, mapOrSingleValue)\n prefixMap.set(prefix, valueTuple)\n this.#inner.set(key, prefixMap)\n } else {\n // Add to existing ValueMap\n const isEmpty = mapOrSingleValue.addValue(value, multiplicity)\n if (isEmpty) {\n this.#inner.delete(key)\n }\n }\n } else {\n // Handle existing PrefixMap\n const isEmpty = mapOrSingleValue.addValue(value, multiplicity)\n if (isEmpty) {\n this.#inner.delete(key)\n }\n }\n }\n\n /**\n * Handle the transition from a single value to either a ValueMap or PrefixMap\n */\n #handleSingleValueTransition(\n key: TKey,\n currentSingleValue: SingleValue<TValue>,\n newValue: TValue,\n multiplicity: number,\n ) {\n const [currentValue, currentMultiplicity] = currentSingleValue\n\n // Check for exact same value (reference equality)\n if (currentValue === newValue) {\n const newMultiplicity = currentMultiplicity + multiplicity\n if (newMultiplicity === 0) {\n this.#inner.delete(key)\n } else {\n this.#inner.set(key, [newValue, newMultiplicity])\n }\n return\n }\n\n // Get prefixes for both values\n const newPrefix = getPrefix<TValue, TPrefix>(newValue)\n const currentPrefix = getPrefix<TValue, TPrefix>(currentValue)\n\n // Check if they're the same value by prefix/suffix comparison\n if (\n currentPrefix === newPrefix &&\n (currentValue === newValue || hash(currentValue) === hash(newValue))\n ) {\n const newMultiplicity = currentMultiplicity + multiplicity\n if (newMultiplicity === 0) {\n this.#inner.delete(key)\n } else {\n this.#inner.set(key, [newValue, newMultiplicity])\n }\n return\n }\n\n // Different values - choose appropriate map type\n if (currentPrefix === NO_PREFIX && newPrefix === NO_PREFIX) {\n // Both have NO_PREFIX, use ValueMap directly\n const valueMap = new ValueMap<TValue>()\n valueMap.set(hash(currentValue), currentSingleValue)\n valueMap.set(hash(newValue), [newValue, multiplicity])\n this.#inner.set(key, valueMap)\n } else {\n // At least one has a prefix, use PrefixMap\n const prefixMap = new PrefixMap<TValue, TPrefix>()\n\n if (currentPrefix === newPrefix) {\n // Same prefix, different suffixes - need ValueMap within PrefixMap\n const valueMap = new ValueMap<TValue>()\n valueMap.set(hash(currentValue), currentSingleValue)\n valueMap.set(hash(newValue), [newValue, multiplicity])\n prefixMap.set(currentPrefix, valueMap)\n } else {\n // Different prefixes - store as separate single values\n prefixMap.set(currentPrefix, currentSingleValue)\n prefixMap.set(newPrefix, [newValue, multiplicity])\n }\n\n this.#inner.set(key, prefixMap)\n }\n }\n\n /**\n * This method appends another index to the current index.\n * @param other - The index to append to the current index.\n */\n append(other: Index<TKey, TValue>): void {\n for (const [key, value] of other.entries()) {\n this.addValue(key, value)\n }\n }\n\n /**\n * This method joins two indexes.\n * @param other - The index to join with the current index.\n * @returns A multiset of the joined values.\n */\n join<TValue2>(\n other: Index<TKey, TValue2>,\n ): MultiSet<[TKey, [TValue, TValue2]]> {\n const result: Array<[[TKey, [TValue, TValue2]], number]> = []\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\n/**\n * This function extracts the prefix from a value.\n * @param value - The value to extract the prefix from.\n * @returns The prefix and the suffix.\n */\nfunction getPrefix<TValue, TPrefix>(value: TValue): TPrefix | NO_PREFIX {\n // If the value is an array and the first element is a string or number, then the\n // first element is the prefix. This is used to distinguish between values without\n // the need for hashing unless there are multiple values for the same prefix.\n if (\n Array.isArray(value) &&\n (typeof value[0] === `string` ||\n typeof value[0] === `number` ||\n typeof value[0] === `bigint`)\n ) {\n return value[0] as TPrefix\n }\n return NO_PREFIX\n}\n\n/**\n * This function checks if a value is a single value.\n * @param value - The value to check.\n * @returns True if the value is a single value, false otherwise.\n */\nfunction isSingleValue<TValue>(\n value: SingleValue<TValue> | unknown,\n): value is SingleValue<TValue> {\n return Array.isArray(value)\n}\n"],"names":["hash","MultiSet"],"mappings":"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"}