@tanstack/db-ivm 0.1.25 → 0.2.0

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.
Files changed (151) hide show
  1. package/README.md +5 -6
  2. package/dist/cjs/d2.d.cts +3 -3
  3. package/dist/cjs/graph.d.cts +2 -2
  4. package/dist/cjs/hashing/hash.cjs +58 -3
  5. package/dist/cjs/hashing/hash.cjs.map +1 -1
  6. package/dist/cjs/hashing/hash.d.cts +9 -0
  7. package/dist/cjs/hashing/index.d.cts +2 -2
  8. package/dist/cjs/hashing/murmur.cjs +26 -15
  9. package/dist/cjs/hashing/murmur.cjs.map +1 -1
  10. package/dist/cjs/hashing/murmur.d.cts +2 -0
  11. package/dist/cjs/index.cjs +25 -51
  12. package/dist/cjs/index.cjs.map +1 -1
  13. package/dist/cjs/index.d.cts +6 -6
  14. package/dist/cjs/indexes.cjs +52 -16
  15. package/dist/cjs/indexes.cjs.map +1 -1
  16. package/dist/cjs/indexes.d.cts +42 -9
  17. package/dist/cjs/multiset.d.cts +8 -2
  18. package/dist/cjs/operators/concat.d.cts +2 -2
  19. package/dist/cjs/operators/consolidate.d.cts +2 -2
  20. package/dist/cjs/operators/distinct.d.cts +2 -2
  21. package/dist/cjs/operators/filter.d.cts +3 -3
  22. package/dist/cjs/operators/groupBy.cjs +33 -36
  23. package/dist/cjs/operators/groupBy.cjs.map +1 -1
  24. package/dist/cjs/operators/groupBy.d.cts +6 -7
  25. package/dist/cjs/operators/groupedOrderBy.d.cts +8 -2
  26. package/dist/cjs/operators/groupedTopKWithFractionalIndex.cjs +3 -9
  27. package/dist/cjs/operators/groupedTopKWithFractionalIndex.cjs.map +1 -1
  28. package/dist/cjs/operators/groupedTopKWithFractionalIndex.d.cts +24 -11
  29. package/dist/cjs/operators/index.d.cts +15 -21
  30. package/dist/cjs/operators/join.cjs +0 -20
  31. package/dist/cjs/operators/join.cjs.map +1 -1
  32. package/dist/cjs/operators/join.d.cts +29 -30
  33. package/dist/cjs/operators/map.d.cts +3 -3
  34. package/dist/cjs/operators/orderBy.cjs +2 -77
  35. package/dist/cjs/operators/orderBy.cjs.map +1 -1
  36. package/dist/cjs/operators/orderBy.d.cts +10 -24
  37. package/dist/cjs/operators/output.d.cts +3 -3
  38. package/dist/cjs/operators/pipe.d.cts +1 -1
  39. package/dist/cjs/operators/reduce.cjs +36 -5
  40. package/dist/cjs/operators/reduce.cjs.map +1 -1
  41. package/dist/cjs/operators/reduce.d.cts +38 -5
  42. package/dist/cjs/operators/tap.d.cts +3 -3
  43. package/dist/cjs/operators/topKArray.cjs +9 -0
  44. package/dist/cjs/operators/topKArray.cjs.map +1 -1
  45. package/dist/cjs/operators/topKArray.d.cts +17 -2
  46. package/dist/cjs/operators/topKState.cjs +3 -0
  47. package/dist/cjs/operators/topKState.cjs.map +1 -1
  48. package/dist/cjs/operators/topKState.d.cts +45 -8
  49. package/dist/cjs/operators/topKWithFractionalIndex.cjs +5 -9
  50. package/dist/cjs/operators/topKWithFractionalIndex.cjs.map +1 -1
  51. package/dist/cjs/operators/topKWithFractionalIndex.d.cts +37 -9
  52. package/dist/cjs/types.d.cts +6 -3
  53. package/dist/cjs/utils.cjs.map +1 -1
  54. package/dist/cjs/utils.d.cts +4 -8
  55. package/dist/esm/hashing/hash.d.ts +9 -0
  56. package/dist/esm/hashing/hash.js +60 -5
  57. package/dist/esm/hashing/hash.js.map +1 -1
  58. package/dist/esm/hashing/murmur.d.ts +2 -0
  59. package/dist/esm/hashing/murmur.js +26 -15
  60. package/dist/esm/hashing/murmur.js.map +1 -1
  61. package/dist/esm/index.js +11 -37
  62. package/dist/esm/index.js.map +1 -1
  63. package/dist/esm/indexes.d.ts +4 -1
  64. package/dist/esm/indexes.js +52 -16
  65. package/dist/esm/indexes.js.map +1 -1
  66. package/dist/esm/operators/groupBy.d.ts +1 -5
  67. package/dist/esm/operators/groupBy.js +33 -36
  68. package/dist/esm/operators/groupBy.js.map +1 -1
  69. package/dist/esm/operators/groupedTopKWithFractionalIndex.d.ts +1 -6
  70. package/dist/esm/operators/groupedTopKWithFractionalIndex.js +3 -9
  71. package/dist/esm/operators/groupedTopKWithFractionalIndex.js.map +1 -1
  72. package/dist/esm/operators/index.d.ts +0 -6
  73. package/dist/esm/operators/join.d.ts +1 -26
  74. package/dist/esm/operators/join.js +1 -21
  75. package/dist/esm/operators/join.js.map +1 -1
  76. package/dist/esm/operators/orderBy.d.ts +2 -22
  77. package/dist/esm/operators/orderBy.js +3 -78
  78. package/dist/esm/operators/orderBy.js.map +1 -1
  79. package/dist/esm/operators/reduce.d.ts +11 -2
  80. package/dist/esm/operators/reduce.js +36 -5
  81. package/dist/esm/operators/reduce.js.map +1 -1
  82. package/dist/esm/operators/topKArray.d.ts +6 -0
  83. package/dist/esm/operators/topKArray.js +10 -1
  84. package/dist/esm/operators/topKArray.js.map +1 -1
  85. package/dist/esm/operators/topKState.d.ts +1 -0
  86. package/dist/esm/operators/topKState.js +3 -0
  87. package/dist/esm/operators/topKState.js.map +1 -1
  88. package/dist/esm/operators/topKWithFractionalIndex.d.ts +7 -3
  89. package/dist/esm/operators/topKWithFractionalIndex.js +6 -10
  90. package/dist/esm/operators/topKWithFractionalIndex.js.map +1 -1
  91. package/dist/esm/utils.d.ts +0 -7
  92. package/dist/esm/utils.js.map +1 -1
  93. package/package.json +3 -5
  94. package/src/hashing/hash.ts +87 -5
  95. package/src/hashing/murmur.ts +31 -17
  96. package/src/indexes.ts +65 -16
  97. package/src/operators/groupBy.ts +48 -59
  98. package/src/operators/groupedTopKWithFractionalIndex.ts +4 -15
  99. package/src/operators/index.ts +0 -6
  100. package/src/operators/join.ts +1 -91
  101. package/src/operators/orderBy.ts +6 -141
  102. package/src/operators/reduce.ts +62 -5
  103. package/src/operators/topKArray.ts +15 -0
  104. package/src/operators/topKState.ts +7 -0
  105. package/src/operators/topKWithFractionalIndex.ts +15 -16
  106. package/src/utils.ts +0 -30
  107. package/dist/cjs/operators/count.cjs +0 -35
  108. package/dist/cjs/operators/count.cjs.map +0 -1
  109. package/dist/cjs/operators/count.d.cts +0 -13
  110. package/dist/cjs/operators/debug.cjs +0 -39
  111. package/dist/cjs/operators/debug.cjs.map +0 -1
  112. package/dist/cjs/operators/debug.d.cts +0 -16
  113. package/dist/cjs/operators/filterBy.cjs +0 -19
  114. package/dist/cjs/operators/filterBy.cjs.map +0 -1
  115. package/dist/cjs/operators/filterBy.d.cts +0 -9
  116. package/dist/cjs/operators/keying.cjs +0 -16
  117. package/dist/cjs/operators/keying.cjs.map +0 -1
  118. package/dist/cjs/operators/keying.d.cts +0 -19
  119. package/dist/cjs/operators/negate.cjs +0 -27
  120. package/dist/cjs/operators/negate.cjs.map +0 -1
  121. package/dist/cjs/operators/negate.d.cts +0 -13
  122. package/dist/cjs/operators/topK.cjs +0 -39
  123. package/dist/cjs/operators/topK.cjs.map +0 -1
  124. package/dist/cjs/operators/topK.d.cts +0 -31
  125. package/dist/cjs/operators/topKWithFractionalIndexBTree.d.cts +0 -28
  126. package/dist/esm/operators/count.d.ts +0 -13
  127. package/dist/esm/operators/count.js +0 -35
  128. package/dist/esm/operators/count.js.map +0 -1
  129. package/dist/esm/operators/debug.d.ts +0 -16
  130. package/dist/esm/operators/debug.js +0 -39
  131. package/dist/esm/operators/debug.js.map +0 -1
  132. package/dist/esm/operators/filterBy.d.ts +0 -9
  133. package/dist/esm/operators/filterBy.js +0 -19
  134. package/dist/esm/operators/filterBy.js.map +0 -1
  135. package/dist/esm/operators/keying.d.ts +0 -19
  136. package/dist/esm/operators/keying.js +0 -16
  137. package/dist/esm/operators/keying.js.map +0 -1
  138. package/dist/esm/operators/negate.d.ts +0 -13
  139. package/dist/esm/operators/negate.js +0 -27
  140. package/dist/esm/operators/negate.js.map +0 -1
  141. package/dist/esm/operators/topK.d.ts +0 -31
  142. package/dist/esm/operators/topK.js +0 -39
  143. package/dist/esm/operators/topK.js.map +0 -1
  144. package/dist/esm/operators/topKWithFractionalIndexBTree.d.ts +0 -28
  145. package/src/operators/count.ts +0 -51
  146. package/src/operators/debug.ts +0 -57
  147. package/src/operators/filterBy.ts +0 -30
  148. package/src/operators/keying.ts +0 -34
  149. package/src/operators/negate.ts +0 -32
  150. package/src/operators/topK.ts +0 -91
  151. package/src/operators/topKWithFractionalIndexBTree.ts +0 -297
