@tanstack/db-ivm 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/d2.cjs +25 -49
- package/dist/cjs/d2.cjs.map +1 -1
- package/dist/cjs/graph.cjs +8 -20
- package/dist/cjs/graph.cjs.map +1 -1
- package/dist/cjs/indexes.cjs +118 -79
- package/dist/cjs/indexes.cjs.map +1 -1
- package/dist/cjs/indexes.d.cts +23 -0
- package/dist/cjs/multiset.cjs +106 -115
- package/dist/cjs/multiset.cjs.map +1 -1
- package/dist/cjs/multiset.d.cts +1 -0
- package/dist/cjs/operators/debug.cjs +5 -15
- package/dist/cjs/operators/debug.cjs.map +1 -1
- package/dist/cjs/operators/distinct.cjs +11 -22
- package/dist/cjs/operators/distinct.cjs.map +1 -1
- package/dist/cjs/operators/filter.cjs +3 -12
- package/dist/cjs/operators/filter.cjs.map +1 -1
- package/dist/cjs/operators/filterBy.cjs.map +1 -1
- package/dist/cjs/operators/filterBy.d.cts +1 -2
- package/dist/cjs/operators/groupBy.cjs +1 -2
- package/dist/cjs/operators/groupBy.cjs.map +1 -1
- package/dist/cjs/operators/join.cjs +104 -93
- package/dist/cjs/operators/join.cjs.map +1 -1
- package/dist/cjs/operators/join.d.cts +11 -8
- package/dist/cjs/operators/map.cjs +3 -12
- package/dist/cjs/operators/map.cjs.map +1 -1
- package/dist/cjs/operators/orderBy.cjs +13 -11
- package/dist/cjs/operators/orderBy.cjs.map +1 -1
- package/dist/cjs/operators/orderBy.d.cts +5 -2
- package/dist/cjs/operators/output.cjs +3 -12
- package/dist/cjs/operators/output.cjs.map +1 -1
- package/dist/cjs/operators/reduce.cjs +11 -22
- package/dist/cjs/operators/reduce.cjs.map +1 -1
- package/dist/cjs/operators/tap.cjs +3 -12
- package/dist/cjs/operators/tap.cjs.map +1 -1
- package/dist/cjs/operators/topK.cjs +4 -4
- package/dist/cjs/operators/topK.cjs.map +1 -1
- package/dist/cjs/operators/topK.d.cts +1 -2
- package/dist/cjs/operators/topKWithFractionalIndex.cjs +117 -82
- package/dist/cjs/operators/topKWithFractionalIndex.cjs.map +1 -1
- package/dist/cjs/operators/topKWithFractionalIndex.d.cts +22 -0
- package/dist/cjs/utils.cjs +21 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +11 -0
- package/dist/esm/d2.js +25 -49
- package/dist/esm/d2.js.map +1 -1
- package/dist/esm/graph.js +8 -20
- package/dist/esm/graph.js.map +1 -1
- package/dist/esm/indexes.d.ts +23 -0
- package/dist/esm/indexes.js +118 -79
- package/dist/esm/indexes.js.map +1 -1
- package/dist/esm/multiset.d.ts +1 -0
- package/dist/esm/multiset.js +106 -115
- package/dist/esm/multiset.js.map +1 -1
- package/dist/esm/operators/debug.js +5 -15
- package/dist/esm/operators/debug.js.map +1 -1
- package/dist/esm/operators/distinct.js +11 -22
- package/dist/esm/operators/distinct.js.map +1 -1
- package/dist/esm/operators/filter.js +3 -12
- package/dist/esm/operators/filter.js.map +1 -1
- package/dist/esm/operators/filterBy.d.ts +1 -2
- package/dist/esm/operators/filterBy.js.map +1 -1
- package/dist/esm/operators/groupBy.js +1 -2
- package/dist/esm/operators/groupBy.js.map +1 -1
- package/dist/esm/operators/join.d.ts +11 -8
- package/dist/esm/operators/join.js +104 -93
- package/dist/esm/operators/join.js.map +1 -1
- package/dist/esm/operators/map.js +3 -12
- package/dist/esm/operators/map.js.map +1 -1
- package/dist/esm/operators/orderBy.d.ts +5 -2
- package/dist/esm/operators/orderBy.js +13 -11
- package/dist/esm/operators/orderBy.js.map +1 -1
- package/dist/esm/operators/output.js +3 -12
- package/dist/esm/operators/output.js.map +1 -1
- package/dist/esm/operators/reduce.js +11 -22
- package/dist/esm/operators/reduce.js.map +1 -1
- package/dist/esm/operators/tap.js +3 -12
- package/dist/esm/operators/tap.js.map +1 -1
- package/dist/esm/operators/topK.d.ts +1 -2
- package/dist/esm/operators/topK.js +4 -4
- package/dist/esm/operators/topK.js.map +1 -1
- package/dist/esm/operators/topKWithFractionalIndex.d.ts +22 -0
- package/dist/esm/operators/topKWithFractionalIndex.js +118 -83
- package/dist/esm/operators/topKWithFractionalIndex.js.map +1 -1
- package/dist/esm/utils.d.ts +11 -0
- package/dist/esm/utils.js +21 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/indexes.ts +54 -0
- package/src/multiset.ts +6 -0
- package/src/operators/filterBy.ts +1 -2
- package/src/operators/join.ts +249 -139
- package/src/operators/orderBy.ts +6 -2
- package/src/operators/topK.ts +1 -2
- package/src/operators/topKWithFractionalIndex.ts +111 -13
- package/src/utils.ts +33 -0
|
@@ -2,7 +2,12 @@ import { generateKeyBetween } from "fractional-indexing"
|
|
|
2
2
|
import { DifferenceStreamWriter, UnaryOperator } from "../graph.js"
|
|
3
3
|
import { StreamBuilder } from "../d2.js"
|
|
4
4
|
import { MultiSet } from "../multiset.js"
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
binarySearch,
|
|
7
|
+
diffHalfOpen,
|
|
8
|
+
globalObjectIdGenerator,
|
|
9
|
+
} from "../utils.js"
|
|
10
|
+
import type { HRange } from "../utils.js"
|
|
6
11
|
import type { DifferenceStreamReader } from "../graph.js"
|
|
7
12
|
import type { IStreamBuilder, PipedOperator } from "../types.js"
|
|
8
13
|
|
|
@@ -10,6 +15,9 @@ export interface TopKWithFractionalIndexOptions {
|
|
|
10
15
|
limit?: number
|
|
11
16
|
offset?: number
|
|
12
17
|
setSizeCallback?: (getSize: () => number) => void
|
|
18
|
+
setWindowFn?: (
|
|
19
|
+
windowFn: (options: { offset?: number; limit?: number }) => void
|
|
20
|
+
) => void
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
export type TopKChanges<V> = {
|
|
@@ -19,6 +27,15 @@ export type TopKChanges<V> = {
|
|
|
19
27
|
moveOut: IndexedValue<V> | null
|
|
20
28
|
}
|
|
21
29
|
|
|
30
|
+
export type TopKMoveChanges<V> = {
|
|
31
|
+
/** Flag that marks whether there were any changes to the topK */
|
|
32
|
+
changes: boolean
|
|
33
|
+
/** Indicates which elements move into the topK (if any) */
|
|
34
|
+
moveIns: Array<IndexedValue<V>>
|
|
35
|
+
/** Indicates which elements move out of the topK (if any) */
|
|
36
|
+
moveOuts: Array<IndexedValue<V>>
|
|
37
|
+
}
|
|
38
|
+
|
|
22
39
|
/**
|
|
23
40
|
* A topK data structure that supports insertions and deletions
|
|
24
41
|
* and returns changes to the topK.
|
|
@@ -58,6 +75,49 @@ class TopKArray<V> implements TopK<V> {
|
|
|
58
75
|
return Math.max(0, Math.min(limit, available))
|
|
59
76
|
}
|
|
60
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Moves the topK window
|
|
80
|
+
*/
|
|
81
|
+
move({
|
|
82
|
+
offset,
|
|
83
|
+
limit,
|
|
84
|
+
}: {
|
|
85
|
+
offset?: number
|
|
86
|
+
limit?: number
|
|
87
|
+
}): TopKMoveChanges<V> {
|
|
88
|
+
const oldOffset = this.#topKStart
|
|
89
|
+
const oldLimit = this.#topKEnd - this.#topKStart
|
|
90
|
+
const oldRange: HRange = [this.#topKStart, this.#topKEnd]
|
|
91
|
+
|
|
92
|
+
this.#topKStart = offset ?? oldOffset
|
|
93
|
+
this.#topKEnd = this.#topKStart + (limit ?? oldLimit)
|
|
94
|
+
|
|
95
|
+
const newRange: HRange = [this.#topKStart, this.#topKEnd]
|
|
96
|
+
const { onlyInA, onlyInB } = diffHalfOpen(oldRange, newRange)
|
|
97
|
+
|
|
98
|
+
const moveIns: Array<IndexedValue<V>> = []
|
|
99
|
+
onlyInB.forEach((index) => {
|
|
100
|
+
const value = this.#sortedValues[index]
|
|
101
|
+
if (value) {
|
|
102
|
+
moveIns.push(value)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const moveOuts: Array<IndexedValue<V>> = []
|
|
107
|
+
onlyInA.forEach((index) => {
|
|
108
|
+
const value = this.#sortedValues[index]
|
|
109
|
+
if (value) {
|
|
110
|
+
moveOuts.push(value)
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// It could be that there are changes (i.e. moveIns or moveOuts)
|
|
115
|
+
// but that the collection is lazy so we don't have the data yet that needs to move in/out
|
|
116
|
+
// so `moveIns` and `moveOuts` will be empty but `changes` will be true
|
|
117
|
+
// this will tell the caller that it needs to run the graph to load more data
|
|
118
|
+
return { moveIns, moveOuts, changes: onlyInA.length + onlyInB.length > 0 }
|
|
119
|
+
}
|
|
120
|
+
|
|
61
121
|
insert(value: V): TopKChanges<V> {
|
|
62
122
|
const result: TopKChanges<V> = { moveIn: null, moveOut: null }
|
|
63
123
|
|
|
@@ -178,8 +238,6 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
178
238
|
*/
|
|
179
239
|
#topK: TopK<TaggedValue<K, T>>
|
|
180
240
|
|
|
181
|
-
#limit: number
|
|
182
|
-
|
|
183
241
|
constructor(
|
|
184
242
|
id: number,
|
|
185
243
|
inputA: DifferenceStreamReader<[K, T]>,
|
|
@@ -188,7 +246,7 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
188
246
|
options: TopKWithFractionalIndexOptions
|
|
189
247
|
) {
|
|
190
248
|
super(id, inputA, output)
|
|
191
|
-
|
|
249
|
+
const limit = options.limit ?? Infinity
|
|
192
250
|
const offset = options.offset ?? 0
|
|
193
251
|
const compareTaggedValues = (
|
|
194
252
|
a: TaggedValue<K, T>,
|
|
@@ -204,8 +262,9 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
204
262
|
const tieBreakerB = getTag(b)
|
|
205
263
|
return tieBreakerA - tieBreakerB
|
|
206
264
|
}
|
|
207
|
-
this.#topK = this.createTopK(offset,
|
|
265
|
+
this.#topK = this.createTopK(offset, limit, compareTaggedValues)
|
|
208
266
|
options.setSizeCallback?.(() => this.#topK.size)
|
|
267
|
+
options.setWindowFn?.(this.moveTopK.bind(this))
|
|
209
268
|
}
|
|
210
269
|
|
|
211
270
|
protected createTopK(
|
|
@@ -216,6 +275,32 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
216
275
|
return new TopKArray(offset, limit, comparator)
|
|
217
276
|
}
|
|
218
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Moves the topK window based on the provided offset and limit.
|
|
280
|
+
* Any changes to the topK are sent to the output.
|
|
281
|
+
*/
|
|
282
|
+
moveTopK({ offset, limit }: { offset?: number; limit?: number }) {
|
|
283
|
+
if (!(this.#topK instanceof TopKArray)) {
|
|
284
|
+
throw new Error(
|
|
285
|
+
`Cannot move B+-tree implementation of TopK with fractional index`
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const result: Array<[[K, IndexedValue<T>], number]> = []
|
|
290
|
+
|
|
291
|
+
const diff = this.#topK.move({ offset, limit })
|
|
292
|
+
|
|
293
|
+
diff.moveIns.forEach((moveIn) => this.handleMoveIn(moveIn, result))
|
|
294
|
+
diff.moveOuts.forEach((moveOut) => this.handleMoveOut(moveOut, result))
|
|
295
|
+
|
|
296
|
+
if (diff.changes) {
|
|
297
|
+
// There are changes to the topK
|
|
298
|
+
// it could be that moveIns and moveOuts are empty
|
|
299
|
+
// because the collection is lazy, so we will run the graph again to load the data
|
|
300
|
+
this.output.sendData(new MultiSet(result))
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
219
304
|
run(): void {
|
|
220
305
|
const result: Array<[[K, IndexedValue<T>], number]> = []
|
|
221
306
|
for (const message of this.inputMessages()) {
|
|
@@ -258,23 +343,36 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
258
343
|
// so it doesn't affect the topK
|
|
259
344
|
}
|
|
260
345
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
346
|
+
this.handleMoveIn(res.moveIn, result)
|
|
347
|
+
this.handleMoveOut(res.moveOut, result)
|
|
348
|
+
|
|
349
|
+
return
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private handleMoveIn(
|
|
353
|
+
moveIn: IndexedValue<TaggedValue<K, T>> | null,
|
|
354
|
+
result: Array<[[K, IndexedValue<T>], number]>
|
|
355
|
+
) {
|
|
356
|
+
if (moveIn) {
|
|
357
|
+
const index = getIndex(moveIn)
|
|
358
|
+
const taggedValue = getValue(moveIn)
|
|
264
359
|
const k = getKey(taggedValue)
|
|
265
360
|
const val = getVal(taggedValue)
|
|
266
361
|
result.push([[k, [val, index]], 1])
|
|
267
362
|
}
|
|
363
|
+
}
|
|
268
364
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
365
|
+
private handleMoveOut(
|
|
366
|
+
moveOut: IndexedValue<TaggedValue<K, T>> | null,
|
|
367
|
+
result: Array<[[K, IndexedValue<T>], number]>
|
|
368
|
+
) {
|
|
369
|
+
if (moveOut) {
|
|
370
|
+
const index = getIndex(moveOut)
|
|
371
|
+
const taggedValue = getValue(moveOut)
|
|
272
372
|
const k = getKey(taggedValue)
|
|
273
373
|
const val = getVal(taggedValue)
|
|
274
374
|
result.push([[k, [val, index]], -1])
|
|
275
375
|
}
|
|
276
|
-
|
|
277
|
-
return
|
|
278
376
|
}
|
|
279
377
|
|
|
280
378
|
private getMultiplicity(key: K): number {
|
package/src/utils.ts
CHANGED
|
@@ -144,3 +144,36 @@ export function* mapIterable<T, U>(
|
|
|
144
144
|
yield fn(t)
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
export type HRange = [number, number] // half-open [start, end[ i.e. end is exclusive
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Computes the difference between two half-open ranges.
|
|
152
|
+
* @param a - The first half-open range
|
|
153
|
+
* @param b - The second half-open range
|
|
154
|
+
* @returns The difference between the two ranges
|
|
155
|
+
*/
|
|
156
|
+
export function diffHalfOpen(a: HRange, b: HRange) {
|
|
157
|
+
const [a1, a2] = a
|
|
158
|
+
const [b1, b2] = b
|
|
159
|
+
|
|
160
|
+
// A \ B can be up to two segments (left and right of the overlap)
|
|
161
|
+
const onlyInA: Array<number> = [
|
|
162
|
+
...range(a1, Math.min(a2, b1)), // left side of A outside B
|
|
163
|
+
...range(Math.max(a1, b2), a2), // right side of A outside B
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
// B \ A similarly
|
|
167
|
+
const onlyInB: Array<number> = [
|
|
168
|
+
...range(b1, Math.min(b2, a1)),
|
|
169
|
+
...range(Math.max(b1, a2), b2),
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
return { onlyInA, onlyInB }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function range(start: number, end: number): Array<number> {
|
|
176
|
+
const out: Array<number> = []
|
|
177
|
+
for (let i = start; i < end; i++) out.push(i)
|
|
178
|
+
return out
|
|
179
|
+
}
|