dataply 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -71,6 +71,33 @@ var ValueComparator = class {
71
71
  isHigher(value, than) {
72
72
  return this.asc(value, than) > 0;
73
73
  }
74
+ /**
75
+ * This method is used for range queries with composite values.
76
+ * By default, it calls the `asc` method, so existing code works without changes.
77
+ *
78
+ * When using composite values (e.g., `{ k: number, v: number }`),
79
+ * override this method to compare only the primary sorting field (e.g., `v`),
80
+ * ignoring the unique identifier field (e.g., `k`).
81
+ *
82
+ * This enables efficient range queries like `primaryEqual` that find all entries
83
+ * with the same primary value regardless of their unique identifiers.
84
+ *
85
+ * @param a Value a.
86
+ * @param b Value b.
87
+ * @returns Negative if a < b, 0 if equal, positive if a > b (based on primary field only).
88
+ */
89
+ primaryAsc(a, b) {
90
+ return this.asc(a, b);
91
+ }
92
+ isPrimarySame(value, than) {
93
+ return this.primaryAsc(value, than) === 0;
94
+ }
95
+ isPrimaryLower(value, than) {
96
+ return this.primaryAsc(value, than) < 0;
97
+ }
98
+ isPrimaryHigher(value, than) {
99
+ return this.primaryAsc(value, than) > 0;
100
+ }
74
101
  };
