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 +137 -14
- package/package.json +2 -2
- package/readme.md +2 -0
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|