@@ -1,21 +1,15 @@
1
- export * from './pipe.js';
2
- export * from './map.js';
3
- export * from './tap.js';
4
- export * from './filter.js';
5
- export * from './negate.js';
6
- export * from './concat.js';
7
- export * from './debug.js';
8
- export * from './output.js';
9
- export * from './consolidate.js';
10
- export * from './join.js';
11
- export * from './reduce.js';
12
- export * from './count.js';
13
- export * from './distinct.js';
14
- export * from './keying.js';
15
- export * from './topK.js';
16
- export * from './topKWithFractionalIndex.js';
17
- export * from './groupedTopKWithFractionalIndex.js';
18
- export * from './orderBy.js';
19
- export * from './groupedOrderBy.js';
20
- export * from './filterBy.js';
21
- export { groupBy, groupByOperators } from './groupBy.js';
1
+ export * from "./pipe.cjs";
2
+ export * from "./map.cjs";
3
+ export * from "./tap.cjs";
4
+ export * from "./filter.cjs";
5
+ export * from "./concat.cjs";
6
+ export * from "./output.cjs";
7
+ export * from "./consolidate.cjs";
8
+ export * from "./join.cjs";
9
+ export * from "./reduce.cjs";
10
+ export * from "./distinct.cjs";
11
+ export * from "./topKWithFractionalIndex.cjs";
12
+ export * from "./groupedTopKWithFractionalIndex.cjs";
13
+ export * from "./orderBy.cjs";
14
+ export * from "./groupedOrderBy.cjs";
15
+ export { groupBy, groupByOperators } from "./groupBy.cjs";
@@ -130,26 +130,6 @@ function join(other, type = `inner`) {
130
130
  return output;
131
131
  };
132
132
  }
133
- function innerJoin(other) {
134
- return join(other, `inner`);
135
- }
136
- function antiJoin(other) {
137
- return join(other, `anti`);
138
- }
139
- function leftJoin(other) {
140
- return join(other, `left`);
141
- }
142
- function rightJoin(other) {
143
- return join(other, `right`);
144
- }
145
- function fullJoin(other) {
146
- return join(other, `full`);
147
- }
148
133
  exports.JoinOperator = JoinOperator;
149
- exports.antiJoin = antiJoin;
150
- exports.fullJoin = fullJoin;
151
- exports.innerJoin = innerJoin;
152
134
  exports.join = join;
153
- exports.leftJoin = leftJoin;
154
- exports.rightJoin = rightJoin;
155
135
  //# sourceMappingURL=join.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"join.cjs","sources":["../../../src/operators/join.ts"],"sourcesContent":["/**\n * # Direct Join Algorithms for Incremental View Maintenance\n *\n * High-performance join operations implementing all join types (inner, left, right, full, anti)\n * with minimal state and optimized performance.\n *\n * ## Algorithm\n *\n * For each tick, the algorithm processes incoming changes (deltas) and emits join results:\n *\n * 1. **Build deltas**: Create delta indexes from input messages using `Index.fromMultiSet()`\n * 2. **Inner results**: Emit `ΔA⋈B_old + A_old⋈ΔB + ΔA⋈ΔB` (matched pairs)\n * 3. **Outer results**: For unmatched rows, emit null-extended tuples:\n * - New unmatched rows from deltas (when opposite side empty)\n * - Presence transitions: when key goes `0→>0` (retract nulls) or `>0→0` (emit nulls)\n * 4. **Update state**: Append deltas to indexes (consolidated multiplicity tracking automatic)\n *\n * **Consolidated multiplicity tracking** enables O(1) presence checks instead of scanning index buckets.\n *\n * ## State\n *\n * **Indexes** store the actual data:\n * - `indexA: Index<K, V1>` - all left-side rows accumulated over time\n * - `indexB: Index<K, V2>` - all right-side rows accumulated over time\n *\n * **Consolidated multiplicity tracking** (built into Index):\n * - Each Index maintains sum of multiplicities per key internally\n * - Provides O(1) presence checks: `index.hasPresence(key)` and `index.getConsolidatedMultiplicity(key)`\n * - Avoids scanning entire index buckets just to check if key has any rows\n *\n * ## Join Types\n *\n * - **Inner**: Standard delta terms only\n * - **Outer**: Inner results + null-extended unmatched rows with transition handling\n * - **Anti**: Unmatched rows only (no inner results)\n *\n * ## Key Optimizations\n *\n * - **No temp copying**: Uses `(A⊎ΔA)⋈ΔB = A⋈ΔB ⊎ ΔA⋈ΔB` distributive property\n * - **Early-out checks**: Skip phases when no deltas present\n * - **Zero-entry pruning**: Keep maps compact, O(distinct keys) memory\n * - **Final presence logic**: Avoid emit→retract churn within same tick\n *\n * ## Correctness\n *\n * - **Ordering**: Pre-append snapshots for emissions, post-emit state updates\n * - **Presence**: Key matched iff mass ≠ 0, transitions trigger null handling\n * - **Bag semantics**: Proper multiplicity handling including negatives\n */\n\nimport { BinaryOperator, DifferenceStreamWriter } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport { MultiSet } from '../multiset.js'\nimport { Index } from '../indexes.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { IStreamBuilder, KeyValue, PipedOperator } from '../types.js'\n\n/**\n * Type of join to perform\n */\nexport type JoinType = `inner` | `left` | `right` | `full` | `anti`\n\n/**\n * Operator that joins two input streams using direct join algorithms\n */\nexport class JoinOperator<K, V1, V2> extends BinaryOperator<\n [K, V1] | [K, V2] | [K, [V1, V2]] | [K, [V1 | null, V2 | null]]\n> {\n #indexA = new Index<K, V1>()\n #indexB = new Index<K, V2>()\n #mode: JoinType\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<[K, V1]>,\n inputB: DifferenceStreamReader<[K, V2]>,\n output: DifferenceStreamWriter<any>,\n mode: JoinType = `inner`,\n ) {\n super(id, inputA, inputB, output)\n this.#mode = mode\n }\n\n run(): void {\n // Build deltas from input messages\n const deltaA = Index.fromMultiSets<K, V1>(\n this.inputAMessages() as Array<MultiSet<[K, V1]>>,\n )\n const deltaB = Index.fromMultiSets<K, V2>(\n this.inputBMessages() as Array<MultiSet<[K, V2]>>,\n )\n\n // Early-out if nothing changed\n if (deltaA.size === 0 && deltaB.size === 0) return\n\n const results = new MultiSet<any>()\n\n // Emit inner results (all modes except anti)\n if (this.#mode !== `anti`) {\n this.emitInnerResults(deltaA, deltaB, results)\n }\n\n // Emit left outer/anti results\n if (\n this.#mode === `left` ||\n this.#mode === `full` ||\n this.#mode === `anti`\n ) {\n this.emitLeftOuterResults(deltaA, deltaB, results)\n }\n\n // Emit right outer results\n if (this.#mode === `right` || this.#mode === `full`) {\n this.emitRightOuterResults(deltaA, deltaB, results)\n }\n\n // Update state and send results\n // IMPORTANT: All emissions use pre-append snapshots of indexA/indexB.\n // Now append ALL deltas to indices - this happens unconditionally for every key,\n // regardless of whether presence flipped. Consolidated multiplicity tracking is automatic.\n this.#indexA.append(deltaA)\n this.#indexB.append(deltaB)\n\n // Send results\n if (results.getInner().length > 0) {\n this.output.sendData(results)\n }\n }\n\n private emitInnerResults(\n deltaA: Index<K, V1>,\n deltaB: Index<K, V2>,\n results: MultiSet<any>,\n ): void {\n // Emit the three standard delta terms: ΔA⋈B_old, A_old⋈ΔB, ΔA⋈ΔB\n if (deltaA.size > 0) results.extend(deltaA.join(this.#indexB))\n if (deltaB.size > 0) results.extend(this.#indexA.join(deltaB))\n if (deltaA.size > 0 && deltaB.size > 0) results.extend(deltaA.join(deltaB))\n }\n\n private emitLeftOuterResults(\n deltaA: Index<K, V1>,\n deltaB: Index<K, V2>,\n results: MultiSet<any>,\n ): void {\n // Emit unmatched left rows from deltaA\n if (deltaA.size > 0) {\n for (const [key, valueIterator] of deltaA.entriesIterators()) {\n const currentMultiplicityB =\n this.#indexB.getConsolidatedMultiplicity(key)\n const deltaMultiplicityB = deltaB.getConsolidatedMultiplicity(key)\n const finalMultiplicityB = currentMultiplicityB + deltaMultiplicityB\n\n if (finalMultiplicityB === 0) {\n for (const [value, multiplicity] of valueIterator) {\n if (multiplicity !== 0) {\n results.add([key, [value, null]], multiplicity)\n }\n }\n }\n }\n }\n\n // Handle presence transitions from right side changes\n if (deltaB.size > 0) {\n for (const key of deltaB.getPresenceKeys()) {\n const before = this.#indexB.getConsolidatedMultiplicity(key)\n const deltaMult = deltaB.getConsolidatedMultiplicity(key)\n if (deltaMult === 0) continue\n const after = before + deltaMult\n\n // Skip transition handling if presence doesn't flip (both zero or both non-zero)\n // Note: Index updates happen later regardless - we're only skipping null-extension emissions here\n if ((before === 0) === (after === 0)) continue\n\n // Determine the type of transition:\n // - 0 → non-zero: Right becomes non-empty, left rows transition from unmatched to matched\n // → RETRACT previously emitted null-extended rows (emit with negative multiplicity)\n // - non-zero → 0: Right becomes empty, left rows transition from matched to unmatched\n // → EMIT new null-extended rows (emit with positive multiplicity)\n const transitioningToMatched = before === 0\n\n for (const [value, multiplicity] of this.#indexA.getIterator(key)) {\n if (multiplicity !== 0) {\n results.add(\n [key, [value, null]],\n transitioningToMatched ? -multiplicity : +multiplicity,\n )\n }\n }\n }\n }\n }\n\n private emitRightOuterResults(\n deltaA: Index<K, V1>,\n deltaB: Index<K, V2>,\n results: MultiSet<any>,\n ): void {\n // Emit unmatched right rows from deltaB\n if (deltaB.size > 0) {\n for (const [key, valueIterator] of deltaB.entriesIterators()) {\n const currentMultiplicityA =\n this.#indexA.getConsolidatedMultiplicity(key)\n const deltaMultiplicityA = deltaA.getConsolidatedMultiplicity(key)\n const finalMultiplicityA = currentMultiplicityA + deltaMultiplicityA\n\n if (finalMultiplicityA === 0) {\n for (const [value, multiplicity] of valueIterator) {\n if (multiplicity !== 0) {\n results.add([key, [null, value]], multiplicity)\n }\n }\n }\n }\n }\n\n // Handle presence transitions from left side changes\n if (deltaA.size > 0) {\n for (const key of deltaA.getPresenceKeys()) {\n const before = this.#indexA.getConsolidatedMultiplicity(key)\n const deltaMult = deltaA.getConsolidatedMultiplicity(key)\n if (deltaMult === 0) continue\n const after = before + deltaMult\n\n // Skip transition handling if presence doesn't flip (both zero or both non-zero)\n // Note: Index updates happen later regardless - we're only skipping null-extension emissions here\n if ((before === 0) === (after === 0)) continue\n\n // Determine the type of transition:\n // - 0 → non-zero: Left becomes non-empty, right rows transition from unmatched to matched\n // → RETRACT previously emitted null-extended rows (emit with negative multiplicity)\n // - non-zero → 0: Left becomes empty, right rows transition from matched to unmatched\n // → EMIT new null-extended rows (emit with positive multiplicity)\n const transitioningToMatched = before === 0\n\n for (const [value, multiplicity] of this.#indexB.getIterator(key)) {\n if (multiplicity !== 0) {\n results.add(\n [key, [null, value]],\n transitioningToMatched ? -multiplicity : +multiplicity,\n )\n }\n }\n }\n }\n }\n}\n\n/**\n * Joins two input streams\n * @param other - The other stream to join with\n * @param type - The type of join to perform\n */\nexport function join<\n K,\n V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never,\n V2,\n T,\n>(\n other: IStreamBuilder<KeyValue<K, V2>>,\n type: JoinType = `inner`,\n): PipedOperator<T, KeyValue<K, [V1 | null, V2 | null]>> {\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<KeyValue<K, [V1 | null, V2 | null]>> => {\n if (stream.graph !== other.graph) {\n throw new Error(`Cannot join streams from different graphs`)\n }\n const output = new StreamBuilder<KeyValue<K, [V1 | null, V2 | null]>>(\n stream.graph,\n new DifferenceStreamWriter<KeyValue<K, [V1 | null, V2 | null]>>(),\n )\n const operator = new JoinOperator<K, V1, V2>(\n stream.graph.getNextOperatorId(),\n stream.connectReader() as DifferenceStreamReader<KeyValue<K, V1>>,\n other.connectReader(),\n output.writer,\n type,\n )\n stream.graph.addOperator(operator)\n return output\n }\n}\n\n/**\n * Joins two input streams (inner join)\n * @param other - The other stream to join with\n */\nexport function innerJoin<\n K,\n V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never,\n V2,\n T,\n>(\n other: IStreamBuilder<KeyValue<K, V2>>,\n): PipedOperator<T, KeyValue<K, [V1, V2]>> {\n return join(other, `inner`) as unknown as PipedOperator<\n T,\n KeyValue<K, [V1, V2]>\n >\n}\n\n/**\n * Joins two input streams (anti join)\n * @param other - The other stream to join with\n */\nexport function antiJoin<\n K,\n V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never,\n V2,\n T,\n>(\n other: IStreamBuilder<KeyValue<K, V2>>,\n): PipedOperator<T, KeyValue<K, [V1, null]>> {\n return join(other, `anti`) as unknown as PipedOperator<\n T,\n KeyValue<K, [V1, null]>\n >\n}\n\n/**\n * Joins two input streams (left join)\n * @param other - The other stream to join with\n */\nexport function leftJoin<\n K,\n V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never,\n V2,\n T,\n>(\n other: IStreamBuilder<KeyValue<K, V2>>,\n): PipedOperator<T, KeyValue<K, [V1, V2 | null]>> {\n return join(other, `left`) as unknown as PipedOperator<\n T,\n KeyValue<K, [V1, V2 | null]>\n >\n}\n\n/**\n * Joins two input streams (right join)\n * @param other - The other stream to join with\n */\nexport function rightJoin<\n K,\n V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never,\n V2,\n T,\n>(\n other: IStreamBuilder<KeyValue<K, V2>>,\n): PipedOperator<T, KeyValue<K, [V1 | null, V2]>> {\n return join(other, `right`) as unknown as PipedOperator<\n T,\n KeyValue<K, [V1 | null, V2]>\n >\n}\n\n/**\n * Joins two input streams (full join)\n * @param other - The other stream to join with\n */\nexport function fullJoin<\n K,\n V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never,\n V2,\n T,\n>(\n other: IStreamBuilder<KeyValue<K, V2>>,\n): PipedOperator<T, KeyValue<K, [V1 | null, V2 | null]>> {\n return join(other, `full`) as unknown as PipedOperator<\n T,\n KeyValue<K, [V1 | null, V2 | null]>\n >\n}\n"],"names":["BinaryOperator","Index","MultiSet","StreamBuilder","DifferenceStreamWriter"],"mappings":"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"}
