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.
@@ -43,6 +43,33 @@ var ValueComparator = class {
43
43
  isHigher(value, than) {
44
44
  return this.asc(value, than) > 0;
45
45
  }
46
+ /**
47
+ * This method is used for range queries with composite values.
48
+ * By default, it calls the `asc` method, so existing code works without changes.
49
+ *
50
+ * When using composite values (e.g., `{ k: number, v: number }`),
51
+ * override this method to compare only the primary sorting field (e.g., `v`),
52
+ * ignoring the unique identifier field (e.g., `k`).
53
+ *
54
+ * This enables efficient range queries like `primaryEqual` that find all entries
55
+ * with the same primary value regardless of their unique identifiers.
56
+ *
57
+ * @param a Value a.
58
+ * @param b Value b.
59
+ * @returns Negative if a < b, 0 if equal, positive if a > b (based on primary field only).
60
+ */
61
+ primaryAsc(a, b) {
62
+ return this.asc(a, b);
63
+ }
64
+ isPrimarySame(value, than) {
65
+ return this.primaryAsc(value, than) === 0;
66
+ }
67
+ isPrimaryLower(value, than) {
68
+ return this.primaryAsc(value, than) < 0;
69
+ }
70
+ isPrimaryHigher(value, than) {
71
+ return this.primaryAsc(value, than) > 0;
72
+ }
46
73
  };
