@tanstack/db-ivm 0.1.24 → 0.1.26
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.d.cts +3 -3
- package/dist/cjs/graph.d.cts +2 -2
- package/dist/cjs/hashing/index.d.cts +2 -2
- package/dist/cjs/index.cjs +42 -42
- package/dist/cjs/index.d.cts +6 -6
- package/dist/cjs/indexes.cjs +6 -3
- package/dist/cjs/indexes.cjs.map +1 -1
- package/dist/cjs/indexes.d.cts +38 -8
- package/dist/cjs/multiset.cjs +12 -4
- package/dist/cjs/multiset.cjs.map +1 -1
- package/dist/cjs/multiset.d.cts +9 -4
- package/dist/cjs/operators/concat.d.cts +2 -2
- package/dist/cjs/operators/consolidate.d.cts +2 -2
- package/dist/cjs/operators/count.cjs.map +1 -1
- package/dist/cjs/operators/count.d.cts +11 -5
- package/dist/cjs/operators/debug.d.cts +2 -2
- package/dist/cjs/operators/distinct.d.cts +2 -2
- package/dist/cjs/operators/filter.d.cts +3 -3
- package/dist/cjs/operators/filterBy.cjs.map +1 -1
- package/dist/cjs/operators/filterBy.d.cts +2 -2
- package/dist/cjs/operators/groupBy.cjs.map +1 -1
- package/dist/cjs/operators/groupBy.d.cts +5 -2
- package/dist/cjs/operators/groupedOrderBy.d.cts +8 -2
- package/dist/cjs/operators/groupedTopKWithFractionalIndex.d.cts +34 -7
- package/dist/cjs/operators/index.d.cts +21 -21
- package/dist/cjs/operators/join.cjs.map +1 -1
- package/dist/cjs/operators/join.d.cts +49 -10
- package/dist/cjs/operators/keying.d.cts +5 -2
- package/dist/cjs/operators/map.d.cts +3 -3
- package/dist/cjs/operators/negate.d.cts +3 -3
- package/dist/cjs/operators/orderBy.d.cts +20 -5
- package/dist/cjs/operators/output.d.cts +3 -3
- package/dist/cjs/operators/pipe.d.cts +1 -1
- package/dist/cjs/operators/reduce.cjs.map +1 -1
- package/dist/cjs/operators/reduce.d.cts +29 -5
- package/dist/cjs/operators/tap.d.cts +3 -3
- package/dist/cjs/operators/topK.cjs.map +1 -1
- package/dist/cjs/operators/topK.d.cts +5 -2
- package/dist/cjs/operators/topKArray.d.cts +11 -2
- package/dist/cjs/operators/topKState.d.cts +44 -8
- package/dist/cjs/operators/topKWithFractionalIndex.d.cts +41 -8
- package/dist/cjs/operators/topKWithFractionalIndexBTree.d.cts +20 -5
- package/dist/cjs/types.d.cts +6 -3
- package/dist/cjs/utils.cjs +9 -7
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +7 -4
- package/dist/esm/index.js +15 -15
- package/dist/esm/indexes.js +6 -3
- package/dist/esm/indexes.js.map +1 -1
- package/dist/esm/multiset.d.ts +1 -2
- package/dist/esm/multiset.js +12 -4
- package/dist/esm/multiset.js.map +1 -1
- package/dist/esm/operators/count.d.ts +1 -1
- package/dist/esm/operators/count.js.map +1 -1
- package/dist/esm/operators/filterBy.d.ts +1 -1
- package/dist/esm/operators/filterBy.js.map +1 -1
- package/dist/esm/operators/groupBy.js.map +1 -1
- package/dist/esm/operators/join.d.ts +6 -6
- package/dist/esm/operators/join.js.map +1 -1
- package/dist/esm/operators/reduce.d.ts +1 -1
- package/dist/esm/operators/reduce.js.map +1 -1
- package/dist/esm/operators/topK.d.ts +2 -2
- package/dist/esm/operators/topK.js.map +1 -1
- package/dist/esm/operators/topKArray.js +1 -1
- package/dist/esm/utils.d.ts +3 -3
- package/dist/esm/utils.js +9 -7
- package/dist/esm/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/indexes.ts +8 -3
- package/src/multiset.ts +13 -7
- package/src/operators/count.ts +2 -2
- package/src/operators/filterBy.ts +1 -1
- package/src/operators/groupBy.ts +1 -2
- package/src/operators/join.ts +6 -6
- package/src/operators/reduce.ts +2 -2
- package/src/operators/topK.ts +4 -4
- package/src/utils.ts +14 -10
package/dist/cjs/d2.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DifferenceStreamWriter, BinaryOperator, DifferenceStreamReader, UnaryOperator } from
|
|
2
|
-
import { MultiSet, MultiSetArray } from
|
|
3
|
-
import { ID2, IStreamBuilder, PipedOperator } from
|
|
1
|
+
import { DifferenceStreamWriter, BinaryOperator, DifferenceStreamReader, UnaryOperator } from "./graph.cjs";
|
|
2
|
+
import { MultiSet, MultiSetArray } from "./multiset.cjs";
|
|
3
|
+
import { ID2, IStreamBuilder, PipedOperator } from "./types.cjs";
|
|
4
4
|
export declare class D2 implements ID2 {
|
|
5
5
|
#private;
|
|
6
6
|
constructor();
|
package/dist/cjs/graph.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MultiSet, MultiSetArray } from
|
|
2
|
-
import { IDifferenceStreamReader, IDifferenceStreamWriter, IOperator } from
|
|
1
|
+
import { MultiSet, MultiSetArray } from "./multiset.cjs";
|
|
2
|
+
import { IDifferenceStreamReader, IDifferenceStreamWriter, IOperator } from "./types.cjs";
|
|
3
3
|
/**
|
|
4
4
|
* A read handle to a dataflow edge that receives data from a writer.
|
|
5
5
|
*/
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { hash } from
|
|
2
|
-
export type { Hash, Hasher } from
|
|
1
|
+
export { hash } from "./hash.cjs";
|
|
2
|
+
export type { Hash, Hasher } from "./murmur.cjs";
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -4,27 +4,27 @@ const d2 = require("./d2.cjs");
|
|
|
4
4
|
const multiset = require("./multiset.cjs");
|
|
5
5
|
const utils = require("./utils.cjs");
|
|
6
6
|
const hash = require("./hashing/hash.cjs");
|
|
7
|
-
const groupBy = require("./operators/groupBy.cjs");
|
|
8
|
-
const pipe = require("./operators/pipe.cjs");
|
|
9
|
-
const map = require("./operators/map.cjs");
|
|
10
|
-
const tap = require("./operators/tap.cjs");
|
|
11
|
-
const filter = require("./operators/filter.cjs");
|
|
12
|
-
const negate = require("./operators/negate.cjs");
|
|
13
7
|
const concat = require("./operators/concat.cjs");
|
|
14
|
-
const debug = require("./operators/debug.cjs");
|
|
15
|
-
const output = require("./operators/output.cjs");
|
|
16
8
|
const consolidate = require("./operators/consolidate.cjs");
|
|
17
|
-
const join = require("./operators/join.cjs");
|
|
18
|
-
const reduce = require("./operators/reduce.cjs");
|
|
19
9
|
const count = require("./operators/count.cjs");
|
|
10
|
+
const debug = require("./operators/debug.cjs");
|
|
20
11
|
const distinct = require("./operators/distinct.cjs");
|
|
21
|
-
const
|
|
22
|
-
const topK = require("./operators/topK.cjs");
|
|
23
|
-
const topKWithFractionalIndex = require("./operators/topKWithFractionalIndex.cjs");
|
|
12
|
+
const filter = require("./operators/filter.cjs");
|
|
24
13
|
const groupedTopKWithFractionalIndex = require("./operators/groupedTopKWithFractionalIndex.cjs");
|
|
25
|
-
const
|
|
26
|
-
const
|
|
14
|
+
const join = require("./operators/join.cjs");
|
|
15
|
+
const map = require("./operators/map.cjs");
|
|
16
|
+
const negate = require("./operators/negate.cjs");
|
|
17
|
+
const output = require("./operators/output.cjs");
|
|
18
|
+
const reduce = require("./operators/reduce.cjs");
|
|
19
|
+
const tap = require("./operators/tap.cjs");
|
|
20
|
+
const topKWithFractionalIndex = require("./operators/topKWithFractionalIndex.cjs");
|
|
27
21
|
const filterBy = require("./operators/filterBy.cjs");
|
|
22
|
+
const groupBy = require("./operators/groupBy.cjs");
|
|
23
|
+
const groupedOrderBy = require("./operators/groupedOrderBy.cjs");
|
|
24
|
+
const keying = require("./operators/keying.cjs");
|
|
25
|
+
const orderBy = require("./operators/orderBy.cjs");
|
|
26
|
+
const pipe = require("./operators/pipe.cjs");
|
|
27
|
+
const topK = require("./operators/topK.cjs");
|
|
28
28
|
exports.D2 = d2.D2;
|
|
29
29
|
exports.RootStreamBuilder = d2.RootStreamBuilder;
|
|
30
30
|
exports.StreamBuilder = d2.StreamBuilder;
|
|
@@ -32,25 +32,20 @@ exports.MultiSet = multiset.MultiSet;
|
|
|
32
32
|
exports.compareKeys = utils.compareKeys;
|
|
33
33
|
exports.serializeValue = utils.serializeValue;
|
|
34
34
|
exports.registerOpaqueHash = hash.registerOpaqueHash;
|
|
35
|
-
exports.groupBy = groupBy.groupBy;
|
|
36
|
-
exports.groupByOperators = groupBy.groupByOperators;
|
|
37
|
-
exports.pipe = pipe.pipe;
|
|
38
|
-
exports.MapOperator = map.MapOperator;
|
|
39
|
-
exports.map = map.map;
|
|
40
|
-
exports.TapOperator = tap.TapOperator;
|
|
41
|
-
exports.tap = tap.tap;
|
|
42
|
-
exports.FilterOperator = filter.FilterOperator;
|
|
43
|
-
exports.filter = filter.filter;
|
|
44
|
-
exports.NegateOperator = negate.NegateOperator;
|
|
45
|
-
exports.negate = negate.negate;
|
|
46
35
|
exports.ConcatOperator = concat.ConcatOperator;
|
|
47
36
|
exports.concat = concat.concat;
|
|
48
|
-
exports.DebugOperator = debug.DebugOperator;
|
|
49
|
-
exports.debug = debug.debug;
|
|
50
|
-
exports.OutputOperator = output.OutputOperator;
|
|
51
|
-
exports.output = output.output;
|
|
52
37
|
exports.ConsolidateOperator = consolidate.ConsolidateOperator;
|
|
53
38
|
exports.consolidate = consolidate.consolidate;
|
|
39
|
+
exports.CountOperator = count.CountOperator;
|
|
40
|
+
exports.count = count.count;
|
|
41
|
+
exports.DebugOperator = debug.DebugOperator;
|
|
42
|
+
exports.debug = debug.debug;
|
|
43
|
+
exports.DistinctOperator = distinct.DistinctOperator;
|
|
44
|
+
exports.distinct = distinct.distinct;
|
|
45
|
+
exports.FilterOperator = filter.FilterOperator;
|
|
46
|
+
exports.filter = filter.filter;
|
|
47
|
+
exports.GroupedTopKWithFractionalIndexOperator = groupedTopKWithFractionalIndex.GroupedTopKWithFractionalIndexOperator;
|
|
48
|
+
exports.groupedTopKWithFractionalIndex = groupedTopKWithFractionalIndex.groupedTopKWithFractionalIndex;
|
|
54
49
|
exports.JoinOperator = join.JoinOperator;
|
|
55
50
|
exports.antiJoin = join.antiJoin;
|
|
56
51
|
exports.fullJoin = join.fullJoin;
|
|
@@ -58,25 +53,30 @@ exports.innerJoin = join.innerJoin;
|
|
|
58
53
|
exports.join = join.join;
|
|
59
54
|
exports.leftJoin = join.leftJoin;
|
|
60
55
|
exports.rightJoin = join.rightJoin;
|
|
56
|
+
exports.MapOperator = map.MapOperator;
|
|
57
|
+
exports.map = map.map;
|
|
58
|
+
exports.NegateOperator = negate.NegateOperator;
|
|
59
|
+
exports.negate = negate.negate;
|
|
60
|
+
exports.OutputOperator = output.OutputOperator;
|
|
61
|
+
exports.output = output.output;
|
|
61
62
|
exports.ReduceOperator = reduce.ReduceOperator;
|
|
62
63
|
exports.reduce = reduce.reduce;
|
|
63
|
-
exports.
|
|
64
|
-
exports.
|
|
65
|
-
exports.
|
|
66
|
-
exports.
|
|
64
|
+
exports.TapOperator = tap.TapOperator;
|
|
65
|
+
exports.tap = tap.tap;
|
|
66
|
+
exports.TopKWithFractionalIndexOperator = topKWithFractionalIndex.TopKWithFractionalIndexOperator;
|
|
67
|
+
exports.topKWithFractionalIndex = topKWithFractionalIndex.topKWithFractionalIndex;
|
|
68
|
+
exports.filterBy = filterBy.filterBy;
|
|
69
|
+
exports.groupBy = groupBy.groupBy;
|
|
70
|
+
exports.groupByOperators = groupBy.groupByOperators;
|
|
71
|
+
exports.groupedOrderByWithFractionalIndex = groupedOrderBy.groupedOrderByWithFractionalIndex;
|
|
67
72
|
exports.keyBy = keying.keyBy;
|
|
68
73
|
exports.rekey = keying.rekey;
|
|
69
74
|
exports.unkey = keying.unkey;
|
|
70
|
-
exports.topK = topK.topK;
|
|
71
|
-
exports.topKWithIndex = topK.topKWithIndex;
|
|
72
|
-
exports.TopKWithFractionalIndexOperator = topKWithFractionalIndex.TopKWithFractionalIndexOperator;
|
|
73
|
-
exports.topKWithFractionalIndex = topKWithFractionalIndex.topKWithFractionalIndex;
|
|
74
|
-
exports.GroupedTopKWithFractionalIndexOperator = groupedTopKWithFractionalIndex.GroupedTopKWithFractionalIndexOperator;
|
|
75
|
-
exports.groupedTopKWithFractionalIndex = groupedTopKWithFractionalIndex.groupedTopKWithFractionalIndex;
|
|
76
75
|
exports.orderBy = orderBy.orderBy;
|
|
77
76
|
exports.orderByWithFractionalIndex = orderBy.orderByWithFractionalIndex;
|
|
78
77
|
exports.orderByWithFractionalIndexBase = orderBy.orderByWithFractionalIndexBase;
|
|
79
78
|
exports.orderByWithIndex = orderBy.orderByWithIndex;
|
|
80
|
-
exports.
|
|
81
|
-
exports.
|
|
79
|
+
exports.pipe = pipe.pipe;
|
|
80
|
+
exports.topK = topK.topK;
|
|
81
|
+
exports.topKWithIndex = topK.topKWithIndex;
|
|
82
82
|
//# sourceMappingURL=index.cjs.map
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export { compareKeys, serializeValue } from
|
|
6
|
-
export { registerOpaqueHash } from
|
|
1
|
+
export * from "./d2.cjs";
|
|
2
|
+
export * from "./multiset.cjs";
|
|
3
|
+
export * from "./operators/index.cjs";
|
|
4
|
+
export * from "./types.cjs";
|
|
5
|
+
export { compareKeys, serializeValue } from "./utils.cjs";
|
|
6
|
+
export { registerOpaqueHash } from "./hashing/hash.cjs";
|
package/dist/cjs/indexes.cjs
CHANGED
|
@@ -14,7 +14,7 @@ class PrefixMap extends Map {
|
|
|
14
14
|
if (isSingleValue(valueMapOrSingleValue)) {
|
|
15
15
|
const [currentValue, currentMultiplicity] = valueMapOrSingleValue;
|
|
16
16
|
const currentPrefix = getPrefix(currentValue);
|
|
17
|
-
if (currentPrefix
|
|
17
|
+
if (!isSamePrefix(currentPrefix, prefix)) {
|
|
18
18
|
throw new Error(`Mismatching prefixes, this should never happen`);
|
|
19
19
|
}
|
|
20
20
|
if (currentValue === value || hash.hash(currentValue) === hash.hash(value)) {
|
|
@@ -268,7 +268,7 @@ class Index {
|
|
|
268
268
|
}
|
|
269
269
|
const newPrefix = getPrefix(newValue);
|
|
270
270
|
const currentPrefix = getPrefix(currentValue);
|
|
271
|
-
if (currentPrefix
|
|
271
|
+
if (isSamePrefix(currentPrefix, newPrefix) && (currentValue === newValue || hash.hash(currentValue) === hash.hash(newValue))) {
|
|
272
272
|
const newMultiplicity = currentMultiplicity + multiplicity;
|
|
273
273
|
if (newMultiplicity === 0) {
|
|
274
274
|
this.#inner.delete(key);
|
|
@@ -284,7 +284,7 @@ class Index {
|
|
|
284
284
|
this.#inner.set(key, valueMap);
|
|
285
285
|
} else {
|
|
286
286
|
const prefixMap = new PrefixMap();
|
|
287
|
-
if (currentPrefix
|
|
287
|
+
if (isSamePrefix(currentPrefix, newPrefix)) {
|
|
288
288
|
const valueMap = new ValueMap();
|
|
289
289
|
valueMap.set(hash.hash(currentValue), currentSingleValue);
|
|
290
290
|
valueMap.set(hash.hash(newValue), [newValue, multiplicity]);
|
|
@@ -340,6 +340,9 @@ class Index {
|
|
|
340
340
|
return new multiset.MultiSet(result);
|
|
341
341
|
}
|
|
342
342
|
}
|
|
343
|
+
function isSamePrefix(a, b) {
|
|
344
|
+
return a === b || Number.isNaN(a) && Number.isNaN(b);
|
|
345
|
+
}
|
|
343
346
|
function getPrefix(value) {
|
|
344
347
|
if (Array.isArray(value) && (typeof value[0] === `string` || typeof value[0] === `number` || typeof value[0] === `bigint`)) {
|
|
345
348
|
return value[0];
|
package/dist/cjs/indexes.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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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"}
|
|
1
|
+
{"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 (!isSamePrefix(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 isSamePrefix(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 (isSamePrefix(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 */\n// Prefixes are Map keys, so they compare as a Map does: NaN equals NaN.\nfunction isSamePrefix(a: unknown, b: unknown): boolean {\n return a === b || (Number.isNaN(a) && Number.isNaN(b))\n}\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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"}
|
package/dist/cjs/indexes.d.cts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { MultiSet } from
|
|
2
|
-
type SingleValue<TValue> = [
|
|
1
|
+
import { MultiSet } from "./multiset.cjs";
|
|
2
|
+
type SingleValue<TValue> = [
|
|
3
|
+
TValue,
|
|
4
|
+
number
|
|
5
|
+
];
|
|
3
6
|
/**
|
|
4
7
|
* A map from a difference collection trace's keys -> (value, multiplicities) that changed.
|
|
5
8
|
* Used in operations like join and reduce where the operation needs to
|
|
@@ -13,7 +16,10 @@ export declare class Index<TKey, TValue, TPrefix = any> {
|
|
|
13
16
|
* @param messages - Array of MultiSet messages to build the index from.
|
|
14
17
|
* @returns A new Index containing all the data from the messages.
|
|
15
18
|
*/
|
|
16
|
-
static fromMultiSets<K, V>(messages: Array<MultiSet<[
|
|
19
|
+
static fromMultiSets<K, V>(messages: Array<MultiSet<[
|
|
20
|
+
K,
|
|
21
|
+
V
|
|
22
|
+
]>>): Index<K, V>;
|
|
17
23
|
/**
|
|
18
24
|
* This method returns a string representation of the index.
|
|
19
25
|
* @param indent - Whether to indent the string representation.
|
|
@@ -52,25 +58,43 @@ export declare class Index<TKey, TValue, TPrefix = any> {
|
|
|
52
58
|
* @param key - The key to get the values for.
|
|
53
59
|
* @returns An array of value tuples [value, multiplicity].
|
|
54
60
|
*/
|
|
55
|
-
get(key: TKey): Array<[
|
|
61
|
+
get(key: TKey): Array<[
|
|
62
|
+
TValue,
|
|
63
|
+
number
|
|
64
|
+
]>;
|
|
56
65
|
/**
|
|
57
66
|
* This method returns an iterator over all values for a given key.
|
|
58
67
|
* @param key - The key to get the values for.
|
|
59
68
|
* @returns An iterator of value tuples [value, multiplicity].
|
|
60
69
|
*/
|
|
61
|
-
getIterator(key: TKey): Iterable<[
|
|
70
|
+
getIterator(key: TKey): Iterable<[
|
|
71
|
+
TValue,
|
|
72
|
+
number
|
|
73
|
+
]>;
|
|
62
74
|
/**
|
|
63
75
|
* This returns an iterator that iterates over all key-value pairs.
|
|
64
76
|
* @returns An iterable of all key-value pairs (and their multiplicities) in the index.
|
|
65
77
|
*/
|
|
66
|
-
entries(): Iterable<[
|
|
78
|
+
entries(): Iterable<[
|
|
79
|
+
TKey,
|
|
80
|
+
[
|
|
81
|
+
TValue,
|
|
82
|
+
number
|
|
83
|
+
]
|
|
84
|
+
]>;
|
|
67
85
|
/**
|
|
68
86
|
* This method only iterates over the keys and not over the values.
|
|
69
87
|
* Hence, it is more efficient than the `#entries` method.
|
|
70
88
|
* It returns an iterator that you can use if you need to iterate over the values for a given key.
|
|
71
89
|
* @returns An iterator of all *keys* in the index and their corresponding value iterator.
|
|
72
90
|
*/
|
|
73
|
-
entriesIterators(): Iterable<[
|
|
91
|
+
entriesIterators(): Iterable<[
|
|
92
|
+
TKey,
|
|
93
|
+
Iterable<[
|
|
94
|
+
TValue,
|
|
95
|
+
number
|
|
96
|
+
]>
|
|
97
|
+
]>;
|
|
74
98
|
/**
|
|
75
99
|
* This method adds a value to the index.
|
|
76
100
|
* @param key - The key to add the value to.
|
|
@@ -87,6 +111,12 @@ export declare class Index<TKey, TValue, TPrefix = any> {
|
|
|
87
111
|
* @param other - The index to join with the current index.
|
|
88
112
|
* @returns A multiset of the joined values.
|
|
89
113
|
*/
|
|
90
|
-
join<TValue2>(other: Index<TKey, TValue2>): MultiSet<[
|
|
114
|
+
join<TValue2>(other: Index<TKey, TValue2>): MultiSet<[
|
|
115
|
+
TKey,
|
|
116
|
+
[
|
|
117
|
+
TValue,
|
|
118
|
+
TValue2
|
|
119
|
+
]
|
|
120
|
+
]>;
|
|
91
121
|
}
|
|
92
122
|
export {};
|
package/dist/cjs/multiset.cjs
CHANGED
|
@@ -57,8 +57,7 @@ class MultiSet {
|
|
|
57
57
|
* - Keyed: every record is a `[key, value]` pair with a string or number
|
|
58
58
|
* key. Keys and primitive values compare by value, and object values by
|
|
59
59
|
* reference. A value that is an array of length 2 is a join tuple, and
|
|
60
|
-
* its two elements compare by the same rule.
|
|
61
|
-
* has known collisions: https://github.com/TanStack/db/issues/1948
|
|
60
|
+
* its two elements compare by the same rule.
|
|
62
61
|
* - Unkeyed, one primitive type: every record is a string, or every record
|
|
63
62
|
* is a number. Records compare as `Map` keys do, and a `-0` record is
|
|
64
63
|
* returned as `0`.
|
|
@@ -73,10 +72,19 @@ class MultiSet {
|
|
|
73
72
|
if (inner.every(
|
|
74
73
|
([data]) => Array.isArray(data) && data.length === 2 && (typeof data[0] === `string` || typeof data[0] === `number`)
|
|
75
74
|
)) {
|
|
75
|
+
const referenceIds = /* @__PURE__ */ new Map();
|
|
76
76
|
return consolidateBy(inner, (data) => {
|
|
77
77
|
const [key, value] = data;
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
let valueId;
|
|
79
|
+
if (Array.isArray(value) && value.length === 2) {
|
|
80
|
+
const firstId = utils.getStringId(value[0], referenceIds);
|
|
81
|
+
const secondId = utils.getStringId(value[1], referenceIds);
|
|
82
|
+
valueId = `tuple_${firstId.length}:${firstId}${secondId}`;
|
|
83
|
+
} else {
|
|
84
|
+
valueId = `leaf_${utils.getStringId(value, referenceIds)}`;
|
|
85
|
+
}
|
|
86
|
+
const keyId = utils.getStringId(key, referenceIds);
|
|
87
|
+
return `${keyId.length}:${keyId}${valueId}`;
|
|
80
88
|
});
|
|
81
89
|
}
|
|
82
90
|
let primitiveType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multiset.cjs","sources":["../../src/multiset.ts"],"sourcesContent":["import { chunkedArrayPush, getStringId } from './utils.js'\nimport { hash } from './hashing/index.js'\n\nexport type MultiSetArray<T> = Array<[T, number]>\nexport type KeyedData<T> = [key: string, value: T]\n\n/**\n * A multiset of data.\n */\nexport class MultiSet<T> {\n #inner: MultiSetArray<T>\n\n constructor(data: MultiSetArray<T> = []) {\n this.#inner = data\n }\n\n toString(indent = false): string {\n return `MultiSet(${JSON.stringify(this.#inner, null, indent ? 2 : undefined)})`\n }\n\n toJSON(): string {\n return JSON.stringify(Array.from(this.getInner()))\n }\n\n static fromJSON<U>(json: string): MultiSet<U> {\n return new MultiSet(JSON.parse(json))\n }\n\n /**\n * Apply a function to all records in the collection.\n */\n map<U>(f: (data: T) => U): MultiSet<U> {\n return new MultiSet(\n this.#inner.map(([data, multiplicity]) => [f(data), multiplicity]),\n )\n }\n\n /**\n * Filter out records for which a function f(record) evaluates to False.\n */\n filter(f: (data: T) => boolean): MultiSet<T> {\n return new MultiSet(this.#inner.filter(([data, _]) => f(data)))\n }\n\n /**\n * Negate all multiplicities in the collection.\n */\n negate(): MultiSet<T> {\n return new MultiSet(\n this.#inner.map(([data, multiplicity]) => [data, -multiplicity]),\n )\n }\n\n /**\n * Concatenate two collections together.\n */\n concat(other: MultiSet<T>): MultiSet<T> {\n const out: MultiSetArray<T> = []\n chunkedArrayPush(out, this.#inner)\n chunkedArrayPush(out, other.getInner())\n return new MultiSet(out)\n }\n\n /**\n * Produce as output a collection that is logically equivalent to the input\n * but which combines identical instances of the same record into one\n * (record, multiplicity) pair.\n *\n * Record identity depends on the shape of the whole multiset:\n *\n * - Keyed: every record is a `[key, value]` pair with a string or number\n * key. Keys and primitive values compare by value, and object values by\n * reference. A value that is an array of length 2 is a join tuple, and\n * its two elements compare by the same rule
|
|
1
|
+
{"version":3,"file":"multiset.cjs","sources":["../../src/multiset.ts"],"sourcesContent":["import { chunkedArrayPush, getStringId } from './utils.js'\nimport { hash } from './hashing/index.js'\n\nexport type MultiSetArray<T> = Array<[T, number]>\nexport type KeyedData<T> = [key: string, value: T]\n\n/**\n * A multiset of data.\n */\nexport class MultiSet<T> {\n #inner: MultiSetArray<T>\n\n constructor(data: MultiSetArray<T> = []) {\n this.#inner = data\n }\n\n toString(indent = false): string {\n return `MultiSet(${JSON.stringify(this.#inner, null, indent ? 2 : undefined)})`\n }\n\n toJSON(): string {\n return JSON.stringify(Array.from(this.getInner()))\n }\n\n static fromJSON<U>(json: string): MultiSet<U> {\n return new MultiSet(JSON.parse(json))\n }\n\n /**\n * Apply a function to all records in the collection.\n */\n map<U>(f: (data: T) => U): MultiSet<U> {\n return new MultiSet(\n this.#inner.map(([data, multiplicity]) => [f(data), multiplicity]),\n )\n }\n\n /**\n * Filter out records for which a function f(record) evaluates to False.\n */\n filter(f: (data: T) => boolean): MultiSet<T> {\n return new MultiSet(this.#inner.filter(([data, _]) => f(data)))\n }\n\n /**\n * Negate all multiplicities in the collection.\n */\n negate(): MultiSet<T> {\n return new MultiSet(\n this.#inner.map(([data, multiplicity]) => [data, -multiplicity]),\n )\n }\n\n /**\n * Concatenate two collections together.\n */\n concat(other: MultiSet<T>): MultiSet<T> {\n const out: MultiSetArray<T> = []\n chunkedArrayPush(out, this.#inner)\n chunkedArrayPush(out, other.getInner())\n return new MultiSet(out)\n }\n\n /**\n * Produce as output a collection that is logically equivalent to the input\n * but which combines identical instances of the same record into one\n * (record, multiplicity) pair.\n *\n * Record identity depends on the shape of the whole multiset:\n *\n * - Keyed: every record is a `[key, value]` pair with a string or number\n * key. Keys and primitive values compare by value, and object values by\n * reference. A value that is an array of length 2 is a join tuple, and\n * its two elements compare by the same rule.\n * - Unkeyed, one primitive type: every record is a string, or every record\n * is a number. Records compare as `Map` keys do, and a `-0` record is\n * returned as `0`.\n * - Otherwise, records compare by structure (`hash`).\n *\n * Each identity keeps its first record in input order with the summed\n * multiplicity. Identities whose sum is zero are dropped. The input is not\n * changed.\n */\n consolidate(): MultiSet<T> {\n const inner = this.#inner\n // Keyed multisets hold [string | number key, value] pairs. Their values are\n // compared by reference, which avoids hashing. Join tuples (values of\n // length 2) are unpacked so ['A', null] and [null, 'X'] stay distinct.\n if (\n inner.every(\n ([data]) =>\n Array.isArray(data) &&\n data.length === 2 &&\n (typeof data[0] === `string` || typeof data[0] === `number`),\n )\n ) {\n const referenceIds = new Map<object | symbol, number>()\n return consolidateBy(inner, (data) => {\n const [key, value] = data as [string | number, unknown]\n // Length prefixes keep user text from crossing component boundaries.\n let valueId: string\n if (Array.isArray(value) && value.length === 2) {\n const firstId = getStringId(value[0], referenceIds)\n const secondId = getStringId(value[1], referenceIds)\n valueId = `tuple_${firstId.length}:${firstId}${secondId}`\n } else {\n valueId = `leaf_${getStringId(value, referenceIds)}`\n }\n const keyId = getStringId(key, referenceIds)\n return `${keyId.length}:${keyId}${valueId}`\n })\n }\n\n // Unkeyed data of a single primitive type is its own identity, and the\n // result holds the Map key, so -0 becomes 0. Anything else is compared\n // structurally by hash.\n let primitiveType: string | undefined\n const requireHash = inner.some(([data]) => {\n const type = typeof data\n if (type !== `string` && type !== `number`) return true\n primitiveType ??= type\n return type !== primitiveType\n })\n return requireHash\n ? consolidateBy(inner, hash)\n : consolidateBy(inner, (data) => data, true)\n }\n\n extend(other: MultiSet<T> | MultiSetArray<T>): void {\n const otherArray = other instanceof MultiSet ? other.getInner() : other\n chunkedArrayPush(this.#inner, otherArray)\n }\n\n add(item: T, multiplicity: number): void {\n if (multiplicity !== 0) {\n this.#inner.push([item, multiplicity])\n }\n }\n\n getInner(): MultiSetArray<T> {\n return this.#inner\n }\n}\n\n/**\n * Sums multiplicities of records with the same identity, keeping the first\n * record seen for each identity (or its Map key, which writes -0 as 0, when\n * `keepIdentity` is set) and dropping records whose sum is zero.\n */\nfunction consolidateBy<T>(\n inner: MultiSetArray<T>,\n identityOf: (data: T) => unknown,\n keepIdentity = false,\n): MultiSet<T> {\n const consolidated = new Map<unknown, [T, number]>()\n for (const [data, multiplicity] of inner) {\n const identity = identityOf(data)\n const entry = consolidated.get(identity)\n if (entry) entry[1] += multiplicity\n else consolidated.set(identity, [data, multiplicity])\n }\n const result: MultiSetArray<T> = []\n for (const [identity, [data, multiplicity]] of consolidated) {\n if (multiplicity !== 0)\n result.push([keepIdentity ? (identity as T) : data, multiplicity])\n }\n return new MultiSet(result)\n}\n"],"names":["chunkedArrayPush","getStringId","hash"],"mappings":"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"}
|
package/dist/cjs/multiset.d.cts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
export type MultiSetArray<T> = Array<[
|
|
2
|
-
|
|
1
|
+
export type MultiSetArray<T> = Array<[
|
|
2
|
+
T,
|
|
3
|
+
number
|
|
4
|
+
]>;
|
|
5
|
+
export type KeyedData<T> = [
|
|
6
|
+
key: string,
|
|
7
|
+
value: T
|
|
8
|
+
];
|
|
3
9
|
/**
|
|
4
10
|
* A multiset of data.
|
|
5
11
|
*/
|
|
@@ -35,8 +41,7 @@ export declare class MultiSet<T> {
|
|
|
35
41
|
* - Keyed: every record is a `[key, value]` pair with a string or number
|
|
36
42
|
* key. Keys and primitive values compare by value, and object values by
|
|
37
43
|
* reference. A value that is an array of length 2 is a join tuple, and
|
|
38
|
-
* its two elements compare by the same rule.
|
|
39
|
-
* has known collisions: https://github.com/TanStack/db/issues/1948
|
|
44
|
+
* its two elements compare by the same rule.
|
|
40
45
|
* - Unkeyed, one primitive type: every record is a string, or every record
|
|
41
46
|
* is a number. Records compare as `Map` keys do, and a `-0` record is
|
|
42
47
|
* returned as `0`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BinaryOperator } from
|
|
2
|
-
import { IStreamBuilder, PipedOperator } from
|
|
1
|
+
import { BinaryOperator } from "../graph.cjs";
|
|
2
|
+
import { IStreamBuilder, PipedOperator } from "../types.cjs";
|
|
3
3
|
/**
|
|
4
4
|
* Operator that concatenates two input streams
|
|
5
5
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"count.cjs","sources":["../../../src/operators/count.ts"],"sourcesContent":["import { DifferenceStreamWriter } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport { ReduceOperator } from './reduce.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\n/**\n * Operator that counts elements by key (version-free)\n */\nexport class CountOperator<K, V> extends ReduceOperator<K, V, number> {\n constructor(\n id: number,\n inputA: DifferenceStreamReader<[K, V]>,\n output: DifferenceStreamWriter<[K, number]>,\n ) {\n const countInner = (vals: Array<[V, number]>): Array<[number, number]> => {\n let totalCount = 0\n for (const [_, diff] of vals) {\n totalCount += diff\n }\n return [[totalCount, 1]]\n }\n\n super(id, inputA, output, countInner)\n }\n}\n\n/**\n * Counts the number of elements by key (version-free)\n */\nexport function count<\n KType extends T extends KeyValue<infer K, infer _V> ? K : never,\n VType extends T extends KeyValue<KType, infer V> ? V : never,\n T,\n>() {\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<KeyValue<KType, number>> => {\n const output = new StreamBuilder<KeyValue<KType, number>>(\n stream.graph,\n new DifferenceStreamWriter<KeyValue<KType, number>>(),\n )\n const operator = new CountOperator<KType, VType>(\n stream.graph.getNextOperatorId(),\n stream.connectReader() as DifferenceStreamReader<KeyValue<KType, VType>>,\n output.writer,\n )\n stream.graph.addOperator(operator)\n return output\n }\n}\n"],"names":["ReduceOperator","StreamBuilder","DifferenceStreamWriter"],"mappings":";;;;;AASO,MAAM,sBAA4BA,OAAAA,eAA6B;AAAA,EACpE,YACE,IACA,QACA,QACA;AACA,UAAM,aAAa,CAAC,SAAsD;AACxE,UAAI,aAAa;AACjB,iBAAW,CAAC,GAAG,IAAI,KAAK,MAAM;AAC5B,sBAAc;AAAA,MAChB;AACA,aAAO,CAAC,CAAC,YAAY,CAAC,CAAC;AAAA,IACzB;AAEA,UAAM,IAAI,QAAQ,QAAQ,UAAU;AAAA,EACtC;AACF;AAKO,SAAS,QAIZ;AACF,SAAO,CACL,WAC4C;AAC5C,UAAM,SAAS,IAAIC,GAAAA;AAAAA,MACjB,OAAO;AAAA,MACP,IAAIC,MAAAA,uBAAA;AAAA,IAAgD;AAEtD,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,OAAO;AAAA,IAAA;AAET,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO;AAAA,EACT;AACF;;;"}
|
|
1
|
+
{"version":3,"file":"count.cjs","sources":["../../../src/operators/count.ts"],"sourcesContent":["import { DifferenceStreamWriter } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport { ReduceOperator } from './reduce.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\n/**\n * Operator that counts elements by key (version-free)\n */\nexport class CountOperator<K, V> extends ReduceOperator<K, V, number> {\n constructor(\n id: number,\n inputA: DifferenceStreamReader<[K, V]>,\n output: DifferenceStreamWriter<[K, number]>,\n ) {\n const countInner = (vals: Array<[V, number]>): Array<[number, number]> => {\n let totalCount = 0\n for (const [_, diff] of vals) {\n totalCount += diff\n }\n return [[totalCount, 1]]\n }\n\n super(id, inputA, output, countInner)\n }\n}\n\n/**\n * Counts the number of elements by key (version-free)\n */\nexport function count<\n KType extends (T extends KeyValue<infer K, infer _V> ? K : never),\n VType extends (T extends KeyValue<KType, infer V> ? V : never),\n T,\n>() {\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<KeyValue<KType, number>> => {\n const output = new StreamBuilder<KeyValue<KType, number>>(\n stream.graph,\n new DifferenceStreamWriter<KeyValue<KType, number>>(),\n )\n const operator = new CountOperator<KType, VType>(\n stream.graph.getNextOperatorId(),\n stream.connectReader() as DifferenceStreamReader<KeyValue<KType, VType>>,\n output.writer,\n )\n stream.graph.addOperator(operator)\n return output\n }\n}\n"],"names":["ReduceOperator","StreamBuilder","DifferenceStreamWriter"],"mappings":";;;;;AASO,MAAM,sBAA4BA,OAAAA,eAA6B;AAAA,EACpE,YACE,IACA,QACA,QACA;AACA,UAAM,aAAa,CAAC,SAAsD;AACxE,UAAI,aAAa;AACjB,iBAAW,CAAC,GAAG,IAAI,KAAK,MAAM;AAC5B,sBAAc;AAAA,MAChB;AACA,aAAO,CAAC,CAAC,YAAY,CAAC,CAAC;AAAA,IACzB;AAEA,UAAM,IAAI,QAAQ,QAAQ,UAAU;AAAA,EACtC;AACF;AAKO,SAAS,QAIZ;AACF,SAAO,CACL,WAC4C;AAC5C,UAAM,SAAS,IAAIC,GAAAA;AAAAA,MACjB,OAAO;AAAA,MACP,IAAIC,MAAAA,uBAAA;AAAA,IAAgD;AAEtD,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,OAAO;AAAA,IAAA;AAET,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO;AAAA,EACT;AACF;;;"}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import { DifferenceStreamWriter, DifferenceStreamReader } from
|
|
2
|
-
import { ReduceOperator } from
|
|
3
|
-
import { IStreamBuilder, KeyValue } from
|
|
1
|
+
import { DifferenceStreamWriter, DifferenceStreamReader } from "../graph.cjs";
|
|
2
|
+
import { ReduceOperator } from "./reduce.cjs";
|
|
3
|
+
import { IStreamBuilder, KeyValue } from "../types.cjs";
|
|
4
4
|
/**
|
|
5
5
|
* Operator that counts elements by key (version-free)
|
|
6
6
|
*/
|
|
7
7
|
export declare class CountOperator<K, V> extends ReduceOperator<K, V, number> {
|
|
8
|
-
constructor(id: number, inputA: DifferenceStreamReader<[
|
|
8
|
+
constructor(id: number, inputA: DifferenceStreamReader<[
|
|
9
|
+
K,
|
|
10
|
+
V
|
|
11
|
+
]>, output: DifferenceStreamWriter<[
|
|
12
|
+
K,
|
|
13
|
+
number
|
|
14
|
+
]>);
|
|
9
15
|
}
|
|
10
16
|
/**
|
|
11
17
|
* Counts the number of elements by key (version-free)
|
|
12
18
|
*/
|
|
13
|
-
export declare function count<KType extends T extends KeyValue<infer K, infer _V> ? K : never, VType extends T extends KeyValue<KType, infer V> ? V : never, T>(): (stream: IStreamBuilder<T>) => IStreamBuilder<KeyValue<KType, number>>;
|
|
19
|
+
export declare function count<KType extends (T extends KeyValue<infer K, infer _V> ? K : never), VType extends (T extends KeyValue<KType, infer V> ? V : never), T>(): (stream: IStreamBuilder<T>) => IStreamBuilder<KeyValue<KType, number>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from
|
|
2
|
-
import { PipedOperator } from
|
|
1
|
+
import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from "../graph.cjs";
|
|
2
|
+
import { PipedOperator } from "../types.cjs";
|
|
3
3
|
/**
|
|
4
4
|
* Operator that logs debug information about the stream
|
|
5
5
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from
|
|
2
|
-
import { IStreamBuilder, KeyValue } from
|
|
1
|
+
import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from "../graph.cjs";
|
|
2
|
+
import { IStreamBuilder, KeyValue } from "../types.cjs";
|
|
3
3
|
type GetValue<T> = T extends KeyValue<any, infer V> ? V : never;
|
|
4
4
|
/**
|
|
5
5
|
* Operator that removes duplicates
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DifferenceStreamWriter, LinearUnaryOperator, DifferenceStreamReader } from
|
|
2
|
-
import { PipedOperator } from
|
|
3
|
-
import { MultiSet } from
|
|
1
|
+
import { DifferenceStreamWriter, LinearUnaryOperator, DifferenceStreamReader } from "../graph.cjs";
|
|
2
|
+
import { PipedOperator } from "../types.cjs";
|
|
3
|
+
import { MultiSet } from "../multiset.cjs";
|
|
4
4
|
/**
|
|
5
5
|
* Operator that filters elements from the input stream
|
|
6
6
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filterBy.cjs","sources":["../../../src/operators/filterBy.ts"],"sourcesContent":["import { map } from './map.js'\nimport { innerJoin } from './join.js'\nimport { consolidate } from './consolidate.js'\nimport type { IStreamBuilder, KeyValue, PipedOperator } from '../types.js'\n\n/**\n * Filters the elements of a keyed stream, by keys of another stream.\n * This allows you to build pipelies where you have multiple outputs that are related,\n * such as a streams of issues and comments for a project.\n *\n * @param other - The other stream to filter by, which must have the same key type as the input stream\n */\nexport function filterBy<\n K,\n V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never,\n T,\n>(\n other: IStreamBuilder<KeyValue<K, unknown>>,\n): PipedOperator<T, KeyValue<K, V1>> {\n return (stream: IStreamBuilder<T>): IStreamBuilder<KeyValue<K, V1>> => {\n const otherKeys = other.pipe(\n map(([key, _]) => [key, null] as KeyValue<K, null>),\n )\n return stream.pipe(\n innerJoin(otherKeys),\n map(([key, [value, _]]) => [key, value] as KeyValue<K, V1>),\n consolidate(),\n )\n }\n}\n"],"names":["map","innerJoin","consolidate"],"mappings":";;;;;AAYO,SAAS,SAKd,OACmC;AACnC,SAAO,CAAC,WAA+D;AACrE,UAAM,YAAY,MAAM;AAAA,MACtBA,IAAAA,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAsB;AAAA,IAAA;AAEpD,WAAO,OAAO;AAAA,MACZC,KAAAA,UAAU,SAAS;AAAA,MACnBD,QAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,CAAoB;AAAA,MAC1DE,YAAAA,YAAA;AAAA,IAAY;AAAA,EAEhB;AACF;;"}
|
|
1
|
+
{"version":3,"file":"filterBy.cjs","sources":["../../../src/operators/filterBy.ts"],"sourcesContent":["import { map } from './map.js'\nimport { innerJoin } from './join.js'\nimport { consolidate } from './consolidate.js'\nimport type { IStreamBuilder, KeyValue, PipedOperator } from '../types.js'\n\n/**\n * Filters the elements of a keyed stream, by keys of another stream.\n * This allows you to build pipelies where you have multiple outputs that are related,\n * such as a streams of issues and comments for a project.\n *\n * @param other - The other stream to filter by, which must have the same key type as the input stream\n */\nexport function filterBy<\n K,\n V1 extends (T extends KeyValue<infer _KT, infer VT> ? VT : never),\n T,\n>(\n other: IStreamBuilder<KeyValue<K, unknown>>,\n): PipedOperator<T, KeyValue<K, V1>> {\n return (stream: IStreamBuilder<T>): IStreamBuilder<KeyValue<K, V1>> => {\n const otherKeys = other.pipe(\n map(([key, _]) => [key, null] as KeyValue<K, null>),\n )\n return stream.pipe(\n innerJoin(otherKeys),\n map(([key, [value, _]]) => [key, value] as KeyValue<K, V1>),\n consolidate(),\n )\n }\n}\n"],"names":["map","innerJoin","consolidate"],"mappings":";;;;;AAYO,SAAS,SAKd,OACmC;AACnC,SAAO,CAAC,WAA+D;AACrE,UAAM,YAAY,MAAM;AAAA,MACtBA,IAAAA,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAsB;AAAA,IAAA;AAEpD,WAAO,OAAO;AAAA,MACZC,KAAAA,UAAU,SAAS;AAAA,MACnBD,QAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,CAAoB;AAAA,MAC1DE,YAAAA,YAAA;AAAA,IAAY;AAAA,EAEhB;AACF;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IStreamBuilder, KeyValue, PipedOperator } from
|
|
1
|
+
import { IStreamBuilder, KeyValue, PipedOperator } from "../types.cjs";
|
|
2
2
|
/**
|
|
3
3
|
* Filters the elements of a keyed stream, by keys of another stream.
|
|
4
4
|
* This allows you to build pipelies where you have multiple outputs that are related,
|
|
@@ -6,4 +6,4 @@ import { IStreamBuilder, KeyValue, PipedOperator } from '../types.js';
|
|
|
6
6
|
*
|
|
7
7
|
* @param other - The other stream to filter by, which must have the same key type as the input stream
|
|
8
8
|
*/
|
|
9
|
-
export declare function filterBy<K, V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never, T>(other: IStreamBuilder<KeyValue<K, unknown>>): PipedOperator<T, KeyValue<K, V1>>;
|
|
9
|
+
export declare function filterBy<K, V1 extends (T extends KeyValue<infer _KT, infer VT> ? VT : never), T>(other: IStreamBuilder<KeyValue<K, unknown>>): PipedOperator<T, KeyValue<K, V1>>;
|