1
+ {"version":3,"file":"join.cjs","sources":["../../../src/operators/join.ts"],"sourcesContent":["/**\n * # Direct Join Algorithms for Incremental View Maintenance\n *\n * High-performance join operations implementing all join types (inner, left, right, full, anti)\n * with minimal state and optimized performance.\n *\n * ## Algorithm\n *\n * For each tick, the algorithm processes incoming changes (deltas) and emits join results:\n *\n * 1. **Build deltas**: Create delta indexes from input messages using `Index.fromMultiSet()`\n * 2. **Inner results**: Emit `ΔA⋈B_old + A_old⋈ΔB + ΔA⋈ΔB` (matched pairs)\n * 3. **Outer results**: For unmatched rows, emit null-extended tuples:\n * - New unmatched rows from deltas (when opposite side empty)\n * - Presence transitions: when key goes `0→>0` (retract nulls) or `>0→0` (emit nulls)\n * 4. **Update state**: Append deltas to indexes (consolidated multiplicity tracking automatic)\n *\n * **Consolidated multiplicity tracking** enables O(1) presence checks instead of scanning index buckets.\n *\n * ## State\n *\n * **Indexes** store the actual data:\n * - `indexA: Index<K, V1>` - all left-side rows accumulated over time\n * - `indexB: Index<K, V2>` - all right-side rows accumulated over time\n *\n * **Consolidated multiplicity tracking** (built into Index):\n * - Each Index maintains sum of multiplicities per key internally\n * - Provides O(1) presence checks: `index.hasPresence(key)` and `index.getConsolidatedMultiplicity(key)`\n * - Avoids scanning entire index buckets just to check if key has any rows\n *\n * ## Join Types\n *\n * - **Inner**: Standard delta terms only\n * - **Outer**: Inner results + null-extended unmatched rows with transition handling\n * - **Anti**: Unmatched rows only (no inner results)\n *\n * ## Key Optimizations\n *\n * - **No temp copying**: Uses `(A⊎ΔA)⋈ΔB = A⋈ΔB ⊎ ΔA⋈ΔB` distributive property\n * - **Early-out checks**: Skip phases when no deltas present\n * - **Zero-entry pruning**: Keep maps compact, O(distinct keys) memory\n * - **Final presence logic**: Avoid emit→retract churn within same tick\n *\n * ## Correctness\n *\n * - **Ordering**: Pre-append snapshots for emissions, post-emit state updates\n * - **Presence**: Key matched iff mass ≠ 0, transitions trigger null handling\n * - **Bag semantics**: Proper multiplicity handling including negatives\n */\n\nimport { BinaryOperator, DifferenceStreamWriter } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport { MultiSet } from '../multiset.js'\nimport { Index } from '../indexes.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { IStreamBuilder, KeyValue, PipedOperator } from '../types.js'\n\n/**\n * Type of join to perform\n */\nexport type JoinType = `inner` | `left` | `right` | `full` | `anti`\n\n/**\n * Operator that joins two input streams using direct join algorithms\n */\nexport class JoinOperator<K, V1, V2> extends BinaryOperator<\n [K, V1] | [K, V2] | [K, [V1, V2]] | [K, [V1 | null, V2 | null]]\n> {\n #indexA = new Index<K, V1>()\n #indexB = new Index<K, V2>()\n #mode: JoinType\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<[K, V1]>,\n inputB: DifferenceStreamReader<[K, V2]>,\n output: DifferenceStreamWriter<any>,\n mode: JoinType = `inner`,\n ) {\n super(id, inputA, inputB, output)\n this.#mode = mode\n }\n\n run(): void {\n // Build deltas from input messages\n const deltaA = Index.fromMultiSets<K, V1>(\n this.inputAMessages() as Array<MultiSet<[K, V1]>>,\n )\n const deltaB = Index.fromMultiSets<K, V2>(\n this.inputBMessages() as Array<MultiSet<[K, V2]>>,\n )\n\n // Early-out if nothing changed\n if (deltaA.size === 0 && deltaB.size === 0) return\n\n const results = new MultiSet<any>()\n\n // Emit inner results (all modes except anti)\n if (this.#mode !== `anti`) {\n this.emitInnerResults(deltaA, deltaB, results)\n }\n\n // Emit left outer/anti results\n if (\n this.#mode === `left` ||\n this.#mode === `full` ||\n this.#mode === `anti`\n ) {\n this.emitLeftOuterResults(deltaA, deltaB, results)\n }\n\n // Emit right outer results\n if (this.#mode === `right` || this.#mode === `full`) {\n this.emitRightOuterResults(deltaA, deltaB, results)\n }\n\n // Update state and send results\n // IMPORTANT: All emissions use pre-append snapshots of indexA/indexB.\n // Now append ALL deltas to indices - this happens unconditionally for every key,\n // regardless of whether presence flipped. Consolidated multiplicity tracking is automatic.\n this.#indexA.append(deltaA)\n this.#indexB.append(deltaB)\n\n // Send results\n if (results.getInner().length > 0) {\n this.output.sendData(results)\n }\n }\n\n private emitInnerResults(\n deltaA: Index<K, V1>,\n deltaB: Index<K, V2>,\n results: MultiSet<any>,\n ): void {\n // Emit the three standard delta terms: ΔA⋈B_old, A_old⋈ΔB, ΔA⋈ΔB\n if (deltaA.size > 0) results.extend(deltaA.join(this.#indexB))\n if (deltaB.size > 0) results.extend(this.#indexA.join(deltaB))\n if (deltaA.size > 0 && deltaB.size > 0) results.extend(deltaA.join(deltaB))\n }\n\n private emitLeftOuterResults(\n deltaA: Index<K, V1>,\n deltaB: Index<K, V2>,\n results: MultiSet<any>,\n ): void {\n // Emit unmatched left rows from deltaA\n if (deltaA.size > 0) {\n for (const [key, valueIterator] of deltaA.entriesIterators()) {\n const currentMultiplicityB =\n this.#indexB.getConsolidatedMultiplicity(key)\n const deltaMultiplicityB = deltaB.getConsolidatedMultiplicity(key)\n const finalMultiplicityB = currentMultiplicityB + deltaMultiplicityB\n\n if (finalMultiplicityB === 0) {\n for (const [value, multiplicity] of valueIterator) {\n if (multiplicity !== 0) {\n results.add([key, [value, null]], multiplicity)\n }\n }\n }\n }\n }\n\n // Handle presence transitions from right side changes\n if (deltaB.size > 0) {\n for (const key of deltaB.getPresenceKeys()) {\n const before = this.#indexB.getConsolidatedMultiplicity(key)\n const deltaMult = deltaB.getConsolidatedMultiplicity(key)\n if (deltaMult === 0) continue\n const after = before + deltaMult\n\n // Skip transition handling if presence doesn't flip (both zero or both non-zero)\n // Note: Index updates happen later regardless - we're only skipping null-extension emissions here\n if ((before === 0) === (after === 0)) continue\n\n // Determine the type of transition:\n // - 0 → non-zero: Right becomes non-empty, left rows transition from unmatched to matched\n // → RETRACT previously emitted null-extended rows (emit with negative multiplicity)\n // - non-zero → 0: Right becomes empty, left rows transition from matched to unmatched\n // → EMIT new null-extended rows (emit with positive multiplicity)\n const transitioningToMatched = before === 0\n\n for (const [value, multiplicity] of this.#indexA.getIterator(key)) {\n if (multiplicity !== 0) {\n results.add(\n [key, [value, null]],\n transitioningToMatched ? -multiplicity : +multiplicity,\n )\n }\n }\n }\n }\n }\n\n private emitRightOuterResults(\n deltaA: Index<K, V1>,\n deltaB: Index<K, V2>,\n results: MultiSet<any>,\n ): void {\n // Emit unmatched right rows from deltaB\n if (deltaB.size > 0) {\n for (const [key, valueIterator] of deltaB.entriesIterators()) {\n const currentMultiplicityA =\n this.#indexA.getConsolidatedMultiplicity(key)\n const deltaMultiplicityA = deltaA.getConsolidatedMultiplicity(key)\n const finalMultiplicityA = currentMultiplicityA + deltaMultiplicityA\n\n if (finalMultiplicityA === 0) {\n for (const [value, multiplicity] of valueIterator) {\n if (multiplicity !== 0) {\n results.add([key, [null, value]], multiplicity)\n }\n }\n }\n }\n }\n\n // Handle presence transitions from left side changes\n if (deltaA.size > 0) {\n for (const key of deltaA.getPresenceKeys()) {\n const before = this.#indexA.getConsolidatedMultiplicity(key)\n const deltaMult = deltaA.getConsolidatedMultiplicity(key)\n if (deltaMult === 0) continue\n const after = before + deltaMult\n\n // Skip transition handling if presence doesn't flip (both zero or both non-zero)\n // Note: Index updates happen later regardless - we're only skipping null-extension emissions here\n if ((before === 0) === (after === 0)) continue\n\n // Determine the type of transition:\n // - 0 → non-zero: Left becomes non-empty, right rows transition from unmatched to matched\n // → RETRACT previously emitted null-extended rows (emit with negative multiplicity)\n // - non-zero → 0: Left becomes empty, right rows transition from matched to unmatched\n // → EMIT new null-extended rows (emit with positive multiplicity)\n const transitioningToMatched = before === 0\n\n for (const [value, multiplicity] of this.#indexB.getIterator(key)) {\n if (multiplicity !== 0) {\n results.add(\n [key, [null, value]],\n transitioningToMatched ? -multiplicity : +multiplicity,\n )\n }\n }\n }\n }\n }\n}\n\n/**\n * Joins two input streams\n * @param other - The other stream to join with\n * @param type - The type of join to perform\n */\nexport function join<\n K,\n V1 extends (T extends KeyValue<infer _KT, infer VT> ? VT : never),\n V2,\n T,\n>(\n other: IStreamBuilder<KeyValue<K, V2>>,\n type: JoinType = `inner`,\n): PipedOperator<T, KeyValue<K, [V1 | null, V2 | null]>> {\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<KeyValue<K, [V1 | null, V2 | null]>> => {\n if (stream.graph !== other.graph) {\n throw new Error(`Cannot join streams from different graphs`)\n }\n const output = new StreamBuilder<KeyValue<K, [V1 | null, V2 | null]>>(\n stream.graph,\n new DifferenceStreamWriter<KeyValue<K, [V1 | null, V2 | null]>>(),\n )\n const operator = new JoinOperator<K, V1, V2>(\n stream.graph.getNextOperatorId(),\n stream.connectReader() as DifferenceStreamReader<KeyValue<K, V1>>,\n other.connectReader(),\n output.writer,\n type,\n )\n stream.graph.addOperator(operator)\n return output\n }\n}\n"],"names":["BinaryOperator","Index","MultiSet","StreamBuilder","DifferenceStreamWriter"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { BinaryOperator, DifferenceStreamWriter, DifferenceStreamReader } from '../graph.js';
2
- import { IStreamBuilder, KeyValue, PipedOperator } from '../types.js';
1
+ import { BinaryOperator, DifferenceStreamWriter, DifferenceStreamReader } from "../graph.cjs";
2
+ import { IStreamBuilder, KeyValue, PipedOperator } from "../types.cjs";
3
3
  /**
4
4
  * Type of join to perform
5
5
  */
@@ -10,9 +10,30 @@ export type JoinType = `inner` | `left` | `right` | `full` | `anti`;
10
10
  export declare class JoinOperator<K, V1, V2> extends BinaryOperator<[
11
11
  K,
12
12
  V1
13
- ] | [K, V2] | [K, [V1, V2]] | [K, [V1 | null, V2 | null]]> {
13
+ ] | [
14
+ K,
15
+ V2
16
+ ] | [
17
+ K,
18
+ [
19
+ V1,
20
+ V2
21
+ ]
22
+ ] | [
23
+ K,
24
+ [
25
+ V1 | null,
26
+ V2 | null
27
+ ]
28
+ ]> {
14
29
  #private;
15
- constructor(id: number, inputA: DifferenceStreamReader<[K, V1]>, inputB: DifferenceStreamReader<[K, V2]>, output: DifferenceStreamWriter<any>, mode?: JoinType);
30
+ constructor(id: number, inputA: DifferenceStreamReader<[
31
+ K,
32
+ V1
33
+ ]>, inputB: DifferenceStreamReader<[
34
+ K,
35
+ V2
36
+ ]>, output: DifferenceStreamWriter<any>, mode?: JoinType);
16
37
  run(): void;
17
38
  private emitInnerResults;
18
39
  private emitLeftOuterResults;
@@ -23,29 +44,7 @@ export declare class JoinOperator<K, V1, V2> extends BinaryOperator<[
23
44
  * @param other - The other stream to join with
24
45
  * @param type - The type of join to perform
25
46
  */
26
- export declare function join<K, V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never, V2, T>(other: IStreamBuilder<KeyValue<K, V2>>, type?: JoinType): PipedOperator<T, KeyValue<K, [V1 | null, V2 | null]>>;
27
- /**
28
- * Joins two input streams (inner join)
29
- * @param other - The other stream to join with
30
- */
31
- export declare function innerJoin<K, V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never, V2, T>(other: IStreamBuilder<KeyValue<K, V2>>): PipedOperator<T, KeyValue<K, [V1, V2]>>;
32
- /**
33
- * Joins two input streams (anti join)
34
- * @param other - The other stream to join with
35
- */
36
- export declare function antiJoin<K, V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never, V2, T>(other: IStreamBuilder<KeyValue<K, V2>>): PipedOperator<T, KeyValue<K, [V1, null]>>;
37
- /**
38
- * Joins two input streams (left join)
39
- * @param other - The other stream to join with
40
- */
41
- export declare function leftJoin<K, V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never, V2, T>(other: IStreamBuilder<KeyValue<K, V2>>): PipedOperator<T, KeyValue<K, [V1, V2 | null]>>;
42
- /**
43
- * Joins two input streams (right join)
44
- * @param other - The other stream to join with
45
- */
46
- export declare function rightJoin<K, V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never, V2, T>(other: IStreamBuilder<KeyValue<K, V2>>): PipedOperator<T, KeyValue<K, [V1 | null, V2]>>;
47
- /**
48
- * Joins two input streams (full join)
49
- * @param other - The other stream to join with
50
- */
51
- export declare function fullJoin<K, V1 extends T extends KeyValue<infer _KT, infer VT> ? VT : never, V2, T>(other: IStreamBuilder<KeyValue<K, V2>>): PipedOperator<T, KeyValue<K, [V1 | null, V2 | null]>>;
47
+ export declare function join<K, V1 extends (T extends KeyValue<infer _KT, infer VT> ? VT : never), V2, T>(other: IStreamBuilder<KeyValue<K, V2>>, type?: JoinType): PipedOperator<T, KeyValue<K, [
48
+ V1 | null,
49
+ V2 | null
50
+ ]>>;
@@ -1,6 +1,6 @@
1
- import { DifferenceStreamWriter, LinearUnaryOperator, DifferenceStreamReader } from '../graph.js';
2
- import { PipedOperator } from '../types.js';
3
- import { MultiSet } from '../multiset.js';
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 applies a function to each element in the input stream
6
6
  */
@@ -1,73 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const topK = require("./topK.cjs");
4
3
  const topKWithFractionalIndex = require("./topKWithFractionalIndex.cjs");
5
- const map = require("./map.cjs");
6
- const join = require("./join.cjs");
7
4
  const consolidate = require("./consolidate.cjs");
8
- function orderBy(valueExtractor, options) {
9
- const limit = options?.limit ?? Infinity;
10
- const offset = options?.offset ?? 0;
11
- const comparator = options?.comparator ?? ((a, b) => {
12
- if (a === b) return 0;
13
- if (a < b) return -1;
14
- return 1;
15
- });
16
- return (stream) => {
17
- return stream.pipe(
18
- map.map(
19
- ([key, value]) => [
20
- null,
21
- [
22
- key,
23
- valueExtractor(
24
- value
25
- )
26
- ]
27
- ]
28
- ),
29
- topK.topK((a, b) => comparator(a[1], b[1]), { limit, offset }),
30
- map.map(([_, [key]]) => [key, null]),
31
- join.innerJoin(stream),
32
- map.map(([key, value]) => {
33
- return [key, value[1]];
34
- }),
35
- consolidate.consolidate()
36
- );
37
- };
38
- }
39
- function orderByWithIndex(valueExtractor, options) {
40
- const limit = options?.limit ?? Infinity;
41
- const offset = options?.offset ?? 0;
42
- const comparator = options?.comparator ?? ((a, b) => {
43
- if (a === b) return 0;
44
- if (a < b) return -1;
45
- return 1;
46
- });
47
- return (stream) => {
48
- return stream.pipe(
49
- map.map(
50
- ([key, value]) => [
51
- null,
52
- [
53
- key,
54
- valueExtractor(
55
- value
56
- )
57
- ]
58
- ]
59
- ),
60
- topK.topKWithIndex((a, b) => comparator(a[1], b[1]), { limit, offset }),
61
- map.map(([_, [[key], index]]) => [key, index]),
62
- join.innerJoin(stream),
63
- map.map(([key, [index, value]]) => {
64
- return [key, [value, index]];
65
- }),
66
- consolidate.consolidate()
67
- );
68
- };
69
- }
70
- function orderByWithFractionalIndexBase(topKFunction, valueExtractor, options) {
5
+ function orderByWithFractionalIndex(valueExtractor, options) {
71
6
  const limit = options?.limit ?? Infinity;
72
7
  const offset = options?.offset ?? 0;
73
8
  const setSizeCallback = options?.setSizeCallback;
@@ -79,7 +14,7 @@ function orderByWithFractionalIndexBase(topKFunction, valueExtractor, options) {
79
14
  });
80
15
  return (stream) => {
81
16
  return stream.pipe(
82
- topKFunction(
17
+ topKWithFractionalIndex.topKWithFractionalIndex(
83
18
  (a, b) => comparator(valueExtractor(a), valueExtractor(b)),
84
19
  {
85
20
  limit,
@@ -92,15 +27,5 @@ function orderByWithFractionalIndexBase(topKFunction, valueExtractor, options) {
92
27
  );
93
28
  };
94
29
  }
95
- function orderByWithFractionalIndex(valueExtractor, options) {
96
- return orderByWithFractionalIndexBase(
97
- topKWithFractionalIndex.topKWithFractionalIndex,
98
- valueExtractor,
99
- options
100
- );
101
- }
102
- exports.orderBy = orderBy;
103
30
  exports.orderByWithFractionalIndex = orderByWithFractionalIndex;
104
- exports.orderByWithFractionalIndexBase = orderByWithFractionalIndexBase;
105
- exports.orderByWithIndex = orderByWithIndex;
106
31
  //# sourceMappingURL=orderBy.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"orderBy.cjs","sources":["../../../src/operators/orderBy.ts"],"sourcesContent":["import { topK, topKWithIndex } from './topK.js'\nimport { topKWithFractionalIndex } from './topKWithFractionalIndex.js'\nimport { map } from './map.js'\nimport { innerJoin } from './join.js'\nimport { consolidate } from './consolidate.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\nexport interface OrderByOptions<Ve> {\n comparator?: (a: Ve, b: Ve) => number\n limit?: number\n offset?: number\n}\n\ntype OrderByWithFractionalIndexOptions<Ve> = OrderByOptions<Ve> & {\n setSizeCallback?: (getSize: () => number) => void\n setWindowFn?: (\n windowFn: (options: { offset?: number; limit?: number }) => void,\n ) => void\n}\n\n/**\n * Orders the elements and limits the number of results, with optional offset\n * This requires a keyed stream, and uses the `topK` operator to order all the elements.\n *\n * @param valueExtractor - A function that extracts the value to order by from the element\n * @param options - An optional object containing comparator, limit and offset properties\n * @returns A piped operator that orders the elements and limits the number of results\n */\nexport function orderBy<T extends KeyValue<unknown, unknown>, Ve = unknown>(\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByOptions<Ve>,\n) {\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n const comparator =\n options?.comparator ??\n ((a, b) => {\n // Default to JS like ordering\n if (a === b) return 0\n if (a < b) return -1\n return 1\n })\n\n return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {\n type KeyType = T extends KeyValue<infer K, unknown> ? K : never\n\n return stream.pipe(\n map(\n ([key, value]) =>\n [\n null,\n [\n key,\n valueExtractor(\n value as T extends KeyValue<unknown, infer V> ? V : never,\n ),\n ],\n ] as KeyValue<null, [KeyType, Ve]>,\n ),\n topK((a, b) => comparator(a[1], b[1]), { limit, offset }),\n map(([_, [key]]) => [key, null] as KeyValue<KeyType, null>),\n innerJoin(stream),\n map(([key, value]) => {\n return [key, value[1]] as T\n }),\n consolidate(),\n )\n }\n}\n\n/**\n * Orders the elements and limits the number of results, with optional offset and\n * annotates the value with the index.\n * This requires a keyed stream, and uses the `topKWithIndex` operator to order all the elements.\n *\n * @param valueExtractor - A function that extracts the value to order by from the element\n * @param options - An optional object containing comparator, limit and offset properties\n * @returns A piped operator that orders the elements and limits the number of results\n */\nexport function orderByWithIndex<\n T extends KeyValue<unknown, unknown>,\n Ve = unknown,\n>(\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByOptions<Ve>,\n) {\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n const comparator =\n options?.comparator ??\n ((a, b) => {\n // Default to JS like ordering\n if (a === b) return 0\n if (a < b) return -1\n return 1\n })\n\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<\n KeyValue<\n T extends KeyValue<infer K, unknown> ? K : never,\n [T extends KeyValue<unknown, infer V> ? V : never, number]\n >\n > => {\n type KeyType = T extends KeyValue<infer K, unknown> ? K : never\n type ValueType = T extends KeyValue<unknown, infer V> ? V : never\n\n return stream.pipe(\n map(\n ([key, value]) =>\n [\n null,\n [\n key,\n valueExtractor(\n value as T extends KeyValue<unknown, infer V> ? V : never,\n ),\n ],\n ] as KeyValue<null, [KeyType, Ve]>,\n ),\n topKWithIndex((a, b) => comparator(a[1], b[1]), { limit, offset }),\n map(([_, [[key], index]]) => [key, index] as KeyValue<KeyType, number>),\n innerJoin(stream),\n map(([key, [index, value]]) => {\n return [key, [value, index]] as KeyValue<KeyType, [ValueType, number]>\n }),\n consolidate(),\n )\n }\n}\n\nexport function orderByWithFractionalIndexBase<\n T extends KeyValue<unknown, unknown>,\n Ve = unknown,\n>(\n topKFunction: typeof topKWithFractionalIndex,\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByWithFractionalIndexOptions<Ve>,\n) {\n type KeyType = T extends KeyValue<infer K, unknown> ? K : never\n type ValueType = T extends KeyValue<unknown, infer V> ? V : never\n\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n const setSizeCallback = options?.setSizeCallback\n const setWindowFn = options?.setWindowFn\n const comparator =\n options?.comparator ??\n ((a, b) => {\n // Default to JS like ordering\n if (a === b) return 0\n if (a < b) return -1\n return 1\n })\n\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<[KeyType, [ValueType, string]]> => {\n return stream.pipe(\n topKFunction(\n (a: ValueType, b: ValueType) =>\n comparator(valueExtractor(a), valueExtractor(b)),\n {\n limit,\n offset,\n setSizeCallback,\n setWindowFn,\n },\n ),\n consolidate(),\n )\n }\n}\n\n/**\n * Orders the elements and limits the number of results, with optional offset and\n * annotates the value with a fractional index.\n * This requires a keyed stream, and uses the `topKWithFractionalIndex` operator to order all the elements.\n *\n * @param valueExtractor - A function that extracts the value to order by from the element\n * @param options - An optional object containing comparator, limit and offset properties\n * @returns A piped operator that orders the elements and limits the number of results\n */\nexport function orderByWithFractionalIndex<\n T extends KeyValue<unknown, unknown>,\n Ve = unknown,\n>(\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByWithFractionalIndexOptions<Ve>,\n) {\n return orderByWithFractionalIndexBase(\n topKWithFractionalIndex,\n valueExtractor,\n options,\n )\n}\n"],"names":["map","topK","innerJoin","consolidate","topKWithIndex","topKWithFractionalIndex"],"mappings":"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"}
1
+ {"version":3,"file":"orderBy.cjs","sources":["../../../src/operators/orderBy.ts"],"sourcesContent":["import { topKWithFractionalIndex } from './topKWithFractionalIndex.js'\nimport { consolidate } from './consolidate.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\nimport type { SetSizeCallback } from './topKWithFractionalIndex.js'\n\nexport interface OrderByOptions<Ve> {\n comparator?: (a: Ve, b: Ve) => number\n limit?: number\n offset?: number\n}\n\ntype OrderByWithFractionalIndexOptions<Ve> = OrderByOptions<Ve> & {\n setSizeCallback?: SetSizeCallback\n setWindowFn?: (\n windowFn: (options: { offset?: number; limit?: number }) => void,\n ) => void\n}\n\n/**\n * Orders the elements and limits the number of results, with optional offset and\n * annotates the value with a fractional index.\n * This requires a keyed stream, and uses the `topKWithFractionalIndex` operator to order all the elements.\n *\n * @param valueExtractor - A function that extracts the value to order by from the element\n * @param options - An optional object containing comparator, limit and offset properties\n * @returns A piped operator that orders the elements and limits the number of results\n */\nexport function orderByWithFractionalIndex<\n T extends KeyValue<unknown, unknown>,\n Ve = unknown,\n>(\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByWithFractionalIndexOptions<Ve>,\n) {\n type KeyType = T extends KeyValue<infer K, unknown> ? K : never\n type ValueType = T extends KeyValue<unknown, infer V> ? V : never\n\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n const setSizeCallback = options?.setSizeCallback\n const setWindowFn = options?.setWindowFn\n const comparator =\n options?.comparator ??\n ((a, b) => {\n // Default to JS like ordering\n if (a === b) return 0\n if (a < b) return -1\n return 1\n })\n\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<[KeyType, [ValueType, string]]> => {\n return stream.pipe(\n topKWithFractionalIndex(\n (a: ValueType, b: ValueType) =>\n comparator(valueExtractor(a), valueExtractor(b)),\n {\n limit,\n offset,\n setSizeCallback,\n setWindowFn,\n },\n ),\n consolidate(),\n )\n }\n}\n"],"names":["topKWithFractionalIndex","consolidate"],"mappings":";;;;AA2BO,SAAS,2BAId,gBAGA,SACA;AAIA,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,kBAAkB,SAAS;AACjC,QAAM,cAAc,SAAS;AAC7B,QAAM,aACJ,SAAS,eACR,CAAC,GAAG,MAAM;AAET,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,IAAI,EAAG,QAAO;AAClB,WAAO;AAAA,EACT;AAEF,SAAO,CACL,WACmD;AACnD,WAAO,OAAO;AAAA,MACZA,wBAAAA;AAAAA,QACE,CAAC,GAAc,MACb,WAAW,eAAe,CAAC,GAAG,eAAe,CAAC,CAAC;AAAA,QACjD;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAAA,MACF;AAAA,MAEFC,YAAAA,YAAA;AAAA,IAAY;AAAA,EAEhB;AACF;;"}
@@ -1,37 +1,17 @@
1
- import { topKWithFractionalIndex } from './topKWithFractionalIndex.js';
2
- import { IStreamBuilder, KeyValue } from '../types.js';
1
+ import { IStreamBuilder, KeyValue } from "../types.cjs";
2
+ import { SetSizeCallback } from "./topKWithFractionalIndex.cjs";
3
3
  export interface OrderByOptions<Ve> {
4
4
  comparator?: (a: Ve, b: Ve) => number;
5
5
  limit?: number;
6
6
  offset?: number;
7
7
  }
8
8
  type OrderByWithFractionalIndexOptions<Ve> = OrderByOptions<Ve> & {
9
- setSizeCallback?: (getSize: () => number) => void;
9
+ setSizeCallback?: SetSizeCallback;
10
10
  setWindowFn?: (windowFn: (options: {
11
11
  offset?: number;
12
12
  limit?: number;
13
13
  }) => void) => void;
14
14
  };
15
- /**
16
- * Orders the elements and limits the number of results, with optional offset
17
- * This requires a keyed stream, and uses the `topK` operator to order all the elements.
18
- *
19
- * @param valueExtractor - A function that extracts the value to order by from the element
20
- * @param options - An optional object containing comparator, limit and offset properties
21
- * @returns A piped operator that orders the elements and limits the number of results
22
- */
23
- export declare function orderBy<T extends KeyValue<unknown, unknown>, Ve = unknown>(valueExtractor: (value: T extends KeyValue<unknown, infer V> ? V : never) => Ve, options?: OrderByOptions<Ve>): (stream: IStreamBuilder<T>) => IStreamBuilder<T>;
24
- /**
25
- * Orders the elements and limits the number of results, with optional offset and
26
- * annotates the value with the index.
27
- * This requires a keyed stream, and uses the `topKWithIndex` operator to order all the elements.
28
- *
29
- * @param valueExtractor - A function that extracts the value to order by from the element
30
- * @param options - An optional object containing comparator, limit and offset properties
31
- * @returns A piped operator that orders the elements and limits the number of results
32
- */
33
- export declare function orderByWithIndex<T extends KeyValue<unknown, unknown>, Ve = unknown>(valueExtractor: (value: T extends KeyValue<unknown, infer V> ? V : never) => Ve, options?: OrderByOptions<Ve>): (stream: IStreamBuilder<T>) => IStreamBuilder<KeyValue<T extends KeyValue<infer K, unknown> ? K : never, [T extends KeyValue<unknown, infer V> ? V : never, number]>>;
34
- export declare function orderByWithFractionalIndexBase<T extends KeyValue<unknown, unknown>, Ve = unknown>(topKFunction: typeof topKWithFractionalIndex, valueExtractor: (value: T extends KeyValue<unknown, infer V> ? V : never) => Ve, options?: OrderByWithFractionalIndexOptions<Ve>): (stream: IStreamBuilder<T>) => IStreamBuilder<[T extends KeyValue<infer K, unknown> ? K : never, [T extends KeyValue<unknown, infer V> ? V : never, string]]>;
35
15
  /**
36
16
  * Orders the elements and limits the number of results, with optional offset and
37
17
  * annotates the value with a fractional index.
@@ -41,5 +21,11 @@ export declare function orderByWithFractionalIndexBase<T extends KeyValue<unknow
41
21
  * @param options - An optional object containing comparator, limit and offset properties
42
22
  * @returns A piped operator that orders the elements and limits the number of results
43
23
  */
44
- export declare function orderByWithFractionalIndex<T extends KeyValue<unknown, unknown>, Ve = unknown>(valueExtractor: (value: T extends KeyValue<unknown, infer V> ? V : never) => Ve, options?: OrderByWithFractionalIndexOptions<Ve>): (stream: IStreamBuilder<T>) => IStreamBuilder<[T extends KeyValue<infer K, unknown> ? K : never, [T extends KeyValue<unknown, infer V> ? V : never, string]]>;
24
+ export declare function orderByWithFractionalIndex<T extends KeyValue<unknown, unknown>, Ve = unknown>(valueExtractor: (value: T extends KeyValue<unknown, infer V> ? V : never) => Ve, options?: OrderByWithFractionalIndexOptions<Ve>): (stream: IStreamBuilder<T>) => IStreamBuilder<[
25
+ T extends KeyValue<infer K, unknown> ? K : never,
26
+ [
27
+ T extends KeyValue<unknown, infer V> ? V : never,
28
+ string
29
+ ]
30
+ ]>;
45
31
  export {};
@@ -1,6 +1,6 @@
1
- import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from '../graph.js';
2
- import { PipedOperator } from '../types.js';
3
- import { MultiSet } from '../multiset.js';
1
+ import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from "../graph.cjs";
2
+ import { PipedOperator } from "../types.cjs";
3
+ import { MultiSet } from "../multiset.cjs";
4
4
  /**
5
5
  * Operator that outputs the messages in the stream
6
6
  */
@@ -1,4 +1,4 @@
1
- import { PipedOperator } from '../types.js';
1
+ import { PipedOperator } from "../types.cjs";
2
2
  export declare function pipe<T, O>(o1: PipedOperator<T, O>): PipedOperator<T, O>;
3
3
  export declare function pipe<T, T2, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, O>): PipedOperator<T, O>;
4
4
  export declare function pipe<T, T2, T3, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, O>): PipedOperator<T, O>;
@@ -4,12 +4,20 @@ const graph = require("../graph.cjs");
4
4
  const d2 = require("../d2.cjs");
5
5
  const multiset = require("../multiset.cjs");
6
6
  const indexes = require("../indexes.cjs");
7
+ function sameOutput(left, right) {
8
+ return left === right || left !== left && right !== right;
9
+ }
10
+ function asMapKey(value) {
11
+ return value === 0 ? 0 : value;
12
+ }
7
13
  class ReduceOperator extends graph.UnaryOperator {
8
- #index = new indexes.Index();
9
- #indexOut = new indexes.Index();
14
+ // Reduce never reads presence, so its indexes skip that bookkeeping.
15
+ #index;
16
+ #indexOut = new indexes.Index({ trackPresence: false });
10
17
  #f;
11
- constructor(id, inputA, output, f) {
18
+ constructor(id, inputA, output, f, options) {
12
19
  super(id, inputA, output);
20
+ this.#index = new indexes.Index({ ...options, trackPresence: false });
13
21
  this.#f = f;
14
22
  }
15
23
  run() {
@@ -26,6 +34,28 @@ class ReduceOperator extends graph.UnaryOperator {
26
34
  const curr = this.#index.get(key);
27
35
  const currOut = this.#indexOut.get(key);
28
36
  const out = this.#f(curr);
37
+ if (out.length <= 1 && currOut.length <= 1) {
38
+ const before = currOut[0];
39
+ const after = out[0];
40
+ const value = after === void 0 ? void 0 : asMapKey(after[0]);
41
+ if (before !== void 0 && after !== void 0 && sameOutput(before[0], value)) {
42
+ const delta = after[1] - before[1];
43
+ if (delta !== 0) {
44
+ result.push([[key, before[0]], delta]);
45
+ this.#indexOut.addValue(key, [before[0], delta]);
46
+ }
47
+ continue;
48
+ }
49
+ if (before !== void 0) {
50
+ result.push([[key, before[0]], -before[1]]);
51
+ this.#indexOut.addValue(key, [before[0], -before[1]]);
52
+ }
53
+ if (after !== void 0 && after[1] !== 0) {
54
+ result.push([[key, value], after[1]]);
55
+ this.#indexOut.addValue(key, [value, after[1]]);
56
+ }
57
+ continue;
58
+ }
29
59
  const newOutputMap = /* @__PURE__ */ new Map();
30
60
  const oldOutputMap = /* @__PURE__ */ new Map();
31
61
  for (const [value, multiplicity] of out) {
@@ -66,7 +96,7 @@ class ReduceOperator extends graph.UnaryOperator {
66
96
  }
67
97
  }
68
98
  }
69
- function reduce(f) {
99
+ function reduce(f, options) {
70
100
  return (stream) => {
71
101
  const output = new d2.StreamBuilder(
72
102
  stream.graph,
@@ -76,7 +106,8 @@ function reduce(f) {
76
106
  stream.graph.getNextOperatorId(),
77
107
  stream.connectReader(),
78
108
  output.writer,
79
- f
109
+ f,
110
+ options
80
111
  );
81
112
  stream.graph.addOperator(operator);
82
113
  return output;
@@ -1 +1 @@
1
- {"version":3,"file":"reduce.cjs","sources":["../../../src/operators/reduce.ts"],"sourcesContent":["import { DifferenceStreamWriter, UnaryOperator } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport { MultiSet } from '../multiset.js'\nimport { Index } from '../indexes.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\n/**\n * Base operator for reduction operations (version-free)\n */\nexport class ReduceOperator<K, V1, V2> extends UnaryOperator<[K, V1], [K, V2]> {\n #index = new Index<K, V1>()\n #indexOut = new Index<K, V2>()\n #f: (values: Array<[V1, number]>) => Array<[V2, number]>\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<[K, V1]>,\n output: DifferenceStreamWriter<[K, V2]>,\n f: (values: Array<[V1, number]>) => Array<[V2, number]>,\n ) {\n super(id, inputA, output)\n this.#f = f\n }\n\n run(): void {\n // Collect all input messages and update the index\n const keysTodo = new Set<K>()\n for (const message of this.inputMessages()) {\n for (const [item, multiplicity] of message.getInner()) {\n const [key, value] = item\n this.#index.addValue(key, [value, multiplicity])\n keysTodo.add(key)\n }\n }\n\n // For each key, compute the reduction and delta\n const result: Array<[[K, V2], number]> = []\n for (const key of keysTodo) {\n const curr = this.#index.get(key)\n const currOut = this.#indexOut.get(key)\n const out = this.#f(curr)\n\n // Create maps for current and previous outputs using values directly as keys\n const newOutputMap = new Map<V2, number>()\n const oldOutputMap = new Map<V2, number>()\n\n // Process new output\n for (const [value, multiplicity] of out) {\n const existing = newOutputMap.get(value) ?? 0\n newOutputMap.set(value, existing + multiplicity)\n }\n\n // Process previous output\n for (const [value, multiplicity] of currOut) {\n const existing = oldOutputMap.get(value) ?? 0\n oldOutputMap.set(value, existing + multiplicity)\n }\n\n // First, emit removals for old values that are no longer present\n for (const [value, multiplicity] of oldOutputMap) {\n if (!newOutputMap.has(value)) {\n // Remove the old value entirely\n result.push([[key, value], -multiplicity])\n this.#indexOut.addValue(key, [value, -multiplicity])\n }\n }\n\n // Then, emit additions for new values that are not present in old\n for (const [value, multiplicity] of newOutputMap) {\n if (!oldOutputMap.has(value)) {\n // Add the new value only if it has non-zero multiplicity\n if (multiplicity !== 0) {\n result.push([[key, value], multiplicity])\n this.#indexOut.addValue(key, [value, multiplicity])\n }\n }\n }\n\n // Finally, emit multiplicity changes for values that were present and are still present\n for (const [value, newMultiplicity] of newOutputMap) {\n const oldMultiplicity = oldOutputMap.get(value)\n if (oldMultiplicity !== undefined) {\n const delta = newMultiplicity - oldMultiplicity\n // Only emit actual changes, i.e. non-zero deltas\n if (delta !== 0) {\n result.push([[key, value], delta])\n this.#indexOut.addValue(key, [value, delta])\n }\n }\n }\n }\n\n if (result.length > 0) {\n this.output.sendData(new MultiSet(result))\n }\n }\n}\n\n/**\n * Reduces the elements in the stream by key (version-free)\n */\nexport function reduce<\n KType extends T extends KeyValue<infer K, infer _V> ? K : never,\n V1Type extends T extends KeyValue<KType, infer V> ? V : never,\n R,\n T,\n>(f: (values: Array<[V1Type, number]>) => Array<[R, number]>) {\n return (stream: IStreamBuilder<T>): IStreamBuilder<KeyValue<KType, R>> => {\n const output = new StreamBuilder<KeyValue<KType, R>>(\n stream.graph,\n new DifferenceStreamWriter<KeyValue<KType, R>>(),\n )\n const operator = new ReduceOperator<KType, V1Type, R>(\n stream.graph.getNextOperatorId(),\n stream.connectReader() as DifferenceStreamReader<KeyValue<KType, V1Type>>,\n output.writer,\n f,\n )\n stream.graph.addOperator(operator)\n return output\n }\n}\n"],"names":["UnaryOperator","Index","MultiSet","StreamBuilder","DifferenceStreamWriter"],"mappings":";;;;;;AAUO,MAAM,uBAAkCA,MAAAA,cAAgC;AAAA,EAC7E,SAAS,IAAIC,QAAAA,MAAA;AAAA,EACb,YAAY,IAAIA,QAAAA,MAAA;AAAA,EAChB;AAAA,EAEA,YACE,IACA,QACA,QACA,GACA;AACA,UAAM,IAAI,QAAQ,MAAM;AACxB,SAAK,KAAK;AAAA,EACZ;AAAA,EAEA,MAAY;AAEV,UAAM,+BAAe,IAAA;AACrB,eAAW,WAAW,KAAK,iBAAiB;AAC1C,iBAAW,CAAC,MAAM,YAAY,KAAK,QAAQ,YAAY;AACrD,cAAM,CAAC,KAAK,KAAK,IAAI;AACrB,aAAK,OAAO,SAAS,KAAK,CAAC,OAAO,YAAY,CAAC;AAC/C,iBAAS,IAAI,GAAG;AAAA,MAClB;AAAA,IACF;AAGA,UAAM,SAAmC,CAAA;AACzC,eAAW,OAAO,UAAU;AAC1B,YAAM,OAAO,KAAK,OAAO,IAAI,GAAG;AAChC,YAAM,UAAU,KAAK,UAAU,IAAI,GAAG;AACtC,YAAM,MAAM,KAAK,GAAG,IAAI;AAGxB,YAAM,mCAAmB,IAAA;AACzB,YAAM,mCAAmB,IAAA;AAGzB,iBAAW,CAAC,OAAO,YAAY,KAAK,KAAK;AACvC,cAAM,WAAW,aAAa,IAAI,KAAK,KAAK;AAC5C,qBAAa,IAAI,OAAO,WAAW,YAAY;AAAA,MACjD;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,SAAS;AAC3C,cAAM,WAAW,aAAa,IAAI,KAAK,KAAK;AAC5C,qBAAa,IAAI,OAAO,WAAW,YAAY;AAAA,MACjD;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,cAAc;AAChD,YAAI,CAAC,aAAa,IAAI,KAAK,GAAG;AAE5B,iBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,YAAY,CAAC;AACzC,eAAK,UAAU,SAAS,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;AAAA,QACrD;AAAA,MACF;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,cAAc;AAChD,YAAI,CAAC,aAAa,IAAI,KAAK,GAAG;AAE5B,cAAI,iBAAiB,GAAG;AACtB,mBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,YAAY,CAAC;AACxC,iBAAK,UAAU,SAAS,KAAK,CAAC,OAAO,YAAY,CAAC;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,CAAC,OAAO,eAAe,KAAK,cAAc;AACnD,cAAM,kBAAkB,aAAa,IAAI,KAAK;AAC9C,YAAI,oBAAoB,QAAW;AACjC,gBAAM,QAAQ,kBAAkB;AAEhC,cAAI,UAAU,GAAG;AACf,mBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,KAAK,CAAC;AACjC,iBAAK,UAAU,SAAS,KAAK,CAAC,OAAO,KAAK,CAAC;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,WAAK,OAAO,SAAS,IAAIC,SAAAA,SAAS,MAAM,CAAC;AAAA,IAC3C;AAAA,EACF;AACF;AAKO,SAAS,OAKd,GAA4D;AAC5D,SAAO,CAAC,WAAkE;AACxE,UAAM,SAAS,IAAIC,GAAAA;AAAAA,MACjB,OAAO;AAAA,MACP,IAAIC,MAAAA,uBAAA;AAAA,IAA2C;AAEjD,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,OAAO;AAAA,MACP;AAAA,IAAA;AAEF,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO;AAAA,EACT;AACF;;;"}
1
+ {"version":3,"file":"reduce.cjs","sources":["../../../src/operators/reduce.ts"],"sourcesContent":["import { DifferenceStreamWriter, UnaryOperator } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport { MultiSet } from '../multiset.js'\nimport { Index } from '../indexes.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\n/** The key identity of a Map: SameValueZero, so NaN equals NaN. */\nfunction sameOutput(left: unknown, right: unknown): boolean {\n return left === right || (left !== left && right !== right)\n}\n\n/** The value a Map stores as a key: -0 becomes 0. */\nfunction asMapKey<T>(value: T): T {\n return (value === 0 ? 0 : value) as T\n}\n\n/**\n * Base operator for reduction operations (version-free)\n */\nexport class ReduceOperator<K, V1, V2> extends UnaryOperator<[K, V1], [K, V2]> {\n // Reduce never reads presence, so its indexes skip that bookkeeping.\n #index: Index<K, V1>\n #indexOut = new Index<K, V2>({ trackPresence: false })\n #f: (values: Array<[V1, number]>) => Array<[V2, number]>\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<[K, V1]>,\n output: DifferenceStreamWriter<[K, V2]>,\n f: (values: Array<[V1, number]>) => Array<[V2, number]>,\n options?: ReduceOptions,\n ) {\n super(id, inputA, output)\n this.#index = new Index<K, V1>({ ...options, trackPresence: false })\n this.#f = f\n }\n\n run(): void {\n // Collect all input messages and update the index\n const keysTodo = new Set<K>()\n for (const message of this.inputMessages()) {\n for (const [item, multiplicity] of message.getInner()) {\n const [key, value] = item\n this.#index.addValue(key, [value, multiplicity])\n keysTodo.add(key)\n }\n }\n\n // For each key, compute the reduction and delta\n const result: Array<[[K, V2], number]> = []\n for (const key of keysTodo) {\n const curr = this.#index.get(key)\n const currOut = this.#indexOut.get(key)\n const out = this.#f(curr)\n\n // Most keys have at most one output before and after. Compare the two\n // directly, by the SameValueZero identity the maps below use.\n if (out.length <= 1 && currOut.length <= 1) {\n // Earlier outputs were stored as Map keys, so only the new one needs it.\n const before = currOut[0]\n const after = out[0]\n const value = after === undefined ? undefined : asMapKey(after[0])\n if (\n before !== undefined &&\n after !== undefined &&\n sameOutput(before[0], value)\n ) {\n const delta = after[1] - before[1]\n if (delta !== 0) {\n result.push([[key, before[0]], delta])\n this.#indexOut.addValue(key, [before[0], delta])\n }\n continue\n }\n if (before !== undefined) {\n result.push([[key, before[0]], -before[1]])\n this.#indexOut.addValue(key, [before[0], -before[1]])\n }\n if (after !== undefined && after[1] !== 0) {\n result.push([[key, value as V2], after[1]])\n this.#indexOut.addValue(key, [value as V2, after[1]])\n }\n continue\n }\n\n // Create maps for current and previous outputs using values directly as keys\n const newOutputMap = new Map<V2, number>()\n const oldOutputMap = new Map<V2, number>()\n\n // Process new output\n for (const [value, multiplicity] of out) {\n const existing = newOutputMap.get(value) ?? 0\n newOutputMap.set(value, existing + multiplicity)\n }\n\n // Process previous output\n for (const [value, multiplicity] of currOut) {\n const existing = oldOutputMap.get(value) ?? 0\n oldOutputMap.set(value, existing + multiplicity)\n }\n\n // First, emit removals for old values that are no longer present\n for (const [value, multiplicity] of oldOutputMap) {\n if (!newOutputMap.has(value)) {\n // Remove the old value entirely\n result.push([[key, value], -multiplicity])\n this.#indexOut.addValue(key, [value, -multiplicity])\n }\n }\n\n // Then, emit additions for new values that are not present in old\n for (const [value, multiplicity] of newOutputMap) {\n if (!oldOutputMap.has(value)) {\n // Add the new value only if it has non-zero multiplicity\n if (multiplicity !== 0) {\n result.push([[key, value], multiplicity])\n this.#indexOut.addValue(key, [value, multiplicity])\n }\n }\n }\n\n // Finally, emit multiplicity changes for values that were present and are still present\n for (const [value, newMultiplicity] of newOutputMap) {\n const oldMultiplicity = oldOutputMap.get(value)\n if (oldMultiplicity !== undefined) {\n const delta = newMultiplicity - oldMultiplicity\n // Only emit actual changes, i.e. non-zero deltas\n if (delta !== 0) {\n result.push([[key, value], delta])\n this.#indexOut.addValue(key, [value, delta])\n }\n }\n }\n }\n\n if (result.length > 0) {\n this.output.sendData(new MultiSet(result))\n }\n }\n}\n\n/** @internal groupBy's option; its producer must keep the identity exact. */\nexport type ReduceOptions = {\n /**\n * Input values are `[identity, value]` with a string or number identity\n * that determines the value: two inputs under one key with an equal\n * identity are equal. They merge without a structural hash.\n */\n prefixIdentity?: boolean\n}\n\n/**\n * Reduces the elements in the stream by key (version-free)\n */\nexport function reduce<\n KType extends (T extends KeyValue<infer K, infer _V> ? K : never),\n V1Type extends (T extends KeyValue<KType, infer V> ? V : never),\n R,\n T,\n>(\n f: (values: Array<[V1Type, number]>) => Array<[R, number]>,\n options?: ReduceOptions,\n) {\n return (stream: IStreamBuilder<T>): IStreamBuilder<KeyValue<KType, R>> => {\n const output = new StreamBuilder<KeyValue<KType, R>>(\n stream.graph,\n new DifferenceStreamWriter<KeyValue<KType, R>>(),\n )\n const operator = new ReduceOperator<KType, V1Type, R>(\n stream.graph.getNextOperatorId(),\n stream.connectReader() as DifferenceStreamReader<KeyValue<KType, V1Type>>,\n output.writer,\n f,\n options,\n )\n stream.graph.addOperator(operator)\n return output\n }\n}\n"],"names":["UnaryOperator","Index","MultiSet","StreamBuilder","DifferenceStreamWriter"],"mappings":";;;;;;AAQA,SAAS,WAAW,MAAe,OAAyB;AAC1D,SAAO,SAAS,SAAU,SAAS,QAAQ,UAAU;AACvD;AAGA,SAAS,SAAY,OAAa;AAChC,SAAQ,UAAU,IAAI,IAAI;AAC5B;AAKO,MAAM,uBAAkCA,MAAAA,cAAgC;AAAA;AAAA,EAE7E;AAAA,EACA,YAAY,IAAIC,QAAAA,MAAa,EAAE,eAAe,OAAO;AAAA,EACrD;AAAA,EAEA,YACE,IACA,QACA,QACA,GACA,SACA;AACA,UAAM,IAAI,QAAQ,MAAM;AACxB,SAAK,SAAS,IAAIA,QAAAA,MAAa,EAAE,GAAG,SAAS,eAAe,OAAO;AACnE,SAAK,KAAK;AAAA,EACZ;AAAA,EAEA,MAAY;AAEV,UAAM,+BAAe,IAAA;AACrB,eAAW,WAAW,KAAK,iBAAiB;AAC1C,iBAAW,CAAC,MAAM,YAAY,KAAK,QAAQ,YAAY;AACrD,cAAM,CAAC,KAAK,KAAK,IAAI;AACrB,aAAK,OAAO,SAAS,KAAK,CAAC,OAAO,YAAY,CAAC;AAC/C,iBAAS,IAAI,GAAG;AAAA,MAClB;AAAA,IACF;AAGA,UAAM,SAAmC,CAAA;AACzC,eAAW,OAAO,UAAU;AAC1B,YAAM,OAAO,KAAK,OAAO,IAAI,GAAG;AAChC,YAAM,UAAU,KAAK,UAAU,IAAI,GAAG;AACtC,YAAM,MAAM,KAAK,GAAG,IAAI;AAIxB,UAAI,IAAI,UAAU,KAAK,QAAQ,UAAU,GAAG;AAE1C,cAAM,SAAS,QAAQ,CAAC;AACxB,cAAM,QAAQ,IAAI,CAAC;AACnB,cAAM,QAAQ,UAAU,SAAY,SAAY,SAAS,MAAM,CAAC,CAAC;AACjE,YACE,WAAW,UACX,UAAU,UACV,WAAW,OAAO,CAAC,GAAG,KAAK,GAC3B;AACA,gBAAM,QAAQ,MAAM,CAAC,IAAI,OAAO,CAAC;AACjC,cAAI,UAAU,GAAG;AACf,mBAAO,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC;AACrC,iBAAK,UAAU,SAAS,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;AAAA,UACjD;AACA;AAAA,QACF;AACA,YAAI,WAAW,QAAW;AACxB,iBAAO,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AAC1C,eAAK,UAAU,SAAS,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AAAA,QACtD;AACA,YAAI,UAAU,UAAa,MAAM,CAAC,MAAM,GAAG;AACzC,iBAAO,KAAK,CAAC,CAAC,KAAK,KAAW,GAAG,MAAM,CAAC,CAAC,CAAC;AAC1C,eAAK,UAAU,SAAS,KAAK,CAAC,OAAa,MAAM,CAAC,CAAC,CAAC;AAAA,QACtD;AACA;AAAA,MACF;AAGA,YAAM,mCAAmB,IAAA;AACzB,YAAM,mCAAmB,IAAA;AAGzB,iBAAW,CAAC,OAAO,YAAY,KAAK,KAAK;AACvC,cAAM,WAAW,aAAa,IAAI,KAAK,KAAK;AAC5C,qBAAa,IAAI,OAAO,WAAW,YAAY;AAAA,MACjD;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,SAAS;AAC3C,cAAM,WAAW,aAAa,IAAI,KAAK,KAAK;AAC5C,qBAAa,IAAI,OAAO,WAAW,YAAY;AAAA,MACjD;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,cAAc;AAChD,YAAI,CAAC,aAAa,IAAI,KAAK,GAAG;AAE5B,iBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,YAAY,CAAC;AACzC,eAAK,UAAU,SAAS,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;AAAA,QACrD;AAAA,MACF;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,cAAc;AAChD,YAAI,CAAC,aAAa,IAAI,KAAK,GAAG;AAE5B,cAAI,iBAAiB,GAAG;AACtB,mBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,YAAY,CAAC;AACxC,iBAAK,UAAU,SAAS,KAAK,CAAC,OAAO,YAAY,CAAC;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,CAAC,OAAO,eAAe,KAAK,cAAc;AACnD,cAAM,kBAAkB,aAAa,IAAI,KAAK;AAC9C,YAAI,oBAAoB,QAAW;AACjC,gBAAM,QAAQ,kBAAkB;AAEhC,cAAI,UAAU,GAAG;AACf,mBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,KAAK,CAAC;AACjC,iBAAK,UAAU,SAAS,KAAK,CAAC,OAAO,KAAK,CAAC;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,WAAK,OAAO,SAAS,IAAIC,SAAAA,SAAS,MAAM,CAAC;AAAA,IAC3C;AAAA,EACF;AACF;AAeO,SAAS,OAMd,GACA,SACA;AACA,SAAO,CAAC,WAAkE;AACxE,UAAM,SAAS,IAAIC,GAAAA;AAAAA,MACjB,OAAO;AAAA,MACP,IAAIC,MAAAA,uBAAA;AAAA,IAA2C;AAEjD,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,OAAO;AAAA,MACP;AAAA,MACA;AAAA,IAAA;AAEF,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO;AAAA,EACT;AACF;;;"}
@@ -1,14 +1,47 @@
1
- import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from '../graph.js';
2
- import { IStreamBuilder, KeyValue } from '../types.js';
1
+ import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from "../graph.cjs";
2
+ import { IStreamBuilder, KeyValue } from "../types.cjs";
3
3
  /**
4
4
  * Base operator for reduction operations (version-free)
5
5
  */
6
- export declare class ReduceOperator<K, V1, V2> extends UnaryOperator<[K, V1], [K, V2]> {
6
+ export declare class ReduceOperator<K, V1, V2> extends UnaryOperator<[
7
+ K,
8
+ V1
9
+ ], [
10
+ K,
11
+ V2
12
+ ]> {
7
13
  #private;
8
- constructor(id: number, inputA: DifferenceStreamReader<[K, V1]>, output: DifferenceStreamWriter<[K, V2]>, f: (values: Array<[V1, number]>) => Array<[V2, number]>);
14
+ constructor(id: number, inputA: DifferenceStreamReader<[
15
+ K,
16
+ V1
17
+ ]>, output: DifferenceStreamWriter<[
18
+ K,
19
+ V2
20
+ ]>, f: (values: Array<[
21
+ V1,
22
+ number
23
+ ]>) => Array<[
24
+ V2,
25
+ number
26
+ ]>, options?: ReduceOptions);
9
27
  run(): void;
10
28
  }
29
+ /** @internal groupBy's option; its producer must keep the identity exact. */
30
+ export type ReduceOptions = {
31
+ /**
32
+ * Input values are `[identity, value]` with a string or number identity
33
+ * that determines the value: two inputs under one key with an equal
34
+ * identity are equal. They merge without a structural hash.
35
+ */
36
+ prefixIdentity?: boolean;
37
+ };
11
38
  /**
12
39
  * Reduces the elements in the stream by key (version-free)
13
40
  */
14
- export declare function reduce<KType extends T extends KeyValue<infer K, infer _V> ? K : never, V1Type extends T extends KeyValue<KType, infer V> ? V : never, R, T>(f: (values: Array<[V1Type, number]>) => Array<[R, number]>): (stream: IStreamBuilder<T>) => IStreamBuilder<KeyValue<KType, R>>;
41
+ export declare function reduce<KType extends (T extends KeyValue<infer K, infer _V> ? K : never), V1Type extends (T extends KeyValue<KType, infer V> ? V : never), R, T>(f: (values: Array<[
42
+ V1Type,
43
+ number
44
+ ]>) => Array<[
45
+ R,
46
+ number
47
+ ]>, options?: ReduceOptions): (stream: IStreamBuilder<T>) => IStreamBuilder<KeyValue<KType, R>>;
@@ -1,6 +1,6 @@
1
- import { DifferenceStreamWriter, LinearUnaryOperator, DifferenceStreamReader } from '../graph.js';
2
- import { PipedOperator } from '../types.js';
3
- import { MultiSet } from '../multiset.js';
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 applies a function to each multi-set in the input stream
6
6
  */
@@ -36,6 +36,15 @@ class TopKArray {
36
36
  const available = this.#sortedValues.length - offset;
37
37
  return Math.max(0, Math.min(limit, available));
38
38
  }
39
+ countLeadingInWindow(predicate) {
40
+ let count = 0;
41
+ const end = this.#topKStart + this.size;
42
+ for (let index = this.#topKStart; index < end; index++) {
43
+ if (!predicate(getValue(this.#sortedValues[index]))) break;
44
+ count++;
45
+ }
46
+ return count;
47
+ }
39
48
  /**
40
49
  * Moves the topK window
41
50
  */