47
74
  var NumericComparator = class extends ValueComparator {
48
75
  asc(a, b) {
@@ -458,6 +485,13 @@ var BPTree = class {
458
485
  equal: (nv, v) => this.comparator.isSame(nv, v),
459
486
  notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
460
487
  or: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isSame(nv, v2)),
488
+ primaryGt: (nv, v) => this.comparator.isPrimaryHigher(nv, v),
489
+ primaryGte: (nv, v) => this.comparator.isPrimaryHigher(nv, v) || this.comparator.isPrimarySame(nv, v),
490
+ primaryLt: (nv, v) => this.comparator.isPrimaryLower(nv, v),
491
+ primaryLte: (nv, v) => this.comparator.isPrimaryLower(nv, v) || this.comparator.isPrimarySame(nv, v),
492
+ primaryEqual: (nv, v) => this.comparator.isPrimarySame(nv, v),
493
+ primaryNotEqual: (nv, v) => this.comparator.isPrimarySame(nv, v) === false,
494
+ primaryOr: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isPrimarySame(nv, v2)),
461
495
  like: (nv, v) => {
462
496
  const nodeValue = this.comparator.match(nv);
463
497
  const value = this.comparator.match(v);
@@ -474,6 +508,13 @@ var BPTree = class {
474
508
  equal: (v) => this.insertableNode(v),
475
509
  notEqual: (v) => this.leftestNode(),
476
510
  or: (v) => this.insertableNode(this.lowestValue(this.ensureValues(v))),
511
+ primaryGt: (v) => this.insertableNodeByPrimary(v),
512
+ primaryGte: (v) => this.insertableNodeByPrimary(v),
513
+ primaryLt: (v) => this.insertableNodeByPrimary(v),
514
+ primaryLte: (v) => this.insertableRightestNodeByPrimary(v),
515
+ primaryEqual: (v) => this.insertableNodeByPrimary(v),
516
+ primaryNotEqual: (v) => this.leftestNode(),
517
+ primaryOr: (v) => this.insertableNodeByPrimary(this.lowestPrimaryValue(this.ensureValues(v))),
477
518
  like: (v) => this.leftestNode()
478
519
  };
479
520
  verifierEndNode = {
@@ -487,6 +528,15 @@ var BPTree = class {
487
528
  this.highestValue(this.ensureValues(v)),
488
529
  this.verifierDirection.or
489
530
  ),
531
+ primaryGt: (v) => null,
532
+ primaryGte: (v) => null,
533
+ primaryLt: (v) => null,
534
+ primaryLte: (v) => null,
535
+ primaryEqual: (v) => null,
536
+ primaryNotEqual: (v) => null,
537
+ primaryOr: (v) => this.insertableRightestEndNodeByPrimary(
538
+ this.highestPrimaryValue(this.ensureValues(v))
539
+ ),
490
540
  like: (v) => null
491
541
  };
492
542
  verifierDirection = {
@@ -497,8 +547,37 @@ var BPTree = class {
497
547
  equal: 1,
498
548
  notEqual: 1,
499
549
  or: 1,
550
+ primaryGt: 1,
551
+ primaryGte: 1,
552
+ primaryLt: -1,
553
+ primaryLte: -1,
554
+ primaryEqual: 1,
555
+ primaryNotEqual: 1,
556
+ primaryOr: 1,
500
557
  like: 1
501
558
  };
559
+ /**
560
+ * Determines whether early termination is allowed for each condition.
561
+ * When true, the search will stop once a match is found and then a non-match is encountered.
562
+ * Only applicable for conditions that guarantee contiguous matches in a sorted B+Tree.
563
+ */
564
+ verifierEarlyTerminate = {
565
+ gt: false,
566
+ gte: false,
567
+ lt: false,
568
+ lte: false,
569
+ equal: true,
570
+ notEqual: false,
571
+ or: false,
572
+ primaryGt: false,
573
+ primaryGte: false,
574
+ primaryLt: false,
575
+ primaryLte: false,
576
+ primaryEqual: true,
577
+ primaryNotEqual: false,
578
+ primaryOr: false,
579
+ like: false
580
+ };
502
581
  constructor(strategy, comparator, option) {
503
582
  this.strategy = strategy;
504
583
  this.comparator = comparator;
@@ -532,6 +611,14 @@ var BPTree = class {
532
611
  const i = v.length - 1;
533
612
  return [...v].sort((a, b) => this.comparator.asc(a, b))[i];
534
613
  }
614
+ lowestPrimaryValue(v) {
615
+ const i = 0;
616
+ return [...v].sort((a, b) => this.comparator.primaryAsc(a, b))[i];
617
+ }
618
+ highestPrimaryValue(v) {
619
+ const i = v.length - 1;
620
+ return [...v].sort((a, b) => this.comparator.primaryAsc(a, b))[i];
621
+ }
535
622
  _insertAtLeaf(node, key, value) {
536
623
  if (node.values.length) {
537
624
  for (let i = 0, len = node.values.length; i < len; i++) {
@@ -611,10 +698,11 @@ var BPTreeSync = class extends BPTree {
611
698
  capacity: this.option.capacity ?? 1e3
612
699
  });
613
700
  }
614
- getPairsRightToLeft(value, startNode, endNode, comparator) {
701
+ getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate) {
615
702
  const pairs = [];
616
703
  let node = startNode;
617
704
  let done = false;
705
+ let hasMatched = false;
618
706
  while (!done) {
619
707
  if (endNode && node.id === endNode.id) {
620
708
  done = true;
@@ -625,12 +713,17 @@ var BPTreeSync = class extends BPTree {
625
713
  const nValue = node.values[i];
626
714
  const keys = node.keys[i];
627
715
  if (comparator(nValue, value)) {
716
+ hasMatched = true;
628
717
  let j = keys.length;
629
718
  while (j--) {
630
719
  pairs.push([keys[j], nValue]);
631
720
  }
721
+ } else if (earlyTerminate && hasMatched) {
722
+ done = true;
723
+ break;
632
724
  }
633
725
  }
726
+ if (done) break;
634
727
  if (!node.prev) {
635
728
  done = true;
636
729
  break;
@@ -639,10 +732,11 @@ var BPTreeSync = class extends BPTree {
639
732
  }
640
733
  return new Map(pairs.reverse());
641
734
  }
642
- getPairsLeftToRight(value, startNode, endNode, comparator) {
735
+ getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate) {
643
736
  const pairs = [];
644
737
  let node = startNode;
645
738
  let done = false;
739
+ let hasMatched = false;
646
740
  while (!done) {
647
741
  if (endNode && node.id === endNode.id) {
648
742
  done = true;
@@ -652,12 +746,17 @@ var BPTreeSync = class extends BPTree {
652
746
  const nValue = node.values[i];
653
747
  const keys = node.keys[i];
654
748
  if (comparator(nValue, value)) {
749
+ hasMatched = true;
655
750
  for (let j = 0, len2 = keys.length; j < len2; j++) {
656
751
  const key = keys[j];
657
752
  pairs.push([key, nValue]);
658
753
  }
754
+ } else if (earlyTerminate && hasMatched) {
755
+ done = true;
756
+ break;
659
757
  }
660
758
  }
759
+ if (done) break;
661
760
  if (!node.next) {
662
761
  done = true;
663
762
  break;
@@ -666,12 +765,12 @@ var BPTreeSync = class extends BPTree {
666
765
  }
667
766
  return new Map(pairs);
668
767
  }
669
- getPairs(value, startNode, endNode, comparator, direction) {
768
+ getPairs(value, startNode, endNode, comparator, direction, earlyTerminate) {
670
769
  switch (direction) {
671
770
  case -1:
672
- return this.getPairsRightToLeft(value, startNode, endNode, comparator);
771
+ return this.getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate);
673
772
  case 1:
674
- return this.getPairsLeftToRight(value, startNode, endNode, comparator);
773
+ return this.getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate);
675
774
  default:
676
775
  throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
677
776
  }
@@ -1020,6 +1119,55 @@ var BPTreeSync = class extends BPTree {
1020
1119
  }
1021
1120
  return node;
1022
1121
  }
1122
+ /**
1123
+ * Find the insertable node using primaryAsc comparison.
1124
+ * This allows finding nodes by primary value only, ignoring unique identifiers.
1125
+ */
1126
+ insertableNodeByPrimary(value) {
1127
+ let node = this.getNode(this.root.id);
1128
+ while (!node.leaf) {
1129
+ for (let i = 0, len = node.values.length; i < len; i++) {
1130
+ const nValue = node.values[i];
1131
+ const k = node.keys;
1132
+ if (this.comparator.isPrimarySame(value, nValue)) {
1133
+ node = this.getNode(k[i]);
1134
+ break;
1135
+ } else if (this.comparator.isPrimaryLower(value, nValue)) {
1136
+ node = this.getNode(k[i]);
1137
+ break;
1138
+ } else if (i + 1 === node.values.length) {
1139
+ node = this.getNode(k[i + 1]);
1140
+ break;
1141
+ }
1142
+ }
1143
+ }
1144
+ return node;
1145
+ }
1146
+ insertableRightestNodeByPrimary(value) {
1147
+ let node = this.getNode(this.root.id);
1148
+ while (!node.leaf) {
1149
+ for (let i = 0, len = node.values.length; i < len; i++) {
1150
+ const nValue = node.values[i];
1151
+ const k = node.keys;
1152
+ if (this.comparator.isPrimaryLower(value, nValue)) {
1153
+ node = this.getNode(k[i]);
1154
+ break;
1155
+ }
1156
+ if (i + 1 === node.values.length) {
1157
+ node = this.getNode(k[i + 1]);
1158
+ break;
1159
+ }
1160
+ }
1161
+ }
1162
+ return node;
1163
+ }
1164
+ insertableRightestEndNodeByPrimary(value) {
1165
+ const node = this.insertableRightestNodeByPrimary(value);
1166
+ if (!node.next) {
1167
+ return null;
1168
+ }
1169
+ return this.getNode(node.next);
1170
+ }
1023
1171
  insertableEndNode(value, direction) {
1024
1172
  const insertableNode = this.insertableNode(value);
1025
1173
  let key;
@@ -1088,7 +1236,8 @@ var BPTreeSync = class extends BPTree {
1088
1236
  const endNode = this.verifierEndNode[key](value);
1089
1237
  const direction = this.verifierDirection[key];
1090
1238
  const comparator = this.verifierMap[key];
1091
- const pairs = this.getPairs(value, startNode, endNode, comparator, direction);
1239
+ const earlyTerminate = this.verifierEarlyTerminate[key];
1240
+ const pairs = this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
1092
1241
  if (!filterValues) {
1093
1242
  filterValues = new Set(pairs.keys());
1094
1243
  } else {
@@ -1113,7 +1262,8 @@ var BPTreeSync = class extends BPTree {
1113
1262
  const endNode = this.verifierEndNode[key](value);
1114
1263
  const direction = this.verifierDirection[key];
1115
1264
  const comparator = this.verifierMap[key];
1116
- const pairs = this.getPairs(value, startNode, endNode, comparator, direction);
1265
+ const earlyTerminate = this.verifierEarlyTerminate[key];
1266
+ const pairs = this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
1117
1267
  if (result === null) {
1118
1268
  result = pairs;
1119
1269
  } else {
@@ -1501,10 +1651,11 @@ var BPTreeAsync = class extends BPTree {
1501
1651
  this.lock.writeUnlock(lockId);
1502
1652
  });
1503
1653
  }
1504
- async getPairsRightToLeft(value, startNode, endNode, comparator) {
1654
+ async getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate) {
1505
1655
  const pairs = [];
1506
1656
  let node = startNode;
1507
1657
  let done = false;
1658
+ let hasMatched = false;
1508
1659
  while (!done) {
1509
1660
  if (endNode && node.id === endNode.id) {
1510
1661
  done = true;
@@ -1515,12 +1666,17 @@ var BPTreeAsync = class extends BPTree {
1515
1666
  const nValue = node.values[i];
1516
1667
  const keys = node.keys[i];
1517
1668
  if (comparator(nValue, value)) {
1669
+ hasMatched = true;
1518
1670
  let j = keys.length;
1519
1671
  while (j--) {
1520
1672
  pairs.push([keys[j], nValue]);
1521
1673
  }
1674
+ } else if (earlyTerminate && hasMatched) {
1675
+ done = true;
1676
+ break;
1522
1677
  }
1523
1678
  }
1679
+ if (done) break;
1524
1680
  if (!node.prev) {
1525
1681
  done = true;
1526
1682
  break;
@@ -1529,10 +1685,11 @@ var BPTreeAsync = class extends BPTree {
1529
1685
  }
1530
1686
  return new Map(pairs.reverse());
1531
1687
  }
1532
- async getPairsLeftToRight(value, startNode, endNode, comparator) {
1688
+ async getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate) {
1533
1689
  const pairs = [];
1534
1690
  let node = startNode;
1535
1691
  let done = false;
1692
+ let hasMatched = false;
1536
1693
  while (!done) {
1537
1694
  if (endNode && node.id === endNode.id) {
1538
1695
  done = true;
@@ -1542,12 +1699,17 @@ var BPTreeAsync = class extends BPTree {
1542
1699
  const nValue = node.values[i];
1543
1700
  const keys = node.keys[i];
1544
1701
  if (comparator(nValue, value)) {
1702
+ hasMatched = true;
1545
1703
  for (let j = 0, len2 = keys.length; j < len2; j++) {
1546
1704
  const key = keys[j];
1547
1705
  pairs.push([key, nValue]);
1548
1706
  }
1707
+ } else if (earlyTerminate && hasMatched) {
1708
+ done = true;
1709
+ break;
1549
1710
  }
1550
1711
  }
1712
+ if (done) break;
1551
1713
  if (!node.next) {
1552
1714
  done = true;
1553
1715
  break;
@@ -1556,12 +1718,12 @@ var BPTreeAsync = class extends BPTree {
1556
1718
  }
1557
1719
  return new Map(pairs);
1558
1720
  }
1559
- async getPairs(value, startNode, endNode, comparator, direction) {
1721
+ async getPairs(value, startNode, endNode, comparator, direction, earlyTerminate) {
1560
1722
  switch (direction) {
1561
1723
  case -1:
1562
- return await this.getPairsRightToLeft(value, startNode, endNode, comparator);
1724
+ return await this.getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate);
1563
1725
  case 1:
1564
- return await this.getPairsLeftToRight(value, startNode, endNode, comparator);
1726
+ return await this.getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate);
1565
1727
  default:
1566
1728
  throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
1567
1729
  }
@@ -1910,6 +2072,55 @@ var BPTreeAsync = class extends BPTree {
1910
2072
  }
1911
2073
  return node;
1912
2074
  }
2075
+ /**
2076
+ * Find the insertable node using primaryAsc comparison.
2077
+ * This allows finding nodes by primary value only, ignoring unique identifiers.
2078
+ */
2079
+ async insertableNodeByPrimary(value) {
2080
+ let node = await this.getNode(this.root.id);
2081
+ while (!node.leaf) {
2082
+ for (let i = 0, len = node.values.length; i < len; i++) {
2083
+ const nValue = node.values[i];
2084
+ const k = node.keys;
2085
+ if (this.comparator.isPrimarySame(value, nValue)) {
2086
+ node = await this.getNode(k[i]);
2087
+ break;
2088
+ } else if (this.comparator.isPrimaryLower(value, nValue)) {
2089
+ node = await this.getNode(k[i]);
2090
+ break;
2091
+ } else if (i + 1 === node.values.length) {
2092
+ node = await this.getNode(k[i + 1]);
2093
+ break;
2094
+ }
2095
+ }
2096
+ }
2097
+ return node;
2098
+ }
2099
+ async insertableRightestNodeByPrimary(value) {
2100
+ let node = await this.getNode(this.root.id);
2101
+ while (!node.leaf) {
2102
+ for (let i = 0, len = node.values.length; i < len; i++) {
2103
+ const nValue = node.values[i];
2104
+ const k = node.keys;
2105
+ if (this.comparator.isPrimaryLower(value, nValue)) {
2106
+ node = await this.getNode(k[i]);
2107
+ break;
2108
+ }
2109
+ if (i + 1 === node.values.length) {
2110
+ node = await this.getNode(k[i + 1]);
2111
+ break;
2112
+ }
2113
+ }
2114
+ }
2115
+ return node;
2116
+ }
2117
+ async insertableRightestEndNodeByPrimary(value) {
2118
+ const node = await this.insertableRightestNodeByPrimary(value);
2119
+ if (!node.next) {
2120
+ return null;
2121
+ }
2122
+ return await this.getNode(node.next);
2123
+ }
1913
2124
  async insertableEndNode(value, direction) {
1914
2125
  const insertableNode = await this.insertableNode(value);
1915
2126
  let key;
@@ -1979,7 +2190,8 @@ var BPTreeAsync = class extends BPTree {
1979
2190
  const endNode = await this.verifierEndNode[key](value);
1980
2191
  const direction = this.verifierDirection[key];
1981
2192
  const comparator = this.verifierMap[key];
1982
- const pairs = await this.getPairs(value, startNode, endNode, comparator, direction);
2193
+ const earlyTerminate = this.verifierEarlyTerminate[key];
2194
+ const pairs = await this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
1983
2195
  if (!filterValues) {
1984
2196
  filterValues = new Set(pairs.keys());
1985
2197
  } else {
@@ -2006,7 +2218,8 @@ var BPTreeAsync = class extends BPTree {
2006
2218
  const endNode = await this.verifierEndNode[key](value);
2007
2219
  const direction = this.verifierDirection[key];
2008
2220
  const comparator = this.verifierMap[key];
2009
- const pairs = await this.getPairs(value, startNode, endNode, comparator, direction);
2221
+ const earlyTerminate = this.verifierEarlyTerminate[key];
2222
+ const pairs = await this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
2010
2223
  if (result === null) {
2011
2224
  result = pairs;
2012
2225
  } else {