serializable-bptree 6.0.2 → 6.1.1
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 +17 -376
- package/dist/cjs/index.cjs +227 -14
- package/dist/esm/index.mjs +227 -14
- package/dist/types/BPTreeAsync.d.ts +10 -3
- package/dist/types/BPTreeSync.d.ts +10 -3
- package/dist/types/base/BPTree.d.ts +32 -3
- package/dist/types/base/ValueComparator.d.ts +19 -0
- package/package.json +1 -1
package/dist/esm/index.mjs
CHANGED
|
@@ -9,6 +9,33 @@ var ValueComparator = class {
|
|
|
9
9
|
isHigher(value, than) {
|
|
10
10
|
return this.asc(value, than) > 0;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* This method is used for range queries with composite values.
|
|
14
|
+
* By default, it calls the `asc` method, so existing code works without changes.
|
|
15
|
+
*
|
|
16
|
+
* When using composite values (e.g., `{ k: number, v: number }`),
|
|
17
|
+
* override this method to compare only the primary sorting field (e.g., `v`),
|
|
18
|
+
* ignoring the unique identifier field (e.g., `k`).
|
|
19
|
+
*
|
|
20
|
+
* This enables efficient range queries like `primaryEqual` that find all entries
|
|
21
|
+
* with the same primary value regardless of their unique identifiers.
|
|
22
|
+
*
|
|
23
|
+
* @param a Value a.
|
|
24
|
+
* @param b Value b.
|
|
25
|
+
* @returns Negative if a < b, 0 if equal, positive if a > b (based on primary field only).
|
|
26
|
+
*/
|
|
27
|
+
primaryAsc(a, b) {
|
|
28
|
+
return this.asc(a, b);
|
|
29
|
+
}
|
|
30
|
+
isPrimarySame(value, than) {
|
|
31
|
+
return this.primaryAsc(value, than) === 0;
|
|
32
|
+
}
|
|
33
|
+
isPrimaryLower(value, than) {
|
|
34
|
+
return this.primaryAsc(value, than) < 0;
|
|
35
|
+
}
|
|
36
|
+
isPrimaryHigher(value, than) {
|
|
37
|
+
return this.primaryAsc(value, than) > 0;
|
|
38
|
+
}
|
|
12
39
|
};
|
|
13
40
|
var NumericComparator = class extends ValueComparator {
|
|
14
41
|
asc(a, b) {
|
|
@@ -424,6 +451,13 @@ var BPTree = class {
|
|
|
424
451
|
equal: (nv, v) => this.comparator.isSame(nv, v),
|
|
425
452
|
notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
|
|
426
453
|
or: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isSame(nv, v2)),
|
|
454
|
+
primaryGt: (nv, v) => this.comparator.isPrimaryHigher(nv, v),
|
|
455
|
+
primaryGte: (nv, v) => this.comparator.isPrimaryHigher(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
456
|
+
primaryLt: (nv, v) => this.comparator.isPrimaryLower(nv, v),
|
|
457
|
+
primaryLte: (nv, v) => this.comparator.isPrimaryLower(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
458
|
+
primaryEqual: (nv, v) => this.comparator.isPrimarySame(nv, v),
|
|
459
|
+
primaryNotEqual: (nv, v) => this.comparator.isPrimarySame(nv, v) === false,
|
|
460
|
+
primaryOr: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isPrimarySame(nv, v2)),
|
|
427
461
|
like: (nv, v) => {
|
|
428
462
|
const nodeValue = this.comparator.match(nv);
|
|
429
463
|
const value = this.comparator.match(v);
|
|
@@ -440,6 +474,13 @@ var BPTree = class {
|
|
|
440
474
|
equal: (v) => this.insertableNode(v),
|
|
441
475
|
notEqual: (v) => this.leftestNode(),
|
|
442
476
|
or: (v) => this.insertableNode(this.lowestValue(this.ensureValues(v))),
|
|
477
|
+
primaryGt: (v) => this.insertableNodeByPrimary(v),
|
|
478
|
+
primaryGte: (v) => this.insertableNodeByPrimary(v),
|
|
479
|
+
primaryLt: (v) => this.insertableNodeByPrimary(v),
|
|
480
|
+
primaryLte: (v) => this.insertableRightestNodeByPrimary(v),
|
|
481
|
+
primaryEqual: (v) => this.insertableNodeByPrimary(v),
|
|
482
|
+
primaryNotEqual: (v) => this.leftestNode(),
|
|
483
|
+
primaryOr: (v) => this.insertableNodeByPrimary(this.lowestPrimaryValue(this.ensureValues(v))),
|
|
443
484
|
like: (v) => this.leftestNode()
|
|
444
485
|
};
|
|
445
486
|
verifierEndNode = {
|
|
@@ -453,6 +494,15 @@ var BPTree = class {
|
|
|
453
494
|
this.highestValue(this.ensureValues(v)),
|
|
454
495
|
this.verifierDirection.or
|
|
455
496
|
),
|
|
497
|
+
primaryGt: (v) => null,
|
|
498
|
+
primaryGte: (v) => null,
|
|
499
|
+
primaryLt: (v) => null,
|
|
500
|
+
primaryLte: (v) => null,
|
|
501
|
+
primaryEqual: (v) => null,
|
|
502
|
+
primaryNotEqual: (v) => null,
|
|
503
|
+
primaryOr: (v) => this.insertableRightestEndNodeByPrimary(
|
|
504
|
+
this.highestPrimaryValue(this.ensureValues(v))
|
|
505
|
+
),
|
|
456
506
|
like: (v) => null
|
|
457
507
|
};
|
|
458
508
|
verifierDirection = {
|
|
@@ -463,8 +513,37 @@ var BPTree = class {
|
|
|
463
513
|
equal: 1,
|
|
464
514
|
notEqual: 1,
|
|
465
515
|
or: 1,
|
|
516
|
+
primaryGt: 1,
|
|
517
|
+
primaryGte: 1,
|
|
518
|
+
primaryLt: -1,
|
|
519
|
+
primaryLte: -1,
|
|
520
|
+
primaryEqual: 1,
|
|
521
|
+
primaryNotEqual: 1,
|
|
522
|
+
primaryOr: 1,
|
|
466
523
|
like: 1
|
|
467
524
|
};
|
|
525
|
+
/**
|
|
526
|
+
* Determines whether early termination is allowed for each condition.
|
|
527
|
+
* When true, the search will stop once a match is found and then a non-match is encountered.
|
|
528
|
+
* Only applicable for conditions that guarantee contiguous matches in a sorted B+Tree.
|
|
529
|
+
*/
|
|
530
|
+
verifierEarlyTerminate = {
|
|
531
|
+
gt: false,
|
|
532
|
+
gte: false,
|
|
533
|
+
lt: false,
|
|
534
|
+
lte: false,
|
|
535
|
+
equal: true,
|
|
536
|
+
notEqual: false,
|
|
537
|
+
or: false,
|
|
538
|
+
primaryGt: false,
|
|
539
|
+
primaryGte: false,
|
|
540
|
+
primaryLt: false,
|
|
541
|
+
primaryLte: false,
|
|
542
|
+
primaryEqual: true,
|
|
543
|
+
primaryNotEqual: false,
|
|
544
|
+
primaryOr: false,
|
|
545
|
+
like: false
|
|
546
|
+
};
|
|
468
547
|
constructor(strategy, comparator, option) {
|
|
469
548
|
this.strategy = strategy;
|
|
470
549
|
this.comparator = comparator;
|
|
@@ -498,6 +577,14 @@ var BPTree = class {
|
|
|
498
577
|
const i = v.length - 1;
|
|
499
578
|
return [...v].sort((a, b) => this.comparator.asc(a, b))[i];
|
|
500
579
|
}
|
|
580
|
+
lowestPrimaryValue(v) {
|
|
581
|
+
const i = 0;
|
|
582
|
+
return [...v].sort((a, b) => this.comparator.primaryAsc(a, b))[i];
|
|
583
|
+
}
|
|
584
|
+
highestPrimaryValue(v) {
|
|
585
|
+
const i = v.length - 1;
|
|
586
|
+
return [...v].sort((a, b) => this.comparator.primaryAsc(a, b))[i];
|
|
587
|
+
}
|
|
501
588
|
_insertAtLeaf(node, key, value) {
|
|
502
589
|
if (node.values.length) {
|
|
503
590
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
@@ -577,10 +664,11 @@ var BPTreeSync = class extends BPTree {
|
|
|
577
664
|
capacity: this.option.capacity ?? 1e3
|
|
578
665
|
});
|
|
579
666
|
}
|
|
580
|
-
getPairsRightToLeft(value, startNode, endNode, comparator) {
|
|
667
|
+
getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate) {
|
|
581
668
|
const pairs = [];
|
|
582
669
|
let node = startNode;
|
|
583
670
|
let done = false;
|
|
671
|
+
let hasMatched = false;
|
|
584
672
|
while (!done) {
|
|
585
673
|
if (endNode && node.id === endNode.id) {
|
|
586
674
|
done = true;
|
|
@@ -591,12 +679,17 @@ var BPTreeSync = class extends BPTree {
|
|
|
591
679
|
const nValue = node.values[i];
|
|
592
680
|
const keys = node.keys[i];
|
|
593
681
|
if (comparator(nValue, value)) {
|
|
682
|
+
hasMatched = true;
|
|
594
683
|
let j = keys.length;
|
|
595
684
|
while (j--) {
|
|
596
685
|
pairs.push([keys[j], nValue]);
|
|
597
686
|
}
|
|
687
|
+
} else if (earlyTerminate && hasMatched) {
|
|
688
|
+
done = true;
|
|
689
|
+
break;
|
|
598
690
|
}
|
|
599
691
|
}
|
|
692
|
+
if (done) break;
|
|
600
693
|
if (!node.prev) {
|
|
601
694
|
done = true;
|
|
602
695
|
break;
|
|
@@ -605,10 +698,11 @@ var BPTreeSync = class extends BPTree {
|
|
|
605
698
|
}
|
|
606
699
|
return new Map(pairs.reverse());
|
|
607
700
|
}
|
|
608
|
-
getPairsLeftToRight(value, startNode, endNode, comparator) {
|
|
701
|
+
getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate) {
|
|
609
702
|
const pairs = [];
|
|
610
703
|
let node = startNode;
|
|
611
704
|
let done = false;
|
|
705
|
+
let hasMatched = false;
|
|
612
706
|
while (!done) {
|
|
613
707
|
if (endNode && node.id === endNode.id) {
|
|
614
708
|
done = true;
|
|
@@ -618,12 +712,17 @@ var BPTreeSync = class extends BPTree {
|
|
|
618
712
|
const nValue = node.values[i];
|
|
619
713
|
const keys = node.keys[i];
|
|
620
714
|
if (comparator(nValue, value)) {
|
|
715
|
+
hasMatched = true;
|
|
621
716
|
for (let j = 0, len2 = keys.length; j < len2; j++) {
|
|
622
717
|
const key = keys[j];
|
|
623
718
|
pairs.push([key, nValue]);
|
|
624
719
|
}
|
|
720
|
+
} else if (earlyTerminate && hasMatched) {
|
|
721
|
+
done = true;
|
|
722
|
+
break;
|
|
625
723
|
}
|
|
626
724
|
}
|
|
725
|
+
if (done) break;
|
|
627
726
|
if (!node.next) {
|
|
628
727
|
done = true;
|
|
629
728
|
break;
|
|
@@ -632,12 +731,12 @@ var BPTreeSync = class extends BPTree {
|
|
|
632
731
|
}
|
|
633
732
|
return new Map(pairs);
|
|
634
733
|
}
|
|
635
|
-
getPairs(value, startNode, endNode, comparator, direction) {
|
|
734
|
+
getPairs(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
636
735
|
switch (direction) {
|
|
637
736
|
case -1:
|
|
638
|
-
return this.getPairsRightToLeft(value, startNode, endNode, comparator);
|
|
737
|
+
return this.getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate);
|
|
639
738
|
case 1:
|
|
640
|
-
return this.getPairsLeftToRight(value, startNode, endNode, comparator);
|
|
739
|
+
return this.getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate);
|
|
641
740
|
default:
|
|
642
741
|
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
643
742
|
}
|
|
@@ -986,6 +1085,55 @@ var BPTreeSync = class extends BPTree {
|
|
|
986
1085
|
}
|
|
987
1086
|
return node;
|
|
988
1087
|
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Find the insertable node using primaryAsc comparison.
|
|
1090
|
+
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
1091
|
+
*/
|
|
1092
|
+
insertableNodeByPrimary(value) {
|
|
1093
|
+
let node = this.getNode(this.root.id);
|
|
1094
|
+
while (!node.leaf) {
|
|
1095
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1096
|
+
const nValue = node.values[i];
|
|
1097
|
+
const k = node.keys;
|
|
1098
|
+
if (this.comparator.isPrimarySame(value, nValue)) {
|
|
1099
|
+
node = this.getNode(k[i]);
|
|
1100
|
+
break;
|
|
1101
|
+
} else if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
1102
|
+
node = this.getNode(k[i]);
|
|
1103
|
+
break;
|
|
1104
|
+
} else if (i + 1 === node.values.length) {
|
|
1105
|
+
node = this.getNode(k[i + 1]);
|
|
1106
|
+
break;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return node;
|
|
1111
|
+
}
|
|
1112
|
+
insertableRightestNodeByPrimary(value) {
|
|
1113
|
+
let node = this.getNode(this.root.id);
|
|
1114
|
+
while (!node.leaf) {
|
|
1115
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1116
|
+
const nValue = node.values[i];
|
|
1117
|
+
const k = node.keys;
|
|
1118
|
+
if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
1119
|
+
node = this.getNode(k[i]);
|
|
1120
|
+
break;
|
|
1121
|
+
}
|
|
1122
|
+
if (i + 1 === node.values.length) {
|
|
1123
|
+
node = this.getNode(k[i + 1]);
|
|
1124
|
+
break;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
return node;
|
|
1129
|
+
}
|
|
1130
|
+
insertableRightestEndNodeByPrimary(value) {
|
|
1131
|
+
const node = this.insertableRightestNodeByPrimary(value);
|
|
1132
|
+
if (!node.next) {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
return this.getNode(node.next);
|
|
1136
|
+
}
|
|
989
1137
|
insertableEndNode(value, direction) {
|
|
990
1138
|
const insertableNode = this.insertableNode(value);
|
|
991
1139
|
let key;
|
|
@@ -1054,7 +1202,8 @@ var BPTreeSync = class extends BPTree {
|
|
|
1054
1202
|
const endNode = this.verifierEndNode[key](value);
|
|
1055
1203
|
const direction = this.verifierDirection[key];
|
|
1056
1204
|
const comparator = this.verifierMap[key];
|
|
1057
|
-
const
|
|
1205
|
+
const earlyTerminate = this.verifierEarlyTerminate[key];
|
|
1206
|
+
const pairs = this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
|
|
1058
1207
|
if (!filterValues) {
|
|
1059
1208
|
filterValues = new Set(pairs.keys());
|
|
1060
1209
|
} else {
|
|
@@ -1079,7 +1228,8 @@ var BPTreeSync = class extends BPTree {
|
|
|
1079
1228
|
const endNode = this.verifierEndNode[key](value);
|
|
1080
1229
|
const direction = this.verifierDirection[key];
|
|
1081
1230
|
const comparator = this.verifierMap[key];
|
|
1082
|
-
const
|
|
1231
|
+
const earlyTerminate = this.verifierEarlyTerminate[key];
|
|
1232
|
+
const pairs = this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
|
|
1083
1233
|
if (result === null) {
|
|
1084
1234
|
result = pairs;
|
|
1085
1235
|
} else {
|
|
@@ -1467,10 +1617,11 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1467
1617
|
this.lock.writeUnlock(lockId);
|
|
1468
1618
|
});
|
|
1469
1619
|
}
|
|
1470
|
-
async getPairsRightToLeft(value, startNode, endNode, comparator) {
|
|
1620
|
+
async getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate) {
|
|
1471
1621
|
const pairs = [];
|
|
1472
1622
|
let node = startNode;
|
|
1473
1623
|
let done = false;
|
|
1624
|
+
let hasMatched = false;
|
|
1474
1625
|
while (!done) {
|
|
1475
1626
|
if (endNode && node.id === endNode.id) {
|
|
1476
1627
|
done = true;
|
|
@@ -1481,12 +1632,17 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1481
1632
|
const nValue = node.values[i];
|
|
1482
1633
|
const keys = node.keys[i];
|
|
1483
1634
|
if (comparator(nValue, value)) {
|
|
1635
|
+
hasMatched = true;
|
|
1484
1636
|
let j = keys.length;
|
|
1485
1637
|
while (j--) {
|
|
1486
1638
|
pairs.push([keys[j], nValue]);
|
|
1487
1639
|
}
|
|
1640
|
+
} else if (earlyTerminate && hasMatched) {
|
|
1641
|
+
done = true;
|
|
1642
|
+
break;
|
|
1488
1643
|
}
|
|
1489
1644
|
}
|
|
1645
|
+
if (done) break;
|
|
1490
1646
|
if (!node.prev) {
|
|
1491
1647
|
done = true;
|
|
1492
1648
|
break;
|
|
@@ -1495,10 +1651,11 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1495
1651
|
}
|
|
1496
1652
|
return new Map(pairs.reverse());
|
|
1497
1653
|
}
|
|
1498
|
-
async getPairsLeftToRight(value, startNode, endNode, comparator) {
|
|
1654
|
+
async getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate) {
|
|
1499
1655
|
const pairs = [];
|
|
1500
1656
|
let node = startNode;
|
|
1501
1657
|
let done = false;
|
|
1658
|
+
let hasMatched = false;
|
|
1502
1659
|
while (!done) {
|
|
1503
1660
|
if (endNode && node.id === endNode.id) {
|
|
1504
1661
|
done = true;
|
|
@@ -1508,12 +1665,17 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1508
1665
|
const nValue = node.values[i];
|
|
1509
1666
|
const keys = node.keys[i];
|
|
1510
1667
|
if (comparator(nValue, value)) {
|
|
1668
|
+
hasMatched = true;
|
|
1511
1669
|
for (let j = 0, len2 = keys.length; j < len2; j++) {
|
|
1512
1670
|
const key = keys[j];
|
|
1513
1671
|
pairs.push([key, nValue]);
|
|
1514
1672
|
}
|
|
1673
|
+
} else if (earlyTerminate && hasMatched) {
|
|
1674
|
+
done = true;
|
|
1675
|
+
break;
|
|
1515
1676
|
}
|
|
1516
1677
|
}
|
|
1678
|
+
if (done) break;
|
|
1517
1679
|
if (!node.next) {
|
|
1518
1680
|
done = true;
|
|
1519
1681
|
break;
|
|
@@ -1522,12 +1684,12 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1522
1684
|
}
|
|
1523
1685
|
return new Map(pairs);
|
|
1524
1686
|
}
|
|
1525
|
-
async getPairs(value, startNode, endNode, comparator, direction) {
|
|
1687
|
+
async getPairs(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
1526
1688
|
switch (direction) {
|
|
1527
1689
|
case -1:
|
|
1528
|
-
return await this.getPairsRightToLeft(value, startNode, endNode, comparator);
|
|
1690
|
+
return await this.getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate);
|
|
1529
1691
|
case 1:
|
|
1530
|
-
return await this.getPairsLeftToRight(value, startNode, endNode, comparator);
|
|
1692
|
+
return await this.getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate);
|
|
1531
1693
|
default:
|
|
1532
1694
|
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
1533
1695
|
}
|
|
@@ -1876,6 +2038,55 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1876
2038
|
}
|
|
1877
2039
|
return node;
|
|
1878
2040
|
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Find the insertable node using primaryAsc comparison.
|
|
2043
|
+
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
2044
|
+
*/
|
|
2045
|
+
async insertableNodeByPrimary(value) {
|
|
2046
|
+
let node = await this.getNode(this.root.id);
|
|
2047
|
+
while (!node.leaf) {
|
|
2048
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2049
|
+
const nValue = node.values[i];
|
|
2050
|
+
const k = node.keys;
|
|
2051
|
+
if (this.comparator.isPrimarySame(value, nValue)) {
|
|
2052
|
+
node = await this.getNode(k[i]);
|
|
2053
|
+
break;
|
|
2054
|
+
} else if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2055
|
+
node = await this.getNode(k[i]);
|
|
2056
|
+
break;
|
|
2057
|
+
} else if (i + 1 === node.values.length) {
|
|
2058
|
+
node = await this.getNode(k[i + 1]);
|
|
2059
|
+
break;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
return node;
|
|
2064
|
+
}
|
|
2065
|
+
async insertableRightestNodeByPrimary(value) {
|
|
2066
|
+
let node = await this.getNode(this.root.id);
|
|
2067
|
+
while (!node.leaf) {
|
|
2068
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2069
|
+
const nValue = node.values[i];
|
|
2070
|
+
const k = node.keys;
|
|
2071
|
+
if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2072
|
+
node = await this.getNode(k[i]);
|
|
2073
|
+
break;
|
|
2074
|
+
}
|
|
2075
|
+
if (i + 1 === node.values.length) {
|
|
2076
|
+
node = await this.getNode(k[i + 1]);
|
|
2077
|
+
break;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
return node;
|
|
2082
|
+
}
|
|
2083
|
+
async insertableRightestEndNodeByPrimary(value) {
|
|
2084
|
+
const node = await this.insertableRightestNodeByPrimary(value);
|
|
2085
|
+
if (!node.next) {
|
|
2086
|
+
return null;
|
|
2087
|
+
}
|
|
2088
|
+
return await this.getNode(node.next);
|
|
2089
|
+
}
|
|
1879
2090
|
async insertableEndNode(value, direction) {
|
|
1880
2091
|
const insertableNode = await this.insertableNode(value);
|
|
1881
2092
|
let key;
|
|
@@ -1945,7 +2156,8 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1945
2156
|
const endNode = await this.verifierEndNode[key](value);
|
|
1946
2157
|
const direction = this.verifierDirection[key];
|
|
1947
2158
|
const comparator = this.verifierMap[key];
|
|
1948
|
-
const
|
|
2159
|
+
const earlyTerminate = this.verifierEarlyTerminate[key];
|
|
2160
|
+
const pairs = await this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
|
|
1949
2161
|
if (!filterValues) {
|
|
1950
2162
|
filterValues = new Set(pairs.keys());
|
|
1951
2163
|
} else {
|
|
@@ -1972,7 +2184,8 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1972
2184
|
const endNode = await this.verifierEndNode[key](value);
|
|
1973
2185
|
const direction = this.verifierDirection[key];
|
|
1974
2186
|
const comparator = this.verifierMap[key];
|
|
1975
|
-
const
|
|
2187
|
+
const earlyTerminate = this.verifierEarlyTerminate[key];
|
|
2188
|
+
const pairs = await this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
|
|
1976
2189
|
if (result === null) {
|
|
1977
2190
|
result = pairs;
|
|
1978
2191
|
} else {
|
|
@@ -10,9 +10,9 @@ export declare class BPTreeAsync<K, V> extends BPTree<K, V> {
|
|
|
10
10
|
private _createCachedNode;
|
|
11
11
|
protected readLock<T>(callback: () => Promise<T>): Promise<T>;
|
|
12
12
|
protected writeLock<T>(callback: () => Promise<T>): Promise<T>;
|
|
13
|
-
protected getPairsRightToLeft(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean): Promise<BPTreePair<K, V>>;
|
|
14
|
-
protected getPairsLeftToRight(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean): Promise<BPTreePair<K, V>>;
|
|
15
|
-
protected getPairs(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, direction: 1 | -1): Promise<BPTreePair<K, V>>;
|
|
13
|
+
protected getPairsRightToLeft(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, earlyTerminate: boolean): Promise<BPTreePair<K, V>>;
|
|
14
|
+
protected getPairsLeftToRight(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, earlyTerminate: boolean): Promise<BPTreePair<K, V>>;
|
|
15
|
+
protected getPairs(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, direction: 1 | -1, earlyTerminate: boolean): Promise<BPTreePair<K, V>>;
|
|
16
16
|
protected _createNodeId(isLeaf: boolean): Promise<string>;
|
|
17
17
|
protected _createNode(isLeaf: boolean, keys: string[] | K[][], values: V[], leaf?: boolean, parent?: string | null, next?: string | null, prev?: string | null): Promise<BPTreeUnknownNode<K, V>>;
|
|
18
18
|
protected _deleteEntry(node: BPTreeUnknownNode<K, V>, key: BPTreeNodeKey<K>, value: V): Promise<void>;
|
|
@@ -20,6 +20,13 @@ export declare class BPTreeAsync<K, V> extends BPTree<K, V> {
|
|
|
20
20
|
init(): Promise<void>;
|
|
21
21
|
protected getNode(id: string): Promise<BPTreeUnknownNode<K, V>>;
|
|
22
22
|
protected insertableNode(value: V): Promise<BPTreeLeafNode<K, V>>;
|
|
23
|
+
/**
|
|
24
|
+
* Find the insertable node using primaryAsc comparison.
|
|
25
|
+
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
26
|
+
*/
|
|
27
|
+
protected insertableNodeByPrimary(value: V): Promise<BPTreeLeafNode<K, V>>;
|
|
28
|
+
protected insertableRightestNodeByPrimary(value: V): Promise<BPTreeLeafNode<K, V>>;
|
|
29
|
+
protected insertableRightestEndNodeByPrimary(value: V): Promise<BPTreeLeafNode<K, V> | null>;
|
|
23
30
|
protected insertableEndNode(value: V, direction: 1 | -1): Promise<BPTreeLeafNode<K, V> | null>;
|
|
24
31
|
protected leftestNode(): Promise<BPTreeLeafNode<K, V>>;
|
|
25
32
|
protected rightestNode(): Promise<BPTreeLeafNode<K, V>>;
|
|
@@ -7,9 +7,9 @@ export declare class BPTreeSync<K, V> extends BPTree<K, V> {
|
|
|
7
7
|
protected readonly nodes: ReturnType<typeof this._createCachedNode>;
|
|
8
8
|
constructor(strategy: SerializeStrategySync<K, V>, comparator: ValueComparator<V>, option?: BPTreeConstructorOption);
|
|
9
9
|
private _createCachedNode;
|
|
10
|
-
protected getPairsRightToLeft(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean): BPTreePair<K, V>;
|
|
11
|
-
protected getPairsLeftToRight(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean): BPTreePair<K, V>;
|
|
12
|
-
protected getPairs(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, direction: 1 | -1): BPTreePair<K, V>;
|
|
10
|
+
protected getPairsRightToLeft(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, earlyTerminate: boolean): BPTreePair<K, V>;
|
|
11
|
+
protected getPairsLeftToRight(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, earlyTerminate: boolean): BPTreePair<K, V>;
|
|
12
|
+
protected getPairs(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, direction: 1 | -1, earlyTerminate: boolean): BPTreePair<K, V>;
|
|
13
13
|
protected _createNodeId(isLeaf: boolean): string;
|
|
14
14
|
protected _createNode(isLeaf: boolean, keys: string[] | K[][], values: V[], leaf?: boolean, parent?: string | null, next?: string | null, prev?: string | null): BPTreeUnknownNode<K, V>;
|
|
15
15
|
protected _deleteEntry(node: BPTreeUnknownNode<K, V>, key: BPTreeNodeKey<K>, value: V): void;
|
|
@@ -17,6 +17,13 @@ export declare class BPTreeSync<K, V> extends BPTree<K, V> {
|
|
|
17
17
|
init(): void;
|
|
18
18
|
protected getNode(id: string): BPTreeUnknownNode<K, V>;
|
|
19
19
|
protected insertableNode(value: V): BPTreeLeafNode<K, V>;
|
|
20
|
+
/**
|
|
21
|
+
* Find the insertable node using primaryAsc comparison.
|
|
22
|
+
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
23
|
+
*/
|
|
24
|
+
protected insertableNodeByPrimary(value: V): BPTreeLeafNode<K, V>;
|
|
25
|
+
protected insertableRightestNodeByPrimary(value: V): BPTreeLeafNode<K, V>;
|
|
26
|
+
protected insertableRightestEndNodeByPrimary(value: V): BPTreeLeafNode<K, V> | null;
|
|
20
27
|
protected insertableEndNode(value: V, direction: 1 | -1): BPTreeLeafNode<K, V> | null;
|
|
21
28
|
protected leftestNode(): BPTreeLeafNode<K, V>;
|
|
22
29
|
protected rightestNode(): BPTreeLeafNode<K, V>;
|
|
@@ -22,6 +22,24 @@ export type BPTreeCondition<V> = Partial<{
|
|
|
22
22
|
or: Partial<V>[];
|
|
23
23
|
/** Searches for values matching the given pattern. '%' matches zero or more characters, and '_' matches exactly one character. */
|
|
24
24
|
like: Partial<V>;
|
|
25
|
+
/**
|
|
26
|
+
* Searches for pairs where the primary field equals the given value.
|
|
27
|
+
* Uses `primaryAsc` method for comparison, which compares only the primary sorting field.
|
|
28
|
+
* Useful for composite values where you want to find all entries with the same primary value.
|
|
29
|
+
*/
|
|
30
|
+
primaryEqual: Partial<V>;
|
|
31
|
+
/** Searches for pairs where the primary field is greater than the given value. */
|
|
32
|
+
primaryGt: Partial<V>;
|
|
33
|
+
/** Searches for pairs where the primary field is greater than or equal to the given value. */
|
|
34
|
+
primaryGte: Partial<V>;
|
|
35
|
+
/** Searches for pairs where the primary field is less than the given value. */
|
|
36
|
+
primaryLt: Partial<V>;
|
|
37
|
+
/** Searches for pairs where the primary field is less than or equal to the given value. */
|
|
38
|
+
primaryLte: Partial<V>;
|
|
39
|
+
/** Searches for pairs where the primary field is not equal to the given value. */
|
|
40
|
+
primaryNotEqual: Partial<V>;
|
|
41
|
+
/** Searches for pairs where the primary field matches at least one of the given values. */
|
|
42
|
+
primaryOr: Partial<V>[];
|
|
25
43
|
}>;
|
|
26
44
|
export type BPTreePair<K, V> = Map<K, V>;
|
|
27
45
|
export type BPTreeUnknownNode<K, V> = BPTreeInternalNode<K, V> | BPTreeLeafNode<K, V>;
|
|
@@ -66,17 +84,26 @@ export declare abstract class BPTree<K, V> {
|
|
|
66
84
|
protected readonly verifierStartNode: Record<keyof BPTreeCondition<V>, (value: V) => Deferred<BPTreeLeafNode<K, V>>>;
|
|
67
85
|
protected readonly verifierEndNode: Record<keyof BPTreeCondition<V>, (value: V) => Deferred<BPTreeLeafNode<K, V> | null>>;
|
|
68
86
|
protected readonly verifierDirection: Record<keyof BPTreeCondition<V>, -1 | 1>;
|
|
87
|
+
/**
|
|
88
|
+
* Determines whether early termination is allowed for each condition.
|
|
89
|
+
* When true, the search will stop once a match is found and then a non-match is encountered.
|
|
90
|
+
* Only applicable for conditions that guarantee contiguous matches in a sorted B+Tree.
|
|
91
|
+
*/
|
|
92
|
+
protected readonly verifierEarlyTerminate: Record<keyof BPTreeCondition<V>, boolean>;
|
|
69
93
|
protected constructor(strategy: SerializeStrategy<K, V>, comparator: ValueComparator<V>, option?: BPTreeConstructorOption);
|
|
70
94
|
private _createCachedRegexp;
|
|
71
|
-
protected abstract getPairsRightToLeft(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean): Deferred<BPTreePair<K, V>>;
|
|
72
|
-
protected abstract getPairsLeftToRight(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean): Deferred<BPTreePair<K, V>>;
|
|
73
|
-
protected abstract getPairs(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, direction: -1 | 1): Deferred<BPTreePair<K, V>>;
|
|
95
|
+
protected abstract getPairsRightToLeft(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, earlyTerminate: boolean): Deferred<BPTreePair<K, V>>;
|
|
96
|
+
protected abstract getPairsLeftToRight(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, earlyTerminate: boolean): Deferred<BPTreePair<K, V>>;
|
|
97
|
+
protected abstract getPairs(value: V, startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, comparator: (nodeValue: V, value: V) => boolean, direction: -1 | 1, earlyTerminate: boolean): Deferred<BPTreePair<K, V>>;
|
|
74
98
|
protected abstract _createNodeId(isLeaf: boolean): Deferred<string>;
|
|
75
99
|
protected abstract _createNode(isLeaf: boolean, keys: string[] | K[][], values: V[], leaf?: boolean, parent?: string | null, next?: string | null, prev?: string | null): Deferred<BPTreeUnknownNode<K, V>>;
|
|
76
100
|
protected abstract _deleteEntry(node: BPTreeUnknownNode<K, V>, key: BPTreeNodeKey<K>, value: V): Deferred<void>;
|
|
77
101
|
protected abstract _insertInParent(node: BPTreeUnknownNode<K, V>, value: V, pointer: BPTreeUnknownNode<K, V>): Deferred<void>;
|
|
78
102
|
protected abstract getNode(id: string): Deferred<BPTreeUnknownNode<K, V>>;
|
|
79
103
|
protected abstract insertableNode(value: V): Deferred<BPTreeLeafNode<K, V>>;
|
|
104
|
+
protected abstract insertableNodeByPrimary(value: V): Deferred<BPTreeLeafNode<K, V>>;
|
|
105
|
+
protected abstract insertableRightestNodeByPrimary(value: V): Deferred<BPTreeLeafNode<K, V>>;
|
|
106
|
+
protected abstract insertableRightestEndNodeByPrimary(value: V): Deferred<BPTreeLeafNode<K, V> | null>;
|
|
80
107
|
protected abstract insertableEndNode(value: V, direction: 1 | -1): Deferred<BPTreeLeafNode<K, V> | null>;
|
|
81
108
|
protected abstract leftestNode(): Deferred<BPTreeLeafNode<K, V>>;
|
|
82
109
|
protected abstract rightestNode(): Deferred<BPTreeLeafNode<K, V>>;
|
|
@@ -140,6 +167,8 @@ export declare abstract class BPTree<K, V> {
|
|
|
140
167
|
protected ensureValues(v: V | V[]): V[];
|
|
141
168
|
protected lowestValue(v: V[]): V;
|
|
142
169
|
protected highestValue(v: V[]): V;
|
|
170
|
+
protected lowestPrimaryValue(v: V[]): V;
|
|
171
|
+
protected highestPrimaryValue(v: V[]): V;
|
|
143
172
|
protected _insertAtLeaf(node: BPTreeLeafNode<K, V>, key: K, value: V): void;
|
|
144
173
|
protected bufferForNodeCreate(node: BPTreeUnknownNode<K, V>): void;
|
|
145
174
|
protected bufferForNodeUpdate(node: BPTreeUnknownNode<K, V>): void;
|
|
@@ -45,6 +45,25 @@ export declare abstract class ValueComparator<V> {
|
|
|
45
45
|
isLower(value: V, than: V): boolean;
|
|
46
46
|
isSame(value: V, than: V): boolean;
|
|
47
47
|
isHigher(value: V, than: V): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* This method is used for range queries with composite values.
|
|
50
|
+
* By default, it calls the `asc` method, so existing code works without changes.
|
|
51
|
+
*
|
|
52
|
+
* When using composite values (e.g., `{ k: number, v: number }`),
|
|
53
|
+
* override this method to compare only the primary sorting field (e.g., `v`),
|
|
54
|
+
* ignoring the unique identifier field (e.g., `k`).
|
|
55
|
+
*
|
|
56
|
+
* This enables efficient range queries like `primaryEqual` that find all entries
|
|
57
|
+
* with the same primary value regardless of their unique identifiers.
|
|
58
|
+
*
|
|
59
|
+
* @param a Value a.
|
|
60
|
+
* @param b Value b.
|
|
61
|
+
* @returns Negative if a < b, 0 if equal, positive if a > b (based on primary field only).
|
|
62
|
+
*/
|
|
63
|
+
primaryAsc(a: V, b: V): number;
|
|
64
|
+
isPrimarySame(value: V, than: V): boolean;
|
|
65
|
+
isPrimaryLower(value: V, than: V): boolean;
|
|
66
|
+
isPrimaryHigher(value: V, than: V): boolean;
|
|
48
67
|
}
|
|
49
68
|
export declare class NumericComparator extends ValueComparator<number> {
|
|
50
69
|
asc(a: number, b: number): number;
|