serializable-bptree 1.1.0 → 2.0.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/README.md +22 -18
- package/dist/cjs/index.js +140 -254
- package/dist/esm/index.js +140 -254
- package/dist/typings/BPTree.d.ts +27 -26
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ tree.where({ gt: 0, lt: 4 }) // [{ key: 'a', value: 1 }, { key: 'c', value: 3 }]
|
|
|
59
59
|
|
|
60
60
|
## Why use a `serializable-bptree`?
|
|
61
61
|
|
|
62
|
-
Firstly, in most cases, there is no need to use a B+tree in JavaScript. This is because there is a great alternative, the Map object. Nonetheless, if you need to retrieve values in a sorted order, a B+tree can be a good solution. These cases are often related to databases, and you may want to store this state not just in memory, but on a remote server or in a file. In this case,
|
|
62
|
+
Firstly, in most cases, there is no need to use a B+tree in JavaScript. This is because there is a great alternative, the Map object. Nonetheless, if you need to retrieve values in a sorted order, a B+tree can be a good solution. These cases are often related to databases, and you may want to store this state not just in memory, but on a remote server or in a file. In this case, **serializable-bptree** can help you.
|
|
63
63
|
|
|
64
64
|
## How to use
|
|
65
65
|
|
|
@@ -77,7 +77,7 @@ import { BPTree } from 'serializable-bptree'
|
|
|
77
77
|
|
|
78
78
|
```html
|
|
79
79
|
<script type="module">
|
|
80
|
-
import { BPTree } from 'https://cdn.jsdelivr.net/npm/serializable-bptree@1.x.x/dist/esm/index.min.js'
|
|
80
|
+
import { BPTree, ValueComparator, NumericComparator, StringComparator, InMemoryStoreStrategy } from 'https://cdn.jsdelivr.net/npm/serializable-bptree@1.x.x/dist/esm/index.min.js'
|
|
81
81
|
</script>
|
|
82
82
|
```
|
|
83
83
|
|
|
@@ -85,15 +85,15 @@ import { BPTree } from 'serializable-bptree'
|
|
|
85
85
|
|
|
86
86
|
### Value comparator
|
|
87
87
|
|
|
88
|
-
B+tree needs to keep values in sorted order. Therefore, a process to compare the sizes of values is needed, and that role is played by the
|
|
88
|
+
B+tree needs to keep values in sorted order. Therefore, a process to compare the sizes of values is needed, and that role is played by the **ValueComparator**.
|
|
89
89
|
|
|
90
|
-
Commonly used numerical and string comparisons are natively supported by the
|
|
90
|
+
Commonly used numerical and string comparisons are natively supported by the **serializable-bptree** library. Use it as follows:
|
|
91
91
|
|
|
92
92
|
```typescript
|
|
93
93
|
import { NumericComparator, StringComparator } from 'serializable-bptree'
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
However, you may want to sort complex objects other than numbers and strings. For example, if you want to sort by the
|
|
96
|
+
However, you may want to sort complex objects other than numbers and strings. For example, if you want to sort by the **age** property order of an object, you need to create a new class that inherits from the **ValueComparator** class. Use it as follows:
|
|
97
97
|
|
|
98
98
|
```typescript
|
|
99
99
|
import { ValueComparator } from 'serializable-bptree'
|
|
@@ -103,13 +103,15 @@ interface MyObject {
|
|
|
103
103
|
name: string
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
class AgeComparator {
|
|
106
|
+
class AgeComparator extends ValueComparator<MyObject> {
|
|
107
107
|
asc(a: MyObject, b: MyObject): number {
|
|
108
108
|
return a.age - b.age
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
The **asc** method should return values in ascending order. If the return value is negative, it means that the parameter **a** is smaller than **b**. If the return value is positive, it means that **a** is greater than **b**. If the return value is **0**, it indicates that **a** and **b** are of the same size.
|
|
114
|
+
|
|
113
115
|
### Serialize strategy
|
|
114
116
|
|
|
115
117
|
A B+tree instance is made up of numerous nodes. You would want to store this value when such nodes are created or updated. Let's assume you want to save it to a file.
|
|
@@ -132,7 +134,7 @@ What does this method mean? And why do we need to construct such a method?
|
|
|
132
134
|
|
|
133
135
|
#### id(): `number`
|
|
134
136
|
|
|
135
|
-
When a node is created in the B+tree, the node needs a unique value to represent itself. This is the
|
|
137
|
+
When a node is created in the B+tree, the node needs a unique value to represent itself. This is the **node.id** attribute, and you can specify this attribute yourself. For example, it could be implemented like this.
|
|
136
138
|
|
|
137
139
|
```typescript
|
|
138
140
|
id(): number {
|
|
@@ -142,7 +144,9 @@ id(): number {
|
|
|
142
144
|
}
|
|
143
145
|
```
|
|
144
146
|
|
|
145
|
-
Or, you could use file input/output to save and load the value of the
|
|
147
|
+
Or, you could use file input/output to save and load the value of the **before** variable.
|
|
148
|
+
|
|
149
|
+
This method is called before a node is created in the tree. Therefore, it can also be used to allocate space for storing the node.
|
|
146
150
|
|
|
147
151
|
#### read(id: `number`): `BPTreeNode<K, V>`
|
|
148
152
|
|
|
@@ -158,7 +162,7 @@ read(id: number): BPTreeNode<K, V> {
|
|
|
158
162
|
}
|
|
159
163
|
```
|
|
160
164
|
|
|
161
|
-
This method is called only once when loading a node from a tree instance.
|
|
165
|
+
This method is called only once when loading a node from a tree instance. The loaded node is loaded into memory, and subsequently, when the tree references the node, it operates based on the values in memory **without** re-invoking this method.
|
|
162
166
|
|
|
163
167
|
#### write(id: `number`, node: `BPTreeNode<K, V>`): `void`
|
|
164
168
|
|
|
@@ -181,7 +185,7 @@ function writeBack(id: number, node: BPTreeNode<K, V>, timer: number) {
|
|
|
181
185
|
|
|
182
186
|
...
|
|
183
187
|
write(id: number, node: BPTreeNode<K, V>): void {
|
|
184
|
-
const writeBackInterval =
|
|
188
|
+
const writeBackInterval = 10
|
|
185
189
|
writeBack(id, node, writeBackInterval)
|
|
186
190
|
}
|
|
187
191
|
```
|
|
@@ -190,32 +194,32 @@ This kind of delay writing should ideally occur within a few milliseconds. If th
|
|
|
190
194
|
|
|
191
195
|
#### readHead(): `SerializeStrategyHead`|`null`
|
|
192
196
|
|
|
193
|
-
This method is called only once when the tree is created. It's a method to restore the saved tree information. If it is the initial creation and there is no stored root node, it should return
|
|
197
|
+
This method is called only once when the tree is created. It's a method to restore the saved tree information. If it is the initial creation and there is no stored root node, it should return **null**.
|
|
194
198
|
|
|
195
|
-
This method should return the value stored in the
|
|
199
|
+
This method should return the value stored in the **writeHead** method.
|
|
196
200
|
|
|
197
201
|
#### writeHead(head: `SerializeStrategyHead`): `void`
|
|
198
202
|
|
|
199
|
-
This method is called whenever the head information of the tree changes, typically when the root node changes.
|
|
203
|
+
This method is called whenever the head information of the tree changes, typically when the root node changes. This method also works when the tree's **setHeadData** method is called. This is because the method attempts to store head data in the root node.
|
|
200
204
|
|
|
201
|
-
As a parameter, it receives the header information of the tree. This value should be serialized and stored. Later, the
|
|
205
|
+
As a parameter, it receives the header information of the tree. This value should be serialized and stored. Later, the **readHead** method should convert this serialized value into a json format and return it.
|
|
202
206
|
|
|
203
207
|
### The Default `ValueComparator` and `SerializeStrategy`
|
|
204
208
|
|
|
205
|
-
To utilize
|
|
209
|
+
To utilize **serializable-bptree**, you need to implement certain functions. However, a few basic helper classes are provided by default.
|
|
206
210
|
|
|
207
211
|
#### ValueComparator
|
|
208
212
|
|
|
209
213
|
* `NumericComparator`
|
|
210
214
|
* `StringComparator`
|
|
211
215
|
|
|
212
|
-
If the values being inserted into the tree are numeric, please use the
|
|
216
|
+
If the values being inserted into the tree are numeric, please use the **NumericComparator** class.
|
|
213
217
|
|
|
214
218
|
```typescript
|
|
215
219
|
import { NumericComparator } from 'serializable-bptree'
|
|
216
220
|
```
|
|
217
221
|
|
|
218
|
-
If the values being inserted into the tree can be strings, you can use the
|
|
222
|
+
If the values being inserted into the tree can be strings, you can use the **StringComparator** class in this case.
|
|
219
223
|
|
|
220
224
|
```typescript
|
|
221
225
|
import { StringComparator } from 'serializable-bptree'
|
|
@@ -225,7 +229,7 @@ import { StringComparator } from 'serializable-bptree'
|
|
|
225
229
|
|
|
226
230
|
* `InMemoryStoreStrategy`
|
|
227
231
|
|
|
228
|
-
As of now, the only class supported by default is the
|
|
232
|
+
As of now, the only class supported by default is the **InMemoryStoreStrategy**. This class is suitable for use when you prefer to operate the tree solely in-memory, similar to a typical B+ tree.
|
|
229
233
|
|
|
230
234
|
```typescript
|
|
231
235
|
import { InMemoryStoreStrategy } from 'serializable-bptree'
|
package/dist/cjs/index.js
CHANGED
|
@@ -97,6 +97,51 @@ var BPTree = class {
|
|
|
97
97
|
_creates;
|
|
98
98
|
_updates;
|
|
99
99
|
_updatedHead;
|
|
100
|
+
_verifierMap = {
|
|
101
|
+
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
102
|
+
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
103
|
+
lt: (nv, v) => this.comparator.isLower(nv, v),
|
|
104
|
+
lte: (nv, v) => this.comparator.isLower(nv, v) || this.comparator.isSame(nv, v),
|
|
105
|
+
equal: (nv, v) => this.comparator.isSame(nv, v),
|
|
106
|
+
notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
|
|
107
|
+
like: (nv, v) => {
|
|
108
|
+
const nodeValue = nv.toString();
|
|
109
|
+
const value = v.toString();
|
|
110
|
+
const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
|
|
111
|
+
const regex = new RegExp(`^${pattern}$`, "i");
|
|
112
|
+
return regex.test(nodeValue);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
_verifierStartNode = {
|
|
116
|
+
gt: (v) => this.insertableNode(v),
|
|
117
|
+
gte: (v) => this.insertableNode(v),
|
|
118
|
+
// todo
|
|
119
|
+
lt: (v) => this.insertableNode(v),
|
|
120
|
+
lte: (v) => this.insertableNode(v),
|
|
121
|
+
// todo
|
|
122
|
+
equal: (v) => this.insertableNode(v),
|
|
123
|
+
notEqual: (v) => this.leftestNode(),
|
|
124
|
+
like: (v) => this.leftestNode()
|
|
125
|
+
// todo
|
|
126
|
+
};
|
|
127
|
+
_verifierDirection = {
|
|
128
|
+
gt: 1,
|
|
129
|
+
gte: 1,
|
|
130
|
+
lt: -1,
|
|
131
|
+
lte: -1,
|
|
132
|
+
equal: 1,
|
|
133
|
+
notEqual: 1,
|
|
134
|
+
like: 1
|
|
135
|
+
};
|
|
136
|
+
_verifierFullSearch = {
|
|
137
|
+
gt: false,
|
|
138
|
+
gte: false,
|
|
139
|
+
lt: false,
|
|
140
|
+
lte: false,
|
|
141
|
+
equal: false,
|
|
142
|
+
notEqual: true,
|
|
143
|
+
like: true
|
|
144
|
+
};
|
|
100
145
|
_createNodeId() {
|
|
101
146
|
const id = this.strategy.id();
|
|
102
147
|
if (id === 0) {
|
|
@@ -104,7 +149,7 @@ var BPTree = class {
|
|
|
104
149
|
}
|
|
105
150
|
return id;
|
|
106
151
|
}
|
|
107
|
-
_createNode(keys, values, leaf = false, parent = 0, next = 0) {
|
|
152
|
+
_createNode(keys, values, leaf = false, parent = 0, next = 0, prev = 0) {
|
|
108
153
|
const id = this._createNodeId();
|
|
109
154
|
const node = {
|
|
110
155
|
id,
|
|
@@ -112,7 +157,8 @@ var BPTree = class {
|
|
|
112
157
|
values,
|
|
113
158
|
leaf,
|
|
114
159
|
parent,
|
|
115
|
-
next
|
|
160
|
+
next,
|
|
161
|
+
prev
|
|
116
162
|
};
|
|
117
163
|
this.nodes.set(id, node);
|
|
118
164
|
return node;
|
|
@@ -199,7 +245,7 @@ var BPTree = class {
|
|
|
199
245
|
}
|
|
200
246
|
return node;
|
|
201
247
|
}
|
|
202
|
-
|
|
248
|
+
insertableNode(value) {
|
|
203
249
|
let node = this.root;
|
|
204
250
|
while (!node.leaf) {
|
|
205
251
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
@@ -225,7 +271,7 @@ var BPTree = class {
|
|
|
225
271
|
* @param value The value to search for.
|
|
226
272
|
*/
|
|
227
273
|
exists(key, value) {
|
|
228
|
-
const node = this.
|
|
274
|
+
const node = this.insertableNode(value);
|
|
229
275
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
230
276
|
const nValue = node.values[i];
|
|
231
277
|
if (this.comparator.isSame(value, nValue)) {
|
|
@@ -311,218 +357,50 @@ var BPTree = class {
|
|
|
311
357
|
}
|
|
312
358
|
}
|
|
313
359
|
}
|
|
314
|
-
|
|
315
|
-
return Object.prototype.hasOwnProperty.call(condition, "equal");
|
|
316
|
-
}
|
|
317
|
-
_notEqualCondition(condition) {
|
|
318
|
-
return Object.prototype.hasOwnProperty.call(condition, "notEqual");
|
|
319
|
-
}
|
|
320
|
-
_onlyGtCondition(condition) {
|
|
321
|
-
return Object.prototype.hasOwnProperty.call(condition, "gt") && !Object.prototype.hasOwnProperty.call(condition, "lt");
|
|
322
|
-
}
|
|
323
|
-
_onlyLtCondition(condition) {
|
|
324
|
-
return Object.prototype.hasOwnProperty.call(condition, "lt") && !Object.prototype.hasOwnProperty.call(condition, "gt");
|
|
325
|
-
}
|
|
326
|
-
_rangeCondition(condition) {
|
|
327
|
-
return Object.prototype.hasOwnProperty.call(condition, "gt") && Object.prototype.hasOwnProperty.call(condition, "lt");
|
|
328
|
-
}
|
|
329
|
-
_getKeysFromValue(value) {
|
|
330
|
-
const keys = /* @__PURE__ */ new Set();
|
|
331
|
-
const node = this._insertableNode(value);
|
|
332
|
-
const [start, end] = this.search.range(node.values, value);
|
|
333
|
-
if (start === -1) {
|
|
334
|
-
return keys;
|
|
335
|
-
}
|
|
336
|
-
for (let i = start; i < end; i++) {
|
|
337
|
-
const pairKeys = node.keys[i];
|
|
338
|
-
for (const key of pairKeys) {
|
|
339
|
-
keys.add(key);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return keys;
|
|
343
|
-
}
|
|
344
|
-
_getKeysFromNEValue(value) {
|
|
345
|
-
const keys = /* @__PURE__ */ new Set();
|
|
346
|
-
let node = this.leftestNode();
|
|
347
|
-
let done = false;
|
|
348
|
-
while (!done) {
|
|
349
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
350
|
-
const nValue = node.values[i];
|
|
351
|
-
const pairKeys = node.keys[i];
|
|
352
|
-
if (this.comparator.isSame(nValue, value) === false) {
|
|
353
|
-
for (const key of pairKeys) {
|
|
354
|
-
keys.add(key);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
if (!node.next) {
|
|
359
|
-
done = true;
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
node = this.getNode(node.next);
|
|
363
|
-
}
|
|
364
|
-
return keys;
|
|
365
|
-
}
|
|
366
|
-
_getKeysFromRange(gt, lt) {
|
|
367
|
-
const keys = /* @__PURE__ */ new Set();
|
|
368
|
-
let node = this._insertableNode(gt);
|
|
369
|
-
let done = false;
|
|
370
|
-
let found = false;
|
|
371
|
-
while (!done) {
|
|
372
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
373
|
-
const nValue = node.values[i];
|
|
374
|
-
const localKeys = node.keys[i];
|
|
375
|
-
if (this.comparator.isHigher(nValue, gt) && this.comparator.isLower(nValue, lt)) {
|
|
376
|
-
found = true;
|
|
377
|
-
for (const key of localKeys) {
|
|
378
|
-
keys.add(key);
|
|
379
|
-
}
|
|
380
|
-
} else if (found) {
|
|
381
|
-
done = true;
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
if (!node.next) {
|
|
386
|
-
done = true;
|
|
387
|
-
break;
|
|
388
|
-
}
|
|
389
|
-
node = this.getNode(node.next);
|
|
390
|
-
}
|
|
391
|
-
return keys;
|
|
392
|
-
}
|
|
393
|
-
_getKeysFromGt(gt) {
|
|
394
|
-
const keys = /* @__PURE__ */ new Set();
|
|
395
|
-
let node = this._insertableNode(gt);
|
|
396
|
-
let done = false;
|
|
397
|
-
let found = false;
|
|
398
|
-
while (!done) {
|
|
399
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
400
|
-
const nValue = node.values[i];
|
|
401
|
-
const localKeys = node.keys[i];
|
|
402
|
-
if (this.comparator.isHigher(nValue, gt)) {
|
|
403
|
-
found = true;
|
|
404
|
-
for (const key of localKeys) {
|
|
405
|
-
keys.add(key);
|
|
406
|
-
}
|
|
407
|
-
} else if (found) {
|
|
408
|
-
done = true;
|
|
409
|
-
break;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
if (!node.next) {
|
|
413
|
-
done = true;
|
|
414
|
-
break;
|
|
415
|
-
}
|
|
416
|
-
node = this.getNode(node.next);
|
|
417
|
-
}
|
|
418
|
-
return keys;
|
|
419
|
-
}
|
|
420
|
-
_getKeysFromLt(lt) {
|
|
421
|
-
const keys = /* @__PURE__ */ new Set();
|
|
422
|
-
let node = this.leftestNode();
|
|
423
|
-
let done = false;
|
|
424
|
-
let found = false;
|
|
425
|
-
while (!done) {
|
|
426
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
427
|
-
const nValue = node.values[i];
|
|
428
|
-
const localKeys = node.keys[i];
|
|
429
|
-
if (this.comparator.isLower(nValue, lt)) {
|
|
430
|
-
found = true;
|
|
431
|
-
for (const key of localKeys) {
|
|
432
|
-
keys.add(key);
|
|
433
|
-
}
|
|
434
|
-
} else if (found) {
|
|
435
|
-
done = true;
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
if (!node.next) {
|
|
440
|
-
done = true;
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
node = this.getNode(node.next);
|
|
444
|
-
}
|
|
445
|
-
return keys;
|
|
446
|
-
}
|
|
447
|
-
_getPairsFromValue(value) {
|
|
448
|
-
const node = this._insertableNode(value);
|
|
449
|
-
const [start, end] = this.search.range(node.values, value);
|
|
450
|
-
if (start === -1) {
|
|
451
|
-
return [];
|
|
452
|
-
}
|
|
453
|
-
const pairs = [];
|
|
454
|
-
for (let i = start; i < end; i++) {
|
|
455
|
-
const keys = node.keys[i];
|
|
456
|
-
for (const key of keys) {
|
|
457
|
-
pairs.push({ key, value });
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
return pairs;
|
|
461
|
-
}
|
|
462
|
-
_getPairsFromNEValue(value) {
|
|
360
|
+
_getPairsRightToLeft(value, startNode, fullSearch, comparator) {
|
|
463
361
|
const pairs = [];
|
|
464
|
-
let node =
|
|
465
|
-
let done = false;
|
|
466
|
-
while (!done) {
|
|
467
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
468
|
-
const nValue = node.values[i];
|
|
469
|
-
const keys = node.keys[i];
|
|
470
|
-
if (this.comparator.isSame(nValue, value) === false) {
|
|
471
|
-
for (const key of keys) {
|
|
472
|
-
pairs.push({ key, value: nValue });
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
if (!node.next) {
|
|
477
|
-
done = true;
|
|
478
|
-
break;
|
|
479
|
-
}
|
|
480
|
-
node = this.getNode(node.next);
|
|
481
|
-
}
|
|
482
|
-
return pairs;
|
|
483
|
-
}
|
|
484
|
-
_getPairsFromRange(gt, lt) {
|
|
485
|
-
const pairs = [];
|
|
486
|
-
let node = this._insertableNode(gt);
|
|
362
|
+
let node = startNode;
|
|
487
363
|
let done = false;
|
|
488
364
|
let found = false;
|
|
489
365
|
while (!done) {
|
|
490
|
-
|
|
366
|
+
let i = node.values.length;
|
|
367
|
+
while (i--) {
|
|
491
368
|
const nValue = node.values[i];
|
|
492
369
|
const keys = node.keys[i];
|
|
493
|
-
if (
|
|
370
|
+
if (comparator(nValue, value)) {
|
|
494
371
|
found = true;
|
|
495
|
-
|
|
496
|
-
|
|
372
|
+
let j = keys.length;
|
|
373
|
+
while (j--) {
|
|
374
|
+
pairs.push({ key: keys[j], value: nValue });
|
|
497
375
|
}
|
|
498
|
-
} else if (found) {
|
|
376
|
+
} else if (found && !fullSearch) {
|
|
499
377
|
done = true;
|
|
500
378
|
break;
|
|
501
379
|
}
|
|
502
380
|
}
|
|
503
|
-
if (!node.
|
|
381
|
+
if (!node.prev) {
|
|
504
382
|
done = true;
|
|
505
383
|
break;
|
|
506
384
|
}
|
|
507
|
-
node = this.getNode(node.
|
|
385
|
+
node = this.getNode(node.prev);
|
|
508
386
|
}
|
|
509
|
-
return pairs;
|
|
387
|
+
return pairs.reverse();
|
|
510
388
|
}
|
|
511
|
-
|
|
389
|
+
_getPairsLeftToRight(value, startNode, fullSearch, comparator) {
|
|
512
390
|
const pairs = [];
|
|
513
|
-
let node =
|
|
391
|
+
let node = startNode;
|
|
514
392
|
let done = false;
|
|
515
393
|
let found = false;
|
|
516
394
|
while (!done) {
|
|
517
395
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
518
396
|
const nValue = node.values[i];
|
|
519
397
|
const keys = node.keys[i];
|
|
520
|
-
if (
|
|
398
|
+
if (comparator(nValue, value)) {
|
|
521
399
|
found = true;
|
|
522
400
|
for (const key of keys) {
|
|
523
401
|
pairs.push({ key, value: nValue });
|
|
524
402
|
}
|
|
525
|
-
} else if (found) {
|
|
403
|
+
} else if (found && !fullSearch) {
|
|
526
404
|
done = true;
|
|
527
405
|
break;
|
|
528
406
|
}
|
|
@@ -535,75 +413,62 @@ var BPTree = class {
|
|
|
535
413
|
}
|
|
536
414
|
return pairs;
|
|
537
415
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const keys = node.keys[i];
|
|
547
|
-
if (this.comparator.isLower(nValue, lt)) {
|
|
548
|
-
found = true;
|
|
549
|
-
for (const key of keys) {
|
|
550
|
-
pairs.push({ key, value: nValue });
|
|
551
|
-
}
|
|
552
|
-
} else if (found) {
|
|
553
|
-
done = true;
|
|
554
|
-
break;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
if (!node.next) {
|
|
558
|
-
done = true;
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
-
node = this.getNode(node.next);
|
|
416
|
+
getPairs(value, startNode, fullSearch, comparator, direction) {
|
|
417
|
+
switch (direction) {
|
|
418
|
+
case -1:
|
|
419
|
+
return this._getPairsRightToLeft(value, startNode, fullSearch, comparator);
|
|
420
|
+
case 1:
|
|
421
|
+
return this._getPairsLeftToRight(value, startNode, fullSearch, comparator);
|
|
422
|
+
default:
|
|
423
|
+
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
562
424
|
}
|
|
563
|
-
return pairs;
|
|
564
425
|
}
|
|
565
426
|
/**
|
|
566
427
|
* It searches for a key within the tree. The result is returned as an array sorted in ascending order based on the value.
|
|
567
|
-
* The result is key set instance, and you can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
428
|
+
* The result is key set instance, and you can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
568
429
|
* This method operates much faster than first searching with `where` and then retrieving only the key list.
|
|
569
|
-
* @param condition You can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
430
|
+
* @param condition You can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
570
431
|
*/
|
|
571
432
|
keys(condition) {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
433
|
+
let result = null;
|
|
434
|
+
for (const k in condition) {
|
|
435
|
+
const key = k;
|
|
436
|
+
const value = condition[key];
|
|
437
|
+
const startNode = this._verifierStartNode[key](value);
|
|
438
|
+
const direction = this._verifierDirection[key];
|
|
439
|
+
const fullSearch = this._verifierFullSearch[key];
|
|
440
|
+
const comparator = this._verifierMap[key];
|
|
441
|
+
const pairs = this.getPairs(value, startNode, fullSearch, comparator, direction);
|
|
442
|
+
if (result === null) {
|
|
443
|
+
result = pairs.map((pair) => pair.key);
|
|
444
|
+
} else {
|
|
445
|
+
result = result.filter((key2) => pairs.find((p) => p.key === key2));
|
|
446
|
+
}
|
|
585
447
|
}
|
|
448
|
+
return new Set(result ?? []);
|
|
586
449
|
}
|
|
587
450
|
/**
|
|
588
451
|
* It searches for a value within the tree. The result is returned as an array sorted in ascending order based on the value.
|
|
589
|
-
* The result includes the key and value attributes, and you can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
590
|
-
* @param condition You can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
452
|
+
* The result includes the key and value attributes, and you can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
453
|
+
* @param condition You can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
591
454
|
*/
|
|
592
455
|
where(condition) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
456
|
+
let result = null;
|
|
457
|
+
for (const k in condition) {
|
|
458
|
+
const key = k;
|
|
459
|
+
const value = condition[key];
|
|
460
|
+
const startNode = this._verifierStartNode[key](value);
|
|
461
|
+
const direction = this._verifierDirection[key];
|
|
462
|
+
const fullSearch = this._verifierFullSearch[key];
|
|
463
|
+
const comparator = this._verifierMap[key];
|
|
464
|
+
const pairs = this.getPairs(value, startNode, fullSearch, comparator, direction);
|
|
465
|
+
if (result === null) {
|
|
466
|
+
result = pairs;
|
|
467
|
+
} else {
|
|
468
|
+
result = result.filter((pair) => pairs.find((p) => p.key === pair.key));
|
|
469
|
+
}
|
|
606
470
|
}
|
|
471
|
+
return result ?? [];
|
|
607
472
|
}
|
|
608
473
|
/**
|
|
609
474
|
* You enter the key and value as a pair. You can later search for the pair by value.
|
|
@@ -612,7 +477,7 @@ var BPTree = class {
|
|
|
612
477
|
* @param value The value of the pair.
|
|
613
478
|
*/
|
|
614
479
|
insert(key, value) {
|
|
615
|
-
const before = this.
|
|
480
|
+
const before = this.insertableNode(value);
|
|
616
481
|
this._insertAtLeaf(before, key, value);
|
|
617
482
|
if (before.values.length === this.order) {
|
|
618
483
|
const after = this._createNode(
|
|
@@ -620,14 +485,21 @@ var BPTree = class {
|
|
|
620
485
|
[],
|
|
621
486
|
true,
|
|
622
487
|
before.parent,
|
|
623
|
-
before.next
|
|
488
|
+
before.next,
|
|
489
|
+
before.id
|
|
624
490
|
);
|
|
625
491
|
const mid = Math.ceil(this.order / 2) - 1;
|
|
492
|
+
const beforeNext = before.next;
|
|
626
493
|
after.values = before.values.slice(mid + 1);
|
|
627
494
|
after.keys = before.keys.slice(mid + 1);
|
|
628
495
|
before.values = before.values.slice(0, mid + 1);
|
|
629
496
|
before.keys = before.keys.slice(0, mid + 1);
|
|
630
497
|
before.next = after.id;
|
|
498
|
+
if (beforeNext) {
|
|
499
|
+
const node = this.getNode(beforeNext);
|
|
500
|
+
node.prev = after.id;
|
|
501
|
+
this._setUpdates(node);
|
|
502
|
+
}
|
|
631
503
|
this._insertInParent(before, after.values[0], after);
|
|
632
504
|
this._setCreates(after);
|
|
633
505
|
this._setUpdates(before);
|
|
@@ -642,7 +514,7 @@ var BPTree = class {
|
|
|
642
514
|
* @param value The value of the pair.
|
|
643
515
|
*/
|
|
644
516
|
delete(key, value) {
|
|
645
|
-
const node = this.
|
|
517
|
+
const node = this.insertableNode(value);
|
|
646
518
|
let i = node.values.length;
|
|
647
519
|
while (i--) {
|
|
648
520
|
const nValue = node.values[i];
|
|
@@ -703,18 +575,18 @@ var BPTree = class {
|
|
|
703
575
|
let parentNode = this.getNode(node.parent);
|
|
704
576
|
let prevNode = null;
|
|
705
577
|
let nextNode = null;
|
|
706
|
-
let
|
|
707
|
-
let
|
|
578
|
+
let prevValue = null;
|
|
579
|
+
let postValue = null;
|
|
708
580
|
for (let i = 0, len = parentNode.keys.length; i < len; i++) {
|
|
709
581
|
const nKey = parentNode.keys[i];
|
|
710
582
|
if (nKey === node.id) {
|
|
711
583
|
if (i > 0) {
|
|
712
584
|
prevNode = this.getNode(parentNode.keys[i - 1]);
|
|
713
|
-
|
|
585
|
+
prevValue = parentNode.values[i - 1];
|
|
714
586
|
}
|
|
715
587
|
if (i < parentNode.keys.length - 1) {
|
|
716
588
|
nextNode = this.getNode(parentNode.keys[i + 1]);
|
|
717
|
-
|
|
589
|
+
postValue = parentNode.values[i];
|
|
718
590
|
}
|
|
719
591
|
}
|
|
720
592
|
}
|
|
@@ -722,19 +594,19 @@ var BPTree = class {
|
|
|
722
594
|
let guess;
|
|
723
595
|
if (prevNode === null) {
|
|
724
596
|
pointer = nextNode;
|
|
725
|
-
guess =
|
|
597
|
+
guess = postValue;
|
|
726
598
|
} else if (nextNode === null) {
|
|
727
599
|
isPredecessor = true;
|
|
728
600
|
pointer = prevNode;
|
|
729
|
-
guess =
|
|
601
|
+
guess = prevValue;
|
|
730
602
|
} else {
|
|
731
603
|
if (node.values.length + nextNode.values.length < this.order) {
|
|
732
604
|
pointer = nextNode;
|
|
733
|
-
guess =
|
|
605
|
+
guess = postValue;
|
|
734
606
|
} else {
|
|
735
607
|
isPredecessor = true;
|
|
736
608
|
pointer = prevNode;
|
|
737
|
-
guess =
|
|
609
|
+
guess = prevValue;
|
|
738
610
|
}
|
|
739
611
|
}
|
|
740
612
|
if (node.values.length + pointer.values.length < this.order) {
|
|
@@ -748,6 +620,20 @@ var BPTree = class {
|
|
|
748
620
|
pointer.values.push(guess);
|
|
749
621
|
} else {
|
|
750
622
|
pointer.next = node.next;
|
|
623
|
+
pointer.prev = node.id;
|
|
624
|
+
if (pointer.next) {
|
|
625
|
+
const n = this.getNode(node.next);
|
|
626
|
+
n.prev = pointer.id;
|
|
627
|
+
this._setUpdates(n);
|
|
628
|
+
}
|
|
629
|
+
if (pointer.prev) {
|
|
630
|
+
const n = this.getNode(node.id);
|
|
631
|
+
n.next = pointer.id;
|
|
632
|
+
this._setUpdates(n);
|
|
633
|
+
}
|
|
634
|
+
if (isPredecessor) {
|
|
635
|
+
pointer.prev = 0;
|
|
636
|
+
}
|
|
751
637
|
}
|
|
752
638
|
pointer.values.push(...node.values);
|
|
753
639
|
if (!pointer.leaf) {
|
package/dist/esm/index.js
CHANGED
|
@@ -66,6 +66,51 @@ var BPTree = class {
|
|
|
66
66
|
_creates;
|
|
67
67
|
_updates;
|
|
68
68
|
_updatedHead;
|
|
69
|
+
_verifierMap = {
|
|
70
|
+
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
71
|
+
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
72
|
+
lt: (nv, v) => this.comparator.isLower(nv, v),
|
|
73
|
+
lte: (nv, v) => this.comparator.isLower(nv, v) || this.comparator.isSame(nv, v),
|
|
74
|
+
equal: (nv, v) => this.comparator.isSame(nv, v),
|
|
75
|
+
notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
|
|
76
|
+
like: (nv, v) => {
|
|
77
|
+
const nodeValue = nv.toString();
|
|
78
|
+
const value = v.toString();
|
|
79
|
+
const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
|
|
80
|
+
const regex = new RegExp(`^${pattern}$`, "i");
|
|
81
|
+
return regex.test(nodeValue);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
_verifierStartNode = {
|
|
85
|
+
gt: (v) => this.insertableNode(v),
|
|
86
|
+
gte: (v) => this.insertableNode(v),
|
|
87
|
+
// todo
|
|
88
|
+
lt: (v) => this.insertableNode(v),
|
|
89
|
+
lte: (v) => this.insertableNode(v),
|
|
90
|
+
// todo
|
|
91
|
+
equal: (v) => this.insertableNode(v),
|
|
92
|
+
notEqual: (v) => this.leftestNode(),
|
|
93
|
+
like: (v) => this.leftestNode()
|
|
94
|
+
// todo
|
|
95
|
+
};
|
|
96
|
+
_verifierDirection = {
|
|
97
|
+
gt: 1,
|
|
98
|
+
gte: 1,
|
|
99
|
+
lt: -1,
|
|
100
|
+
lte: -1,
|
|
101
|
+
equal: 1,
|
|
102
|
+
notEqual: 1,
|
|
103
|
+
like: 1
|
|
104
|
+
};
|
|
105
|
+
_verifierFullSearch = {
|
|
106
|
+
gt: false,
|
|
107
|
+
gte: false,
|
|
108
|
+
lt: false,
|
|
109
|
+
lte: false,
|
|
110
|
+
equal: false,
|
|
111
|
+
notEqual: true,
|
|
112
|
+
like: true
|
|
113
|
+
};
|
|
69
114
|
_createNodeId() {
|
|
70
115
|
const id = this.strategy.id();
|
|
71
116
|
if (id === 0) {
|
|
@@ -73,7 +118,7 @@ var BPTree = class {
|
|
|
73
118
|
}
|
|
74
119
|
return id;
|
|
75
120
|
}
|
|
76
|
-
_createNode(keys, values, leaf = false, parent = 0, next = 0) {
|
|
121
|
+
_createNode(keys, values, leaf = false, parent = 0, next = 0, prev = 0) {
|
|
77
122
|
const id = this._createNodeId();
|
|
78
123
|
const node = {
|
|
79
124
|
id,
|
|
@@ -81,7 +126,8 @@ var BPTree = class {
|
|
|
81
126
|
values,
|
|
82
127
|
leaf,
|
|
83
128
|
parent,
|
|
84
|
-
next
|
|
129
|
+
next,
|
|
130
|
+
prev
|
|
85
131
|
};
|
|
86
132
|
this.nodes.set(id, node);
|
|
87
133
|
return node;
|
|
@@ -168,7 +214,7 @@ var BPTree = class {
|
|
|
168
214
|
}
|
|
169
215
|
return node;
|
|
170
216
|
}
|
|
171
|
-
|
|
217
|
+
insertableNode(value) {
|
|
172
218
|
let node = this.root;
|
|
173
219
|
while (!node.leaf) {
|
|
174
220
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
@@ -194,7 +240,7 @@ var BPTree = class {
|
|
|
194
240
|
* @param value The value to search for.
|
|
195
241
|
*/
|
|
196
242
|
exists(key, value) {
|
|
197
|
-
const node = this.
|
|
243
|
+
const node = this.insertableNode(value);
|
|
198
244
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
199
245
|
const nValue = node.values[i];
|
|
200
246
|
if (this.comparator.isSame(value, nValue)) {
|
|
@@ -280,218 +326,50 @@ var BPTree = class {
|
|
|
280
326
|
}
|
|
281
327
|
}
|
|
282
328
|
}
|
|
283
|
-
|
|
284
|
-
return Object.prototype.hasOwnProperty.call(condition, "equal");
|
|
285
|
-
}
|
|
286
|
-
_notEqualCondition(condition) {
|
|
287
|
-
return Object.prototype.hasOwnProperty.call(condition, "notEqual");
|
|
288
|
-
}
|
|
289
|
-
_onlyGtCondition(condition) {
|
|
290
|
-
return Object.prototype.hasOwnProperty.call(condition, "gt") && !Object.prototype.hasOwnProperty.call(condition, "lt");
|
|
291
|
-
}
|
|
292
|
-
_onlyLtCondition(condition) {
|
|
293
|
-
return Object.prototype.hasOwnProperty.call(condition, "lt") && !Object.prototype.hasOwnProperty.call(condition, "gt");
|
|
294
|
-
}
|
|
295
|
-
_rangeCondition(condition) {
|
|
296
|
-
return Object.prototype.hasOwnProperty.call(condition, "gt") && Object.prototype.hasOwnProperty.call(condition, "lt");
|
|
297
|
-
}
|
|
298
|
-
_getKeysFromValue(value) {
|
|
299
|
-
const keys = /* @__PURE__ */ new Set();
|
|
300
|
-
const node = this._insertableNode(value);
|
|
301
|
-
const [start, end] = this.search.range(node.values, value);
|
|
302
|
-
if (start === -1) {
|
|
303
|
-
return keys;
|
|
304
|
-
}
|
|
305
|
-
for (let i = start; i < end; i++) {
|
|
306
|
-
const pairKeys = node.keys[i];
|
|
307
|
-
for (const key of pairKeys) {
|
|
308
|
-
keys.add(key);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
return keys;
|
|
312
|
-
}
|
|
313
|
-
_getKeysFromNEValue(value) {
|
|
314
|
-
const keys = /* @__PURE__ */ new Set();
|
|
315
|
-
let node = this.leftestNode();
|
|
316
|
-
let done = false;
|
|
317
|
-
while (!done) {
|
|
318
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
319
|
-
const nValue = node.values[i];
|
|
320
|
-
const pairKeys = node.keys[i];
|
|
321
|
-
if (this.comparator.isSame(nValue, value) === false) {
|
|
322
|
-
for (const key of pairKeys) {
|
|
323
|
-
keys.add(key);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
if (!node.next) {
|
|
328
|
-
done = true;
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
node = this.getNode(node.next);
|
|
332
|
-
}
|
|
333
|
-
return keys;
|
|
334
|
-
}
|
|
335
|
-
_getKeysFromRange(gt, lt) {
|
|
336
|
-
const keys = /* @__PURE__ */ new Set();
|
|
337
|
-
let node = this._insertableNode(gt);
|
|
338
|
-
let done = false;
|
|
339
|
-
let found = false;
|
|
340
|
-
while (!done) {
|
|
341
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
342
|
-
const nValue = node.values[i];
|
|
343
|
-
const localKeys = node.keys[i];
|
|
344
|
-
if (this.comparator.isHigher(nValue, gt) && this.comparator.isLower(nValue, lt)) {
|
|
345
|
-
found = true;
|
|
346
|
-
for (const key of localKeys) {
|
|
347
|
-
keys.add(key);
|
|
348
|
-
}
|
|
349
|
-
} else if (found) {
|
|
350
|
-
done = true;
|
|
351
|
-
break;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
if (!node.next) {
|
|
355
|
-
done = true;
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
358
|
-
node = this.getNode(node.next);
|
|
359
|
-
}
|
|
360
|
-
return keys;
|
|
361
|
-
}
|
|
362
|
-
_getKeysFromGt(gt) {
|
|
363
|
-
const keys = /* @__PURE__ */ new Set();
|
|
364
|
-
let node = this._insertableNode(gt);
|
|
365
|
-
let done = false;
|
|
366
|
-
let found = false;
|
|
367
|
-
while (!done) {
|
|
368
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
369
|
-
const nValue = node.values[i];
|
|
370
|
-
const localKeys = node.keys[i];
|
|
371
|
-
if (this.comparator.isHigher(nValue, gt)) {
|
|
372
|
-
found = true;
|
|
373
|
-
for (const key of localKeys) {
|
|
374
|
-
keys.add(key);
|
|
375
|
-
}
|
|
376
|
-
} else if (found) {
|
|
377
|
-
done = true;
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
if (!node.next) {
|
|
382
|
-
done = true;
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
node = this.getNode(node.next);
|
|
386
|
-
}
|
|
387
|
-
return keys;
|
|
388
|
-
}
|
|
389
|
-
_getKeysFromLt(lt) {
|
|
390
|
-
const keys = /* @__PURE__ */ new Set();
|
|
391
|
-
let node = this.leftestNode();
|
|
392
|
-
let done = false;
|
|
393
|
-
let found = false;
|
|
394
|
-
while (!done) {
|
|
395
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
396
|
-
const nValue = node.values[i];
|
|
397
|
-
const localKeys = node.keys[i];
|
|
398
|
-
if (this.comparator.isLower(nValue, lt)) {
|
|
399
|
-
found = true;
|
|
400
|
-
for (const key of localKeys) {
|
|
401
|
-
keys.add(key);
|
|
402
|
-
}
|
|
403
|
-
} else if (found) {
|
|
404
|
-
done = true;
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
if (!node.next) {
|
|
409
|
-
done = true;
|
|
410
|
-
break;
|
|
411
|
-
}
|
|
412
|
-
node = this.getNode(node.next);
|
|
413
|
-
}
|
|
414
|
-
return keys;
|
|
415
|
-
}
|
|
416
|
-
_getPairsFromValue(value) {
|
|
417
|
-
const node = this._insertableNode(value);
|
|
418
|
-
const [start, end] = this.search.range(node.values, value);
|
|
419
|
-
if (start === -1) {
|
|
420
|
-
return [];
|
|
421
|
-
}
|
|
422
|
-
const pairs = [];
|
|
423
|
-
for (let i = start; i < end; i++) {
|
|
424
|
-
const keys = node.keys[i];
|
|
425
|
-
for (const key of keys) {
|
|
426
|
-
pairs.push({ key, value });
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return pairs;
|
|
430
|
-
}
|
|
431
|
-
_getPairsFromNEValue(value) {
|
|
329
|
+
_getPairsRightToLeft(value, startNode, fullSearch, comparator) {
|
|
432
330
|
const pairs = [];
|
|
433
|
-
let node =
|
|
434
|
-
let done = false;
|
|
435
|
-
while (!done) {
|
|
436
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
437
|
-
const nValue = node.values[i];
|
|
438
|
-
const keys = node.keys[i];
|
|
439
|
-
if (this.comparator.isSame(nValue, value) === false) {
|
|
440
|
-
for (const key of keys) {
|
|
441
|
-
pairs.push({ key, value: nValue });
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
if (!node.next) {
|
|
446
|
-
done = true;
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
node = this.getNode(node.next);
|
|
450
|
-
}
|
|
451
|
-
return pairs;
|
|
452
|
-
}
|
|
453
|
-
_getPairsFromRange(gt, lt) {
|
|
454
|
-
const pairs = [];
|
|
455
|
-
let node = this._insertableNode(gt);
|
|
331
|
+
let node = startNode;
|
|
456
332
|
let done = false;
|
|
457
333
|
let found = false;
|
|
458
334
|
while (!done) {
|
|
459
|
-
|
|
335
|
+
let i = node.values.length;
|
|
336
|
+
while (i--) {
|
|
460
337
|
const nValue = node.values[i];
|
|
461
338
|
const keys = node.keys[i];
|
|
462
|
-
if (
|
|
339
|
+
if (comparator(nValue, value)) {
|
|
463
340
|
found = true;
|
|
464
|
-
|
|
465
|
-
|
|
341
|
+
let j = keys.length;
|
|
342
|
+
while (j--) {
|
|
343
|
+
pairs.push({ key: keys[j], value: nValue });
|
|
466
344
|
}
|
|
467
|
-
} else if (found) {
|
|
345
|
+
} else if (found && !fullSearch) {
|
|
468
346
|
done = true;
|
|
469
347
|
break;
|
|
470
348
|
}
|
|
471
349
|
}
|
|
472
|
-
if (!node.
|
|
350
|
+
if (!node.prev) {
|
|
473
351
|
done = true;
|
|
474
352
|
break;
|
|
475
353
|
}
|
|
476
|
-
node = this.getNode(node.
|
|
354
|
+
node = this.getNode(node.prev);
|
|
477
355
|
}
|
|
478
|
-
return pairs;
|
|
356
|
+
return pairs.reverse();
|
|
479
357
|
}
|
|
480
|
-
|
|
358
|
+
_getPairsLeftToRight(value, startNode, fullSearch, comparator) {
|
|
481
359
|
const pairs = [];
|
|
482
|
-
let node =
|
|
360
|
+
let node = startNode;
|
|
483
361
|
let done = false;
|
|
484
362
|
let found = false;
|
|
485
363
|
while (!done) {
|
|
486
364
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
487
365
|
const nValue = node.values[i];
|
|
488
366
|
const keys = node.keys[i];
|
|
489
|
-
if (
|
|
367
|
+
if (comparator(nValue, value)) {
|
|
490
368
|
found = true;
|
|
491
369
|
for (const key of keys) {
|
|
492
370
|
pairs.push({ key, value: nValue });
|
|
493
371
|
}
|
|
494
|
-
} else if (found) {
|
|
372
|
+
} else if (found && !fullSearch) {
|
|
495
373
|
done = true;
|
|
496
374
|
break;
|
|
497
375
|
}
|
|
@@ -504,75 +382,62 @@ var BPTree = class {
|
|
|
504
382
|
}
|
|
505
383
|
return pairs;
|
|
506
384
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const keys = node.keys[i];
|
|
516
|
-
if (this.comparator.isLower(nValue, lt)) {
|
|
517
|
-
found = true;
|
|
518
|
-
for (const key of keys) {
|
|
519
|
-
pairs.push({ key, value: nValue });
|
|
520
|
-
}
|
|
521
|
-
} else if (found) {
|
|
522
|
-
done = true;
|
|
523
|
-
break;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
if (!node.next) {
|
|
527
|
-
done = true;
|
|
528
|
-
break;
|
|
529
|
-
}
|
|
530
|
-
node = this.getNode(node.next);
|
|
385
|
+
getPairs(value, startNode, fullSearch, comparator, direction) {
|
|
386
|
+
switch (direction) {
|
|
387
|
+
case -1:
|
|
388
|
+
return this._getPairsRightToLeft(value, startNode, fullSearch, comparator);
|
|
389
|
+
case 1:
|
|
390
|
+
return this._getPairsLeftToRight(value, startNode, fullSearch, comparator);
|
|
391
|
+
default:
|
|
392
|
+
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
531
393
|
}
|
|
532
|
-
return pairs;
|
|
533
394
|
}
|
|
534
395
|
/**
|
|
535
396
|
* It searches for a key within the tree. The result is returned as an array sorted in ascending order based on the value.
|
|
536
|
-
* The result is key set instance, and you can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
397
|
+
* The result is key set instance, and you can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
537
398
|
* This method operates much faster than first searching with `where` and then retrieving only the key list.
|
|
538
|
-
* @param condition You can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
399
|
+
* @param condition You can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
539
400
|
*/
|
|
540
401
|
keys(condition) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
402
|
+
let result = null;
|
|
403
|
+
for (const k in condition) {
|
|
404
|
+
const key = k;
|
|
405
|
+
const value = condition[key];
|
|
406
|
+
const startNode = this._verifierStartNode[key](value);
|
|
407
|
+
const direction = this._verifierDirection[key];
|
|
408
|
+
const fullSearch = this._verifierFullSearch[key];
|
|
409
|
+
const comparator = this._verifierMap[key];
|
|
410
|
+
const pairs = this.getPairs(value, startNode, fullSearch, comparator, direction);
|
|
411
|
+
if (result === null) {
|
|
412
|
+
result = pairs.map((pair) => pair.key);
|
|
413
|
+
} else {
|
|
414
|
+
result = result.filter((key2) => pairs.find((p) => p.key === key2));
|
|
415
|
+
}
|
|
554
416
|
}
|
|
417
|
+
return new Set(result ?? []);
|
|
555
418
|
}
|
|
556
419
|
/**
|
|
557
420
|
* It searches for a value within the tree. The result is returned as an array sorted in ascending order based on the value.
|
|
558
|
-
* The result includes the key and value attributes, and you can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
559
|
-
* @param condition You can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
421
|
+
* The result includes the key and value attributes, and you can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
422
|
+
* @param condition You can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
560
423
|
*/
|
|
561
424
|
where(condition) {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
425
|
+
let result = null;
|
|
426
|
+
for (const k in condition) {
|
|
427
|
+
const key = k;
|
|
428
|
+
const value = condition[key];
|
|
429
|
+
const startNode = this._verifierStartNode[key](value);
|
|
430
|
+
const direction = this._verifierDirection[key];
|
|
431
|
+
const fullSearch = this._verifierFullSearch[key];
|
|
432
|
+
const comparator = this._verifierMap[key];
|
|
433
|
+
const pairs = this.getPairs(value, startNode, fullSearch, comparator, direction);
|
|
434
|
+
if (result === null) {
|
|
435
|
+
result = pairs;
|
|
436
|
+
} else {
|
|
437
|
+
result = result.filter((pair) => pairs.find((p) => p.key === pair.key));
|
|
438
|
+
}
|
|
575
439
|
}
|
|
440
|
+
return result ?? [];
|
|
576
441
|
}
|
|
577
442
|
/**
|
|
578
443
|
* You enter the key and value as a pair. You can later search for the pair by value.
|
|
@@ -581,7 +446,7 @@ var BPTree = class {
|
|
|
581
446
|
* @param value The value of the pair.
|
|
582
447
|
*/
|
|
583
448
|
insert(key, value) {
|
|
584
|
-
const before = this.
|
|
449
|
+
const before = this.insertableNode(value);
|
|
585
450
|
this._insertAtLeaf(before, key, value);
|
|
586
451
|
if (before.values.length === this.order) {
|
|
587
452
|
const after = this._createNode(
|
|
@@ -589,14 +454,21 @@ var BPTree = class {
|
|
|
589
454
|
[],
|
|
590
455
|
true,
|
|
591
456
|
before.parent,
|
|
592
|
-
before.next
|
|
457
|
+
before.next,
|
|
458
|
+
before.id
|
|
593
459
|
);
|
|
594
460
|
const mid = Math.ceil(this.order / 2) - 1;
|
|
461
|
+
const beforeNext = before.next;
|
|
595
462
|
after.values = before.values.slice(mid + 1);
|
|
596
463
|
after.keys = before.keys.slice(mid + 1);
|
|
597
464
|
before.values = before.values.slice(0, mid + 1);
|
|
598
465
|
before.keys = before.keys.slice(0, mid + 1);
|
|
599
466
|
before.next = after.id;
|
|
467
|
+
if (beforeNext) {
|
|
468
|
+
const node = this.getNode(beforeNext);
|
|
469
|
+
node.prev = after.id;
|
|
470
|
+
this._setUpdates(node);
|
|
471
|
+
}
|
|
600
472
|
this._insertInParent(before, after.values[0], after);
|
|
601
473
|
this._setCreates(after);
|
|
602
474
|
this._setUpdates(before);
|
|
@@ -611,7 +483,7 @@ var BPTree = class {
|
|
|
611
483
|
* @param value The value of the pair.
|
|
612
484
|
*/
|
|
613
485
|
delete(key, value) {
|
|
614
|
-
const node = this.
|
|
486
|
+
const node = this.insertableNode(value);
|
|
615
487
|
let i = node.values.length;
|
|
616
488
|
while (i--) {
|
|
617
489
|
const nValue = node.values[i];
|
|
@@ -672,18 +544,18 @@ var BPTree = class {
|
|
|
672
544
|
let parentNode = this.getNode(node.parent);
|
|
673
545
|
let prevNode = null;
|
|
674
546
|
let nextNode = null;
|
|
675
|
-
let
|
|
676
|
-
let
|
|
547
|
+
let prevValue = null;
|
|
548
|
+
let postValue = null;
|
|
677
549
|
for (let i = 0, len = parentNode.keys.length; i < len; i++) {
|
|
678
550
|
const nKey = parentNode.keys[i];
|
|
679
551
|
if (nKey === node.id) {
|
|
680
552
|
if (i > 0) {
|
|
681
553
|
prevNode = this.getNode(parentNode.keys[i - 1]);
|
|
682
|
-
|
|
554
|
+
prevValue = parentNode.values[i - 1];
|
|
683
555
|
}
|
|
684
556
|
if (i < parentNode.keys.length - 1) {
|
|
685
557
|
nextNode = this.getNode(parentNode.keys[i + 1]);
|
|
686
|
-
|
|
558
|
+
postValue = parentNode.values[i];
|
|
687
559
|
}
|
|
688
560
|
}
|
|
689
561
|
}
|
|
@@ -691,19 +563,19 @@ var BPTree = class {
|
|
|
691
563
|
let guess;
|
|
692
564
|
if (prevNode === null) {
|
|
693
565
|
pointer = nextNode;
|
|
694
|
-
guess =
|
|
566
|
+
guess = postValue;
|
|
695
567
|
} else if (nextNode === null) {
|
|
696
568
|
isPredecessor = true;
|
|
697
569
|
pointer = prevNode;
|
|
698
|
-
guess =
|
|
570
|
+
guess = prevValue;
|
|
699
571
|
} else {
|
|
700
572
|
if (node.values.length + nextNode.values.length < this.order) {
|
|
701
573
|
pointer = nextNode;
|
|
702
|
-
guess =
|
|
574
|
+
guess = postValue;
|
|
703
575
|
} else {
|
|
704
576
|
isPredecessor = true;
|
|
705
577
|
pointer = prevNode;
|
|
706
|
-
guess =
|
|
578
|
+
guess = prevValue;
|
|
707
579
|
}
|
|
708
580
|
}
|
|
709
581
|
if (node.values.length + pointer.values.length < this.order) {
|
|
@@ -717,6 +589,20 @@ var BPTree = class {
|
|
|
717
589
|
pointer.values.push(guess);
|
|
718
590
|
} else {
|
|
719
591
|
pointer.next = node.next;
|
|
592
|
+
pointer.prev = node.id;
|
|
593
|
+
if (pointer.next) {
|
|
594
|
+
const n = this.getNode(node.next);
|
|
595
|
+
n.prev = pointer.id;
|
|
596
|
+
this._setUpdates(n);
|
|
597
|
+
}
|
|
598
|
+
if (pointer.prev) {
|
|
599
|
+
const n = this.getNode(node.id);
|
|
600
|
+
n.next = pointer.id;
|
|
601
|
+
this._setUpdates(n);
|
|
602
|
+
}
|
|
603
|
+
if (isPredecessor) {
|
|
604
|
+
pointer.prev = 0;
|
|
605
|
+
}
|
|
720
606
|
}
|
|
721
607
|
pointer.values.push(...node.values);
|
|
722
608
|
if (!pointer.leaf) {
|
package/dist/typings/BPTree.d.ts
CHANGED
|
@@ -2,14 +2,22 @@ import type { Json } from './utils/types';
|
|
|
2
2
|
import { BinarySearch } from './utils/BinarySearch';
|
|
3
3
|
import { ValueComparator } from './ValueComparator';
|
|
4
4
|
import { SerializeStrategy } from './SerializeStrategy';
|
|
5
|
-
type BPTreeCondition<V> = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
type BPTreeCondition<V> = Partial<{
|
|
6
|
+
/** Searches for pairs greater than the given value. */
|
|
7
|
+
gt: V;
|
|
8
|
+
/** Searches for pairs less than the given value. */
|
|
9
|
+
lt: V;
|
|
10
|
+
/** Searches for pairs greater than or equal to the given value. */
|
|
11
|
+
gte: V;
|
|
12
|
+
/** Searches for pairs less than or equal to the given value. */
|
|
13
|
+
lte: V;
|
|
14
|
+
/** "Searches for pairs equal to the given value. */
|
|
9
15
|
equal: V;
|
|
10
|
-
|
|
16
|
+
/** Searches for pairs not equal to the given value. */
|
|
11
17
|
notEqual: V;
|
|
12
|
-
|
|
18
|
+
/** Searches for values matching the given pattern. '%' matches zero or more characters, and '_' matches exactly one character. */
|
|
19
|
+
like: V;
|
|
20
|
+
}>;
|
|
13
21
|
type BPTreePair<K, V> = {
|
|
14
22
|
key: K;
|
|
15
23
|
value: V;
|
|
@@ -22,6 +30,7 @@ export interface BPTreeNode<K, V> {
|
|
|
22
30
|
leaf: boolean;
|
|
23
31
|
parent: number;
|
|
24
32
|
next: number;
|
|
33
|
+
prev: number;
|
|
25
34
|
}
|
|
26
35
|
export interface BPTreeInternalNode<K, V> extends BPTreeNode<K, V> {
|
|
27
36
|
leaf: false;
|
|
@@ -42,6 +51,10 @@ export declare class BPTree<K, V> {
|
|
|
42
51
|
private readonly _creates;
|
|
43
52
|
private readonly _updates;
|
|
44
53
|
private _updatedHead;
|
|
54
|
+
private readonly _verifierMap;
|
|
55
|
+
private readonly _verifierStartNode;
|
|
56
|
+
private readonly _verifierDirection;
|
|
57
|
+
private readonly _verifierFullSearch;
|
|
45
58
|
private _createNodeId;
|
|
46
59
|
private _createNode;
|
|
47
60
|
/**
|
|
@@ -58,7 +71,7 @@ export declare class BPTree<K, V> {
|
|
|
58
71
|
private _emitUpdates;
|
|
59
72
|
protected getNode(id: number): BPTreeUnknownNode<K, V>;
|
|
60
73
|
protected leftestNode(): BPTreeLeafNode<K, V>;
|
|
61
|
-
|
|
74
|
+
protected insertableNode(value: V): BPTreeLeafNode<K, V>;
|
|
62
75
|
/**
|
|
63
76
|
* It returns whether there is a value in the tree.
|
|
64
77
|
* @param key The key value to search for.
|
|
@@ -67,32 +80,20 @@ export declare class BPTree<K, V> {
|
|
|
67
80
|
exists(key: K, value: V): boolean;
|
|
68
81
|
private _insertAtLeaf;
|
|
69
82
|
private _insertInParent;
|
|
70
|
-
private
|
|
71
|
-
private
|
|
72
|
-
|
|
73
|
-
private _onlyLtCondition;
|
|
74
|
-
private _rangeCondition;
|
|
75
|
-
private _getKeysFromValue;
|
|
76
|
-
private _getKeysFromNEValue;
|
|
77
|
-
private _getKeysFromRange;
|
|
78
|
-
private _getKeysFromGt;
|
|
79
|
-
private _getKeysFromLt;
|
|
80
|
-
private _getPairsFromValue;
|
|
81
|
-
private _getPairsFromNEValue;
|
|
82
|
-
private _getPairsFromRange;
|
|
83
|
-
private _getPairsFromGt;
|
|
84
|
-
private _getPairsFromLt;
|
|
83
|
+
private _getPairsRightToLeft;
|
|
84
|
+
private _getPairsLeftToRight;
|
|
85
|
+
protected getPairs(value: V, startNode: BPTreeLeafNode<K, V>, fullSearch: boolean, comparator: (nodeValue: V, value: V) => boolean, direction: -1 | 1): BPTreePair<K, V>[];
|
|
85
86
|
/**
|
|
86
87
|
* It searches for a key within the tree. The result is returned as an array sorted in ascending order based on the value.
|
|
87
|
-
* The result is key set instance, and you can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
88
|
+
* The result is key set instance, and you can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
88
89
|
* This method operates much faster than first searching with `where` and then retrieving only the key list.
|
|
89
|
-
* @param condition You can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
90
|
+
* @param condition You can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
90
91
|
*/
|
|
91
92
|
keys(condition: BPTreeCondition<V>): Set<K>;
|
|
92
93
|
/**
|
|
93
94
|
* It searches for a value within the tree. The result is returned as an array sorted in ascending order based on the value.
|
|
94
|
-
* The result includes the key and value attributes, and you can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
95
|
-
* @param condition You can use the `gt`, `lt`, `equal`, `notEqual` condition statements.
|
|
95
|
+
* The result includes the key and value attributes, and you can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
96
|
+
* @param condition You can use the `gt`, `lt`, `gte`, `lte`, `equal`, `notEqual`, `like` condition statements.
|
|
96
97
|
*/
|
|
97
98
|
where(condition: BPTreeCondition<V>): BPTreePair<K, V>[];
|
|
98
99
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serializable-bptree",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Store the B+tree flexibly, not only in-memory.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"serializable-bptree",
|
|
18
18
|
"b-plus-tree",
|
|
19
19
|
"serialize",
|
|
20
|
+
"serialization",
|
|
20
21
|
"store",
|
|
21
22
|
"btree",
|
|
22
23
|
"bptree",
|