@tanstack/db 0.5.32 → 0.6.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.
- package/dist/cjs/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/change-events.d.cts +3 -2
- package/dist/cjs/collection/changes.cjs +13 -4
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/changes.d.cts +10 -1
- package/dist/cjs/collection/cleanup-queue.cjs +89 -0
- package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
- package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
- package/dist/cjs/collection/events.cjs +14 -0
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +39 -1
- package/dist/cjs/collection/index.cjs +66 -28
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +49 -36
- package/dist/cjs/collection/indexes.cjs +211 -62
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/indexes.d.cts +27 -17
- package/dist/cjs/collection/lifecycle.cjs +5 -22
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.d.cts +0 -1
- package/dist/cjs/collection/mutations.cjs +18 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/mutations.d.cts +1 -0
- package/dist/cjs/collection/state.cjs +381 -53
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +65 -1
- package/dist/cjs/collection/subscription.cjs +6 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +4 -0
- package/dist/cjs/collection/sync.cjs +108 -1
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/collection/sync.d.cts +2 -0
- package/dist/cjs/collection/transaction-metadata.cjs +5 -0
- package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
- package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +24 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +12 -3
- package/dist/cjs/indexes/auto-index.cjs +13 -6
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs +0 -3
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -6
- package/dist/cjs/indexes/basic-index.cjs +361 -0
- package/dist/cjs/indexes/basic-index.cjs.map +1 -0
- package/dist/cjs/indexes/basic-index.d.cts +102 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/indexes/index-options.d.cts +8 -9
- package/dist/cjs/indexes/index-registry.cjs +89 -0
- package/dist/cjs/indexes/index-registry.cjs.map +1 -0
- package/dist/cjs/indexes/index-registry.d.cts +61 -0
- package/dist/cjs/local-only.cjs +5 -0
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +27 -11
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +25 -3
- package/dist/cjs/query/builder/index.cjs +200 -39
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +4 -3
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
- package/dist/cjs/query/builder/types.d.cts +84 -19
- package/dist/cjs/query/compiler/evaluators.cjs +51 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +100 -28
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.d.cts +4 -2
- package/dist/cjs/query/compiler/index.cjs +283 -11
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +30 -2
- package/dist/cjs/query/compiler/order-by.cjs +29 -10
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/compiler/select.cjs +8 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/effect.cjs +602 -0
- package/dist/cjs/query/effect.cjs.map +1 -0
- package/dist/cjs/query/effect.d.cts +94 -0
- package/dist/cjs/query/index.d.cts +2 -1
- package/dist/cjs/query/ir.cjs +18 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +21 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +493 -66
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
- package/dist/cjs/query/live/collection-subscriber.cjs +33 -100
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/collection-subscriber.d.cts +0 -1
- package/dist/cjs/query/live/types.d.cts +3 -3
- package/dist/cjs/query/live/utils.cjs +219 -0
- package/dist/cjs/query/live/utils.cjs.map +1 -0
- package/dist/cjs/query/live/utils.d.cts +110 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +9 -6
- package/dist/cjs/query/query-once.cjs.map +1 -1
- package/dist/cjs/query/query-once.d.cts +7 -5
- package/dist/cjs/query/subset-dedupe.cjs +9 -3
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/types.d.cts +42 -8
- package/dist/cjs/utils/array-utils.cjs +27 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +16 -0
- package/dist/cjs/utils/comparison.cjs +11 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +4 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs +7 -9
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/cjs/virtual-props.cjs +33 -0
- package/dist/cjs/virtual-props.cjs.map +1 -0
- package/dist/cjs/virtual-props.d.cts +196 -0
- package/dist/esm/collection/change-events.d.ts +3 -2
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.d.ts +10 -1
- package/dist/esm/collection/changes.js +13 -4
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/cleanup-queue.d.ts +30 -0
- package/dist/esm/collection/cleanup-queue.js +89 -0
- package/dist/esm/collection/cleanup-queue.js.map +1 -0
- package/dist/esm/collection/events.d.ts +39 -1
- package/dist/esm/collection/events.js +14 -0
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +49 -36
- package/dist/esm/collection/index.js +67 -29
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.d.ts +27 -17
- package/dist/esm/collection/indexes.js +211 -62
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.d.ts +0 -1
- package/dist/esm/collection/lifecycle.js +5 -22
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.d.ts +1 -0
- package/dist/esm/collection/mutations.js +18 -0
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +65 -1
- package/dist/esm/collection/state.js +381 -53
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +4 -0
- package/dist/esm/collection/subscription.js +6 -0
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.d.ts +2 -0
- package/dist/esm/collection/sync.js +108 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/collection/transaction-metadata.d.ts +1 -0
- package/dist/esm/collection/transaction-metadata.js +5 -0
- package/dist/esm/collection/transaction-metadata.js.map +1 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +12 -3
- package/dist/esm/index.js +27 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +13 -6
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -6
- package/dist/esm/indexes/base-index.js +1 -4
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/basic-index.d.ts +102 -0
- package/dist/esm/indexes/basic-index.js +361 -0
- package/dist/esm/indexes/basic-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/index-options.d.ts +8 -9
- package/dist/esm/indexes/index-registry.d.ts +61 -0
- package/dist/esm/indexes/index-registry.js +89 -0
- package/dist/esm/indexes/index-registry.js.map +1 -0
- package/dist/esm/local-only.js +5 -0
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +25 -3
- package/dist/esm/query/builder/functions.js +27 -11
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +4 -3
- package/dist/esm/query/builder/index.js +201 -40
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +84 -19
- package/dist/esm/query/compiler/evaluators.js +51 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.d.ts +4 -2
- package/dist/esm/query/compiler/group-by.js +101 -29
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +30 -2
- package/dist/esm/query/compiler/index.js +285 -13
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js +30 -11
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +8 -0
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/effect.d.ts +94 -0
- package/dist/esm/query/effect.js +602 -0
- package/dist/esm/query/effect.js.map +1 -0
- package/dist/esm/query/index.d.ts +2 -1
- package/dist/esm/query/ir.d.ts +21 -1
- package/dist/esm/query/ir.js +18 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
- package/dist/esm/query/live/collection-config-builder.js +492 -65
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-subscriber.d.ts +0 -1
- package/dist/esm/query/live/collection-subscriber.js +31 -98
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +3 -3
- package/dist/esm/query/live/utils.d.ts +110 -0
- package/dist/esm/query/live/utils.js +219 -0
- package/dist/esm/query/live/utils.js.map +1 -0
- package/dist/esm/query/live-query-collection.d.ts +9 -6
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/query-once.d.ts +7 -5
- package/dist/esm/query/query-once.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js +9 -3
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/types.d.ts +42 -8
- package/dist/esm/utils/array-utils.d.ts +16 -0
- package/dist/esm/utils/array-utils.js +27 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.js +11 -0
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/index-optimization.js +4 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +7 -9
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/virtual-props.d.ts +196 -0
- package/dist/esm/virtual-props.js +33 -0
- package/dist/esm/virtual-props.js.map +1 -0
- package/package.json +2 -2
- package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
- package/src/collection/change-events.ts +13 -9
- package/src/collection/changes.ts +30 -7
- package/src/collection/cleanup-queue.ts +105 -0
- package/src/collection/events.ts +65 -0
- package/src/collection/index.ts +110 -45
- package/src/collection/indexes.ts +283 -76
- package/src/collection/lifecycle.ts +5 -26
- package/src/collection/mutations.ts +21 -0
- package/src/collection/state.ts +545 -71
- package/src/collection/subscription.ts +7 -0
- package/src/collection/sync.ts +137 -0
- package/src/collection/transaction-metadata.ts +1 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +57 -3
- package/src/indexes/auto-index.ts +18 -8
- package/src/indexes/base-index.ts +2 -10
- package/src/indexes/basic-index.ts +507 -0
- package/src/indexes/btree-index.ts +1 -1
- package/src/indexes/index-options.ts +17 -37
- package/src/indexes/index-registry.ts +174 -0
- package/src/local-only.ts +7 -0
- package/src/query/builder/functions.ts +84 -7
- package/src/query/builder/index.ts +329 -9
- package/src/query/builder/ref-proxy.ts +22 -4
- package/src/query/builder/types.ts +257 -62
- package/src/query/compiler/evaluators.ts +57 -0
- package/src/query/compiler/group-by.ts +156 -35
- package/src/query/compiler/index.ts +445 -15
- package/src/query/compiler/order-by.ts +51 -12
- package/src/query/compiler/select.ts +9 -0
- package/src/query/effect.ts +1119 -0
- package/src/query/index.ts +7 -0
- package/src/query/ir.ts +23 -2
- package/src/query/live/collection-config-builder.ts +778 -104
- package/src/query/live/collection-subscriber.ts +40 -156
- package/src/query/live/types.ts +10 -4
- package/src/query/live/utils.ts +417 -0
- package/src/query/live-query-collection.ts +43 -18
- package/src/query/query-once.ts +31 -12
- package/src/query/subset-dedupe.ts +11 -7
- package/src/types.ts +49 -9
- package/src/utils/array-utils.ts +49 -0
- package/src/utils/comparison.ts +14 -0
- package/src/utils/index-optimization.ts +4 -0
- package/src/utils.ts +12 -9
- package/src/virtual-props.ts +282 -0
- package/dist/cjs/indexes/lazy-index.cjs +0 -190
- package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
- package/dist/cjs/indexes/lazy-index.d.cts +0 -96
- package/dist/esm/indexes/lazy-index.d.ts +0 -96
- package/dist/esm/indexes/lazy-index.js +0 -190
- package/dist/esm/indexes/lazy-index.js.map +0 -1
- package/src/indexes/lazy-index.ts +0 -251
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { defaultComparator, normalizeValue } from '../utils/comparison.js'
|
|
2
|
+
import {
|
|
3
|
+
deleteInSortedArray,
|
|
4
|
+
findInsertPositionInArray,
|
|
5
|
+
} from '../utils/array-utils.js'
|
|
6
|
+
import { BaseIndex } from './base-index.js'
|
|
7
|
+
import type { CompareOptions } from '../query/builder/types.js'
|
|
8
|
+
import type { BasicExpression } from '../query/ir.js'
|
|
9
|
+
import type { IndexOperation } from './base-index.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Options for range queries
|
|
13
|
+
*/
|
|
14
|
+
export interface RangeQueryOptions {
|
|
15
|
+
from?: any
|
|
16
|
+
to?: any
|
|
17
|
+
fromInclusive?: boolean
|
|
18
|
+
toInclusive?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for Basic index
|
|
23
|
+
*/
|
|
24
|
+
export interface BasicIndexOptions {
|
|
25
|
+
compareFn?: (a: any, b: any) => number
|
|
26
|
+
compareOptions?: CompareOptions
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Basic index using Map + sorted Array.
|
|
31
|
+
*
|
|
32
|
+
* - Map for O(1) equality lookups
|
|
33
|
+
* - Sorted Array for O(log n) range queries via binary search
|
|
34
|
+
* - O(n) updates to maintain sort order
|
|
35
|
+
*
|
|
36
|
+
* Simpler and smaller than BTreeIndex, good for read-heavy workloads.
|
|
37
|
+
* Use BTreeIndex for write-heavy workloads with large collections.
|
|
38
|
+
*/
|
|
39
|
+
export class BasicIndex<
|
|
40
|
+
TKey extends string | number = string | number,
|
|
41
|
+
> extends BaseIndex<TKey> {
|
|
42
|
+
public readonly supportedOperations = new Set<IndexOperation>([
|
|
43
|
+
`eq`,
|
|
44
|
+
`gt`,
|
|
45
|
+
`gte`,
|
|
46
|
+
`lt`,
|
|
47
|
+
`lte`,
|
|
48
|
+
`in`,
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
// Map for O(1) equality lookups: indexedValue -> Set of PKs
|
|
52
|
+
private valueMap = new Map<any, Set<TKey>>()
|
|
53
|
+
// Sorted array of unique indexed values for range queries
|
|
54
|
+
private sortedValues: Array<any> = []
|
|
55
|
+
// Set of all indexed PKs
|
|
56
|
+
private indexedKeys = new Set<TKey>()
|
|
57
|
+
// Comparator function
|
|
58
|
+
private compareFn: (a: any, b: any) => number = defaultComparator
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
id: number,
|
|
62
|
+
expression: BasicExpression,
|
|
63
|
+
name?: string,
|
|
64
|
+
options?: any,
|
|
65
|
+
) {
|
|
66
|
+
super(id, expression, name, options)
|
|
67
|
+
this.compareFn = options?.compareFn ?? defaultComparator
|
|
68
|
+
if (options?.compareOptions) {
|
|
69
|
+
this.compareOptions = options!.compareOptions
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected initialize(_options?: BasicIndexOptions): void {}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Adds a value to the index
|
|
77
|
+
*/
|
|
78
|
+
add(key: TKey, item: any): void {
|
|
79
|
+
let indexedValue: any
|
|
80
|
+
try {
|
|
81
|
+
indexedValue = this.evaluateIndexExpression(item)
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Failed to evaluate index expression for key ${key}: ${error}`,
|
|
85
|
+
{ cause: error },
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const normalizedValue = normalizeValue(indexedValue)
|
|
90
|
+
|
|
91
|
+
if (this.valueMap.has(normalizedValue)) {
|
|
92
|
+
// Value already exists, just add the key to the set
|
|
93
|
+
this.valueMap.get(normalizedValue)!.add(key)
|
|
94
|
+
} else {
|
|
95
|
+
// New value - add to map and insert into sorted array
|
|
96
|
+
this.valueMap.set(normalizedValue, new Set([key]))
|
|
97
|
+
|
|
98
|
+
// Insert into sorted position
|
|
99
|
+
const insertIdx = findInsertPositionInArray(
|
|
100
|
+
this.sortedValues,
|
|
101
|
+
normalizedValue,
|
|
102
|
+
this.compareFn,
|
|
103
|
+
)
|
|
104
|
+
this.sortedValues.splice(insertIdx, 0, normalizedValue)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.indexedKeys.add(key)
|
|
108
|
+
this.updateTimestamp()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Removes a value from the index
|
|
113
|
+
*/
|
|
114
|
+
remove(key: TKey, item: any): void {
|
|
115
|
+
let indexedValue: any
|
|
116
|
+
try {
|
|
117
|
+
indexedValue = this.evaluateIndexExpression(item)
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.warn(
|
|
120
|
+
`Failed to evaluate index expression for key ${key} during removal:`,
|
|
121
|
+
error,
|
|
122
|
+
)
|
|
123
|
+
this.indexedKeys.delete(key)
|
|
124
|
+
this.updateTimestamp()
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const normalizedValue = normalizeValue(indexedValue)
|
|
129
|
+
|
|
130
|
+
if (this.valueMap.has(normalizedValue)) {
|
|
131
|
+
const keySet = this.valueMap.get(normalizedValue)!
|
|
132
|
+
keySet.delete(key)
|
|
133
|
+
|
|
134
|
+
if (keySet.size === 0) {
|
|
135
|
+
// No more keys for this value, remove from map and sorted array
|
|
136
|
+
this.valueMap.delete(normalizedValue)
|
|
137
|
+
deleteInSortedArray(this.sortedValues, normalizedValue, this.compareFn)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.indexedKeys.delete(key)
|
|
142
|
+
this.updateTimestamp()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Updates a value in the index
|
|
147
|
+
*/
|
|
148
|
+
update(key: TKey, oldItem: any, newItem: any): void {
|
|
149
|
+
this.remove(key, oldItem)
|
|
150
|
+
this.add(key, newItem)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Builds the index from a collection of entries
|
|
155
|
+
*/
|
|
156
|
+
build(entries: Iterable<[TKey, any]>): void {
|
|
157
|
+
this.clear()
|
|
158
|
+
|
|
159
|
+
// Collect all entries first
|
|
160
|
+
const entriesArray: Array<{ key: TKey; value: any }> = []
|
|
161
|
+
for (const [key, item] of entries) {
|
|
162
|
+
let indexedValue: any
|
|
163
|
+
try {
|
|
164
|
+
indexedValue = this.evaluateIndexExpression(item)
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Failed to evaluate index expression for key ${key}: ${error}`,
|
|
168
|
+
{ cause: error },
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
entriesArray.push({ key, value: normalizeValue(indexedValue) })
|
|
172
|
+
this.indexedKeys.add(key)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Group by value
|
|
176
|
+
for (const { key, value } of entriesArray) {
|
|
177
|
+
if (this.valueMap.has(value)) {
|
|
178
|
+
this.valueMap.get(value)!.add(key)
|
|
179
|
+
} else {
|
|
180
|
+
this.valueMap.set(value, new Set([key]))
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Build sorted array from unique values
|
|
185
|
+
this.sortedValues = Array.from(this.valueMap.keys()).sort(this.compareFn)
|
|
186
|
+
|
|
187
|
+
this.updateTimestamp()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Clears all data from the index
|
|
192
|
+
*/
|
|
193
|
+
clear(): void {
|
|
194
|
+
this.valueMap.clear()
|
|
195
|
+
this.sortedValues = []
|
|
196
|
+
this.indexedKeys.clear()
|
|
197
|
+
this.updateTimestamp()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Performs a lookup operation
|
|
202
|
+
*/
|
|
203
|
+
lookup(operation: IndexOperation, value: any): Set<TKey> {
|
|
204
|
+
const startTime = performance.now()
|
|
205
|
+
|
|
206
|
+
let result: Set<TKey>
|
|
207
|
+
|
|
208
|
+
switch (operation) {
|
|
209
|
+
case `eq`:
|
|
210
|
+
result = this.equalityLookup(value)
|
|
211
|
+
break
|
|
212
|
+
case `gt`:
|
|
213
|
+
result = this.rangeQuery({ from: value, fromInclusive: false })
|
|
214
|
+
break
|
|
215
|
+
case `gte`:
|
|
216
|
+
result = this.rangeQuery({ from: value, fromInclusive: true })
|
|
217
|
+
break
|
|
218
|
+
case `lt`:
|
|
219
|
+
result = this.rangeQuery({ to: value, toInclusive: false })
|
|
220
|
+
break
|
|
221
|
+
case `lte`:
|
|
222
|
+
result = this.rangeQuery({ to: value, toInclusive: true })
|
|
223
|
+
break
|
|
224
|
+
case `in`:
|
|
225
|
+
result = this.inArrayLookup(value)
|
|
226
|
+
break
|
|
227
|
+
default:
|
|
228
|
+
throw new Error(`Operation ${operation} not supported by BasicIndex`)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this.trackLookup(startTime)
|
|
232
|
+
return result
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Gets the number of indexed keys
|
|
237
|
+
*/
|
|
238
|
+
get keyCount(): number {
|
|
239
|
+
return this.indexedKeys.size
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Performs an equality lookup - O(1)
|
|
244
|
+
*/
|
|
245
|
+
equalityLookup(value: any): Set<TKey> {
|
|
246
|
+
const normalizedValue = normalizeValue(value)
|
|
247
|
+
return this.valueMap.get(normalizedValue) ?? new Set()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Performs a range query using binary search - O(log n + m)
|
|
252
|
+
*/
|
|
253
|
+
rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {
|
|
254
|
+
const { from, to, fromInclusive = true, toInclusive = true } = options
|
|
255
|
+
const result = new Set<TKey>()
|
|
256
|
+
|
|
257
|
+
if (this.sortedValues.length === 0) {
|
|
258
|
+
return result
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const normalizedFrom = normalizeValue(from)
|
|
262
|
+
const normalizedTo = normalizeValue(to)
|
|
263
|
+
|
|
264
|
+
// Find start index
|
|
265
|
+
let startIdx = 0
|
|
266
|
+
if (normalizedFrom !== undefined) {
|
|
267
|
+
startIdx = findInsertPositionInArray(
|
|
268
|
+
this.sortedValues,
|
|
269
|
+
normalizedFrom,
|
|
270
|
+
this.compareFn,
|
|
271
|
+
)
|
|
272
|
+
// If not inclusive and we found exact match, skip it
|
|
273
|
+
if (
|
|
274
|
+
!fromInclusive &&
|
|
275
|
+
startIdx < this.sortedValues.length &&
|
|
276
|
+
this.compareFn(this.sortedValues[startIdx], normalizedFrom) === 0
|
|
277
|
+
) {
|
|
278
|
+
startIdx++
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Find end index
|
|
283
|
+
let endIdx = this.sortedValues.length
|
|
284
|
+
if (normalizedTo !== undefined) {
|
|
285
|
+
endIdx = findInsertPositionInArray(
|
|
286
|
+
this.sortedValues,
|
|
287
|
+
normalizedTo,
|
|
288
|
+
this.compareFn,
|
|
289
|
+
)
|
|
290
|
+
// If inclusive and we found the value, include it
|
|
291
|
+
if (
|
|
292
|
+
toInclusive &&
|
|
293
|
+
endIdx < this.sortedValues.length &&
|
|
294
|
+
this.compareFn(this.sortedValues[endIdx], normalizedTo) === 0
|
|
295
|
+
) {
|
|
296
|
+
endIdx++
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Collect all keys in range
|
|
301
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
302
|
+
const keys = this.valueMap.get(this.sortedValues[i])
|
|
303
|
+
if (keys) {
|
|
304
|
+
keys.forEach((key) => result.add(key))
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return result
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Performs a reversed range query
|
|
313
|
+
*/
|
|
314
|
+
rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {
|
|
315
|
+
const { from, to, fromInclusive = true, toInclusive = true } = options
|
|
316
|
+
|
|
317
|
+
// Swap from/to and fromInclusive/toInclusive to handle reversed ranges
|
|
318
|
+
// If to is undefined, we want to start from the end (max value)
|
|
319
|
+
// If from is undefined, we want to end at the beginning (min value)
|
|
320
|
+
const swappedFrom =
|
|
321
|
+
to ??
|
|
322
|
+
(this.sortedValues.length > 0
|
|
323
|
+
? this.sortedValues[this.sortedValues.length - 1]
|
|
324
|
+
: undefined)
|
|
325
|
+
const swappedTo =
|
|
326
|
+
from ?? (this.sortedValues.length > 0 ? this.sortedValues[0] : undefined)
|
|
327
|
+
|
|
328
|
+
return this.rangeQuery({
|
|
329
|
+
from: swappedFrom,
|
|
330
|
+
to: swappedTo,
|
|
331
|
+
fromInclusive: toInclusive,
|
|
332
|
+
toInclusive: fromInclusive,
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Returns the next n items in sorted order
|
|
338
|
+
*/
|
|
339
|
+
take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey> {
|
|
340
|
+
const result: Array<TKey> = []
|
|
341
|
+
|
|
342
|
+
let startIdx = 0
|
|
343
|
+
if (from !== undefined) {
|
|
344
|
+
const normalizedFrom = normalizeValue(from)
|
|
345
|
+
startIdx = findInsertPositionInArray(
|
|
346
|
+
this.sortedValues,
|
|
347
|
+
normalizedFrom,
|
|
348
|
+
this.compareFn,
|
|
349
|
+
)
|
|
350
|
+
// Skip past the 'from' value (exclusive)
|
|
351
|
+
while (
|
|
352
|
+
startIdx < this.sortedValues.length &&
|
|
353
|
+
this.compareFn(this.sortedValues[startIdx], normalizedFrom) <= 0
|
|
354
|
+
) {
|
|
355
|
+
startIdx++
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for (
|
|
360
|
+
let i = startIdx;
|
|
361
|
+
i < this.sortedValues.length && result.length < n;
|
|
362
|
+
i++
|
|
363
|
+
) {
|
|
364
|
+
const keys = this.valueMap.get(this.sortedValues[i])
|
|
365
|
+
if (keys) {
|
|
366
|
+
for (const key of keys) {
|
|
367
|
+
if (result.length >= n) break
|
|
368
|
+
if (!filterFn || filterFn(key)) {
|
|
369
|
+
result.push(key)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return result
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Returns the next n items in reverse sorted order
|
|
380
|
+
*/
|
|
381
|
+
takeReversed(
|
|
382
|
+
n: number,
|
|
383
|
+
from?: any,
|
|
384
|
+
filterFn?: (key: TKey) => boolean,
|
|
385
|
+
): Array<TKey> {
|
|
386
|
+
const result: Array<TKey> = []
|
|
387
|
+
|
|
388
|
+
let startIdx = this.sortedValues.length - 1
|
|
389
|
+
if (from !== undefined) {
|
|
390
|
+
const normalizedFrom = normalizeValue(from)
|
|
391
|
+
startIdx =
|
|
392
|
+
findInsertPositionInArray(
|
|
393
|
+
this.sortedValues,
|
|
394
|
+
normalizedFrom,
|
|
395
|
+
this.compareFn,
|
|
396
|
+
) - 1
|
|
397
|
+
// Skip past the 'from' value (exclusive)
|
|
398
|
+
while (
|
|
399
|
+
startIdx >= 0 &&
|
|
400
|
+
this.compareFn(this.sortedValues[startIdx], normalizedFrom) >= 0
|
|
401
|
+
) {
|
|
402
|
+
startIdx--
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
for (let i = startIdx; i >= 0 && result.length < n; i--) {
|
|
407
|
+
const keys = this.valueMap.get(this.sortedValues[i])
|
|
408
|
+
if (keys) {
|
|
409
|
+
for (const key of keys) {
|
|
410
|
+
if (result.length >= n) break
|
|
411
|
+
if (!filterFn || filterFn(key)) {
|
|
412
|
+
result.push(key)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return result
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Returns the first n items in sorted order (from the start)
|
|
423
|
+
*/
|
|
424
|
+
takeFromStart(n: number, filterFn?: (key: TKey) => boolean): Array<TKey> {
|
|
425
|
+
const result: Array<TKey> = []
|
|
426
|
+
for (let i = 0; i < this.sortedValues.length && result.length < n; i++) {
|
|
427
|
+
const keys = this.valueMap.get(this.sortedValues[i])
|
|
428
|
+
if (keys) {
|
|
429
|
+
for (const key of keys) {
|
|
430
|
+
if (result.length >= n) break
|
|
431
|
+
if (!filterFn || filterFn(key)) {
|
|
432
|
+
result.push(key)
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return result
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Returns the first n items in reverse sorted order (from the end)
|
|
442
|
+
*/
|
|
443
|
+
takeReversedFromEnd(
|
|
444
|
+
n: number,
|
|
445
|
+
filterFn?: (key: TKey) => boolean,
|
|
446
|
+
): Array<TKey> {
|
|
447
|
+
const result: Array<TKey> = []
|
|
448
|
+
for (
|
|
449
|
+
let i = this.sortedValues.length - 1;
|
|
450
|
+
i >= 0 && result.length < n;
|
|
451
|
+
i--
|
|
452
|
+
) {
|
|
453
|
+
const keys = this.valueMap.get(this.sortedValues[i])
|
|
454
|
+
if (keys) {
|
|
455
|
+
for (const key of keys) {
|
|
456
|
+
if (result.length >= n) break
|
|
457
|
+
if (!filterFn || filterFn(key)) {
|
|
458
|
+
result.push(key)
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return result
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Performs an IN array lookup - O(k) where k is values.length
|
|
468
|
+
*/
|
|
469
|
+
inArrayLookup(values: Array<any>): Set<TKey> {
|
|
470
|
+
const result = new Set<TKey>()
|
|
471
|
+
|
|
472
|
+
for (const value of values) {
|
|
473
|
+
const normalizedValue = normalizeValue(value)
|
|
474
|
+
const keys = this.valueMap.get(normalizedValue)
|
|
475
|
+
if (keys) {
|
|
476
|
+
keys.forEach((key) => result.add(key))
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return result
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Getter methods for testing/compatibility
|
|
484
|
+
get indexedKeysSet(): Set<TKey> {
|
|
485
|
+
return this.indexedKeys
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
get orderedEntriesArray(): Array<[any, Set<TKey>]> {
|
|
489
|
+
return this.sortedValues.map((value) => [
|
|
490
|
+
value,
|
|
491
|
+
this.valueMap.get(value) ?? new Set(),
|
|
492
|
+
])
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]> {
|
|
496
|
+
const result: Array<[any, Set<TKey>]> = []
|
|
497
|
+
for (let i = this.sortedValues.length - 1; i >= 0; i--) {
|
|
498
|
+
const value = this.sortedValues[i]
|
|
499
|
+
result.push([value, this.valueMap.get(value) ?? new Set()])
|
|
500
|
+
}
|
|
501
|
+
return result
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
get valueMapData(): Map<any, Set<TKey>> {
|
|
505
|
+
return this.valueMap
|
|
506
|
+
}
|
|
507
|
+
}
|
|
@@ -33,7 +33,7 @@ export interface RangeQueryOptions {
|
|
|
33
33
|
* This maintains items in sorted order and provides efficient range operations
|
|
34
34
|
*/
|
|
35
35
|
export class BTreeIndex<
|
|
36
|
-
TKey extends string | number
|
|
36
|
+
TKey extends string | number = string | number,
|
|
37
37
|
> extends BaseIndex<TKey> {
|
|
38
38
|
public readonly supportedOperations = new Set<IndexOperation>([
|
|
39
39
|
`eq`,
|
|
@@ -1,42 +1,22 @@
|
|
|
1
|
-
import type { IndexConstructor
|
|
1
|
+
import type { IndexConstructor } from './base-index.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Options for creating an index
|
|
5
5
|
*/
|
|
6
|
-
export interface IndexOptions<
|
|
6
|
+
export interface IndexOptions<
|
|
7
|
+
TIndexType extends IndexConstructor = IndexConstructor,
|
|
8
|
+
> {
|
|
9
|
+
/** Optional name for the index */
|
|
7
10
|
name?: string
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
:
|
|
19
|
-
? TCtor extends new (
|
|
20
|
-
id: number,
|
|
21
|
-
expr: any,
|
|
22
|
-
name?: string,
|
|
23
|
-
options?: infer O,
|
|
24
|
-
) => any
|
|
25
|
-
? O
|
|
26
|
-
: never
|
|
27
|
-
: never
|
|
11
|
+
/** Index type to use (e.g., BasicIndex, BTreeIndex) */
|
|
12
|
+
indexType?: TIndexType
|
|
13
|
+
/** Options passed to the index constructor */
|
|
14
|
+
options?: TIndexType extends new (
|
|
15
|
+
id: number,
|
|
16
|
+
expr: any,
|
|
17
|
+
name?: string,
|
|
18
|
+
options?: infer O,
|
|
19
|
+
) => any
|
|
20
|
+
? O
|
|
21
|
+
: never
|
|
28
22
|
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Utility type to extract the constructed index type from a resolver
|
|
32
|
-
*/
|
|
33
|
-
export type ResolvedIndexType<TResolver extends IndexResolver> =
|
|
34
|
-
TResolver extends IndexConstructor<any>
|
|
35
|
-
? InstanceType<TResolver>
|
|
36
|
-
: TResolver extends () => Promise<IndexConstructor<any>>
|
|
37
|
-
? TResolver extends () => Promise<infer TCtor>
|
|
38
|
-
? TCtor extends IndexConstructor<any>
|
|
39
|
-
? InstanceType<TCtor>
|
|
40
|
-
: never
|
|
41
|
-
: never
|
|
42
|
-
: never
|