@tanstack/db 0.0.29 → 0.0.30
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.cjs +6 -6
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +4 -4
- package/dist/cjs/index.cjs +2 -2
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/indexes/auto-index.cjs +2 -2
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/{ordered-index.cjs → btree-index.cjs} +27 -63
- package/dist/cjs/indexes/btree-index.cjs.map +1 -0
- package/dist/{esm/indexes/ordered-index.d.ts → cjs/indexes/btree-index.d.cts} +6 -4
- package/dist/cjs/utils/btree.cjs +677 -0
- package/dist/cjs/utils/btree.cjs.map +1 -0
- package/dist/cjs/utils/btree.d.cts +197 -0
- package/dist/esm/collection.d.ts +4 -4
- package/dist/esm/collection.js +6 -6
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/indexes/auto-index.js +2 -2
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/{cjs/indexes/ordered-index.d.cts → esm/indexes/btree-index.d.ts} +6 -4
- package/dist/esm/indexes/{ordered-index.js → btree-index.js} +27 -63
- package/dist/esm/indexes/btree-index.js.map +1 -0
- package/dist/esm/utils/btree.d.ts +197 -0
- package/dist/esm/utils/btree.js +677 -0
- package/dist/esm/utils/btree.js.map +1 -0
- package/package.json +1 -1
- package/src/collection.ts +9 -11
- package/src/index.ts +1 -1
- package/src/indexes/auto-index.ts +2 -2
- package/src/indexes/{ordered-index.ts → btree-index.ts} +42 -84
- package/src/utils/btree.ts +1010 -0
- package/dist/cjs/indexes/ordered-index.cjs.map +0 -1
- package/dist/cjs/utils/array-utils.cjs +0 -18
- package/dist/cjs/utils/array-utils.cjs.map +0 -1
- package/dist/esm/indexes/ordered-index.js.map +0 -1
- package/dist/esm/utils/array-utils.js +0 -18
- package/dist/esm/utils/array-utils.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ascComparator } from "../utils/comparison.js";
|
|
2
|
-
import {
|
|
2
|
+
import { BTree } from "../utils/btree.js";
|
|
3
3
|
import { BaseIndex } from "./base-index.js";
|
|
4
|
-
class
|
|
5
|
-
constructor() {
|
|
6
|
-
super(
|
|
4
|
+
class BTreeIndex extends BaseIndex {
|
|
5
|
+
constructor(id, expression, name, options) {
|
|
6
|
+
super(id, expression, name, options);
|
|
7
7
|
this.supportedOperations = /* @__PURE__ */ new Set([
|
|
8
8
|
`eq`,
|
|
9
9
|
`gt`,
|
|
@@ -12,13 +12,13 @@ class OrderedIndex extends BaseIndex {
|
|
|
12
12
|
`lte`,
|
|
13
13
|
`in`
|
|
14
14
|
]);
|
|
15
|
-
this.orderedEntries = [];
|
|
16
15
|
this.valueMap = /* @__PURE__ */ new Map();
|
|
17
16
|
this.indexedKeys = /* @__PURE__ */ new Set();
|
|
18
17
|
this.compareFn = ascComparator;
|
|
19
|
-
}
|
|
20
|
-
initialize(options) {
|
|
21
18
|
this.compareFn = (options == null ? void 0 : options.compareFn) ?? ascComparator;
|
|
19
|
+
this.orderedEntries = new BTree(this.compareFn);
|
|
20
|
+
}
|
|
21
|
+
initialize(_options) {
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Adds a value to the index
|
|
@@ -37,12 +37,7 @@ class OrderedIndex extends BaseIndex {
|
|
|
37
37
|
} else {
|
|
38
38
|
const keySet = /* @__PURE__ */ new Set([key]);
|
|
39
39
|
this.valueMap.set(indexedValue, keySet);
|
|
40
|
-
|
|
41
|
-
this.orderedEntries,
|
|
42
|
-
indexedValue,
|
|
43
|
-
this.compareFn
|
|
44
|
-
);
|
|
45
|
-
this.orderedEntries.splice(insertIndex, 0, [indexedValue, keySet]);
|
|
40
|
+
this.orderedEntries.set(indexedValue, void 0);
|
|
46
41
|
}
|
|
47
42
|
this.indexedKeys.add(key);
|
|
48
43
|
this.updateTimestamp();
|
|
@@ -66,12 +61,7 @@ class OrderedIndex extends BaseIndex {
|
|
|
66
61
|
keySet.delete(key);
|
|
67
62
|
if (keySet.size === 0) {
|
|
68
63
|
this.valueMap.delete(indexedValue);
|
|
69
|
-
|
|
70
|
-
([value]) => this.compareFn(value, indexedValue) === 0
|
|
71
|
-
);
|
|
72
|
-
if (index !== -1) {
|
|
73
|
-
this.orderedEntries.splice(index, 1);
|
|
74
|
-
}
|
|
64
|
+
this.orderedEntries.delete(indexedValue);
|
|
75
65
|
}
|
|
76
66
|
}
|
|
77
67
|
this.indexedKeys.delete(key);
|
|
@@ -97,7 +87,7 @@ class OrderedIndex extends BaseIndex {
|
|
|
97
87
|
* Clears all data from the index
|
|
98
88
|
*/
|
|
99
89
|
clear() {
|
|
100
|
-
this.orderedEntries
|
|
90
|
+
this.orderedEntries.clear();
|
|
101
91
|
this.valueMap.clear();
|
|
102
92
|
this.indexedKeys.clear();
|
|
103
93
|
this.updateTimestamp();
|
|
@@ -128,7 +118,7 @@ class OrderedIndex extends BaseIndex {
|
|
|
128
118
|
result = this.inArrayLookup(value);
|
|
129
119
|
break;
|
|
130
120
|
default:
|
|
131
|
-
throw new Error(`Operation ${operation} not supported by
|
|
121
|
+
throw new Error(`Operation ${operation} not supported by BTreeIndex`);
|
|
132
122
|
}
|
|
133
123
|
this.trackLookup(startTime);
|
|
134
124
|
return result;
|
|
@@ -153,48 +143,22 @@ class OrderedIndex extends BaseIndex {
|
|
|
153
143
|
rangeQuery(options = {}) {
|
|
154
144
|
const { from, to, fromInclusive = true, toInclusive = true } = options;
|
|
155
145
|
const result = /* @__PURE__ */ new Set();
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
from
|
|
164
|
-
|
|
165
|
-
);
|
|
166
|
-
if (fromInclusive) {
|
|
167
|
-
startIndex = fromInsertIndex;
|
|
168
|
-
} else {
|
|
169
|
-
startIndex = fromInsertIndex;
|
|
170
|
-
if (startIndex < this.orderedEntries.length && this.compareFn(this.orderedEntries[startIndex][0], from) === 0) {
|
|
171
|
-
startIndex++;
|
|
146
|
+
const fromKey = from ?? this.orderedEntries.minKey();
|
|
147
|
+
const toKey = to ?? this.orderedEntries.maxKey();
|
|
148
|
+
this.orderedEntries.forRange(
|
|
149
|
+
fromKey,
|
|
150
|
+
toKey,
|
|
151
|
+
toInclusive,
|
|
152
|
+
(indexedValue, _) => {
|
|
153
|
+
if (!fromInclusive && this.compareFn(indexedValue, from) === 0) {
|
|
154
|
+
return;
|
|
172
155
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (to !== void 0) {
|
|
177
|
-
const toInsertIndex = findInsertPosition(
|
|
178
|
-
this.orderedEntries,
|
|
179
|
-
to,
|
|
180
|
-
this.compareFn
|
|
181
|
-
);
|
|
182
|
-
if (toInclusive) {
|
|
183
|
-
endIndex = toInsertIndex;
|
|
184
|
-
if (toInsertIndex < this.orderedEntries.length && this.compareFn(this.orderedEntries[toInsertIndex][0], to) === 0) {
|
|
185
|
-
endIndex = toInsertIndex + 1;
|
|
156
|
+
const keys = this.valueMap.get(indexedValue);
|
|
157
|
+
if (keys) {
|
|
158
|
+
keys.forEach((key) => result.add(key));
|
|
186
159
|
}
|
|
187
|
-
} else {
|
|
188
|
-
endIndex = toInsertIndex;
|
|
189
160
|
}
|
|
190
|
-
|
|
191
|
-
if (startIndex >= endIndex) {
|
|
192
|
-
return result;
|
|
193
|
-
}
|
|
194
|
-
for (let i = startIndex; i < endIndex; i++) {
|
|
195
|
-
const keys = this.orderedEntries[i][1];
|
|
196
|
-
keys.forEach((key) => result.add(key));
|
|
197
|
-
}
|
|
161
|
+
);
|
|
198
162
|
return result;
|
|
199
163
|
}
|
|
200
164
|
/**
|
|
@@ -215,13 +179,13 @@ class OrderedIndex extends BaseIndex {
|
|
|
215
179
|
return this.indexedKeys;
|
|
216
180
|
}
|
|
217
181
|
get orderedEntriesArray() {
|
|
218
|
-
return this.orderedEntries;
|
|
182
|
+
return this.orderedEntries.keysArray().map((key) => [key, this.valueMap.get(key) ?? /* @__PURE__ */ new Set()]);
|
|
219
183
|
}
|
|
220
184
|
get valueMapData() {
|
|
221
185
|
return this.valueMap;
|
|
222
186
|
}
|
|
223
187
|
}
|
|
224
188
|
export {
|
|
225
|
-
|
|
189
|
+
BTreeIndex
|
|
226
190
|
};
|
|
227
|
-
//# sourceMappingURL=
|
|
191
|
+
//# sourceMappingURL=btree-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"btree-index.js","sources":["../../../src/indexes/btree-index.ts"],"sourcesContent":["import { ascComparator } from \"../utils/comparison.js\"\nimport { BTree } from \"../utils/btree.js\"\nimport { BaseIndex } from \"./base-index.js\"\nimport type { BasicExpression } from \"../query/ir.js\"\nimport type { IndexOperation } from \"./base-index.js\"\n\n/**\n * Options for Ordered index\n */\nexport interface BTreeIndexOptions {\n compareFn?: (a: any, b: any) => number\n}\n\n/**\n * Options for range queries\n */\nexport interface RangeQueryOptions {\n from?: any\n to?: any\n fromInclusive?: boolean\n toInclusive?: boolean\n}\n\n/**\n * B+Tree index for sorted data with range queries\n * This maintains items in sorted order and provides efficient range operations\n */\nexport class BTreeIndex<\n TKey extends string | number = string | number,\n> extends BaseIndex<TKey> {\n public readonly supportedOperations = new Set<IndexOperation>([\n `eq`,\n `gt`,\n `gte`,\n `lt`,\n `lte`,\n `in`,\n ])\n\n // Internal data structures - private to hide implementation details\n // The `orderedEntries` B+ tree is used for efficient range queries\n // The `valueMap` is used for O(1) lookups of PKs by indexed value\n private orderedEntries: BTree<any, undefined> // we don't associate values with the keys of the B+ tree (the keys are indexed values)\n private valueMap = new Map<any, Set<TKey>>() // instead we store a mapping of indexed values to a set of PKs\n private indexedKeys = new Set<TKey>()\n private compareFn: (a: any, b: any) => number = ascComparator\n\n constructor(\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any\n ) {\n super(id, expression, name, options)\n this.compareFn = options?.compareFn ?? ascComparator\n this.orderedEntries = new BTree(this.compareFn)\n }\n\n protected initialize(_options?: BTreeIndexOptions): void {}\n\n /**\n * Adds a value to the index\n */\n add(key: TKey, item: any): void {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n throw new Error(\n `Failed to evaluate index expression for key ${key}: ${error}`\n )\n }\n\n // Check if this value already exists\n if (this.valueMap.has(indexedValue)) {\n // Add to existing set\n this.valueMap.get(indexedValue)!.add(key)\n } else {\n // Create new set for this value\n const keySet = new Set<TKey>([key])\n this.valueMap.set(indexedValue, keySet)\n this.orderedEntries.set(indexedValue, undefined)\n }\n\n this.indexedKeys.add(key)\n this.updateTimestamp()\n }\n\n /**\n * Removes a value from the index\n */\n remove(key: TKey, item: any): void {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n console.warn(\n `Failed to evaluate index expression for key ${key} during removal:`,\n error\n )\n return\n }\n\n if (this.valueMap.has(indexedValue)) {\n const keySet = this.valueMap.get(indexedValue)!\n keySet.delete(key)\n\n // If set is now empty, remove the entry entirely\n if (keySet.size === 0) {\n this.valueMap.delete(indexedValue)\n\n // Remove from ordered entries\n this.orderedEntries.delete(indexedValue)\n }\n }\n\n this.indexedKeys.delete(key)\n this.updateTimestamp()\n }\n\n /**\n * Updates a value in the index\n */\n update(key: TKey, oldItem: any, newItem: any): void {\n this.remove(key, oldItem)\n this.add(key, newItem)\n }\n\n /**\n * Builds the index from a collection of entries\n */\n build(entries: Iterable<[TKey, any]>): void {\n this.clear()\n\n for (const [key, item] of entries) {\n this.add(key, item)\n }\n }\n\n /**\n * Clears all data from the index\n */\n clear(): void {\n this.orderedEntries.clear()\n this.valueMap.clear()\n this.indexedKeys.clear()\n this.updateTimestamp()\n }\n\n /**\n * Performs a lookup operation\n */\n lookup(operation: IndexOperation, value: any): Set<TKey> {\n const startTime = performance.now()\n\n let result: Set<TKey>\n\n switch (operation) {\n case `eq`:\n result = this.equalityLookup(value)\n break\n case `gt`:\n result = this.rangeQuery({ from: value, fromInclusive: false })\n break\n case `gte`:\n result = this.rangeQuery({ from: value, fromInclusive: true })\n break\n case `lt`:\n result = this.rangeQuery({ to: value, toInclusive: false })\n break\n case `lte`:\n result = this.rangeQuery({ to: value, toInclusive: true })\n break\n case `in`:\n result = this.inArrayLookup(value)\n break\n default:\n throw new Error(`Operation ${operation} not supported by BTreeIndex`)\n }\n\n this.trackLookup(startTime)\n return result\n }\n\n /**\n * Gets the number of indexed keys\n */\n get keyCount(): number {\n return this.indexedKeys.size\n }\n\n // Public methods for backward compatibility (used by tests)\n\n /**\n * Performs an equality lookup\n */\n equalityLookup(value: any): Set<TKey> {\n return new Set(this.valueMap.get(value) ?? [])\n }\n\n /**\n * Performs a range query with options\n * This is more efficient for compound queries like \"WHERE a > 5 AND a < 10\"\n */\n rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {\n const { from, to, fromInclusive = true, toInclusive = true } = options\n const result = new Set<TKey>()\n\n const fromKey = from ?? this.orderedEntries.minKey()\n const toKey = to ?? this.orderedEntries.maxKey()\n\n this.orderedEntries.forRange(\n fromKey,\n toKey,\n toInclusive,\n (indexedValue, _) => {\n if (!fromInclusive && this.compareFn(indexedValue, from) === 0) {\n // the B+ tree `forRange` method does not support exclusive lower bounds\n // so we need to exclude it manually\n return\n }\n\n const keys = this.valueMap.get(indexedValue)\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n )\n\n return result\n }\n\n /**\n * Performs an IN array lookup\n */\n inArrayLookup(values: Array<any>): Set<TKey> {\n const result = new Set<TKey>()\n\n for (const value of values) {\n const keys = this.valueMap.get(value)\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n\n return result\n }\n\n // Getter methods for testing compatibility\n get indexedKeysSet(): Set<TKey> {\n return this.indexedKeys\n }\n\n get orderedEntriesArray(): Array<[any, Set<TKey>]> {\n return this.orderedEntries\n .keysArray()\n .map((key) => [key, this.valueMap.get(key) ?? new Set()])\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n return this.valueMap\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
type EditRangeResult<V, R = number> = {
|
|
2
|
+
value?: V;
|
|
3
|
+
break?: R;
|
|
4
|
+
delete?: boolean;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* A reasonably fast collection of key-value pairs with a powerful API.
|
|
8
|
+
* Largely compatible with the standard Map. BTree is a B+ tree data structure,
|
|
9
|
+
* so the collection is sorted by key.
|
|
10
|
+
*
|
|
11
|
+
* B+ trees tend to use memory more efficiently than hashtables such as the
|
|
12
|
+
* standard Map, especially when the collection contains a large number of
|
|
13
|
+
* items. However, maintaining the sort order makes them modestly slower:
|
|
14
|
+
* O(log size) rather than O(1). This B+ tree implementation supports O(1)
|
|
15
|
+
* fast cloning. It also supports freeze(), which can be used to ensure that
|
|
16
|
+
* a BTree is not changed accidentally.
|
|
17
|
+
*
|
|
18
|
+
* Confusingly, the ES6 Map.forEach(c) method calls c(value,key) instead of
|
|
19
|
+
* c(key,value), in contrast to other methods such as set() and entries()
|
|
20
|
+
* which put the key first. I can only assume that the order was reversed on
|
|
21
|
+
* the theory that users would usually want to examine values and ignore keys.
|
|
22
|
+
* BTree's forEach() therefore works the same way, but a second method
|
|
23
|
+
* `.forEachPair((key,value)=>{...})` is provided which sends you the key
|
|
24
|
+
* first and the value second; this method is slightly faster because it is
|
|
25
|
+
* the "native" for-each method for this class.
|
|
26
|
+
*
|
|
27
|
+
* Out of the box, BTree supports keys that are numbers, strings, arrays of
|
|
28
|
+
* numbers/strings, Date, and objects that have a valueOf() method returning a
|
|
29
|
+
* number or string. Other data types, such as arrays of Date or custom
|
|
30
|
+
* objects, require a custom comparator, which you must pass as the second
|
|
31
|
+
* argument to the constructor (the first argument is an optional list of
|
|
32
|
+
* initial items). Symbols cannot be used as keys because they are unordered
|
|
33
|
+
* (one Symbol is never "greater" or "less" than another).
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* Given a {name: string, age: number} object, you can create a tree sorted by
|
|
37
|
+
* name and then by age like this:
|
|
38
|
+
*
|
|
39
|
+
* var tree = new BTree(undefined, (a, b) => {
|
|
40
|
+
* if (a.name > b.name)
|
|
41
|
+
* return 1; // Return a number >0 when a > b
|
|
42
|
+
* else if (a.name < b.name)
|
|
43
|
+
* return -1; // Return a number <0 when a < b
|
|
44
|
+
* else // names are equal (or incomparable)
|
|
45
|
+
* return a.age - b.age; // Return >0 when a.age > b.age
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* tree.set({name:"Bill", age:17}, "happy");
|
|
49
|
+
* tree.set({name:"Fran", age:40}, "busy & stressed");
|
|
50
|
+
* tree.set({name:"Bill", age:55}, "recently laid off");
|
|
51
|
+
* tree.forEachPair((k, v) => {
|
|
52
|
+
* console.log(`Name: ${k.name} Age: ${k.age} Status: ${v}`);
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* @description
|
|
56
|
+
* The "range" methods (`forEach, forRange, editRange`) will return the number
|
|
57
|
+
* of elements that were scanned. In addition, the callback can return {break:R}
|
|
58
|
+
* to stop early and return R from the outer function.
|
|
59
|
+
*
|
|
60
|
+
* - TODO: Test performance of preallocating values array at max size
|
|
61
|
+
* - TODO: Add fast initialization when a sorted array is provided to constructor
|
|
62
|
+
*
|
|
63
|
+
* For more documentation see https://github.com/qwertie/btree-typescript
|
|
64
|
+
*
|
|
65
|
+
* Are you a C# developer? You might like the similar data structures I made for C#:
|
|
66
|
+
* BDictionary, BList, etc. See http://core.loyc.net/collections/
|
|
67
|
+
*
|
|
68
|
+
* @author David Piepgrass
|
|
69
|
+
*/
|
|
70
|
+
export declare class BTree<K = any, V = any> {
|
|
71
|
+
private _root;
|
|
72
|
+
_size: number;
|
|
73
|
+
_maxNodeSize: number;
|
|
74
|
+
/**
|
|
75
|
+
* provides a total order over keys (and a strict partial order over the type K)
|
|
76
|
+
* @returns a negative value if a < b, 0 if a === b and a positive value if a > b
|
|
77
|
+
*/
|
|
78
|
+
_compare: (a: K, b: K) => number;
|
|
79
|
+
/**
|
|
80
|
+
* Initializes an empty B+ tree.
|
|
81
|
+
* @param compare Custom function to compare pairs of elements in the tree.
|
|
82
|
+
* If not specified, defaultComparator will be used which is valid as long as K extends DefaultComparable.
|
|
83
|
+
* @param entries A set of key-value pairs to initialize the tree
|
|
84
|
+
* @param maxNodeSize Branching factor (maximum items or children per node)
|
|
85
|
+
* Must be in range 4..256. If undefined or <4 then default is used; if >256 then 256.
|
|
86
|
+
*/
|
|
87
|
+
constructor(compare: (a: K, b: K) => number, entries?: Array<[K, V]>, maxNodeSize?: number);
|
|
88
|
+
/** Gets the number of key-value pairs in the tree. */
|
|
89
|
+
get size(): number;
|
|
90
|
+
/** Gets the number of key-value pairs in the tree. */
|
|
91
|
+
get length(): number;
|
|
92
|
+
/** Returns true iff the tree contains no key-value pairs. */
|
|
93
|
+
get isEmpty(): boolean;
|
|
94
|
+
/** Releases the tree so that its size is 0. */
|
|
95
|
+
clear(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Finds a pair in the tree and returns the associated value.
|
|
98
|
+
* @param defaultValue a value to return if the key was not found.
|
|
99
|
+
* @returns the value, or defaultValue if the key was not found.
|
|
100
|
+
* @description Computational complexity: O(log size)
|
|
101
|
+
*/
|
|
102
|
+
get(key: K, defaultValue?: V): V | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Adds or overwrites a key-value pair in the B+ tree.
|
|
105
|
+
* @param key the key is used to determine the sort order of
|
|
106
|
+
* data in the tree.
|
|
107
|
+
* @param value data to associate with the key (optional)
|
|
108
|
+
* @param overwrite Whether to overwrite an existing key-value pair
|
|
109
|
+
* (default: true). If this is false and there is an existing
|
|
110
|
+
* key-value pair then this method has no effect.
|
|
111
|
+
* @returns true if a new key-value pair was added.
|
|
112
|
+
* @description Computational complexity: O(log size)
|
|
113
|
+
* Note: when overwriting a previous entry, the key is updated
|
|
114
|
+
* as well as the value. This has no effect unless the new key
|
|
115
|
+
* has data that does not affect its sort order.
|
|
116
|
+
*/
|
|
117
|
+
set(key: K, value: V, overwrite?: boolean): boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Returns true if the key exists in the B+ tree, false if not.
|
|
120
|
+
* Use get() for best performance; use has() if you need to
|
|
121
|
+
* distinguish between "undefined value" and "key not present".
|
|
122
|
+
* @param key Key to detect
|
|
123
|
+
* @description Computational complexity: O(log size)
|
|
124
|
+
*/
|
|
125
|
+
has(key: K): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Removes a single key-value pair from the B+ tree.
|
|
128
|
+
* @param key Key to find
|
|
129
|
+
* @returns true if a pair was found and removed, false otherwise.
|
|
130
|
+
* @description Computational complexity: O(log size)
|
|
131
|
+
*/
|
|
132
|
+
delete(key: K): boolean;
|
|
133
|
+
/** Returns the maximum number of children/values before nodes will split. */
|
|
134
|
+
get maxNodeSize(): number;
|
|
135
|
+
/** Gets the lowest key in the tree. Complexity: O(log size) */
|
|
136
|
+
minKey(): K | undefined;
|
|
137
|
+
/** Gets the highest key in the tree. Complexity: O(1) */
|
|
138
|
+
maxKey(): K | undefined;
|
|
139
|
+
/** Gets an array of all keys, sorted */
|
|
140
|
+
keysArray(): K[];
|
|
141
|
+
/** Returns the next pair whose key is larger than the specified key (or undefined if there is none).
|
|
142
|
+
* If key === undefined, this function returns the lowest pair.
|
|
143
|
+
* @param key The key to search for.
|
|
144
|
+
* @param reusedArray Optional array used repeatedly to store key-value pairs, to
|
|
145
|
+
* avoid creating a new array on every iteration.
|
|
146
|
+
*/
|
|
147
|
+
nextHigherPair(key: K | undefined, reusedArray?: [K, V]): [K, V] | undefined;
|
|
148
|
+
/** Returns the next pair whose key is smaller than the specified key (or undefined if there is none).
|
|
149
|
+
* If key === undefined, this function returns the highest pair.
|
|
150
|
+
* @param key The key to search for.
|
|
151
|
+
* @param reusedArray Optional array used repeatedly to store key-value pairs, to
|
|
152
|
+
* avoid creating a new array each time you call this method.
|
|
153
|
+
*/
|
|
154
|
+
nextLowerPair(key: K | undefined, reusedArray?: [K, V]): [K, V] | undefined;
|
|
155
|
+
/** Adds all pairs from a list of key-value pairs.
|
|
156
|
+
* @param pairs Pairs to add to this tree. If there are duplicate keys,
|
|
157
|
+
* later pairs currently overwrite earlier ones (e.g. [[0,1],[0,7]]
|
|
158
|
+
* associates 0 with 7.)
|
|
159
|
+
* @param overwrite Whether to overwrite pairs that already exist (if false,
|
|
160
|
+
* pairs[i] is ignored when the key pairs[i][0] already exists.)
|
|
161
|
+
* @returns The number of pairs added to the collection.
|
|
162
|
+
* @description Computational complexity: O(pairs.length * log(size + pairs.length))
|
|
163
|
+
*/
|
|
164
|
+
setPairs(pairs: Array<[K, V]>, overwrite?: boolean): number;
|
|
165
|
+
forRange(low: K, high: K, includeHigh: boolean, onFound?: (k: K, v: V, counter: number) => void, initialCounter?: number): number;
|
|
166
|
+
/**
|
|
167
|
+
* Scans and potentially modifies values for a subsequence of keys.
|
|
168
|
+
* Note: the callback `onFound` should ideally be a pure function.
|
|
169
|
+
* Specfically, it must not insert items, call clone(), or change
|
|
170
|
+
* the collection except via return value; out-of-band editing may
|
|
171
|
+
* cause an exception or may cause incorrect data to be sent to
|
|
172
|
+
* the callback (duplicate or missed items). It must not cause a
|
|
173
|
+
* clone() of the collection, otherwise the clone could be modified
|
|
174
|
+
* by changes requested by the callback.
|
|
175
|
+
* @param low The first key scanned will be greater than or equal to `low`.
|
|
176
|
+
* @param high Scanning stops when a key larger than this is reached.
|
|
177
|
+
* @param includeHigh If the `high` key is present, `onFound` is called for
|
|
178
|
+
* that final pair if and only if this parameter is true.
|
|
179
|
+
* @param onFound A function that is called for each key-value pair. This
|
|
180
|
+
* function can return `{value:v}` to change the value associated
|
|
181
|
+
* with the current key, `{delete:true}` to delete the current pair,
|
|
182
|
+
* `{break:R}` to stop early with result R, or it can return nothing
|
|
183
|
+
* (undefined or {}) to cause no effect and continue iterating.
|
|
184
|
+
* `{break:R}` can be combined with one of the other two commands.
|
|
185
|
+
* The third argument `counter` is the number of items iterated
|
|
186
|
+
* previously; it equals 0 when `onFound` is called the first time.
|
|
187
|
+
* @returns The number of values scanned, or R if the callback returned
|
|
188
|
+
* `{break:R}` to stop early.
|
|
189
|
+
* @description
|
|
190
|
+
* Computational complexity: O(number of items scanned + log size)
|
|
191
|
+
* Note: if the tree has been cloned with clone(), any shared
|
|
192
|
+
* nodes are copied before `onFound` is called. This takes O(n) time
|
|
193
|
+
* where n is proportional to the amount of shared data scanned.
|
|
194
|
+
*/
|
|
195
|
+
editRange<R = V>(low: K, high: K, includeHigh: boolean, onFound: (k: K, v: V, counter: number) => EditRangeResult<V, R> | void, initialCounter?: number): R | number;
|
|
196
|
+
}
|
|
197
|
+
export {};
|