75
102
  var NumericComparator = class extends ValueComparator {
76
103
  asc(a, b) {
@@ -482,6 +509,13 @@ var BPTree = class {
482
509
  equal: (nv, v) => this.comparator.isSame(nv, v),
483
510
  notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
484
511
  or: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isSame(nv, v2)),
512
+ primaryGt: (nv, v) => this.comparator.isPrimaryHigher(nv, v),
513
+ primaryGte: (nv, v) => this.comparator.isPrimaryHigher(nv, v) || this.comparator.isPrimarySame(nv, v),
514
+ primaryLt: (nv, v) => this.comparator.isPrimaryLower(nv, v),
515
+ primaryLte: (nv, v) => this.comparator.isPrimaryLower(nv, v) || this.comparator.isPrimarySame(nv, v),
516
+ primaryEqual: (nv, v) => this.comparator.isPrimarySame(nv, v),
517
+ primaryNotEqual: (nv, v) => this.comparator.isPrimarySame(nv, v) === false,
518
+ primaryOr: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isPrimarySame(nv, v2)),
485
519
  like: (nv, v) => {
486
520
  const nodeValue = this.comparator.match(nv);
487
521
  const value = this.comparator.match(v);
@@ -498,6 +532,13 @@ var BPTree = class {
498
532
  equal: (v) => this.insertableNode(v),
499
533
  notEqual: (v) => this.leftestNode(),
500
534
  or: (v) => this.insertableNode(this.lowestValue(this.ensureValues(v))),
535
+ primaryGt: (v) => this.insertableNodeByPrimary(v),
536
+ primaryGte: (v) => this.insertableNodeByPrimary(v),
537
+ primaryLt: (v) => this.insertableNodeByPrimary(v),
538
+ primaryLte: (v) => this.insertableRightestNodeByPrimary(v),
539
+ primaryEqual: (v) => this.insertableNodeByPrimary(v),
540
+ primaryNotEqual: (v) => this.leftestNode(),
541
+ primaryOr: (v) => this.insertableNodeByPrimary(this.lowestPrimaryValue(this.ensureValues(v))),
501
542
  like: (v) => this.leftestNode()
502
543
  };
503
544
  verifierEndNode = {
@@ -511,6 +552,15 @@ var BPTree = class {
511
552
  this.highestValue(this.ensureValues(v)),
512
553
  this.verifierDirection.or
513
554
  ),
555
+ primaryGt: (v) => null,
556
+ primaryGte: (v) => null,
557
+ primaryLt: (v) => null,
558
+ primaryLte: (v) => null,
559
+ primaryEqual: (v) => null,
560
+ primaryNotEqual: (v) => null,
561
+ primaryOr: (v) => this.insertableRightestEndNodeByPrimary(
562
+ this.highestPrimaryValue(this.ensureValues(v))
563
+ ),
514
564
  like: (v) => null
515
565
  };
516
566
  verifierDirection = {
@@ -521,8 +571,37 @@ var BPTree = class {
521
571
  equal: 1,
522
572
  notEqual: 1,
523
573
  or: 1,
574
+ primaryGt: 1,
575
+ primaryGte: 1,
576
+ primaryLt: -1,
577
+ primaryLte: -1,
578
+ primaryEqual: 1,
579
+ primaryNotEqual: 1,
580
+ primaryOr: 1,
524
581
  like: 1
525
582
  };
583
+ /**
584
+ * Determines whether early termination is allowed for each condition.
585
+ * When true, the search will stop once a match is found and then a non-match is encountered.
586
+ * Only applicable for conditions that guarantee contiguous matches in a sorted B+Tree.
587
+ */
588
+ verifierEarlyTerminate = {
589
+ gt: false,
590
+ gte: false,
591
+ lt: false,
592
+ lte: false,
593
+ equal: true,
594
+ notEqual: false,
595
+ or: false,
596
+ primaryGt: false,
597
+ primaryGte: false,
598
+ primaryLt: false,
599
+ primaryLte: false,
600
+ primaryEqual: true,
601
+ primaryNotEqual: false,
602
+ primaryOr: false,
603
+ like: false
604
+ };
526
605
  constructor(strategy, comparator, option) {
527
606
  this.strategy = strategy;
528
607
  this.comparator = comparator;
@@ -556,6 +635,14 @@ var BPTree = class {
556
635
  const i = v.length - 1;
557
636
  return [...v].sort((a, b) => this.comparator.asc(a, b))[i];
558
637
  }
638
+ lowestPrimaryValue(v) {
639
+ const i = 0;
640
+ return [...v].sort((a, b) => this.comparator.primaryAsc(a, b))[i];
641
+ }
642
+ highestPrimaryValue(v) {
643
+ const i = v.length - 1;
644
+ return [...v].sort((a, b) => this.comparator.primaryAsc(a, b))[i];
645
+ }
559
646
  _insertAtLeaf(node, key, value) {
560
647
  if (node.values.length) {
561
648
  for (let i = 0, len = node.values.length; i < len; i++) {
@@ -633,10 +720,11 @@ var BPTreeSync = class extends BPTree {
633
720
  capacity: this.option.capacity ?? 1e3
634
721
  });
635
722
  }
636
- getPairsRightToLeft(value, startNode, endNode, comparator) {
723
+ getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate) {
637
724
  const pairs = [];
638
725
  let node = startNode;
639
726
  let done = false;
727
+ let hasMatched = false;
640
728
  while (!done) {
641
729
  if (endNode && node.id === endNode.id) {
642
730
  done = true;
@@ -647,12 +735,17 @@ var BPTreeSync = class extends BPTree {
647
735
  const nValue = node.values[i];
648
736
  const keys = node.keys[i];
649
737
  if (comparator(nValue, value)) {
738
+ hasMatched = true;
650
739
  let j = keys.length;
651
740
  while (j--) {
652
741
  pairs.push([keys[j], nValue]);
653
742
  }
743
+ } else if (earlyTerminate && hasMatched) {
744
+ done = true;
745
+ break;
654
746
  }
655
747
  }
748
+ if (done) break;
656
749
  if (!node.prev) {
657
750
  done = true;
658
751
  break;
@@ -661,10 +754,11 @@ var BPTreeSync = class extends BPTree {
661
754
  }
662
755
  return new Map(pairs.reverse());
663
756
  }
664
- getPairsLeftToRight(value, startNode, endNode, comparator) {
757
+ getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate) {
665
758
  const pairs = [];
666
759
  let node = startNode;
667
760
  let done = false;
761
+ let hasMatched = false;
668
762
  while (!done) {
669
763
  if (endNode && node.id === endNode.id) {
670
764
  done = true;
@@ -674,12 +768,17 @@ var BPTreeSync = class extends BPTree {
674
768
  const nValue = node.values[i];
675
769
  const keys = node.keys[i];
676
770
  if (comparator(nValue, value)) {
771
+ hasMatched = true;
677
772
  for (let j = 0, len2 = keys.length; j < len2; j++) {
678
773
  const key = keys[j];
679
774
  pairs.push([key, nValue]);
680
775
  }
776
+ } else if (earlyTerminate && hasMatched) {
777
+ done = true;
778
+ break;
681
779
  }
682
780
  }
781
+ if (done) break;
683
782
  if (!node.next) {
684
783
  done = true;
685
784
  break;
@@ -688,12 +787,12 @@ var BPTreeSync = class extends BPTree {
688
787
  }
689
788
  return new Map(pairs);
690
789
  }
691
- getPairs(value, startNode, endNode, comparator, direction) {
790
+ getPairs(value, startNode, endNode, comparator, direction, earlyTerminate) {
692
791
  switch (direction) {
693
792
  case -1:
694
- return this.getPairsRightToLeft(value, startNode, endNode, comparator);
793
+ return this.getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate);
695
794
  case 1:
696
- return this.getPairsLeftToRight(value, startNode, endNode, comparator);
795
+ return this.getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate);
697
796
  default:
698
797
  throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
699
798
  }
@@ -1042,6 +1141,55 @@ var BPTreeSync = class extends BPTree {
1042
1141
  }
1043
1142
  return node;
1044
1143
  }
1144
+ /**
1145
+ * Find the insertable node using primaryAsc comparison.
1146
+ * This allows finding nodes by primary value only, ignoring unique identifiers.
1147
+ */
1148
+ insertableNodeByPrimary(value) {
1149
+ let node = this.getNode(this.root.id);
1150
+ while (!node.leaf) {
1151
+ for (let i = 0, len = node.values.length; i < len; i++) {
1152
+ const nValue = node.values[i];
1153
+ const k = node.keys;
1154
+ if (this.comparator.isPrimarySame(value, nValue)) {
1155
+ node = this.getNode(k[i]);
1156
+ break;
1157
+ } else if (this.comparator.isPrimaryLower(value, nValue)) {
1158
+ node = this.getNode(k[i]);
1159
+ break;
1160
+ } else if (i + 1 === node.values.length) {
1161
+ node = this.getNode(k[i + 1]);
1162
+ break;
1163
+ }
1164
+ }
1165
+ }
1166
+ return node;
1167
+ }
1168
+ insertableRightestNodeByPrimary(value) {
1169
+ let node = this.getNode(this.root.id);
1170
+ while (!node.leaf) {
1171
+ for (let i = 0, len = node.values.length; i < len; i++) {
1172
+ const nValue = node.values[i];
1173
+ const k = node.keys;
1174
+ if (this.comparator.isPrimaryLower(value, nValue)) {
1175
+ node = this.getNode(k[i]);
1176
+ break;
1177
+ }
1178
+ if (i + 1 === node.values.length) {
1179
+ node = this.getNode(k[i + 1]);
1180
+ break;
1181
+ }
1182
+ }
1183
+ }
1184
+ return node;
1185
+ }
1186
+ insertableRightestEndNodeByPrimary(value) {
1187
+ const node = this.insertableRightestNodeByPrimary(value);
1188
+ if (!node.next) {
1189
+ return null;
1190
+ }
1191
+ return this.getNode(node.next);
1192
+ }
1045
1193
  insertableEndNode(value, direction) {
1046
1194
  const insertableNode = this.insertableNode(value);
1047
1195
  let key;
@@ -1110,7 +1258,8 @@ var BPTreeSync = class extends BPTree {
1110
1258
  const endNode = this.verifierEndNode[key](value);
1111
1259
  const direction = this.verifierDirection[key];
1112
1260
  const comparator = this.verifierMap[key];
1113
- const pairs = this.getPairs(value, startNode, endNode, comparator, direction);
1261
+ const earlyTerminate = this.verifierEarlyTerminate[key];
1262
+ const pairs = this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
1114
1263
  if (!filterValues) {
1115
1264
  filterValues = new Set(pairs.keys());
1116
1265
  } else {
@@ -1135,7 +1284,8 @@ var BPTreeSync = class extends BPTree {
1135
1284
  const endNode = this.verifierEndNode[key](value);
1136
1285
  const direction = this.verifierDirection[key];
1137
1286
  const comparator = this.verifierMap[key];
1138
- const pairs = this.getPairs(value, startNode, endNode, comparator, direction);
1287
+ const earlyTerminate = this.verifierEarlyTerminate[key];
1288
+ const pairs = this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
1139
1289
  if (result === null) {
1140
1290
  result = pairs;
1141
1291
  } else {
@@ -1519,10 +1669,11 @@ var BPTreeAsync = class extends BPTree {
1519
1669
  this.lock.writeUnlock(lockId);
1520
1670
  });
1521
1671
  }
1522
- async getPairsRightToLeft(value, startNode, endNode, comparator) {
1672
+ async getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate) {
1523
1673
  const pairs = [];
1524
1674
  let node = startNode;
1525
1675
  let done = false;
1676
+ let hasMatched = false;
1526
1677
  while (!done) {
1527
1678
  if (endNode && node.id === endNode.id) {
1528
1679
  done = true;
@@ -1533,12 +1684,17 @@ var BPTreeAsync = class extends BPTree {
1533
1684
  const nValue = node.values[i];
1534
1685
  const keys = node.keys[i];
1535
1686
  if (comparator(nValue, value)) {
1687
+ hasMatched = true;
1536
1688
  let j = keys.length;
1537
1689
  while (j--) {
1538
1690
  pairs.push([keys[j], nValue]);
1539
1691
  }
1692
+ } else if (earlyTerminate && hasMatched) {
1693
+ done = true;
1694
+ break;
1540
1695
  }
1541
1696
  }
1697
+ if (done) break;
1542
1698
  if (!node.prev) {
1543
1699
  done = true;
1544
1700
  break;
@@ -1547,10 +1703,11 @@ var BPTreeAsync = class extends BPTree {
1547
1703
  }
1548
1704
  return new Map(pairs.reverse());
1549
1705
  }
1550
- async getPairsLeftToRight(value, startNode, endNode, comparator) {
1706
+ async getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate) {
1551
1707
  const pairs = [];
1552
1708
  let node = startNode;
1553
1709
  let done = false;
1710
+ let hasMatched = false;
1554
1711
  while (!done) {
1555
1712
  if (endNode && node.id === endNode.id) {
1556
1713
  done = true;
@@ -1560,12 +1717,17 @@ var BPTreeAsync = class extends BPTree {
1560
1717
  const nValue = node.values[i];
1561
1718
  const keys = node.keys[i];
1562
1719
  if (comparator(nValue, value)) {
1720
+ hasMatched = true;
1563
1721
  for (let j = 0, len2 = keys.length; j < len2; j++) {
1564
1722
  const key = keys[j];
1565
1723
  pairs.push([key, nValue]);
1566
1724
  }
1725
+ } else if (earlyTerminate && hasMatched) {
1726
+ done = true;
1727
+ break;
1567
1728
  }
1568
1729
  }
1730
+ if (done) break;
1569
1731
  if (!node.next) {
1570
1732
  done = true;
1571
1733
  break;
@@ -1574,12 +1736,12 @@ var BPTreeAsync = class extends BPTree {
1574
1736
  }
1575
1737
  return new Map(pairs);
1576
1738
  }
1577
- async getPairs(value, startNode, endNode, comparator, direction) {
1739
+ async getPairs(value, startNode, endNode, comparator, direction, earlyTerminate) {
1578
1740
  switch (direction) {
1579
1741
  case -1:
1580
- return await this.getPairsRightToLeft(value, startNode, endNode, comparator);
1742
+ return await this.getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate);
1581
1743
  case 1:
1582
- return await this.getPairsLeftToRight(value, startNode, endNode, comparator);
1744
+ return await this.getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate);
1583
1745
  default:
1584
1746
  throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
1585
1747
  }
@@ -1928,6 +2090,55 @@ var BPTreeAsync = class extends BPTree {
1928
2090
  }
1929
2091
  return node;
1930
2092
  }
2093
+ /**
2094
+ * Find the insertable node using primaryAsc comparison.
2095
+ * This allows finding nodes by primary value only, ignoring unique identifiers.
2096
+ */
2097
+ async insertableNodeByPrimary(value) {
2098
+ let node = await this.getNode(this.root.id);
2099
+ while (!node.leaf) {
2100
+ for (let i = 0, len = node.values.length; i < len; i++) {
2101
+ const nValue = node.values[i];
2102
+ const k = node.keys;
2103
+ if (this.comparator.isPrimarySame(value, nValue)) {
2104
+ node = await this.getNode(k[i]);
2105
+ break;
2106
+ } else if (this.comparator.isPrimaryLower(value, nValue)) {
2107
+ node = await this.getNode(k[i]);
2108
+ break;
2109
+ } else 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 insertableRightestNodeByPrimary(value) {
2118
+ let node = await this.getNode(this.root.id);
2119
+ while (!node.leaf) {
2120
+ for (let i = 0, len = node.values.length; i < len; i++) {
2121
+ const nValue = node.values[i];
2122
+ const k = node.keys;
2123
+ if (this.comparator.isPrimaryLower(value, nValue)) {
2124
+ node = await this.getNode(k[i]);
2125
+ break;
2126
+ }
2127
+ if (i + 1 === node.values.length) {
2128
+ node = await this.getNode(k[i + 1]);
2129
+ break;
2130
+ }
2131
+ }
2132
+ }
2133
+ return node;
2134
+ }
2135
+ async insertableRightestEndNodeByPrimary(value) {
2136
+ const node = await this.insertableRightestNodeByPrimary(value);
2137
+ if (!node.next) {
2138
+ return null;
2139
+ }
2140
+ return await this.getNode(node.next);
2141
+ }
1931
2142
  async insertableEndNode(value, direction) {
1932
2143
  const insertableNode = await this.insertableNode(value);
1933
2144
  let key;
@@ -1997,7 +2208,8 @@ var BPTreeAsync = class extends BPTree {
1997
2208
  const endNode = await this.verifierEndNode[key](value);
1998
2209
  const direction = this.verifierDirection[key];
1999
2210
  const comparator = this.verifierMap[key];
2000
- const pairs = await this.getPairs(value, startNode, endNode, comparator, direction);
2211
+ const earlyTerminate = this.verifierEarlyTerminate[key];
2212
+ const pairs = await this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
2001
2213
  if (!filterValues) {
2002
2214
  filterValues = new Set(pairs.keys());
2003
2215
  } else {
@@ -2024,7 +2236,8 @@ var BPTreeAsync = class extends BPTree {
2024
2236
  const endNode = await this.verifierEndNode[key](value);
2025
2237
  const direction = this.verifierDirection[key];
2026
2238
  const comparator = this.verifierMap[key];
2027
- const pairs = await this.getPairs(value, startNode, endNode, comparator, direction);
2239
+ const earlyTerminate = this.verifierEarlyTerminate[key];
2240
+ const pairs = await this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
2028
2241
  if (result === null) {
2029
2242
  result = pairs;
2030
2243
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataply",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "A lightweight storage engine for Node.js with support for MVCC, WAL.",
5
5
  "license": "MIT",
6
6
  "author": "izure <admin@izure.org>",
@@ -47,6 +47,6 @@
47
47
  "cache-entanglement": "^1.7.1",
48
48
  "hookall": "^2.2.0",
49
49
  "ryoiki": "^1.2.0",
50
- "serializable-bptree": "^6.0.2"
50
+ "serializable-bptree": "^6.1.1"
51
51
  }
52
52
  }
package/readme.md CHANGED
@@ -15,6 +15,7 @@
15
15
  - **📝 WAL (Write-Ahead Logging)**: Ensures data integrity and provides recovery capabilities in case of system failures.
16
16
  - **💼 Transaction Mechanism**: Supports Commit and Rollback for atomic operations.
17
17
  - **📦 Page-Based Storage**: Efficient page caching and disk I/O optimization through Virtual File System (VFS).
18
+ - **📉 Bitmap Space Optimization**: Uses bitmapped management to efficiently track page usage and maximize disk space utilization.
18
19
  - **⌨️ TypeScript Support**: Provides comprehensive type definitions for all APIs.
19
20
 
20
21
  ## Installation
@@ -224,6 +225,7 @@ graph TD
224
225
  - **Fixed-size Pages**: All data is managed in fixed-size units (default 8KB) called pages.
225
226
  - **VFS Cache**: Minimizes disk I/O by caching frequently accessed pages in memory.
226
227
  - **Dirty Page Tracking**: Tracks modified pages (Dirty) to synchronize them with disk efficiently only at the time of commit.
228
+ - **Bitmap Management**: Efficiently tracks the allocation and deallocation of pages using a bitmap structure, facilitating fast space reclamation and reuse. For more details on this mechanism, see [Page Reclamation and Reuse Guide](docs/page_reclamation.md).
227
229
  - **Detailed Structure**: For technical details on the physical layout, see [structure.md](docs/structure.md).
228
230
 
229
231
  #### Page & Row Layout