@tanstack/db-ivm 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/d2.cjs +25 -49
- package/dist/cjs/d2.cjs.map +1 -1
- package/dist/cjs/graph.cjs +8 -20
- package/dist/cjs/graph.cjs.map +1 -1
- package/dist/cjs/indexes.cjs +118 -79
- package/dist/cjs/indexes.cjs.map +1 -1
- package/dist/cjs/indexes.d.cts +23 -0
- package/dist/cjs/multiset.cjs +106 -115
- package/dist/cjs/multiset.cjs.map +1 -1
- package/dist/cjs/multiset.d.cts +1 -0
- package/dist/cjs/operators/debug.cjs +5 -15
- package/dist/cjs/operators/debug.cjs.map +1 -1
- package/dist/cjs/operators/distinct.cjs +11 -22
- package/dist/cjs/operators/distinct.cjs.map +1 -1
- package/dist/cjs/operators/filter.cjs +3 -12
- package/dist/cjs/operators/filter.cjs.map +1 -1
- package/dist/cjs/operators/filterBy.cjs.map +1 -1
- package/dist/cjs/operators/filterBy.d.cts +1 -2
- package/dist/cjs/operators/groupBy.cjs +1 -2
- package/dist/cjs/operators/groupBy.cjs.map +1 -1
- package/dist/cjs/operators/join.cjs +104 -93
- package/dist/cjs/operators/join.cjs.map +1 -1
- package/dist/cjs/operators/join.d.cts +11 -8
- package/dist/cjs/operators/map.cjs +3 -12
- package/dist/cjs/operators/map.cjs.map +1 -1
- package/dist/cjs/operators/orderBy.cjs +10 -10
- package/dist/cjs/operators/orderBy.cjs.map +1 -1
- package/dist/cjs/operators/orderBy.d.cts +1 -2
- package/dist/cjs/operators/output.cjs +3 -12
- package/dist/cjs/operators/output.cjs.map +1 -1
- package/dist/cjs/operators/reduce.cjs +11 -22
- package/dist/cjs/operators/reduce.cjs.map +1 -1
- package/dist/cjs/operators/tap.cjs +3 -12
- package/dist/cjs/operators/tap.cjs.map +1 -1
- package/dist/cjs/operators/topK.cjs +4 -4
- package/dist/cjs/operators/topK.cjs.map +1 -1
- package/dist/cjs/operators/topK.d.cts +1 -2
- package/dist/cjs/operators/topKWithFractionalIndex.cjs +56 -75
- package/dist/cjs/operators/topKWithFractionalIndex.cjs.map +1 -1
- package/dist/esm/d2.js +25 -49
- package/dist/esm/d2.js.map +1 -1
- package/dist/esm/graph.js +8 -20
- package/dist/esm/graph.js.map +1 -1
- package/dist/esm/indexes.d.ts +23 -0
- package/dist/esm/indexes.js +118 -79
- package/dist/esm/indexes.js.map +1 -1
- package/dist/esm/multiset.d.ts +1 -0
- package/dist/esm/multiset.js +106 -115
- package/dist/esm/multiset.js.map +1 -1
- package/dist/esm/operators/debug.js +5 -15
- package/dist/esm/operators/debug.js.map +1 -1
- package/dist/esm/operators/distinct.js +11 -22
- package/dist/esm/operators/distinct.js.map +1 -1
- package/dist/esm/operators/filter.js +3 -12
- package/dist/esm/operators/filter.js.map +1 -1
- package/dist/esm/operators/filterBy.d.ts +1 -2
- package/dist/esm/operators/filterBy.js.map +1 -1
- package/dist/esm/operators/groupBy.js +1 -2
- package/dist/esm/operators/groupBy.js.map +1 -1
- package/dist/esm/operators/join.d.ts +11 -8
- package/dist/esm/operators/join.js +104 -93
- package/dist/esm/operators/join.js.map +1 -1
- package/dist/esm/operators/map.js +3 -12
- package/dist/esm/operators/map.js.map +1 -1
- package/dist/esm/operators/orderBy.d.ts +1 -2
- package/dist/esm/operators/orderBy.js +10 -10
- package/dist/esm/operators/orderBy.js.map +1 -1
- package/dist/esm/operators/output.js +3 -12
- package/dist/esm/operators/output.js.map +1 -1
- package/dist/esm/operators/reduce.js +11 -22
- package/dist/esm/operators/reduce.js.map +1 -1
- package/dist/esm/operators/tap.js +3 -12
- package/dist/esm/operators/tap.js.map +1 -1
- package/dist/esm/operators/topK.d.ts +1 -2
- package/dist/esm/operators/topK.js +4 -4
- package/dist/esm/operators/topK.js.map +1 -1
- package/dist/esm/operators/topKWithFractionalIndex.js +56 -75
- package/dist/esm/operators/topKWithFractionalIndex.js.map +1 -1
- package/package.json +1 -1
- package/src/indexes.ts +54 -0
- package/src/multiset.ts +6 -0
- package/src/operators/filterBy.ts +1 -2
- package/src/operators/join.ts +249 -139
- package/src/operators/orderBy.ts +1 -2
- package/src/operators/topK.ts +1 -2
package/dist/esm/indexes.js
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __typeError = (msg) => {
|
|
2
|
-
throw TypeError(msg);
|
|
3
|
-
};
|
|
4
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
5
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
6
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
-
var _inner, _Index_instances, handleSingleValueTransition_fn;
|
|
10
1
|
import { MultiSet } from "./multiset.js";
|
|
11
2
|
import { hash } from "./hashing/hash.js";
|
|
12
3
|
const NO_PREFIX = Symbol(`NO_PREFIX`);
|
|
@@ -74,20 +65,36 @@ class ValueMap extends Map {
|
|
|
74
65
|
}
|
|
75
66
|
}
|
|
76
67
|
class Index {
|
|
68
|
+
/*
|
|
69
|
+
* This index maintains a nested map of keys -> (value, multiplicities), where:
|
|
70
|
+
* - initially the values are stored against the key as a single value tuple
|
|
71
|
+
* - when a key gets additional values, the values are stored against the key in a
|
|
72
|
+
* prefix map
|
|
73
|
+
* - the prefix is extract where possible from values that are structured as
|
|
74
|
+
* [rowPrimaryKey, rowValue], as they are in the Tanstack DB query pipeline.
|
|
75
|
+
* - only when there are multiple values for a given prefix do we fall back to a
|
|
76
|
+
* hash to identify identical values, storing them in a third level value map.
|
|
77
|
+
*/
|
|
78
|
+
#inner;
|
|
79
|
+
#consolidatedMultiplicity = /* @__PURE__ */ new Map();
|
|
80
|
+
// sum of multiplicities per key
|
|
77
81
|
constructor() {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
this.#inner = /* @__PURE__ */ new Map();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create an Index from multiple MultiSet messages.
|
|
86
|
+
* @param messages - Array of MultiSet messages to build the index from.
|
|
87
|
+
* @returns A new Index containing all the data from the messages.
|
|
88
|
+
*/
|
|
89
|
+
static fromMultiSets(messages) {
|
|
90
|
+
const index = new Index();
|
|
91
|
+
for (const message of messages) {
|
|
92
|
+
for (const [item, multiplicity] of message.getInner()) {
|
|
93
|
+
const [key, value] = item;
|
|
94
|
+
index.addValue(key, [value, multiplicity]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return index;
|
|
91
98
|
}
|
|
92
99
|
/**
|
|
93
100
|
* This method returns a string representation of the index.
|
|
@@ -105,7 +112,7 @@ class Index {
|
|
|
105
112
|
* The size of the index.
|
|
106
113
|
*/
|
|
107
114
|
get size() {
|
|
108
|
-
return
|
|
115
|
+
return this.#inner.size;
|
|
109
116
|
}
|
|
110
117
|
/**
|
|
111
118
|
* This method checks if the index has a given key.
|
|
@@ -113,7 +120,30 @@ class Index {
|
|
|
113
120
|
* @returns True if the index has the key, false otherwise.
|
|
114
121
|
*/
|
|
115
122
|
has(key) {
|
|
116
|
-
return
|
|
123
|
+
return this.#inner.has(key);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if a key has presence (non-zero consolidated multiplicity).
|
|
127
|
+
* @param key - The key to check.
|
|
128
|
+
* @returns True if the key has non-zero consolidated multiplicity, false otherwise.
|
|
129
|
+
*/
|
|
130
|
+
hasPresence(key) {
|
|
131
|
+
return (this.#consolidatedMultiplicity.get(key) || 0) !== 0;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get the consolidated multiplicity (sum of multiplicities) for a key.
|
|
135
|
+
* @param key - The key to get the consolidated multiplicity for.
|
|
136
|
+
* @returns The consolidated multiplicity for the key.
|
|
137
|
+
*/
|
|
138
|
+
getConsolidatedMultiplicity(key) {
|
|
139
|
+
return this.#consolidatedMultiplicity.get(key) || 0;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get all keys that have presence (non-zero consolidated multiplicity).
|
|
143
|
+
* @returns An iterator of keys with non-zero consolidated multiplicity.
|
|
144
|
+
*/
|
|
145
|
+
getPresenceKeys() {
|
|
146
|
+
return this.#consolidatedMultiplicity.keys();
|
|
117
147
|
}
|
|
118
148
|
/**
|
|
119
149
|
* This method returns all values for a given key.
|
|
@@ -129,7 +159,7 @@ class Index {
|
|
|
129
159
|
* @returns An iterator of value tuples [value, multiplicity].
|
|
130
160
|
*/
|
|
131
161
|
*getIterator(key) {
|
|
132
|
-
const mapOrSingleValue =
|
|
162
|
+
const mapOrSingleValue = this.#inner.get(key);
|
|
133
163
|
if (isSingleValue(mapOrSingleValue)) {
|
|
134
164
|
yield mapOrSingleValue;
|
|
135
165
|
} else if (mapOrSingleValue === void 0) {
|
|
@@ -155,7 +185,7 @@ class Index {
|
|
|
155
185
|
* @returns An iterable of all key-value pairs (and their multiplicities) in the index.
|
|
156
186
|
*/
|
|
157
187
|
*entries() {
|
|
158
|
-
for (const key of
|
|
188
|
+
for (const key of this.#inner.keys()) {
|
|
159
189
|
for (const valueTuple of this.getIterator(key)) {
|
|
160
190
|
yield [key, valueTuple];
|
|
161
191
|
}
|
|
@@ -168,7 +198,7 @@ class Index {
|
|
|
168
198
|
* @returns An iterator of all *keys* in the index and their corresponding value iterator.
|
|
169
199
|
*/
|
|
170
200
|
*entriesIterators() {
|
|
171
|
-
for (const key of
|
|
201
|
+
for (const key of this.#inner.keys()) {
|
|
172
202
|
yield [key, this.getIterator(key)];
|
|
173
203
|
}
|
|
174
204
|
}
|
|
@@ -180,13 +210,24 @@ class Index {
|
|
|
180
210
|
addValue(key, valueTuple) {
|
|
181
211
|
const [value, multiplicity] = valueTuple;
|
|
182
212
|
if (multiplicity === 0) return;
|
|
183
|
-
const
|
|
213
|
+
const newConsolidatedMultiplicity = (this.#consolidatedMultiplicity.get(key) || 0) + multiplicity;
|
|
214
|
+
if (newConsolidatedMultiplicity === 0) {
|
|
215
|
+
this.#consolidatedMultiplicity.delete(key);
|
|
216
|
+
} else {
|
|
217
|
+
this.#consolidatedMultiplicity.set(key, newConsolidatedMultiplicity);
|
|
218
|
+
}
|
|
219
|
+
const mapOrSingleValue = this.#inner.get(key);
|
|
184
220
|
if (mapOrSingleValue === void 0) {
|
|
185
|
-
|
|
221
|
+
this.#inner.set(key, valueTuple);
|
|
186
222
|
return;
|
|
187
223
|
}
|
|
188
224
|
if (isSingleValue(mapOrSingleValue)) {
|
|
189
|
-
|
|
225
|
+
this.#handleSingleValueTransition(
|
|
226
|
+
key,
|
|
227
|
+
mapOrSingleValue,
|
|
228
|
+
value,
|
|
229
|
+
multiplicity
|
|
230
|
+
);
|
|
190
231
|
return;
|
|
191
232
|
}
|
|
192
233
|
if (mapOrSingleValue instanceof ValueMap) {
|
|
@@ -195,20 +236,64 @@ class Index {
|
|
|
195
236
|
const prefixMap = new PrefixMap();
|
|
196
237
|
prefixMap.set(NO_PREFIX, mapOrSingleValue);
|
|
197
238
|
prefixMap.set(prefix, valueTuple);
|
|
198
|
-
|
|
239
|
+
this.#inner.set(key, prefixMap);
|
|
199
240
|
} else {
|
|
200
241
|
const isEmpty = mapOrSingleValue.addValue(value, multiplicity);
|
|
201
242
|
if (isEmpty) {
|
|
202
|
-
|
|
243
|
+
this.#inner.delete(key);
|
|
203
244
|
}
|
|
204
245
|
}
|
|
205
246
|
} else {
|
|
206
247
|
const isEmpty = mapOrSingleValue.addValue(value, multiplicity);
|
|
207
248
|
if (isEmpty) {
|
|
208
|
-
|
|
249
|
+
this.#inner.delete(key);
|
|
209
250
|
}
|
|
210
251
|
}
|
|
211
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Handle the transition from a single value to either a ValueMap or PrefixMap
|
|
255
|
+
*/
|
|
256
|
+
#handleSingleValueTransition(key, currentSingleValue, newValue, multiplicity) {
|
|
257
|
+
const [currentValue, currentMultiplicity] = currentSingleValue;
|
|
258
|
+
if (currentValue === newValue) {
|
|
259
|
+
const newMultiplicity = currentMultiplicity + multiplicity;
|
|
260
|
+
if (newMultiplicity === 0) {
|
|
261
|
+
this.#inner.delete(key);
|
|
262
|
+
} else {
|
|
263
|
+
this.#inner.set(key, [newValue, newMultiplicity]);
|
|
264
|
+
}
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const newPrefix = getPrefix(newValue);
|
|
268
|
+
const currentPrefix = getPrefix(currentValue);
|
|
269
|
+
if (currentPrefix === newPrefix && (currentValue === newValue || hash(currentValue) === hash(newValue))) {
|
|
270
|
+
const newMultiplicity = currentMultiplicity + multiplicity;
|
|
271
|
+
if (newMultiplicity === 0) {
|
|
272
|
+
this.#inner.delete(key);
|
|
273
|
+
} else {
|
|
274
|
+
this.#inner.set(key, [newValue, newMultiplicity]);
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (currentPrefix === NO_PREFIX && newPrefix === NO_PREFIX) {
|
|
279
|
+
const valueMap = new ValueMap();
|
|
280
|
+
valueMap.set(hash(currentValue), currentSingleValue);
|
|
281
|
+
valueMap.set(hash(newValue), [newValue, multiplicity]);
|
|
282
|
+
this.#inner.set(key, valueMap);
|
|
283
|
+
} else {
|
|
284
|
+
const prefixMap = new PrefixMap();
|
|
285
|
+
if (currentPrefix === newPrefix) {
|
|
286
|
+
const valueMap = new ValueMap();
|
|
287
|
+
valueMap.set(hash(currentValue), currentSingleValue);
|
|
288
|
+
valueMap.set(hash(newValue), [newValue, multiplicity]);
|
|
289
|
+
prefixMap.set(currentPrefix, valueMap);
|
|
290
|
+
} else {
|
|
291
|
+
prefixMap.set(currentPrefix, currentSingleValue);
|
|
292
|
+
prefixMap.set(newPrefix, [newValue, multiplicity]);
|
|
293
|
+
}
|
|
294
|
+
this.#inner.set(key, prefixMap);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
212
297
|
/**
|
|
213
298
|
* This method appends another index to the current index.
|
|
214
299
|
* @param other - The index to append to the current index.
|
|
@@ -253,52 +338,6 @@ class Index {
|
|
|
253
338
|
return new MultiSet(result);
|
|
254
339
|
}
|
|
255
340
|
}
|
|
256
|
-
_inner = new WeakMap();
|
|
257
|
-
_Index_instances = new WeakSet();
|
|
258
|
-
/**
|
|
259
|
-
* Handle the transition from a single value to either a ValueMap or PrefixMap
|
|
260
|
-
*/
|
|
261
|
-
handleSingleValueTransition_fn = function(key, currentSingleValue, newValue, multiplicity) {
|
|
262
|
-
const [currentValue, currentMultiplicity] = currentSingleValue;
|
|
263
|
-
if (currentValue === newValue) {
|
|
264
|
-
const newMultiplicity = currentMultiplicity + multiplicity;
|
|
265
|
-
if (newMultiplicity === 0) {
|
|
266
|
-
__privateGet(this, _inner).delete(key);
|
|
267
|
-
} else {
|
|
268
|
-
__privateGet(this, _inner).set(key, [newValue, newMultiplicity]);
|
|
269
|
-
}
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
const newPrefix = getPrefix(newValue);
|
|
273
|
-
const currentPrefix = getPrefix(currentValue);
|
|
274
|
-
if (currentPrefix === newPrefix && (currentValue === newValue || hash(currentValue) === hash(newValue))) {
|
|
275
|
-
const newMultiplicity = currentMultiplicity + multiplicity;
|
|
276
|
-
if (newMultiplicity === 0) {
|
|
277
|
-
__privateGet(this, _inner).delete(key);
|
|
278
|
-
} else {
|
|
279
|
-
__privateGet(this, _inner).set(key, [newValue, newMultiplicity]);
|
|
280
|
-
}
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
if (currentPrefix === NO_PREFIX && newPrefix === NO_PREFIX) {
|
|
284
|
-
const valueMap = new ValueMap();
|
|
285
|
-
valueMap.set(hash(currentValue), currentSingleValue);
|
|
286
|
-
valueMap.set(hash(newValue), [newValue, multiplicity]);
|
|
287
|
-
__privateGet(this, _inner).set(key, valueMap);
|
|
288
|
-
} else {
|
|
289
|
-
const prefixMap = new PrefixMap();
|
|
290
|
-
if (currentPrefix === newPrefix) {
|
|
291
|
-
const valueMap = new ValueMap();
|
|
292
|
-
valueMap.set(hash(currentValue), currentSingleValue);
|
|
293
|
-
valueMap.set(hash(newValue), [newValue, multiplicity]);
|
|
294
|
-
prefixMap.set(currentPrefix, valueMap);
|
|
295
|
-
} else {
|
|
296
|
-
prefixMap.set(currentPrefix, currentSingleValue);
|
|
297
|
-
prefixMap.set(newPrefix, [newValue, multiplicity]);
|
|
298
|
-
}
|
|
299
|
-
__privateGet(this, _inner).set(key, prefixMap);
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
341
|
function getPrefix(value) {
|
|
303
342
|
if (Array.isArray(value) && (typeof value[0] === `string` || typeof value[0] === `number` || typeof value[0] === `bigint`)) {
|
|
304
343
|
return value[0];
|
package/dist/esm/indexes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexes.js","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\n constructor() {\n this.#inner = new Map()\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 * 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 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":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"indexes.js","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":[],"mappings":";;AAyCA,MAAM,YAAY,OAAO,WAAW;AAapC,MAAM,kBAAmC,IAGvC;AAAA;AAAA;AAAA;AAAA,EAIA,SAAS,OAAe,cAA+B;AACrD,QAAI,iBAAiB,EAAG,QAAO,KAAK,SAAS;AAE7C,UAAM,SAAS,UAA2B,KAAK;AAC/C,UAAM,wBAAwB,KAAK,IAAI,MAAM;AAE7C,QAAI,cAAc,qBAAqB,GAAG;AACxC,YAAM,CAAC,cAAc,mBAAmB,IAAI;AAC5C,YAAM,gBAAgB,UAA2B,YAAY;AAE7D,UAAI,kBAAkB,QAAQ;AAC5B,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AAEA,UAAI,iBAAiB,SAAS,KAAK,YAAY,MAAM,KAAK,KAAK,GAAG;AAEhE,cAAM,kBAAkB,sBAAsB;AAC9C,YAAI,oBAAoB,GAAG;AACzB,eAAK,OAAO,MAAM;AAAA,QACpB,OAAO;AACL,eAAK,IAAI,QAAQ,CAAC,OAAO,eAAe,CAAC;AAAA,QAC3C;AAAA,MACF,OAAO;AAEL,cAAM,WAAW,IAAI,SAAA;AACrB,iBAAS,IAAI,KAAK,YAAY,GAAG,qBAAqB;AACtD,iBAAS,IAAI,KAAK,KAAK,GAAG,CAAC,OAAO,YAAY,CAAC;AAC/C,aAAK,IAAI,QAAQ,QAAQ;AAAA,MAC3B;AAAA,IACF,WAAW,0BAA0B,QAAW;AAE9C,WAAK,IAAI,QAAQ,CAAC,OAAO,YAAY,CAAC;AAAA,IACxC,OAAO;AAEL,YAAM,UAAU,sBAAsB,SAAS,OAAO,YAAY;AAClE,UAAI,SAAS;AACX,aAAK,OAAO,MAAM;AAAA,MACpB;AAAA,IACF;AAEA,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;AAGA,MAAM,iBAAyB,IAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOzD,SAAS,OAAe,cAA+B;AACrD,QAAI,iBAAiB,EAAG,QAAO,KAAK,SAAS;AAE7C,UAAM,MAAM,KAAK,KAAK;AACtB,UAAM,eAAe,KAAK,IAAI,GAAG;AAEjC,QAAI,cAAc;AAChB,YAAM,CAAA,EAAG,mBAAmB,IAAI;AAChC,YAAM,kBAAkB,sBAAsB;AAC9C,UAAI,oBAAoB,GAAG;AACzB,aAAK,OAAO,GAAG;AAAA,MACjB,OAAO;AACL,aAAK,IAAI,KAAK,CAAC,OAAO,eAAe,CAAC;AAAA,MACxC;AAAA,IACF,OAAO;AACL,WAAK,IAAI,KAAK,CAAC,OAAO,YAAY,CAAC;AAAA,IACrC;AAEA,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;AAOO,MAAM,MAAmC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW9C;AAAA,EACA,gDAAmD,IAAA;AAAA;AAAA,EAEnD,cAAc;AACZ,SAAK,6BAAa,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,cAAoB,UAAgD;AACzE,UAAM,QAAQ,IAAI,MAAA;AAElB,eAAW,WAAW,UAAU;AAC9B,iBAAW,CAAC,MAAM,YAAY,KAAK,QAAQ,YAAY;AACrD,cAAM,CAAC,KAAK,KAAK,IAAI;AACrB,cAAM,SAAS,KAAK,CAAC,OAAO,YAAY,CAAC;AAAA,MAC3C;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,SAAS,OAAe;AAC/B,WAAO,SAAS,KAAK;AAAA,MACnB,CAAC,GAAG,KAAK,SAAS;AAAA,MAClB;AAAA,MACA,SAAS,IAAI;AAAA,IAAA,CACd;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,OAAe;AACjB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,KAAoB;AACtB,WAAO,KAAK,OAAO,IAAI,GAAG;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,KAAoB;AAC9B,YAAQ,KAAK,0BAA0B,IAAI,GAAG,KAAK,OAAO;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,4BAA4B,KAAmB;AAC7C,WAAO,KAAK,0BAA0B,IAAI,GAAG,KAAK;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAkC;AAChC,WAAO,KAAK,0BAA0B,KAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,KAAoC;AACtC,WAAO,CAAC,GAAG,KAAK,YAAY,GAAG,CAAC;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,CAAC,YAAY,KAAuC;AAClD,UAAM,mBAAmB,KAAK,OAAO,IAAI,GAAG;AAC5C,QAAI,cAAc,gBAAgB,GAAG;AACnC,YAAM;AAAA,IACR,WAAW,qBAAqB,QAAW;AACzC;AAAA,IACF,WAAW,4BAA4B,UAAU;AAE/C,iBAAW,cAAc,iBAAiB,UAAU;AAClD,cAAM;AAAA,MACR;AAAA,IACF,OAAO;AAEL,iBAAW,yBAAyB,iBAAiB,UAAU;AAC7D,YAAI,cAAc,qBAAqB,GAAG;AACxC,gBAAM;AAAA,QACR,OAAO;AACL,qBAAW,cAAc,sBAAsB,UAAU;AACvD,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,CAAC,UAA8C;AAC7C,eAAW,OAAO,KAAK,OAAO,KAAA,GAAQ;AACpC,iBAAW,cAAc,KAAK,YAAY,GAAG,GAAG;AAC9C,cAAM,CAAC,KAAK,UAAU;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,CAAC,mBAAiE;AAChE,eAAW,OAAO,KAAK,OAAO,KAAA,GAAQ;AACpC,YAAM,CAAC,KAAK,KAAK,YAAY,GAAG,CAAC;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,KAAW,YAAiC;AACnD,UAAM,CAAC,OAAO,YAAY,IAAI;AAE9B,QAAI,iBAAiB,EAAG;AAGxB,UAAM,+BACH,KAAK,0BAA0B,IAAI,GAAG,KAAK,KAAK;AACnD,QAAI,gCAAgC,GAAG;AACrC,WAAK,0BAA0B,OAAO,GAAG;AAAA,IAC3C,OAAO;AACL,WAAK,0BAA0B,IAAI,KAAK,2BAA2B;AAAA,IACrE;AAEA,UAAM,mBAAmB,KAAK,OAAO,IAAI,GAAG;AAE5C,QAAI,qBAAqB,QAAW;AAElC,WAAK,OAAO,IAAI,KAAK,UAAU;AAC/B;AAAA,IACF;AAEA,QAAI,cAAc,gBAAgB,GAAG;AAEnC,WAAK;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAEF;AAAA,IACF;AAEA,QAAI,4BAA4B,UAAU;AAExC,YAAM,SAAS,UAA2B,KAAK;AAC/C,UAAI,WAAW,WAAW;AAExB,cAAM,YAAY,IAAI,UAAA;AACtB,kBAAU,IAAI,WAAW,gBAAgB;AACzC,kBAAU,IAAI,QAAQ,UAAU;AAChC,aAAK,OAAO,IAAI,KAAK,SAAS;AAAA,MAChC,OAAO;AAEL,cAAM,UAAU,iBAAiB,SAAS,OAAO,YAAY;AAC7D,YAAI,SAAS;AACX,eAAK,OAAO,OAAO,GAAG;AAAA,QACxB;AAAA,MACF;AAAA,IACF,OAAO;AAEL,YAAM,UAAU,iBAAiB,SAAS,OAAO,YAAY;AAC7D,UAAI,SAAS;AACX,aAAK,OAAO,OAAO,GAAG;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,6BACE,KACA,oBACA,UACA,cACA;AACA,UAAM,CAAC,cAAc,mBAAmB,IAAI;AAG5C,QAAI,iBAAiB,UAAU;AAC7B,YAAM,kBAAkB,sBAAsB;AAC9C,UAAI,oBAAoB,GAAG;AACzB,aAAK,OAAO,OAAO,GAAG;AAAA,MACxB,OAAO;AACL,aAAK,OAAO,IAAI,KAAK,CAAC,UAAU,eAAe,CAAC;AAAA,MAClD;AACA;AAAA,IACF;AAGA,UAAM,YAAY,UAA2B,QAAQ;AACrD,UAAM,gBAAgB,UAA2B,YAAY;AAG7D,QACE,kBAAkB,cACjB,iBAAiB,YAAY,KAAK,YAAY,MAAM,KAAK,QAAQ,IAClE;AACA,YAAM,kBAAkB,sBAAsB;AAC9C,UAAI,oBAAoB,GAAG;AACzB,aAAK,OAAO,OAAO,GAAG;AAAA,MACxB,OAAO;AACL,aAAK,OAAO,IAAI,KAAK,CAAC,UAAU,eAAe,CAAC;AAAA,MAClD;AACA;AAAA,IACF;AAGA,QAAI,kBAAkB,aAAa,cAAc,WAAW;AAE1D,YAAM,WAAW,IAAI,SAAA;AACrB,eAAS,IAAI,KAAK,YAAY,GAAG,kBAAkB;AACnD,eAAS,IAAI,KAAK,QAAQ,GAAG,CAAC,UAAU,YAAY,CAAC;AACrD,WAAK,OAAO,IAAI,KAAK,QAAQ;AAAA,IAC/B,OAAO;AAEL,YAAM,YAAY,IAAI,UAAA;AAEtB,UAAI,kBAAkB,WAAW;AAE/B,cAAM,WAAW,IAAI,SAAA;AACrB,iBAAS,IAAI,KAAK,YAAY,GAAG,kBAAkB;AACnD,iBAAS,IAAI,KAAK,QAAQ,GAAG,CAAC,UAAU,YAAY,CAAC;AACrD,kBAAU,IAAI,eAAe,QAAQ;AAAA,MACvC,OAAO;AAEL,kBAAU,IAAI,eAAe,kBAAkB;AAC/C,kBAAU,IAAI,WAAW,CAAC,UAAU,YAAY,CAAC;AAAA,MACnD;AAEA,WAAK,OAAO,IAAI,KAAK,SAAS;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,OAAkC;AACvC,eAAW,CAAC,KAAK,KAAK,KAAK,MAAM,WAAW;AAC1C,WAAK,SAAS,KAAK,KAAK;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACE,OACqC;AACrC,UAAM,SAAqD,CAAA;AAG3D,QAAI,KAAK,QAAQ,MAAM,MAAM;AAC3B,iBAAW,CAAC,KAAK,OAAO,KAAK,KAAK,oBAAoB;AACpD,YAAI,CAAC,MAAM,IAAI,GAAG,EAAG;AACrB,cAAM,cAAc,MAAM,IAAI,GAAG;AACjC,mBAAW,CAAC,MAAM,IAAI,KAAK,SAAS;AAClC,qBAAW,CAAC,MAAM,IAAI,KAAK,aAAa;AACtC,gBAAI,SAAS,KAAK,SAAS,GAAG;AAC5B,qBAAO,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,OAAO,IAAI,CAAC;AAAA,YAChD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,iBAAW,CAAC,KAAK,YAAY,KAAK,MAAM,oBAAoB;AAC1D,YAAI,CAAC,KAAK,IAAI,GAAG,EAAG;AACpB,cAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,mBAAW,CAAC,MAAM,IAAI,KAAK,cAAc;AACvC,qBAAW,CAAC,MAAM,IAAI,KAAK,QAAQ;AACjC,gBAAI,SAAS,KAAK,SAAS,GAAG;AAC5B,qBAAO,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,OAAO,IAAI,CAAC;AAAA,YAChD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO,IAAI,SAAS,MAAM;AAAA,EAC5B;AACF;AAOA,SAAS,UAA2B,OAAoC;AAItE,MACE,MAAM,QAAQ,KAAK,MAClB,OAAO,MAAM,CAAC,MAAM,YACnB,OAAO,MAAM,CAAC,MAAM,YACpB,OAAO,MAAM,CAAC,MAAM,WACtB;AACA,WAAO,MAAM,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAOA,SAAS,cACP,OAC8B;AAC9B,SAAO,MAAM,QAAQ,KAAK;AAC5B;"}
|