@tanstack/db-ivm 0.1.13 → 0.1.14
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/README.md +3 -3
- package/dist/cjs/d2.cjs.map +1 -1
- package/dist/cjs/graph.cjs.map +1 -1
- package/dist/cjs/hashing/hash.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/indexes.cjs.map +1 -1
- package/dist/cjs/multiset.cjs.map +1 -1
- package/dist/cjs/operators/concat.cjs.map +1 -1
- package/dist/cjs/operators/consolidate.cjs.map +1 -1
- package/dist/cjs/operators/count.cjs.map +1 -1
- package/dist/cjs/operators/debug.cjs.map +1 -1
- package/dist/cjs/operators/distinct.cjs.map +1 -1
- package/dist/cjs/operators/filter.cjs.map +1 -1
- package/dist/cjs/operators/filterBy.cjs.map +1 -1
- package/dist/cjs/operators/groupBy.cjs.map +1 -1
- package/dist/cjs/operators/join.cjs.map +1 -1
- package/dist/cjs/operators/keying.cjs.map +1 -1
- package/dist/cjs/operators/map.cjs.map +1 -1
- package/dist/cjs/operators/negate.cjs.map +1 -1
- package/dist/cjs/operators/orderBy.cjs.map +1 -1
- package/dist/cjs/operators/output.cjs.map +1 -1
- package/dist/cjs/operators/pipe.cjs.map +1 -1
- package/dist/cjs/operators/reduce.cjs.map +1 -1
- package/dist/cjs/operators/tap.cjs.map +1 -1
- package/dist/cjs/operators/topK.cjs.map +1 -1
- package/dist/cjs/operators/topKWithFractionalIndex.cjs +20 -35
- package/dist/cjs/operators/topKWithFractionalIndex.cjs.map +1 -1
- package/dist/cjs/operators/topKWithFractionalIndex.d.cts +3 -11
- package/dist/cjs/operators/topKWithFractionalIndexBTree.d.cts +4 -4
- package/dist/cjs/utils.cjs +9 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +5 -0
- package/dist/esm/d2.js.map +1 -1
- package/dist/esm/graph.js.map +1 -1
- package/dist/esm/hashing/hash.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes.js.map +1 -1
- package/dist/esm/multiset.js.map +1 -1
- package/dist/esm/operators/concat.js.map +1 -1
- package/dist/esm/operators/consolidate.js.map +1 -1
- package/dist/esm/operators/count.js.map +1 -1
- package/dist/esm/operators/debug.js.map +1 -1
- package/dist/esm/operators/distinct.js.map +1 -1
- package/dist/esm/operators/filter.js.map +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.js.map +1 -1
- package/dist/esm/operators/keying.js.map +1 -1
- package/dist/esm/operators/map.js.map +1 -1
- package/dist/esm/operators/negate.js.map +1 -1
- package/dist/esm/operators/orderBy.js.map +1 -1
- package/dist/esm/operators/output.js.map +1 -1
- package/dist/esm/operators/pipe.js.map +1 -1
- package/dist/esm/operators/reduce.js.map +1 -1
- package/dist/esm/operators/tap.js.map +1 -1
- package/dist/esm/operators/topK.js.map +1 -1
- package/dist/esm/operators/topKWithFractionalIndex.d.ts +3 -11
- package/dist/esm/operators/topKWithFractionalIndex.js +21 -36
- package/dist/esm/operators/topKWithFractionalIndex.js.map +1 -1
- package/dist/esm/operators/topKWithFractionalIndexBTree.d.ts +4 -4
- package/dist/esm/utils.d.ts +5 -0
- package/dist/esm/utils.js +9 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +29 -29
- package/src/d2.ts +4 -4
- package/src/graph.ts +6 -6
- package/src/hashing/hash.ts +3 -3
- package/src/hashing/index.ts +2 -2
- package/src/index.ts +5 -4
- package/src/indexes.ts +8 -8
- package/src/multiset.ts +5 -5
- package/src/operators/concat.ts +6 -6
- package/src/operators/consolidate.ts +6 -6
- package/src/operators/count.ts +9 -9
- package/src/operators/debug.ts +8 -8
- package/src/operators/distinct.ts +11 -11
- package/src/operators/filter.ts +8 -8
- package/src/operators/filterBy.ts +7 -7
- package/src/operators/groupBy.ts +22 -22
- package/src/operators/index.ts +19 -19
- package/src/operators/join.ts +23 -23
- package/src/operators/keying.ts +4 -4
- package/src/operators/map.ts +8 -8
- package/src/operators/negate.ts +6 -6
- package/src/operators/orderBy.ts +26 -26
- package/src/operators/orderByBTree.ts +7 -7
- package/src/operators/output.ts +9 -9
- package/src/operators/pipe.ts +1 -1
- package/src/operators/reduce.ts +9 -9
- package/src/operators/tap.ts +8 -8
- package/src/operators/topK.ts +8 -8
- package/src/operators/topKWithFractionalIndex.ts +62 -83
- package/src/operators/topKWithFractionalIndexBTree.ts +19 -20
- package/src/types.ts +22 -22
- package/src/utils.ts +19 -4
package/src/operators/reduce.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DifferenceStreamWriter, UnaryOperator } from
|
|
2
|
-
import { StreamBuilder } from
|
|
3
|
-
import { MultiSet } from
|
|
4
|
-
import { Index } from
|
|
5
|
-
import type { DifferenceStreamReader } from
|
|
6
|
-
import type { IStreamBuilder, KeyValue } from
|
|
1
|
+
import { DifferenceStreamWriter, UnaryOperator } from '../graph.js'
|
|
2
|
+
import { StreamBuilder } from '../d2.js'
|
|
3
|
+
import { MultiSet } from '../multiset.js'
|
|
4
|
+
import { Index } from '../indexes.js'
|
|
5
|
+
import type { DifferenceStreamReader } from '../graph.js'
|
|
6
|
+
import type { IStreamBuilder, KeyValue } from '../types.js'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Base operator for reduction operations (version-free)
|
|
@@ -17,7 +17,7 @@ export class ReduceOperator<K, V1, V2> extends UnaryOperator<[K, V1], [K, V2]> {
|
|
|
17
17
|
id: number,
|
|
18
18
|
inputA: DifferenceStreamReader<[K, V1]>,
|
|
19
19
|
output: DifferenceStreamWriter<[K, V2]>,
|
|
20
|
-
f: (values: Array<[V1, number]>) => Array<[V2, number]
|
|
20
|
+
f: (values: Array<[V1, number]>) => Array<[V2, number]>,
|
|
21
21
|
) {
|
|
22
22
|
super(id, inputA, output)
|
|
23
23
|
this.#f = f
|
|
@@ -109,13 +109,13 @@ export function reduce<
|
|
|
109
109
|
return (stream: IStreamBuilder<T>): IStreamBuilder<KeyValue<KType, R>> => {
|
|
110
110
|
const output = new StreamBuilder<KeyValue<KType, R>>(
|
|
111
111
|
stream.graph,
|
|
112
|
-
new DifferenceStreamWriter<KeyValue<KType, R>>()
|
|
112
|
+
new DifferenceStreamWriter<KeyValue<KType, R>>(),
|
|
113
113
|
)
|
|
114
114
|
const operator = new ReduceOperator<KType, V1Type, R>(
|
|
115
115
|
stream.graph.getNextOperatorId(),
|
|
116
116
|
stream.connectReader() as DifferenceStreamReader<KeyValue<KType, V1Type>>,
|
|
117
117
|
output.writer,
|
|
118
|
-
f
|
|
118
|
+
f,
|
|
119
119
|
)
|
|
120
120
|
stream.graph.addOperator(operator)
|
|
121
121
|
return output
|
package/src/operators/tap.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { DifferenceStreamWriter, LinearUnaryOperator } from
|
|
2
|
-
import { StreamBuilder } from
|
|
3
|
-
import type { IStreamBuilder, PipedOperator } from
|
|
4
|
-
import type { DifferenceStreamReader } from
|
|
5
|
-
import type { MultiSet } from
|
|
1
|
+
import { DifferenceStreamWriter, LinearUnaryOperator } from '../graph.js'
|
|
2
|
+
import { StreamBuilder } from '../d2.js'
|
|
3
|
+
import type { IStreamBuilder, PipedOperator } from '../types.js'
|
|
4
|
+
import type { DifferenceStreamReader } from '../graph.js'
|
|
5
|
+
import type { MultiSet } from '../multiset.js'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Operator that applies a function to each multi-set in the input stream
|
|
@@ -14,7 +14,7 @@ export class TapOperator<T> extends LinearUnaryOperator<T, T> {
|
|
|
14
14
|
id: number,
|
|
15
15
|
inputA: DifferenceStreamReader<T>,
|
|
16
16
|
output: DifferenceStreamWriter<T>,
|
|
17
|
-
f: (data: MultiSet<T>) => void
|
|
17
|
+
f: (data: MultiSet<T>) => void,
|
|
18
18
|
) {
|
|
19
19
|
super(id, inputA, output)
|
|
20
20
|
this.#f = f
|
|
@@ -36,13 +36,13 @@ export function tap<T>(f: (data: MultiSet<T>) => void): PipedOperator<T, T> {
|
|
|
36
36
|
return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {
|
|
37
37
|
const output = new StreamBuilder<T>(
|
|
38
38
|
stream.graph,
|
|
39
|
-
new DifferenceStreamWriter<T>()
|
|
39
|
+
new DifferenceStreamWriter<T>(),
|
|
40
40
|
)
|
|
41
41
|
const operator = new TapOperator<T>(
|
|
42
42
|
stream.graph.getNextOperatorId(),
|
|
43
43
|
stream.connectReader(),
|
|
44
44
|
output.writer,
|
|
45
|
-
f
|
|
45
|
+
f,
|
|
46
46
|
)
|
|
47
47
|
stream.graph.addOperator(operator)
|
|
48
48
|
return output
|
package/src/operators/topK.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { MultiSet } from
|
|
2
|
-
import { reduce } from
|
|
3
|
-
import type { IStreamBuilder, KeyValue, PipedOperator } from
|
|
1
|
+
import { MultiSet } from '../multiset.js'
|
|
2
|
+
import { reduce } from './reduce.js'
|
|
3
|
+
import type { IStreamBuilder, KeyValue, PipedOperator } from '../types'
|
|
4
4
|
|
|
5
5
|
interface TopKOptions {
|
|
6
6
|
limit?: number
|
|
@@ -24,7 +24,7 @@ export function topK<
|
|
|
24
24
|
T,
|
|
25
25
|
>(
|
|
26
26
|
comparator: (a: V1Type, b: V1Type) => number,
|
|
27
|
-
options?: TopKOptions
|
|
27
|
+
options?: TopKOptions,
|
|
28
28
|
): PipedOperator<T, T> {
|
|
29
29
|
const limit = options?.limit ?? Infinity
|
|
30
30
|
const offset = options?.offset ?? 0
|
|
@@ -38,7 +38,7 @@ export function topK<
|
|
|
38
38
|
.getInner()
|
|
39
39
|
.sort((a, b) => comparator(a[0] as V1Type, b[0] as V1Type))
|
|
40
40
|
return sortedValues.slice(offset, offset + limit)
|
|
41
|
-
})
|
|
41
|
+
}),
|
|
42
42
|
)
|
|
43
43
|
return reduced as IStreamBuilder<T>
|
|
44
44
|
}
|
|
@@ -62,13 +62,13 @@ export function topKWithIndex<
|
|
|
62
62
|
T,
|
|
63
63
|
>(
|
|
64
64
|
comparator: (a: V1Type, b: V1Type) => number,
|
|
65
|
-
options?: TopKOptions
|
|
65
|
+
options?: TopKOptions,
|
|
66
66
|
): PipedOperator<T, KeyValue<KType, [V1Type, number]>> {
|
|
67
67
|
const limit = options?.limit ?? Infinity
|
|
68
68
|
const offset = options?.offset ?? 0
|
|
69
69
|
|
|
70
70
|
return (
|
|
71
|
-
stream: IStreamBuilder<T
|
|
71
|
+
stream: IStreamBuilder<T>,
|
|
72
72
|
): IStreamBuilder<KeyValue<KType, [V1Type, number]>> => {
|
|
73
73
|
const reduced = stream.pipe(
|
|
74
74
|
reduce<KType, V1Type, [V1Type, number], T>((values) => {
|
|
@@ -84,7 +84,7 @@ export function topKWithIndex<
|
|
|
84
84
|
multiplicity,
|
|
85
85
|
])
|
|
86
86
|
return sortedValues
|
|
87
|
-
})
|
|
87
|
+
}),
|
|
88
88
|
)
|
|
89
89
|
return reduced
|
|
90
90
|
}
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import { generateKeyBetween } from
|
|
2
|
-
import { DifferenceStreamWriter, UnaryOperator } from
|
|
3
|
-
import { StreamBuilder } from
|
|
4
|
-
import { MultiSet } from
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "../utils.js"
|
|
10
|
-
import type { HRange } from "../utils.js"
|
|
11
|
-
import type { DifferenceStreamReader } from "../graph.js"
|
|
12
|
-
import type { IStreamBuilder, PipedOperator } from "../types.js"
|
|
1
|
+
import { generateKeyBetween } from 'fractional-indexing'
|
|
2
|
+
import { DifferenceStreamWriter, UnaryOperator } from '../graph.js'
|
|
3
|
+
import { StreamBuilder } from '../d2.js'
|
|
4
|
+
import { MultiSet } from '../multiset.js'
|
|
5
|
+
import { binarySearch, compareKeys, diffHalfOpen } from '../utils.js'
|
|
6
|
+
import type { HRange } from '../utils.js'
|
|
7
|
+
import type { DifferenceStreamReader } from '../graph.js'
|
|
8
|
+
import type { IStreamBuilder, PipedOperator } from '../types.js'
|
|
13
9
|
|
|
14
10
|
export interface TopKWithFractionalIndexOptions {
|
|
15
11
|
limit?: number
|
|
16
12
|
offset?: number
|
|
17
13
|
setSizeCallback?: (getSize: () => number) => void
|
|
18
14
|
setWindowFn?: (
|
|
19
|
-
windowFn: (options: { offset?: number; limit?: number }) => void
|
|
15
|
+
windowFn: (options: { offset?: number; limit?: number }) => void,
|
|
20
16
|
) => void
|
|
21
17
|
}
|
|
22
18
|
|
|
@@ -61,7 +57,7 @@ class TopKArray<V> implements TopK<V> {
|
|
|
61
57
|
constructor(
|
|
62
58
|
offset: number,
|
|
63
59
|
limit: number,
|
|
64
|
-
comparator: (a: V, b: V) => number
|
|
60
|
+
comparator: (a: V, b: V) => number,
|
|
65
61
|
) {
|
|
66
62
|
this.#topKStart = offset
|
|
67
63
|
this.#topKEnd = offset + limit
|
|
@@ -229,7 +225,7 @@ class TopKArray<V> implements TopK<V> {
|
|
|
229
225
|
|
|
230
226
|
#findIndex(value: V): number {
|
|
231
227
|
return binarySearch(this.#sortedValues, indexedValue(value, ``), (a, b) =>
|
|
232
|
-
this.#comparator(getValue(a), getValue(b))
|
|
228
|
+
this.#comparator(getValue(a), getValue(b)),
|
|
233
229
|
)
|
|
234
230
|
}
|
|
235
231
|
}
|
|
@@ -239,43 +235,34 @@ class TopKArray<V> implements TopK<V> {
|
|
|
239
235
|
* This operator maintains fractional indices for sorted elements
|
|
240
236
|
* and only updates indices when elements move position
|
|
241
237
|
*/
|
|
242
|
-
export class TopKWithFractionalIndexOperator<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
> {
|
|
238
|
+
export class TopKWithFractionalIndexOperator<
|
|
239
|
+
K extends string | number,
|
|
240
|
+
T,
|
|
241
|
+
> extends UnaryOperator<[K, T], [K, IndexedValue<T>]> {
|
|
246
242
|
#index: Map<K, number> = new Map() // maps keys to their multiplicity
|
|
247
243
|
|
|
248
244
|
/**
|
|
249
245
|
* topK data structure that supports insertions and deletions
|
|
250
246
|
* and returns changes to the topK.
|
|
247
|
+
* Elements are stored as [key, value] tuples for stable tie-breaking.
|
|
251
248
|
*/
|
|
252
|
-
#topK: TopK<
|
|
249
|
+
#topK: TopK<[K, T]>
|
|
253
250
|
|
|
254
251
|
constructor(
|
|
255
252
|
id: number,
|
|
256
253
|
inputA: DifferenceStreamReader<[K, T]>,
|
|
257
254
|
output: DifferenceStreamWriter<[K, IndexedValue<T>]>,
|
|
258
255
|
comparator: (a: T, b: T) => number,
|
|
259
|
-
options: TopKWithFractionalIndexOptions
|
|
256
|
+
options: TopKWithFractionalIndexOptions,
|
|
260
257
|
) {
|
|
261
258
|
super(id, inputA, output)
|
|
262
259
|
const limit = options.limit ?? Infinity
|
|
263
260
|
const offset = options.offset ?? 0
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const valueComparison = comparator(getVal(a), getVal(b))
|
|
270
|
-
if (valueComparison !== 0) {
|
|
271
|
-
return valueComparison
|
|
272
|
-
}
|
|
273
|
-
// If the values are equal, compare on the tag (object identity)
|
|
274
|
-
const tieBreakerA = getTag(a)
|
|
275
|
-
const tieBreakerB = getTag(b)
|
|
276
|
-
return tieBreakerA - tieBreakerB
|
|
277
|
-
}
|
|
278
|
-
this.#topK = this.createTopK(offset, limit, compareTaggedValues)
|
|
261
|
+
this.#topK = this.createTopK(
|
|
262
|
+
offset,
|
|
263
|
+
limit,
|
|
264
|
+
createKeyedComparator(comparator),
|
|
265
|
+
)
|
|
279
266
|
options.setSizeCallback?.(() => this.#topK.size)
|
|
280
267
|
options.setWindowFn?.(this.moveTopK.bind(this))
|
|
281
268
|
}
|
|
@@ -283,8 +270,8 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
283
270
|
protected createTopK(
|
|
284
271
|
offset: number,
|
|
285
272
|
limit: number,
|
|
286
|
-
comparator: (a:
|
|
287
|
-
): TopK<
|
|
273
|
+
comparator: (a: [K, T], b: [K, T]) => number,
|
|
274
|
+
): TopK<[K, T]> {
|
|
288
275
|
return new TopKArray(offset, limit, comparator)
|
|
289
276
|
}
|
|
290
277
|
|
|
@@ -295,7 +282,7 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
295
282
|
moveTopK({ offset, limit }: { offset?: number; limit?: number }) {
|
|
296
283
|
if (!(this.#topK instanceof TopKArray)) {
|
|
297
284
|
throw new Error(
|
|
298
|
-
`Cannot move B+-tree implementation of TopK with fractional index
|
|
285
|
+
`Cannot move B+-tree implementation of TopK with fractional index`,
|
|
299
286
|
)
|
|
300
287
|
}
|
|
301
288
|
|
|
@@ -332,24 +319,22 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
332
319
|
key: K,
|
|
333
320
|
value: T,
|
|
334
321
|
multiplicity: number,
|
|
335
|
-
result: Array<[[K, IndexedValue<T>], number]
|
|
322
|
+
result: Array<[[K, IndexedValue<T>], number]>,
|
|
336
323
|
): void {
|
|
337
324
|
const { oldMultiplicity, newMultiplicity } = this.addKey(key, multiplicity)
|
|
338
325
|
|
|
339
|
-
let res: TopKChanges<
|
|
326
|
+
let res: TopKChanges<[K, T]> = {
|
|
340
327
|
moveIn: null,
|
|
341
328
|
moveOut: null,
|
|
342
329
|
}
|
|
343
330
|
if (oldMultiplicity <= 0 && newMultiplicity > 0) {
|
|
344
331
|
// The value was invisible but should now be visible
|
|
345
332
|
// Need to insert it into the array of sorted values
|
|
346
|
-
|
|
347
|
-
res = this.#topK.insert(taggedValue)
|
|
333
|
+
res = this.#topK.insert([key, value])
|
|
348
334
|
} else if (oldMultiplicity > 0 && newMultiplicity <= 0) {
|
|
349
335
|
// The value was visible but should now be invisible
|
|
350
336
|
// Need to remove it from the array of sorted values
|
|
351
|
-
|
|
352
|
-
res = this.#topK.delete(taggedValue)
|
|
337
|
+
res = this.#topK.delete([key, value])
|
|
353
338
|
} else {
|
|
354
339
|
// The value was invisible and it remains invisible
|
|
355
340
|
// or it was visible and remains visible
|
|
@@ -363,28 +348,22 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
363
348
|
}
|
|
364
349
|
|
|
365
350
|
private handleMoveIn(
|
|
366
|
-
moveIn: IndexedValue<
|
|
367
|
-
result: Array<[[K, IndexedValue<T>], number]
|
|
351
|
+
moveIn: IndexedValue<[K, T]> | null,
|
|
352
|
+
result: Array<[[K, IndexedValue<T>], number]>,
|
|
368
353
|
) {
|
|
369
354
|
if (moveIn) {
|
|
370
|
-
const index =
|
|
371
|
-
|
|
372
|
-
const k = getKey(taggedValue)
|
|
373
|
-
const val = getVal(taggedValue)
|
|
374
|
-
result.push([[k, [val, index]], 1])
|
|
355
|
+
const [[key, value], index] = moveIn
|
|
356
|
+
result.push([[key, [value, index]], 1])
|
|
375
357
|
}
|
|
376
358
|
}
|
|
377
359
|
|
|
378
360
|
private handleMoveOut(
|
|
379
|
-
moveOut: IndexedValue<
|
|
380
|
-
result: Array<[[K, IndexedValue<T>], number]
|
|
361
|
+
moveOut: IndexedValue<[K, T]> | null,
|
|
362
|
+
result: Array<[[K, IndexedValue<T>], number]>,
|
|
381
363
|
) {
|
|
382
364
|
if (moveOut) {
|
|
383
|
-
const index =
|
|
384
|
-
|
|
385
|
-
const k = getKey(taggedValue)
|
|
386
|
-
const val = getVal(taggedValue)
|
|
387
|
-
result.push([[k, [val, index]], -1])
|
|
365
|
+
const [[key, value], index] = moveOut
|
|
366
|
+
result.push([[key, [value, index]], -1])
|
|
388
367
|
}
|
|
389
368
|
}
|
|
390
369
|
|
|
@@ -394,7 +373,7 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
394
373
|
|
|
395
374
|
private addKey(
|
|
396
375
|
key: K,
|
|
397
|
-
multiplicity: number
|
|
376
|
+
multiplicity: number,
|
|
398
377
|
): { oldMultiplicity: number; newMultiplicity: number } {
|
|
399
378
|
const oldMultiplicity = this.getMultiplicity(key)
|
|
400
379
|
const newMultiplicity = oldMultiplicity + multiplicity
|
|
@@ -417,25 +396,25 @@ export class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<
|
|
|
417
396
|
* @param options - An optional object containing limit and offset properties
|
|
418
397
|
* @returns A piped operator that orders the elements and limits the number of results
|
|
419
398
|
*/
|
|
420
|
-
export function topKWithFractionalIndex<KType, T>(
|
|
399
|
+
export function topKWithFractionalIndex<KType extends string | number, T>(
|
|
421
400
|
comparator: (a: T, b: T) => number,
|
|
422
|
-
options?: TopKWithFractionalIndexOptions
|
|
401
|
+
options?: TopKWithFractionalIndexOptions,
|
|
423
402
|
): PipedOperator<[KType, T], [KType, IndexedValue<T>]> {
|
|
424
403
|
const opts = options || {}
|
|
425
404
|
|
|
426
405
|
return (
|
|
427
|
-
stream: IStreamBuilder<[KType, T]
|
|
406
|
+
stream: IStreamBuilder<[KType, T]>,
|
|
428
407
|
): IStreamBuilder<[KType, IndexedValue<T>]> => {
|
|
429
408
|
const output = new StreamBuilder<[KType, IndexedValue<T>]>(
|
|
430
409
|
stream.graph,
|
|
431
|
-
new DifferenceStreamWriter<[KType, IndexedValue<T>]>()
|
|
410
|
+
new DifferenceStreamWriter<[KType, IndexedValue<T>]>(),
|
|
432
411
|
)
|
|
433
412
|
const operator = new TopKWithFractionalIndexOperator<KType, T>(
|
|
434
413
|
stream.graph.getNextOperatorId(),
|
|
435
414
|
stream.connectReader(),
|
|
436
415
|
output.writer,
|
|
437
416
|
comparator,
|
|
438
|
-
opts
|
|
417
|
+
opts,
|
|
439
418
|
)
|
|
440
419
|
stream.graph.addOperator(operator)
|
|
441
420
|
return output
|
|
@@ -448,7 +427,7 @@ export type IndexedValue<V> = [V, FractionalIndex]
|
|
|
448
427
|
|
|
449
428
|
export function indexedValue<V>(
|
|
450
429
|
value: V,
|
|
451
|
-
index: FractionalIndex
|
|
430
|
+
index: FractionalIndex,
|
|
452
431
|
): IndexedValue<V> {
|
|
453
432
|
return [value, index]
|
|
454
433
|
}
|
|
@@ -461,21 +440,21 @@ export function getIndex<V>(indexedVal: IndexedValue<V>): FractionalIndex {
|
|
|
461
440
|
return indexedVal[1]
|
|
462
441
|
}
|
|
463
442
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
443
|
+
/**
|
|
444
|
+
* Creates a comparator for [key, value] tuples that first compares values,
|
|
445
|
+
* then uses the row key as a stable tie-breaker.
|
|
446
|
+
*/
|
|
447
|
+
function createKeyedComparator<K extends string | number, T>(
|
|
448
|
+
comparator: (a: T, b: T) => number,
|
|
449
|
+
): (a: [K, T], b: [K, T]) => number {
|
|
450
|
+
return ([aKey, aVal], [bKey, bVal]) => {
|
|
451
|
+
// First compare on the value
|
|
452
|
+
const valueComparison = comparator(aVal, bVal)
|
|
453
|
+
if (valueComparison !== 0) {
|
|
454
|
+
return valueComparison
|
|
455
|
+
}
|
|
456
|
+
// If the values are equal, use the row key as tie-breaker
|
|
457
|
+
// This provides stable, deterministic ordering since keys are string | number
|
|
458
|
+
return compareKeys(aKey, bKey)
|
|
459
|
+
}
|
|
481
460
|
}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { generateKeyBetween } from
|
|
2
|
-
import { DifferenceStreamWriter } from
|
|
3
|
-
import { StreamBuilder } from
|
|
1
|
+
import { generateKeyBetween } from 'fractional-indexing'
|
|
2
|
+
import { DifferenceStreamWriter } from '../graph.js'
|
|
3
|
+
import { StreamBuilder } from '../d2.js'
|
|
4
4
|
import {
|
|
5
5
|
TopKWithFractionalIndexOperator,
|
|
6
6
|
getIndex,
|
|
7
7
|
getValue,
|
|
8
8
|
indexedValue,
|
|
9
|
-
} from
|
|
10
|
-
import type { IStreamBuilder, PipedOperator } from
|
|
9
|
+
} from './topKWithFractionalIndex.js'
|
|
10
|
+
import type { IStreamBuilder, PipedOperator } from '../types.js'
|
|
11
11
|
import type {
|
|
12
12
|
IndexedValue,
|
|
13
|
-
TaggedValue,
|
|
14
13
|
TopK,
|
|
15
14
|
TopKChanges,
|
|
16
15
|
TopKWithFractionalIndexOptions,
|
|
17
|
-
} from
|
|
16
|
+
} from './topKWithFractionalIndex.js'
|
|
18
17
|
|
|
19
18
|
interface BTree<Key, Value> {
|
|
20
19
|
nextLowerPair: (key: Key) => [Key, Value] | undefined
|
|
@@ -30,7 +29,7 @@ interface BTreeClass {
|
|
|
30
29
|
new <Key, Value>(
|
|
31
30
|
entries?: Array<[Key, Value]>,
|
|
32
31
|
compare?: (a: Key, b: Key) => number,
|
|
33
|
-
maxNodeSize?: number
|
|
32
|
+
maxNodeSize?: number,
|
|
34
33
|
): BTree<Key, Value>
|
|
35
34
|
}
|
|
36
35
|
|
|
@@ -61,11 +60,11 @@ class TopKTree<V> implements TopK<V> {
|
|
|
61
60
|
constructor(
|
|
62
61
|
offset: number,
|
|
63
62
|
limit: number,
|
|
64
|
-
comparator: (a: V, b: V) => number
|
|
63
|
+
comparator: (a: V, b: V) => number,
|
|
65
64
|
) {
|
|
66
65
|
if (BTree === undefined) {
|
|
67
66
|
throw new Error(
|
|
68
|
-
`B+ tree not loaded. You need to call loadBTree() before using TopKTree
|
|
67
|
+
`B+ tree not loaded. You need to call loadBTree() before using TopKTree.`,
|
|
69
68
|
)
|
|
70
69
|
}
|
|
71
70
|
|
|
@@ -243,17 +242,17 @@ class TopKTree<V> implements TopK<V> {
|
|
|
243
242
|
* and only updates indices when elements move position
|
|
244
243
|
*/
|
|
245
244
|
export class TopKWithFractionalIndexBTreeOperator<
|
|
246
|
-
K,
|
|
245
|
+
K extends string | number,
|
|
247
246
|
T,
|
|
248
247
|
> extends TopKWithFractionalIndexOperator<K, T> {
|
|
249
248
|
protected override createTopK(
|
|
250
249
|
offset: number,
|
|
251
250
|
limit: number,
|
|
252
|
-
comparator: (a:
|
|
253
|
-
): TopK<
|
|
251
|
+
comparator: (a: [K, T], b: [K, T]) => number,
|
|
252
|
+
): TopK<[K, T]> {
|
|
254
253
|
if (BTree === undefined) {
|
|
255
254
|
throw new Error(
|
|
256
|
-
`B+ tree not loaded. You need to call loadBTree() before using TopKWithFractionalIndexBTreeOperator
|
|
255
|
+
`B+ tree not loaded. You need to call loadBTree() before using TopKWithFractionalIndexBTreeOperator.`,
|
|
257
256
|
)
|
|
258
257
|
}
|
|
259
258
|
return new TopKTree(offset, limit, comparator)
|
|
@@ -275,31 +274,31 @@ export class TopKWithFractionalIndexBTreeOperator<
|
|
|
275
274
|
* @param options - An optional object containing limit and offset properties
|
|
276
275
|
* @returns A piped operator that orders the elements and limits the number of results
|
|
277
276
|
*/
|
|
278
|
-
export function topKWithFractionalIndexBTree<KType, T>(
|
|
277
|
+
export function topKWithFractionalIndexBTree<KType extends string | number, T>(
|
|
279
278
|
comparator: (a: T, b: T) => number,
|
|
280
|
-
options?: TopKWithFractionalIndexOptions
|
|
279
|
+
options?: TopKWithFractionalIndexOptions,
|
|
281
280
|
): PipedOperator<[KType, T], [KType, IndexedValue<T>]> {
|
|
282
281
|
const opts = options || {}
|
|
283
282
|
|
|
284
283
|
if (BTree === undefined) {
|
|
285
284
|
throw new Error(
|
|
286
|
-
`B+ tree not loaded. You need to call loadBTree() before using topKWithFractionalIndexBTree
|
|
285
|
+
`B+ tree not loaded. You need to call loadBTree() before using topKWithFractionalIndexBTree.`,
|
|
287
286
|
)
|
|
288
287
|
}
|
|
289
288
|
|
|
290
289
|
return (
|
|
291
|
-
stream: IStreamBuilder<[KType, T]
|
|
290
|
+
stream: IStreamBuilder<[KType, T]>,
|
|
292
291
|
): IStreamBuilder<[KType, IndexedValue<T>]> => {
|
|
293
292
|
const output = new StreamBuilder<[KType, IndexedValue<T>]>(
|
|
294
293
|
stream.graph,
|
|
295
|
-
new DifferenceStreamWriter<[KType, IndexedValue<T>]>()
|
|
294
|
+
new DifferenceStreamWriter<[KType, IndexedValue<T>]>(),
|
|
296
295
|
)
|
|
297
296
|
const operator = new TopKWithFractionalIndexBTreeOperator<KType, T>(
|
|
298
297
|
stream.graph.getNextOperatorId(),
|
|
299
298
|
stream.connectReader(),
|
|
300
299
|
output.writer,
|
|
301
300
|
comparator,
|
|
302
|
-
opts
|
|
301
|
+
opts,
|
|
303
302
|
)
|
|
304
303
|
stream.graph.addOperator(operator)
|
|
305
304
|
return output
|