dataply 0.0.10 → 0.0.11

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) {
@@ -480,6 +507,7 @@ var BPTree = class {
480
507
  lt: (nv, v) => this.comparator.isLower(nv, v),
481
508
  lte: (nv, v) => this.comparator.isLower(nv, v) || this.comparator.isSame(nv, v),
482
509
  equal: (nv, v) => this.comparator.isSame(nv, v),
510
+ primaryEqual: (nv, v) => this.comparator.isPrimarySame(nv, v),
483
511
  notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
484
512
  or: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isSame(nv, v2)),
485
513
  like: (nv, v) => {
@@ -496,6 +524,7 @@ var BPTree = class {
496
524
  lt: (v) => this.insertableNode(v),
497
525
  lte: (v) => this.insertableNode(v),
498
526
  equal: (v) => this.insertableNode(v),
527
+ primaryEqual: (v) => this.insertableNodeByPrimary(v),
499
528
  notEqual: (v) => this.leftestNode(),
500
529
  or: (v) => this.insertableNode(this.lowestValue(this.ensureValues(v))),
501
530
  like: (v) => this.leftestNode()
@@ -506,6 +535,7 @@ var BPTree = class {
506
535
  lt: (v) => null,
507
536
  lte: (v) => null,
508
537
  equal: (v) => this.insertableEndNode(v, this.verifierDirection.equal),
538
+ primaryEqual: (v) => null,
509
539
  notEqual: (v) => null,
510
540
  or: (v) => this.insertableEndNode(
511
541
  this.highestValue(this.ensureValues(v)),
@@ -519,10 +549,27 @@ var BPTree = class {
519
549
  lt: -1,
520
550
  lte: -1,
521
551
  equal: 1,
552
+ primaryEqual: 1,
522
553
  notEqual: 1,
523
554
  or: 1,
524
555
  like: 1
525
556
  };
557
+ /**
558
+ * Determines whether early termination is allowed for each condition.
559
+ * When true, the search will stop once a match is found and then a non-match is encountered.
560
+ * Only applicable for conditions that guarantee contiguous matches in a sorted B+Tree.
561
+ */
562
+ verifierEarlyTerminate = {
563
+ gt: false,
564
+ gte: false,
565
+ lt: false,
566
+ lte: false,
567
+ equal: true,
568
+ primaryEqual: true,
569
+ notEqual: false,
570
+ or: false,
571
+ like: false
572
+ };
526
573
  constructor(strategy, comparator, option) {
527
574
  this.strategy = strategy;
528
575
  this.comparator = comparator;
@@ -633,10 +680,11 @@ var BPTreeSync = class extends BPTree {
633
680
  capacity: this.option.capacity ?? 1e3
634
681
  });
635
682
  }
636
- getPairsRightToLeft(value, startNode, endNode, comparator) {
683
+ getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate) {
637
684
  const pairs = [];
638
685
  let node = startNode;
639
686
  let done = false;
687
+ let hasMatched = false;
640
688
  while (!done) {
641
689
  if (endNode && node.id === endNode.id) {
642
690
  done = true;
@@ -647,12 +695,17 @@ var BPTreeSync = class extends BPTree {
647
695
  const nValue = node.values[i];
648
696
  const keys = node.keys[i];
649
697
  if (comparator(nValue, value)) {
698
+ hasMatched = true;
650
699
  let j = keys.length;
651
700
  while (j--) {
652
701
  pairs.push([keys[j], nValue]);
653
702
  }
703
+ } else if (earlyTerminate && hasMatched) {
704
+ done = true;
705
+ break;
654
706
  }
655
707
  }
708
+ if (done) break;
656
709
  if (!node.prev) {
657
710
  done = true;
658
711
  break;
@@ -661,10 +714,11 @@ var BPTreeSync = class extends BPTree {
661
714
  }
662
715
  return new Map(pairs.reverse());
663
716
  }
664
- getPairsLeftToRight(value, startNode, endNode, comparator) {
717
+ getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate) {
665
718
  const pairs = [];
666
719
  let node = startNode;
667
720
  let done = false;
721
+ let hasMatched = false;
668
722
  while (!done) {
669
723
  if (endNode && node.id === endNode.id) {
670
724
  done = true;
@@ -674,12 +728,17 @@ var BPTreeSync = class extends BPTree {
674
728
  const nValue = node.values[i];
675
729
  const keys = node.keys[i];
676
730
  if (comparator(nValue, value)) {
731
+ hasMatched = true;
677
732
  for (let j = 0, len2 = keys.length; j < len2; j++) {
678
733
  const key = keys[j];
679
734
  pairs.push([key, nValue]);
680
735
  }
736
+ } else if (earlyTerminate && hasMatched) {
737
+ done = true;
738
+ break;
681
739
  }
682
740
  }
741
+ if (done) break;
683
742
  if (!node.next) {
684
743
  done = true;
685
744
  break;
@@ -688,12 +747,12 @@ var BPTreeSync = class extends BPTree {
688
747
  }
689
748
  return new Map(pairs);
690
749
  }
691
- getPairs(value, startNode, endNode, comparator, direction) {
750
+ getPairs(value, startNode, endNode, comparator, direction, earlyTerminate) {
692
751
  switch (direction) {
693
752
  case -1:
694
- return this.getPairsRightToLeft(value, startNode, endNode, comparator);
753
+ return this.getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate);
695
754
  case 1:
696
- return this.getPairsLeftToRight(value, startNode, endNode, comparator);
755
+ return this.getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate);
697
756
  default:
698
757
  throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
699
758
  }
@@ -1042,6 +1101,30 @@ var BPTreeSync = class extends BPTree {
1042
1101
  }
1043
1102
  return node;
1044
1103
  }
1104
+ /**
1105
+ * Find the insertable node using primaryAsc comparison.
1106
+ * This allows finding nodes by primary value only, ignoring unique identifiers.
1107
+ */
1108
+ insertableNodeByPrimary(value) {
1109
+ let node = this.getNode(this.root.id);
1110
+ while (!node.leaf) {
1111
+ for (let i = 0, len = node.values.length; i < len; i++) {
1112
+ const nValue = node.values[i];
1113
+ const k = node.keys;
1114
+ if (this.comparator.isPrimarySame(value, nValue)) {
1115
+ node = this.getNode(k[i]);
1116
+ break;
1117
+ } else if (this.comparator.isPrimaryLower(value, nValue)) {
1118
+ node = this.getNode(k[i]);
1119
+ break;
1120
+ } else if (i + 1 === node.values.length) {
1121
+ node = this.getNode(k[i + 1]);
1122
+ break;
1123
+ }
1124
+ }
1125
+ }
1126
+ return node;
1127
+ }
1045
1128
  insertableEndNode(value, direction) {
1046
1129
  const insertableNode = this.insertableNode(value);
1047
1130
  let key;
@@ -1110,7 +1193,8 @@ var BPTreeSync = class extends BPTree {
1110
1193
  const endNode = this.verifierEndNode[key](value);
1111
1194
  const direction = this.verifierDirection[key];
1112
1195
  const comparator = this.verifierMap[key];
1113
- const pairs = this.getPairs(value, startNode, endNode, comparator, direction);
1196
+ const earlyTerminate = this.verifierEarlyTerminate[key];
1197
+ const pairs = this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
1114
1198
  if (!filterValues) {
1115
1199
  filterValues = new Set(pairs.keys());
1116
1200
  } else {
@@ -1135,7 +1219,8 @@ var BPTreeSync = class extends BPTree {
1135
1219
  const endNode = this.verifierEndNode[key](value);
1136
1220
  const direction = this.verifierDirection[key];
1137
1221
  const comparator = this.verifierMap[key];
1138
- const pairs = this.getPairs(value, startNode, endNode, comparator, direction);
1222
+ const earlyTerminate = this.verifierEarlyTerminate[key];
1223
+ const pairs = this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
1139
1224
  if (result === null) {
1140
1225
  result = pairs;
1141
1226
  } else {
@@ -1519,10 +1604,11 @@ var BPTreeAsync = class extends BPTree {
1519
1604
  this.lock.writeUnlock(lockId);
1520
1605
  });
1521
1606
  }
1522
- async getPairsRightToLeft(value, startNode, endNode, comparator) {
1607
+ async getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate) {
1523
1608
  const pairs = [];
1524
1609
  let node = startNode;
1525
1610
  let done = false;
1611
+ let hasMatched = false;
1526
1612
  while (!done) {
1527
1613
  if (endNode && node.id === endNode.id) {
1528
1614
  done = true;
@@ -1533,12 +1619,17 @@ var BPTreeAsync = class extends BPTree {
1533
1619
  const nValue = node.values[i];
1534
1620
  const keys = node.keys[i];
1535
1621
  if (comparator(nValue, value)) {
1622
+ hasMatched = true;
1536
1623
  let j = keys.length;
1537
1624
  while (j--) {
1538
1625
  pairs.push([keys[j], nValue]);
1539
1626
  }
1627
+ } else if (earlyTerminate && hasMatched) {
1628
+ done = true;
1629
+ break;
1540
1630
  }
1541
1631
  }
1632
+ if (done) break;
1542
1633
  if (!node.prev) {
1543
1634
  done = true;
1544
1635
  break;
@@ -1547,10 +1638,11 @@ var BPTreeAsync = class extends BPTree {
1547
1638
  }
1548
1639
  return new Map(pairs.reverse());
1549
1640
  }
1550
- async getPairsLeftToRight(value, startNode, endNode, comparator) {
1641
+ async getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate) {
1551
1642
  const pairs = [];
1552
1643
  let node = startNode;
1553
1644
  let done = false;
1645
+ let hasMatched = false;
1554
1646
  while (!done) {
1555
1647
  if (endNode && node.id === endNode.id) {
1556
1648
  done = true;
@@ -1560,12 +1652,17 @@ var BPTreeAsync = class extends BPTree {
1560
1652
  const nValue = node.values[i];
1561
1653
  const keys = node.keys[i];
1562
1654
  if (comparator(nValue, value)) {
1655
+ hasMatched = true;
1563
1656
  for (let j = 0, len2 = keys.length; j < len2; j++) {
1564
1657
  const key = keys[j];
1565
1658
  pairs.push([key, nValue]);
1566
1659
  }
1660
+ } else if (earlyTerminate && hasMatched) {
1661
+ done = true;
1662
+ break;
1567
1663
  }
1568
1664
  }
1665
+ if (done) break;
1569
1666
  if (!node.next) {
1570
1667
  done = true;
1571
1668
  break;
@@ -1574,12 +1671,12 @@ var BPTreeAsync = class extends BPTree {
1574
1671
  }
1575
1672
  return new Map(pairs);
1576
1673
  }
1577
- async getPairs(value, startNode, endNode, comparator, direction) {
1674
+ async getPairs(value, startNode, endNode, comparator, direction, earlyTerminate) {
1578
1675
  switch (direction) {
1579
1676
  case -1:
1580
- return await this.getPairsRightToLeft(value, startNode, endNode, comparator);
1677
+ return await this.getPairsRightToLeft(value, startNode, endNode, comparator, earlyTerminate);
1581
1678
  case 1:
1582
- return await this.getPairsLeftToRight(value, startNode, endNode, comparator);
1679
+ return await this.getPairsLeftToRight(value, startNode, endNode, comparator, earlyTerminate);
1583
1680
  default:
1584
1681
  throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
1585
1682
  }
@@ -1928,6 +2025,30 @@ var BPTreeAsync = class extends BPTree {
1928
2025
  }
1929
2026
  return node;
1930
2027
  }
2028
+ /**
2029
+ * Find the insertable node using primaryAsc comparison.
2030
+ * This allows finding nodes by primary value only, ignoring unique identifiers.
2031
+ */
2032
+ async insertableNodeByPrimary(value) {
2033
+ let node = await this.getNode(this.root.id);
2034
+ while (!node.leaf) {
2035
+ for (let i = 0, len = node.values.length; i < len; i++) {
2036
+ const nValue = node.values[i];
2037
+ const k = node.keys;
2038
+ if (this.comparator.isPrimarySame(value, nValue)) {
2039
+ node = await this.getNode(k[i]);
2040
+ break;
2041
+ } else if (this.comparator.isPrimaryLower(value, nValue)) {
2042
+ node = await this.getNode(k[i]);
2043
+ break;
2044
+ } else if (i + 1 === node.values.length) {
2045
+ node = await this.getNode(k[i + 1]);
2046
+ break;
2047
+ }
2048
+ }
2049
+ }
2050
+ return node;
2051
+ }
1931
2052
  async insertableEndNode(value, direction) {
1932
2053
  const insertableNode = await this.insertableNode(value);
1933
2054
  let key;
@@ -1997,7 +2118,8 @@ var BPTreeAsync = class extends BPTree {
1997
2118
  const endNode = await this.verifierEndNode[key](value);
1998
2119
  const direction = this.verifierDirection[key];
1999
2120
  const comparator = this.verifierMap[key];
2000
- const pairs = await this.getPairs(value, startNode, endNode, comparator, direction);
2121
+ const earlyTerminate = this.verifierEarlyTerminate[key];
2122
+ const pairs = await this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
2001
2123
  if (!filterValues) {
2002
2124
  filterValues = new Set(pairs.keys());
2003
2125
  } else {
@@ -2024,7 +2146,8 @@ var BPTreeAsync = class extends BPTree {
2024
2146
  const endNode = await this.verifierEndNode[key](value);
2025
2147
  const direction = this.verifierDirection[key];
2026
2148
  const comparator = this.verifierMap[key];
2027
- const pairs = await this.getPairs(value, startNode, endNode, comparator, direction);
2149
+ const earlyTerminate = this.verifierEarlyTerminate[key];
2150
+ const pairs = await this.getPairs(value, startNode, endNode, comparator, direction, earlyTerminate);
2028
2151
  if (result === null) {
2029
2152
  result = pairs;
2030
2153
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataply",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
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.0"
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