dataply 0.0.24-alpha.8 → 0.0.24
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 +496 -277
- package/dist/types/core/PageMVCCStrategy.d.ts +6 -0
- package/dist/types/utils/array.d.ts +7 -0
- package/package.json +3 -3
- package/readme.md +10 -2
package/dist/cjs/index.js
CHANGED
|
@@ -126,39 +126,125 @@ var StringComparator = class extends ValueComparator {
|
|
|
126
126
|
var MVCCStrategy = class {
|
|
127
127
|
};
|
|
128
128
|
var LRUMap = class {
|
|
129
|
-
cache = /* @__PURE__ */ new Map();
|
|
130
129
|
capacity;
|
|
130
|
+
map;
|
|
131
|
+
head = null;
|
|
132
|
+
tail = null;
|
|
133
|
+
/**
|
|
134
|
+
* Creates an instance of LRUMap.
|
|
135
|
+
* @param capacity The maximum number of items the cache can hold.
|
|
136
|
+
*/
|
|
131
137
|
constructor(capacity) {
|
|
132
138
|
this.capacity = capacity;
|
|
139
|
+
this.map = /* @__PURE__ */ new Map();
|
|
133
140
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Promotes a node to the head of the linked list (marks as most recently used).
|
|
143
|
+
* @param node The node to promote.
|
|
144
|
+
*/
|
|
145
|
+
promote(node) {
|
|
146
|
+
this.extract(node);
|
|
147
|
+
this.prepend(node);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Disconnects a node from the doubly linked list.
|
|
151
|
+
* @param node The node to extract.
|
|
152
|
+
*/
|
|
153
|
+
extract(node) {
|
|
154
|
+
if (node.prev) node.prev.next = node.next;
|
|
155
|
+
else this.head = node.next;
|
|
156
|
+
if (node.next) node.next.prev = node.prev;
|
|
157
|
+
else this.tail = node.prev;
|
|
158
|
+
node.prev = null;
|
|
159
|
+
node.next = null;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Inserts a node at the head of the doubly linked list.
|
|
163
|
+
* @param node The node to prepend.
|
|
164
|
+
*/
|
|
165
|
+
prepend(node) {
|
|
166
|
+
node.next = this.head;
|
|
167
|
+
if (this.head) this.head.prev = node;
|
|
168
|
+
this.head = node;
|
|
169
|
+
if (!this.tail) this.tail = node;
|
|
140
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Stores or updates a value by key.
|
|
173
|
+
* If the capacity is exceeded, the least recently used item (tail) is removed.
|
|
174
|
+
* @param key The key to store.
|
|
175
|
+
* @param value The value to store.
|
|
176
|
+
*/
|
|
141
177
|
set(key, value) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
178
|
+
const existing = this.map.get(key);
|
|
179
|
+
if (existing) {
|
|
180
|
+
existing.value = value;
|
|
181
|
+
this.promote(existing);
|
|
182
|
+
return;
|
|
147
183
|
}
|
|
148
|
-
|
|
149
|
-
|
|
184
|
+
const newNode = { key, value, prev: null, next: null };
|
|
185
|
+
this.map.set(key, newNode);
|
|
186
|
+
this.prepend(newNode);
|
|
187
|
+
if (this.map.size > this.capacity && this.tail) {
|
|
188
|
+
this.map.delete(this.tail.key);
|
|
189
|
+
this.extract(this.tail);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Retrieves a value by key.
|
|
194
|
+
* Accessing the item moves it to the "most recently used" position.
|
|
195
|
+
* @param key The key to look for.
|
|
196
|
+
* @returns The value associated with the key, or undefined if not found.
|
|
197
|
+
*/
|
|
198
|
+
get(key) {
|
|
199
|
+
const node = this.map.get(key);
|
|
200
|
+
if (!node) return void 0;
|
|
201
|
+
this.promote(node);
|
|
202
|
+
return node.value;
|
|
150
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Checks if a key exists in the cache without changing its access order.
|
|
206
|
+
* @param key The key to check.
|
|
207
|
+
* @returns True if the key exists, false otherwise.
|
|
208
|
+
*/
|
|
151
209
|
has(key) {
|
|
152
|
-
return this.
|
|
210
|
+
return this.map.has(key);
|
|
153
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Removes a key and its associated value from the cache.
|
|
214
|
+
* @param key The key to remove.
|
|
215
|
+
* @returns True if the key was found and removed, false otherwise.
|
|
216
|
+
*/
|
|
154
217
|
delete(key) {
|
|
155
|
-
|
|
218
|
+
const node = this.map.get(key);
|
|
219
|
+
if (!node) return false;
|
|
220
|
+
this.extract(node);
|
|
221
|
+
this.map.delete(key);
|
|
222
|
+
return true;
|
|
156
223
|
}
|
|
157
|
-
|
|
158
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Returns an iterator of keys in the order of most recently used to least recently used.
|
|
226
|
+
* @returns An iterable iterator of keys.
|
|
227
|
+
*/
|
|
228
|
+
*keys() {
|
|
229
|
+
let current = this.head;
|
|
230
|
+
while (current) {
|
|
231
|
+
yield current.key;
|
|
232
|
+
current = current.next;
|
|
233
|
+
}
|
|
159
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Returns the current number of items in the cache.
|
|
237
|
+
*/
|
|
160
238
|
get size() {
|
|
161
|
-
return this.
|
|
239
|
+
return this.map.size;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Clears all items from the cache.
|
|
243
|
+
*/
|
|
244
|
+
clear() {
|
|
245
|
+
this.map.clear();
|
|
246
|
+
this.head = null;
|
|
247
|
+
this.tail = null;
|
|
162
248
|
}
|
|
163
249
|
};
|
|
164
250
|
var MVCCTransaction = class {
|
|
@@ -1552,28 +1638,28 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1552
1638
|
searchConfigs = {
|
|
1553
1639
|
gt: {
|
|
1554
1640
|
asc: {
|
|
1555
|
-
start: (tx, v) => tx.
|
|
1641
|
+
start: (tx, v) => tx.findUpperBoundLeaf(v[0]),
|
|
1556
1642
|
end: () => null,
|
|
1557
1643
|
direction: 1,
|
|
1558
1644
|
earlyTerminate: false
|
|
1559
1645
|
},
|
|
1560
1646
|
desc: {
|
|
1561
1647
|
start: (tx) => tx.rightestNode(),
|
|
1562
|
-
end: (tx, v) => tx.
|
|
1648
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], -1),
|
|
1563
1649
|
direction: -1,
|
|
1564
1650
|
earlyTerminate: true
|
|
1565
1651
|
}
|
|
1566
1652
|
},
|
|
1567
1653
|
gte: {
|
|
1568
1654
|
asc: {
|
|
1569
|
-
start: (tx, v) => tx.
|
|
1655
|
+
start: (tx, v) => tx.findLowerBoundLeaf(v[0]),
|
|
1570
1656
|
end: () => null,
|
|
1571
1657
|
direction: 1,
|
|
1572
1658
|
earlyTerminate: false
|
|
1573
1659
|
},
|
|
1574
1660
|
desc: {
|
|
1575
1661
|
start: (tx) => tx.rightestNode(),
|
|
1576
|
-
end: (tx, v) => tx.
|
|
1662
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], -1),
|
|
1577
1663
|
direction: -1,
|
|
1578
1664
|
earlyTerminate: true
|
|
1579
1665
|
}
|
|
@@ -1581,12 +1667,12 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1581
1667
|
lt: {
|
|
1582
1668
|
asc: {
|
|
1583
1669
|
start: (tx) => tx.leftestNode(),
|
|
1584
|
-
end: (tx, v) => tx.
|
|
1670
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], 1),
|
|
1585
1671
|
direction: 1,
|
|
1586
1672
|
earlyTerminate: true
|
|
1587
1673
|
},
|
|
1588
1674
|
desc: {
|
|
1589
|
-
start: (tx, v) => tx.
|
|
1675
|
+
start: (tx, v) => tx.findLowerBoundLeaf(v[0]),
|
|
1590
1676
|
end: () => null,
|
|
1591
1677
|
direction: -1,
|
|
1592
1678
|
earlyTerminate: false
|
|
@@ -1595,12 +1681,12 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1595
1681
|
lte: {
|
|
1596
1682
|
asc: {
|
|
1597
1683
|
start: (tx) => tx.leftestNode(),
|
|
1598
|
-
end: (tx, v) => tx.
|
|
1684
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], 1),
|
|
1599
1685
|
direction: 1,
|
|
1600
1686
|
earlyTerminate: true
|
|
1601
1687
|
},
|
|
1602
1688
|
desc: {
|
|
1603
|
-
start: (tx, v) => tx.
|
|
1689
|
+
start: (tx, v) => tx.findUpperBoundLeaf(v[0]),
|
|
1604
1690
|
end: () => null,
|
|
1605
1691
|
direction: -1,
|
|
1606
1692
|
earlyTerminate: false
|
|
@@ -1608,14 +1694,14 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1608
1694
|
},
|
|
1609
1695
|
equal: {
|
|
1610
1696
|
asc: {
|
|
1611
|
-
start: (tx, v) => tx.
|
|
1612
|
-
end: (tx, v) => tx.
|
|
1697
|
+
start: (tx, v) => tx.findLowerBoundLeaf(v[0]),
|
|
1698
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], 1),
|
|
1613
1699
|
direction: 1,
|
|
1614
1700
|
earlyTerminate: true
|
|
1615
1701
|
},
|
|
1616
1702
|
desc: {
|
|
1617
|
-
start: (tx, v) => tx.
|
|
1618
|
-
end: (tx, v) => tx.
|
|
1703
|
+
start: (tx, v) => tx.findOuterBoundaryLeaf(v[0], 1),
|
|
1704
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], -1),
|
|
1619
1705
|
direction: -1,
|
|
1620
1706
|
earlyTerminate: true
|
|
1621
1707
|
}
|
|
@@ -1636,42 +1722,42 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1636
1722
|
},
|
|
1637
1723
|
or: {
|
|
1638
1724
|
asc: {
|
|
1639
|
-
start: (tx, v) => tx.
|
|
1640
|
-
end: (tx, v) => tx.
|
|
1725
|
+
start: (tx, v) => tx.findLowerBoundLeaf(tx.lowestValue(v)),
|
|
1726
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(tx.highestValue(v), 1),
|
|
1641
1727
|
direction: 1,
|
|
1642
1728
|
earlyTerminate: false
|
|
1643
1729
|
},
|
|
1644
1730
|
desc: {
|
|
1645
|
-
start: (tx, v) => tx.
|
|
1646
|
-
end: (tx, v) => tx.
|
|
1731
|
+
start: (tx, v) => tx.findOuterBoundaryLeaf(tx.highestValue(v), 1),
|
|
1732
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(tx.lowestValue(v), -1),
|
|
1647
1733
|
direction: -1,
|
|
1648
1734
|
earlyTerminate: false
|
|
1649
1735
|
}
|
|
1650
1736
|
},
|
|
1651
1737
|
primaryGt: {
|
|
1652
1738
|
asc: {
|
|
1653
|
-
start: (tx, v) => tx.
|
|
1739
|
+
start: (tx, v) => tx.findUpperBoundLeaf(v[0]),
|
|
1654
1740
|
end: () => null,
|
|
1655
1741
|
direction: 1,
|
|
1656
1742
|
earlyTerminate: false
|
|
1657
1743
|
},
|
|
1658
1744
|
desc: {
|
|
1659
1745
|
start: (tx) => tx.rightestNode(),
|
|
1660
|
-
end: (tx, v) => tx.
|
|
1746
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], -1),
|
|
1661
1747
|
direction: -1,
|
|
1662
1748
|
earlyTerminate: true
|
|
1663
1749
|
}
|
|
1664
1750
|
},
|
|
1665
1751
|
primaryGte: {
|
|
1666
1752
|
asc: {
|
|
1667
|
-
start: (tx, v) => tx.
|
|
1753
|
+
start: (tx, v) => tx.findLowerBoundLeaf(v[0]),
|
|
1668
1754
|
end: () => null,
|
|
1669
1755
|
direction: 1,
|
|
1670
1756
|
earlyTerminate: false
|
|
1671
1757
|
},
|
|
1672
1758
|
desc: {
|
|
1673
1759
|
start: (tx) => tx.rightestNode(),
|
|
1674
|
-
end: (tx, v) => tx.
|
|
1760
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], -1),
|
|
1675
1761
|
direction: -1,
|
|
1676
1762
|
earlyTerminate: true
|
|
1677
1763
|
}
|
|
@@ -1679,12 +1765,12 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1679
1765
|
primaryLt: {
|
|
1680
1766
|
asc: {
|
|
1681
1767
|
start: (tx) => tx.leftestNode(),
|
|
1682
|
-
end: (tx, v) => tx.
|
|
1768
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], 1),
|
|
1683
1769
|
direction: 1,
|
|
1684
1770
|
earlyTerminate: true
|
|
1685
1771
|
},
|
|
1686
1772
|
desc: {
|
|
1687
|
-
start: (tx, v) => tx.
|
|
1773
|
+
start: (tx, v) => tx.findLowerBoundLeaf(v[0]),
|
|
1688
1774
|
end: () => null,
|
|
1689
1775
|
direction: -1,
|
|
1690
1776
|
earlyTerminate: false
|
|
@@ -1693,12 +1779,12 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1693
1779
|
primaryLte: {
|
|
1694
1780
|
asc: {
|
|
1695
1781
|
start: (tx) => tx.leftestNode(),
|
|
1696
|
-
end: (tx, v) => tx.
|
|
1782
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], 1),
|
|
1697
1783
|
direction: 1,
|
|
1698
1784
|
earlyTerminate: true
|
|
1699
1785
|
},
|
|
1700
1786
|
desc: {
|
|
1701
|
-
start: (tx, v) => tx.
|
|
1787
|
+
start: (tx, v) => tx.findUpperBoundLeaf(v[0]),
|
|
1702
1788
|
end: () => null,
|
|
1703
1789
|
direction: -1,
|
|
1704
1790
|
earlyTerminate: false
|
|
@@ -1706,14 +1792,14 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1706
1792
|
},
|
|
1707
1793
|
primaryEqual: {
|
|
1708
1794
|
asc: {
|
|
1709
|
-
start: (tx, v) => tx.
|
|
1710
|
-
end: (tx, v) => tx.
|
|
1795
|
+
start: (tx, v) => tx.findLowerBoundLeaf(v[0]),
|
|
1796
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], 1),
|
|
1711
1797
|
direction: 1,
|
|
1712
1798
|
earlyTerminate: true
|
|
1713
1799
|
},
|
|
1714
1800
|
desc: {
|
|
1715
|
-
start: (tx, v) => tx.
|
|
1716
|
-
end: (tx, v) => tx.
|
|
1801
|
+
start: (tx, v) => tx.findUpperBoundLeaf(v[0]),
|
|
1802
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(v[0], -1),
|
|
1717
1803
|
direction: -1,
|
|
1718
1804
|
earlyTerminate: true
|
|
1719
1805
|
}
|
|
@@ -1734,14 +1820,14 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1734
1820
|
},
|
|
1735
1821
|
primaryOr: {
|
|
1736
1822
|
asc: {
|
|
1737
|
-
start: (tx, v) => tx.
|
|
1738
|
-
end: (tx, v) => tx.
|
|
1823
|
+
start: (tx, v) => tx.findLowerBoundLeaf(tx.lowestPrimaryValue(v)),
|
|
1824
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(tx.highestPrimaryValue(v), 1),
|
|
1739
1825
|
direction: 1,
|
|
1740
1826
|
earlyTerminate: false
|
|
1741
1827
|
},
|
|
1742
1828
|
desc: {
|
|
1743
|
-
start: (tx, v) => tx.
|
|
1744
|
-
end: (tx, v) => tx.
|
|
1829
|
+
start: (tx, v) => tx.findUpperBoundLeaf(tx.highestPrimaryValue(v)),
|
|
1830
|
+
end: (tx, v) => tx.findOuterBoundaryLeaf(tx.lowestPrimaryValue(v), -1),
|
|
1745
1831
|
direction: -1,
|
|
1746
1832
|
earlyTerminate: false
|
|
1747
1833
|
}
|
|
@@ -1897,30 +1983,69 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1897
1983
|
return JSON.parse(JSON.stringify(node));
|
|
1898
1984
|
}
|
|
1899
1985
|
/**
|
|
1900
|
-
*
|
|
1901
|
-
*
|
|
1902
|
-
*
|
|
1986
|
+
* Resolves the best start/end configuration by independently examining
|
|
1987
|
+
* all conditions. Selects the tightest lower bound for start and the
|
|
1988
|
+
* tightest upper bound for end (in asc; reversed for desc).
|
|
1989
|
+
*
|
|
1903
1990
|
* @param condition The condition to analyze.
|
|
1904
|
-
* @
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1991
|
+
* @param order The sort order ('asc' or 'desc').
|
|
1992
|
+
* @returns The resolved start/end keys, values, and traversal direction.
|
|
1993
|
+
*/
|
|
1994
|
+
resolveStartEndConfigs(condition, order) {
|
|
1995
|
+
const direction = order === "asc" ? 1 : -1;
|
|
1996
|
+
const startCandidates = order === "asc" ? _BPTreeTransaction._lowerBoundKeys : _BPTreeTransaction._upperBoundKeys;
|
|
1997
|
+
const endCandidates = order === "asc" ? _BPTreeTransaction._upperBoundKeys : _BPTreeTransaction._lowerBoundKeys;
|
|
1998
|
+
let startKey = null;
|
|
1999
|
+
let endKey = null;
|
|
2000
|
+
let startValues = [];
|
|
2001
|
+
let endValues = [];
|
|
2002
|
+
for (let i = 0, len = startCandidates.length; i < len; i++) {
|
|
2003
|
+
const key = startCandidates[i];
|
|
2004
|
+
if (key in condition) {
|
|
2005
|
+
startKey = key;
|
|
2006
|
+
startValues = _BPTreeTransaction._multiValueKeys.includes(key) ? this.ensureValues(condition[key]) : [condition[key]];
|
|
2007
|
+
break;
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
for (let i = 0, len = endCandidates.length; i < len; i++) {
|
|
2011
|
+
const key = endCandidates[i];
|
|
2012
|
+
if (key in condition) {
|
|
2013
|
+
endKey = key;
|
|
2014
|
+
endValues = _BPTreeTransaction._multiValueKeys.includes(key) ? this.ensureValues(condition[key]) : [condition[key]];
|
|
2015
|
+
break;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return { startKey, endKey, startValues, endValues, direction };
|
|
2019
|
+
}
|
|
2020
|
+
// Lower bound providers, ordered by selectivity (tightest first)
|
|
2021
|
+
// Used for asc start / desc end
|
|
2022
|
+
static _lowerBoundKeys = [
|
|
2023
|
+
"primaryEqual",
|
|
2024
|
+
"equal",
|
|
2025
|
+
"primaryGt",
|
|
2026
|
+
"gt",
|
|
2027
|
+
"primaryGte",
|
|
2028
|
+
"gte",
|
|
2029
|
+
"primaryOr",
|
|
2030
|
+
"or"
|
|
2031
|
+
];
|
|
2032
|
+
// Upper bound providers, ordered by selectivity (tightest first)
|
|
2033
|
+
// Used for asc end / desc start
|
|
2034
|
+
static _upperBoundKeys = [
|
|
2035
|
+
"primaryEqual",
|
|
2036
|
+
"equal",
|
|
2037
|
+
"primaryLt",
|
|
2038
|
+
"lt",
|
|
2039
|
+
"primaryLte",
|
|
2040
|
+
"lte",
|
|
2041
|
+
"primaryOr",
|
|
2042
|
+
"or"
|
|
2043
|
+
];
|
|
2044
|
+
// Condition keys that accept multiple values (V[]) rather than a single value (V)
|
|
2045
|
+
static _multiValueKeys = [
|
|
2046
|
+
"or",
|
|
2047
|
+
"primaryOr"
|
|
2048
|
+
];
|
|
1924
2049
|
constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
|
|
1925
2050
|
this.rootTx = rootTx === null ? this : rootTx;
|
|
1926
2051
|
this.mvccRoot = mvccRoot;
|
|
@@ -2157,7 +2282,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2157
2282
|
this._insertInParent(parentNode, midValue, newSiblingNodeRecursive);
|
|
2158
2283
|
}
|
|
2159
2284
|
}
|
|
2160
|
-
|
|
2285
|
+
locateLeaf(value) {
|
|
2161
2286
|
let node = this.getNode(this.rootId);
|
|
2162
2287
|
while (!node.leaf) {
|
|
2163
2288
|
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
@@ -2165,7 +2290,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2165
2290
|
}
|
|
2166
2291
|
return node;
|
|
2167
2292
|
}
|
|
2168
|
-
|
|
2293
|
+
findLowerBoundLeaf(value) {
|
|
2169
2294
|
let node = this.getNode(this.rootId);
|
|
2170
2295
|
while (!node.leaf) {
|
|
2171
2296
|
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
@@ -2173,7 +2298,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2173
2298
|
}
|
|
2174
2299
|
return node;
|
|
2175
2300
|
}
|
|
2176
|
-
|
|
2301
|
+
findUpperBoundLeaf(value) {
|
|
2177
2302
|
let node = this.getNode(this.rootId);
|
|
2178
2303
|
while (!node.leaf) {
|
|
2179
2304
|
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
@@ -2181,15 +2306,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2181
2306
|
}
|
|
2182
2307
|
return node;
|
|
2183
2308
|
}
|
|
2184
|
-
|
|
2185
|
-
const
|
|
2186
|
-
if (!node.next) {
|
|
2187
|
-
return null;
|
|
2188
|
-
}
|
|
2189
|
-
return this.getNode(node.next);
|
|
2190
|
-
}
|
|
2191
|
-
insertableEndNode(value, direction) {
|
|
2192
|
-
const insertableNode = this.insertableNode(value);
|
|
2309
|
+
findOuterBoundaryLeaf(value, direction) {
|
|
2310
|
+
const insertableNode = direction === -1 ? this.findLowerBoundLeaf(value) : this.findUpperBoundLeaf(value);
|
|
2193
2311
|
let key;
|
|
2194
2312
|
switch (direction) {
|
|
2195
2313
|
case -1:
|
|
@@ -2226,13 +2344,10 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2226
2344
|
}
|
|
2227
2345
|
return node;
|
|
2228
2346
|
}
|
|
2229
|
-
*getPairsGenerator(
|
|
2347
|
+
*getPairsGenerator(startNode, endNode, direction) {
|
|
2230
2348
|
let node = startNode;
|
|
2231
|
-
|
|
2232
|
-
let hasMatched = false;
|
|
2233
|
-
while (!done) {
|
|
2349
|
+
while (true) {
|
|
2234
2350
|
if (endNode && node.id === endNode.id) {
|
|
2235
|
-
done = true;
|
|
2236
2351
|
break;
|
|
2237
2352
|
}
|
|
2238
2353
|
const len = node.values.length;
|
|
@@ -2240,14 +2355,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2240
2355
|
for (let i = 0; i < len; i++) {
|
|
2241
2356
|
const nValue = node.values[i];
|
|
2242
2357
|
const keys = node.keys[i];
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
for (let j = 0; j < keys.length; j++) {
|
|
2246
|
-
yield [keys[j], nValue];
|
|
2247
|
-
}
|
|
2248
|
-
} else if (earlyTerminate && hasMatched) {
|
|
2249
|
-
done = true;
|
|
2250
|
-
break;
|
|
2358
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
2359
|
+
yield [keys[j], nValue];
|
|
2251
2360
|
}
|
|
2252
2361
|
}
|
|
2253
2362
|
} else {
|
|
@@ -2255,30 +2364,17 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2255
2364
|
while (i--) {
|
|
2256
2365
|
const nValue = node.values[i];
|
|
2257
2366
|
const keys = node.keys[i];
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
while (j--) {
|
|
2262
|
-
yield [keys[j], nValue];
|
|
2263
|
-
}
|
|
2264
|
-
} else if (earlyTerminate && hasMatched) {
|
|
2265
|
-
done = true;
|
|
2266
|
-
break;
|
|
2367
|
+
let j = keys.length;
|
|
2368
|
+
while (j--) {
|
|
2369
|
+
yield [keys[j], nValue];
|
|
2267
2370
|
}
|
|
2268
2371
|
}
|
|
2269
2372
|
}
|
|
2270
|
-
if (done) break;
|
|
2271
2373
|
if (direction === 1) {
|
|
2272
|
-
if (!node.next)
|
|
2273
|
-
done = true;
|
|
2274
|
-
break;
|
|
2275
|
-
}
|
|
2374
|
+
if (!node.next) break;
|
|
2276
2375
|
node = this.getNode(node.next);
|
|
2277
2376
|
} else {
|
|
2278
|
-
if (!node.prev)
|
|
2279
|
-
done = true;
|
|
2280
|
-
break;
|
|
2281
|
-
}
|
|
2377
|
+
if (!node.prev) break;
|
|
2282
2378
|
node = this.getNode(node.prev);
|
|
2283
2379
|
}
|
|
2284
2380
|
}
|
|
@@ -2323,7 +2419,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2323
2419
|
}
|
|
2324
2420
|
}
|
|
2325
2421
|
exists(key, value) {
|
|
2326
|
-
const node = this.
|
|
2422
|
+
const node = this.locateLeaf(value);
|
|
2327
2423
|
const { index, found } = this._binarySearchValues(node.values, value);
|
|
2328
2424
|
if (found) {
|
|
2329
2425
|
const keys = node.keys[index];
|
|
@@ -2367,49 +2463,36 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2367
2463
|
}
|
|
2368
2464
|
*whereStream(condition, options) {
|
|
2369
2465
|
const { filterValues, limit, order = "asc" } = options ?? {};
|
|
2370
|
-
const
|
|
2371
|
-
if (
|
|
2372
|
-
const
|
|
2373
|
-
const
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
if (
|
|
2383
|
-
|
|
2466
|
+
const conditionKeys = Object.keys(condition);
|
|
2467
|
+
if (conditionKeys.length === 0) return;
|
|
2468
|
+
const resolved = this.resolveStartEndConfigs(condition, order);
|
|
2469
|
+
const direction = resolved.direction;
|
|
2470
|
+
let startNode;
|
|
2471
|
+
if (resolved.startKey) {
|
|
2472
|
+
const startConfig = this.searchConfigs[resolved.startKey][order];
|
|
2473
|
+
startNode = startConfig.start(this, resolved.startValues);
|
|
2474
|
+
} else {
|
|
2475
|
+
startNode = order === "asc" ? this.leftestNode() : this.rightestNode();
|
|
2476
|
+
}
|
|
2477
|
+
let endNode = null;
|
|
2478
|
+
if (resolved.endKey) {
|
|
2479
|
+
const endConfig = this.searchConfigs[resolved.endKey][order];
|
|
2480
|
+
endNode = endConfig.end(this, resolved.endValues);
|
|
2384
2481
|
}
|
|
2385
2482
|
if (!startNode) return;
|
|
2386
|
-
const comparator = this.verifierMap[driverKey];
|
|
2387
2483
|
const generator = this.getPairsGenerator(
|
|
2388
|
-
value,
|
|
2389
2484
|
startNode,
|
|
2390
2485
|
endNode,
|
|
2391
|
-
|
|
2392
|
-
direction,
|
|
2393
|
-
earlyTerminate
|
|
2486
|
+
direction
|
|
2394
2487
|
);
|
|
2395
2488
|
let count = 0;
|
|
2396
2489
|
const intersection = filterValues && filterValues.size > 0 ? filterValues : null;
|
|
2397
2490
|
for (const pair of generator) {
|
|
2398
|
-
const [k,
|
|
2491
|
+
const [k, v] = pair;
|
|
2399
2492
|
if (intersection && !intersection.has(k)) {
|
|
2400
2493
|
continue;
|
|
2401
2494
|
}
|
|
2402
|
-
|
|
2403
|
-
for (const key in condition) {
|
|
2404
|
-
if (key === driverKey) continue;
|
|
2405
|
-
const verify = this.verifierMap[key];
|
|
2406
|
-
const condValue = condition[key];
|
|
2407
|
-
if (!verify(v2, condValue)) {
|
|
2408
|
-
isMatch = false;
|
|
2409
|
-
break;
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
if (isMatch) {
|
|
2495
|
+
if (this.verify(v, condition)) {
|
|
2413
2496
|
yield pair;
|
|
2414
2497
|
count++;
|
|
2415
2498
|
if (limit !== void 0 && count >= limit) {
|
|
@@ -2433,7 +2516,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2433
2516
|
return map;
|
|
2434
2517
|
}
|
|
2435
2518
|
insert(key, value) {
|
|
2436
|
-
let before = this.
|
|
2519
|
+
let before = this.locateLeaf(value);
|
|
2437
2520
|
before = this._insertAtLeaf(before, key, value);
|
|
2438
2521
|
if (before.values.length === this.order) {
|
|
2439
2522
|
let after = this._createNode(
|
|
@@ -2461,7 +2544,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2461
2544
|
let currentLeaf = null;
|
|
2462
2545
|
let modified = false;
|
|
2463
2546
|
for (const [key, value] of sorted) {
|
|
2464
|
-
const targetLeaf = this.
|
|
2547
|
+
const targetLeaf = this.locateLeaf(value);
|
|
2465
2548
|
if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
|
|
2466
2549
|
} else {
|
|
2467
2550
|
if (currentLeaf !== null && modified) {
|
|
@@ -2703,7 +2786,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2703
2786
|
if (value === void 0) {
|
|
2704
2787
|
return;
|
|
2705
2788
|
}
|
|
2706
|
-
let node = this.
|
|
2789
|
+
let node = this.findLowerBoundLeaf(value);
|
|
2707
2790
|
let found = false;
|
|
2708
2791
|
while (true) {
|
|
2709
2792
|
let i = node.values.length;
|
|
@@ -3285,7 +3368,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3285
3368
|
await this._insertInParent(parentNode, midValue, newSiblingNodeRecursive);
|
|
3286
3369
|
}
|
|
3287
3370
|
}
|
|
3288
|
-
async
|
|
3371
|
+
async locateLeaf(value) {
|
|
3289
3372
|
let node = await this.getNode(this.rootId);
|
|
3290
3373
|
while (!node.leaf) {
|
|
3291
3374
|
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
@@ -3293,7 +3376,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3293
3376
|
}
|
|
3294
3377
|
return node;
|
|
3295
3378
|
}
|
|
3296
|
-
async
|
|
3379
|
+
async findLowerBoundLeaf(value) {
|
|
3297
3380
|
let node = await this.getNode(this.rootId);
|
|
3298
3381
|
while (!node.leaf) {
|
|
3299
3382
|
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
@@ -3301,7 +3384,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3301
3384
|
}
|
|
3302
3385
|
return node;
|
|
3303
3386
|
}
|
|
3304
|
-
async
|
|
3387
|
+
async findUpperBoundLeaf(value) {
|
|
3305
3388
|
let node = await this.getNode(this.rootId);
|
|
3306
3389
|
while (!node.leaf) {
|
|
3307
3390
|
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
@@ -3309,15 +3392,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3309
3392
|
}
|
|
3310
3393
|
return node;
|
|
3311
3394
|
}
|
|
3312
|
-
async
|
|
3313
|
-
const
|
|
3314
|
-
if (!node.next) {
|
|
3315
|
-
return null;
|
|
3316
|
-
}
|
|
3317
|
-
return await this.getNode(node.next);
|
|
3318
|
-
}
|
|
3319
|
-
async insertableEndNode(value, direction) {
|
|
3320
|
-
const insertableNode = await this.insertableNode(value);
|
|
3395
|
+
async findOuterBoundaryLeaf(value, direction) {
|
|
3396
|
+
const insertableNode = direction === -1 ? await this.findLowerBoundLeaf(value) : await this.findUpperBoundLeaf(value);
|
|
3321
3397
|
let key;
|
|
3322
3398
|
switch (direction) {
|
|
3323
3399
|
case -1:
|
|
@@ -3354,22 +3430,19 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3354
3430
|
}
|
|
3355
3431
|
return node;
|
|
3356
3432
|
}
|
|
3357
|
-
async *getPairsGenerator(
|
|
3433
|
+
async *getPairsGenerator(startNode, endNode, direction) {
|
|
3358
3434
|
let node = startNode;
|
|
3359
|
-
let done = false;
|
|
3360
|
-
let hasMatched = false;
|
|
3361
3435
|
let nextNodePromise = null;
|
|
3362
|
-
while (
|
|
3436
|
+
while (true) {
|
|
3363
3437
|
if (endNode && node.id === endNode.id) {
|
|
3364
|
-
done = true;
|
|
3365
3438
|
break;
|
|
3366
3439
|
}
|
|
3367
3440
|
if (direction === 1) {
|
|
3368
|
-
if (node.next
|
|
3441
|
+
if (node.next) {
|
|
3369
3442
|
nextNodePromise = this.getNode(node.next);
|
|
3370
3443
|
}
|
|
3371
3444
|
} else {
|
|
3372
|
-
if (node.prev
|
|
3445
|
+
if (node.prev) {
|
|
3373
3446
|
nextNodePromise = this.getNode(node.prev);
|
|
3374
3447
|
}
|
|
3375
3448
|
}
|
|
@@ -3378,14 +3451,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3378
3451
|
for (let i = 0; i < len; i++) {
|
|
3379
3452
|
const nValue = node.values[i];
|
|
3380
3453
|
const keys = node.keys[i];
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
for (let j = 0; j < keys.length; j++) {
|
|
3384
|
-
yield [keys[j], nValue];
|
|
3385
|
-
}
|
|
3386
|
-
} else if (earlyTerminate && hasMatched) {
|
|
3387
|
-
done = true;
|
|
3388
|
-
break;
|
|
3454
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
3455
|
+
yield [keys[j], nValue];
|
|
3389
3456
|
}
|
|
3390
3457
|
}
|
|
3391
3458
|
} else {
|
|
@@ -3393,27 +3460,17 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3393
3460
|
while (i--) {
|
|
3394
3461
|
const nValue = node.values[i];
|
|
3395
3462
|
const keys = node.keys[i];
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
while (j--) {
|
|
3400
|
-
yield [keys[j], nValue];
|
|
3401
|
-
}
|
|
3402
|
-
} else if (earlyTerminate && hasMatched) {
|
|
3403
|
-
done = true;
|
|
3404
|
-
break;
|
|
3463
|
+
let j = keys.length;
|
|
3464
|
+
while (j--) {
|
|
3465
|
+
yield [keys[j], nValue];
|
|
3405
3466
|
}
|
|
3406
3467
|
}
|
|
3407
3468
|
}
|
|
3408
|
-
if (done) {
|
|
3409
|
-
if (nextNodePromise) await nextNodePromise;
|
|
3410
|
-
break;
|
|
3411
|
-
}
|
|
3412
3469
|
if (nextNodePromise) {
|
|
3413
3470
|
node = await nextNodePromise;
|
|
3414
3471
|
nextNodePromise = null;
|
|
3415
3472
|
} else {
|
|
3416
|
-
|
|
3473
|
+
break;
|
|
3417
3474
|
}
|
|
3418
3475
|
}
|
|
3419
3476
|
}
|
|
@@ -3457,7 +3514,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3457
3514
|
}
|
|
3458
3515
|
}
|
|
3459
3516
|
async exists(key, value) {
|
|
3460
|
-
const node = await this.
|
|
3517
|
+
const node = await this.locateLeaf(value);
|
|
3461
3518
|
const { index, found } = this._binarySearchValues(node.values, value);
|
|
3462
3519
|
if (found) {
|
|
3463
3520
|
const keys = node.keys[index];
|
|
@@ -3501,49 +3558,36 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3501
3558
|
}
|
|
3502
3559
|
async *whereStream(condition, options) {
|
|
3503
3560
|
const { filterValues, limit, order = "asc" } = options ?? {};
|
|
3504
|
-
const
|
|
3505
|
-
if (
|
|
3506
|
-
const
|
|
3507
|
-
const
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
if (
|
|
3517
|
-
|
|
3561
|
+
const conditionKeys = Object.keys(condition);
|
|
3562
|
+
if (conditionKeys.length === 0) return;
|
|
3563
|
+
const resolved = this.resolveStartEndConfigs(condition, order);
|
|
3564
|
+
const direction = resolved.direction;
|
|
3565
|
+
let startNode;
|
|
3566
|
+
if (resolved.startKey) {
|
|
3567
|
+
const startConfig = this.searchConfigs[resolved.startKey][order];
|
|
3568
|
+
startNode = await startConfig.start(this, resolved.startValues);
|
|
3569
|
+
} else {
|
|
3570
|
+
startNode = order === "asc" ? await this.leftestNode() : await this.rightestNode();
|
|
3571
|
+
}
|
|
3572
|
+
let endNode = null;
|
|
3573
|
+
if (resolved.endKey) {
|
|
3574
|
+
const endConfig = this.searchConfigs[resolved.endKey][order];
|
|
3575
|
+
endNode = await endConfig.end(this, resolved.endValues);
|
|
3518
3576
|
}
|
|
3519
3577
|
if (!startNode) return;
|
|
3520
|
-
const comparator = this.verifierMap[driverKey];
|
|
3521
3578
|
const generator = this.getPairsGenerator(
|
|
3522
|
-
value,
|
|
3523
3579
|
startNode,
|
|
3524
3580
|
endNode,
|
|
3525
|
-
|
|
3526
|
-
direction,
|
|
3527
|
-
earlyTerminate
|
|
3581
|
+
direction
|
|
3528
3582
|
);
|
|
3529
3583
|
let count = 0;
|
|
3530
3584
|
const intersection = filterValues && filterValues.size > 0 ? filterValues : null;
|
|
3531
3585
|
for await (const pair of generator) {
|
|
3532
|
-
const [k,
|
|
3586
|
+
const [k, v] = pair;
|
|
3533
3587
|
if (intersection && !intersection.has(k)) {
|
|
3534
3588
|
continue;
|
|
3535
3589
|
}
|
|
3536
|
-
|
|
3537
|
-
for (const key in condition) {
|
|
3538
|
-
if (key === driverKey) continue;
|
|
3539
|
-
const verify = this.verifierMap[key];
|
|
3540
|
-
const condValue = condition[key];
|
|
3541
|
-
if (!verify(v2, condValue)) {
|
|
3542
|
-
isMatch = false;
|
|
3543
|
-
break;
|
|
3544
|
-
}
|
|
3545
|
-
}
|
|
3546
|
-
if (isMatch) {
|
|
3590
|
+
if (this.verify(v, condition)) {
|
|
3547
3591
|
yield pair;
|
|
3548
3592
|
count++;
|
|
3549
3593
|
if (limit !== void 0 && count >= limit) {
|
|
@@ -3568,7 +3612,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3568
3612
|
}
|
|
3569
3613
|
async insert(key, value) {
|
|
3570
3614
|
return this.writeLock(0, async () => {
|
|
3571
|
-
let before = await this.
|
|
3615
|
+
let before = await this.locateLeaf(value);
|
|
3572
3616
|
before = await this._insertAtLeaf(before, key, value);
|
|
3573
3617
|
if (before.values.length === this.order) {
|
|
3574
3618
|
let after = await this._createNode(
|
|
@@ -3598,7 +3642,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3598
3642
|
let currentLeaf = null;
|
|
3599
3643
|
let modified = false;
|
|
3600
3644
|
for (const [key, value] of sorted) {
|
|
3601
|
-
const targetLeaf = await this.
|
|
3645
|
+
const targetLeaf = await this.locateLeaf(value);
|
|
3602
3646
|
if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
|
|
3603
3647
|
} else {
|
|
3604
3648
|
if (currentLeaf !== null && modified) {
|
|
@@ -3842,7 +3886,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3842
3886
|
if (value === void 0) {
|
|
3843
3887
|
return;
|
|
3844
3888
|
}
|
|
3845
|
-
let node = await this.
|
|
3889
|
+
let node = await this.findLowerBoundLeaf(value);
|
|
3846
3890
|
let found = false;
|
|
3847
3891
|
while (true) {
|
|
3848
3892
|
let i = node.values.length;
|
|
@@ -4844,39 +4888,125 @@ var InvertedWeakMap = class {
|
|
|
4844
4888
|
var MVCCStrategy2 = class {
|
|
4845
4889
|
};
|
|
4846
4890
|
var LRUMap3 = class {
|
|
4847
|
-
cache = /* @__PURE__ */ new Map();
|
|
4848
4891
|
capacity;
|
|
4892
|
+
map;
|
|
4893
|
+
head = null;
|
|
4894
|
+
tail = null;
|
|
4895
|
+
/**
|
|
4896
|
+
* Creates an instance of LRUMap.
|
|
4897
|
+
* @param capacity The maximum number of items the cache can hold.
|
|
4898
|
+
*/
|
|
4849
4899
|
constructor(capacity) {
|
|
4850
4900
|
this.capacity = capacity;
|
|
4901
|
+
this.map = /* @__PURE__ */ new Map();
|
|
4851
4902
|
}
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4903
|
+
/**
|
|
4904
|
+
* Promotes a node to the head of the linked list (marks as most recently used).
|
|
4905
|
+
* @param node The node to promote.
|
|
4906
|
+
*/
|
|
4907
|
+
promote(node) {
|
|
4908
|
+
this.extract(node);
|
|
4909
|
+
this.prepend(node);
|
|
4910
|
+
}
|
|
4911
|
+
/**
|
|
4912
|
+
* Disconnects a node from the doubly linked list.
|
|
4913
|
+
* @param node The node to extract.
|
|
4914
|
+
*/
|
|
4915
|
+
extract(node) {
|
|
4916
|
+
if (node.prev) node.prev.next = node.next;
|
|
4917
|
+
else this.head = node.next;
|
|
4918
|
+
if (node.next) node.next.prev = node.prev;
|
|
4919
|
+
else this.tail = node.prev;
|
|
4920
|
+
node.prev = null;
|
|
4921
|
+
node.next = null;
|
|
4922
|
+
}
|
|
4923
|
+
/**
|
|
4924
|
+
* Inserts a node at the head of the doubly linked list.
|
|
4925
|
+
* @param node The node to prepend.
|
|
4926
|
+
*/
|
|
4927
|
+
prepend(node) {
|
|
4928
|
+
node.next = this.head;
|
|
4929
|
+
if (this.head) this.head.prev = node;
|
|
4930
|
+
this.head = node;
|
|
4931
|
+
if (!this.tail) this.tail = node;
|
|
4858
4932
|
}
|
|
4933
|
+
/**
|
|
4934
|
+
* Stores or updates a value by key.
|
|
4935
|
+
* If the capacity is exceeded, the least recently used item (tail) is removed.
|
|
4936
|
+
* @param key The key to store.
|
|
4937
|
+
* @param value The value to store.
|
|
4938
|
+
*/
|
|
4859
4939
|
set(key, value) {
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4940
|
+
const existing = this.map.get(key);
|
|
4941
|
+
if (existing) {
|
|
4942
|
+
existing.value = value;
|
|
4943
|
+
this.promote(existing);
|
|
4944
|
+
return;
|
|
4945
|
+
}
|
|
4946
|
+
const newNode = { key, value, prev: null, next: null };
|
|
4947
|
+
this.map.set(key, newNode);
|
|
4948
|
+
this.prepend(newNode);
|
|
4949
|
+
if (this.map.size > this.capacity && this.tail) {
|
|
4950
|
+
this.map.delete(this.tail.key);
|
|
4951
|
+
this.extract(this.tail);
|
|
4865
4952
|
}
|
|
4866
|
-
this.cache.set(key, value);
|
|
4867
|
-
return this;
|
|
4868
4953
|
}
|
|
4954
|
+
/**
|
|
4955
|
+
* Retrieves a value by key.
|
|
4956
|
+
* Accessing the item moves it to the "most recently used" position.
|
|
4957
|
+
* @param key The key to look for.
|
|
4958
|
+
* @returns The value associated with the key, or undefined if not found.
|
|
4959
|
+
*/
|
|
4960
|
+
get(key) {
|
|
4961
|
+
const node = this.map.get(key);
|
|
4962
|
+
if (!node) return void 0;
|
|
4963
|
+
this.promote(node);
|
|
4964
|
+
return node.value;
|
|
4965
|
+
}
|
|
4966
|
+
/**
|
|
4967
|
+
* Checks if a key exists in the cache without changing its access order.
|
|
4968
|
+
* @param key The key to check.
|
|
4969
|
+
* @returns True if the key exists, false otherwise.
|
|
4970
|
+
*/
|
|
4869
4971
|
has(key) {
|
|
4870
|
-
return this.
|
|
4972
|
+
return this.map.has(key);
|
|
4871
4973
|
}
|
|
4974
|
+
/**
|
|
4975
|
+
* Removes a key and its associated value from the cache.
|
|
4976
|
+
* @param key The key to remove.
|
|
4977
|
+
* @returns True if the key was found and removed, false otherwise.
|
|
4978
|
+
*/
|
|
4872
4979
|
delete(key) {
|
|
4873
|
-
|
|
4980
|
+
const node = this.map.get(key);
|
|
4981
|
+
if (!node) return false;
|
|
4982
|
+
this.extract(node);
|
|
4983
|
+
this.map.delete(key);
|
|
4984
|
+
return true;
|
|
4874
4985
|
}
|
|
4875
|
-
|
|
4876
|
-
|
|
4986
|
+
/**
|
|
4987
|
+
* Returns an iterator of keys in the order of most recently used to least recently used.
|
|
4988
|
+
* @returns An iterable iterator of keys.
|
|
4989
|
+
*/
|
|
4990
|
+
*keys() {
|
|
4991
|
+
let current = this.head;
|
|
4992
|
+
while (current) {
|
|
4993
|
+
yield current.key;
|
|
4994
|
+
current = current.next;
|
|
4995
|
+
}
|
|
4877
4996
|
}
|
|
4997
|
+
/**
|
|
4998
|
+
* Returns the current number of items in the cache.
|
|
4999
|
+
*/
|
|
4878
5000
|
get size() {
|
|
4879
|
-
return this.
|
|
5001
|
+
return this.map.size;
|
|
5002
|
+
}
|
|
5003
|
+
/**
|
|
5004
|
+
* Clears all items from the cache.
|
|
5005
|
+
*/
|
|
5006
|
+
clear() {
|
|
5007
|
+
this.map.clear();
|
|
5008
|
+
this.head = null;
|
|
5009
|
+
this.tail = null;
|
|
4880
5010
|
}
|
|
4881
5011
|
};
|
|
4882
5012
|
var MVCCTransaction2 = class {
|
|
@@ -6333,21 +6463,66 @@ function crc32(buf) {
|
|
|
6333
6463
|
}
|
|
6334
6464
|
|
|
6335
6465
|
// src/utils/array.ts
|
|
6336
|
-
function
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6466
|
+
function calcThreshold(sortedGaps, n) {
|
|
6467
|
+
const gLen = sortedGaps.length;
|
|
6468
|
+
if (gLen === 0) return 0;
|
|
6469
|
+
const median = sortedGaps[Math.floor(gLen * 0.5)];
|
|
6470
|
+
const q1 = sortedGaps[Math.floor(gLen * 0.25)];
|
|
6471
|
+
const q3 = sortedGaps[Math.floor(gLen * 0.75)];
|
|
6472
|
+
const iqr = q3 - q1;
|
|
6473
|
+
const logN = Math.max(1, Math.log10(n));
|
|
6474
|
+
if (iqr > 0) {
|
|
6475
|
+
const threshold2 = q3 + iqr * 1.5 * logN;
|
|
6476
|
+
const minJump = Math.max(median * 5, 20);
|
|
6477
|
+
return Math.max(threshold2, minJump);
|
|
6478
|
+
}
|
|
6479
|
+
const baseGap = median > 0 ? median : 1;
|
|
6480
|
+
const p90 = sortedGaps[Math.floor(gLen * 0.9)];
|
|
6481
|
+
if (p90 > baseGap) {
|
|
6482
|
+
const threshold2 = baseGap + (p90 - baseGap) * 0.5 * logN;
|
|
6483
|
+
return Math.max(threshold2, baseGap * 5, 20);
|
|
6484
|
+
}
|
|
6485
|
+
let mean = 0;
|
|
6486
|
+
for (let i = 0; i < gLen; i++) mean += sortedGaps[i];
|
|
6487
|
+
mean /= gLen;
|
|
6488
|
+
let variance = 0;
|
|
6489
|
+
for (let i = 0; i < gLen; i++) {
|
|
6490
|
+
const d = sortedGaps[i] - mean;
|
|
6491
|
+
variance += d * d;
|
|
6492
|
+
}
|
|
6493
|
+
const stddev = Math.sqrt(variance / gLen);
|
|
6494
|
+
if (stddev === 0) {
|
|
6495
|
+
return baseGap * 2;
|
|
6496
|
+
}
|
|
6497
|
+
const threshold = mean + stddev * logN;
|
|
6498
|
+
return Math.max(threshold, baseGap * 5, 20);
|
|
6499
|
+
}
|
|
6500
|
+
function clusterNumbers(numbers, maxGap) {
|
|
6501
|
+
const n = numbers.length;
|
|
6502
|
+
if (n === 0) return [];
|
|
6503
|
+
if (n === 1) return [new Float64Array([numbers[0]])];
|
|
6504
|
+
const sorted = (numbers instanceof Float64Array ? numbers.slice() : Float64Array.from(numbers)).sort();
|
|
6505
|
+
const gaps = new Float64Array(n - 1);
|
|
6506
|
+
for (let i = 0, len = n - 1; i < len; i++) {
|
|
6507
|
+
gaps[i] = sorted[i + 1] - sorted[i];
|
|
6508
|
+
}
|
|
6509
|
+
const sortedGaps = gaps.slice().sort();
|
|
6510
|
+
let threshold;
|
|
6511
|
+
if (maxGap !== void 0) {
|
|
6512
|
+
threshold = maxGap;
|
|
6513
|
+
} else {
|
|
6514
|
+
threshold = calcThreshold(sortedGaps, n);
|
|
6515
|
+
}
|
|
6516
|
+
const clusters = [];
|
|
6517
|
+
let clusterStart = 0;
|
|
6518
|
+
for (let i = 0, len = n - 1; i < len; i++) {
|
|
6519
|
+
if (gaps[i] > threshold) {
|
|
6520
|
+
clusters.push(sorted.subarray(clusterStart, i + 1));
|
|
6521
|
+
clusterStart = i + 1;
|
|
6347
6522
|
}
|
|
6348
|
-
i++;
|
|
6349
6523
|
}
|
|
6350
|
-
|
|
6524
|
+
clusters.push(sorted.subarray(clusterStart));
|
|
6525
|
+
return clusters;
|
|
6351
6526
|
}
|
|
6352
6527
|
|
|
6353
6528
|
// src/core/Row.ts
|
|
@@ -8203,6 +8378,22 @@ var PageMVCCStrategy = class {
|
|
|
8203
8378
|
this.dirtyPages.delete(pageId);
|
|
8204
8379
|
}
|
|
8205
8380
|
}
|
|
8381
|
+
/**
|
|
8382
|
+
* 지정된 페이지들만 디스크에 기록합니다.
|
|
8383
|
+
* WAL 없이 트랜잭션 커밋 시, 해당 트랜잭션의 dirty pages만 선택적으로 flush합니다.
|
|
8384
|
+
* @param pages 기록할 페이지 맵 (PageID -> PageData)
|
|
8385
|
+
*/
|
|
8386
|
+
async flushPages(pages) {
|
|
8387
|
+
if (pages.size === 0) {
|
|
8388
|
+
return;
|
|
8389
|
+
}
|
|
8390
|
+
const sortedPageIds = Array.from(pages.keys()).sort((a, b) => a - b);
|
|
8391
|
+
for (const pageId of sortedPageIds) {
|
|
8392
|
+
const data = pages.get(pageId);
|
|
8393
|
+
const position = pageId * this.pageSize;
|
|
8394
|
+
await this._writeToDisk(data, position);
|
|
8395
|
+
}
|
|
8396
|
+
}
|
|
8206
8397
|
/**
|
|
8207
8398
|
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
8208
8399
|
*/
|
|
@@ -9295,17 +9486,40 @@ var RowTableEngine = class {
|
|
|
9295
9486
|
for (let i = 0, len = pks.length; i < len; i++) {
|
|
9296
9487
|
pkIndexMap.set(pks[i], i);
|
|
9297
9488
|
}
|
|
9298
|
-
const
|
|
9299
|
-
const
|
|
9489
|
+
const validCount = pks.length;
|
|
9490
|
+
const pkArray = new Float64Array(validCount).fill(0);
|
|
9491
|
+
const ridArray = new Float64Array(validCount).fill(0);
|
|
9492
|
+
const indexArray = new Float64Array(validCount).fill(0);
|
|
9300
9493
|
const btx = await this.getBPTreeTransaction(tx);
|
|
9301
|
-
const
|
|
9302
|
-
for
|
|
9303
|
-
const
|
|
9304
|
-
|
|
9305
|
-
|
|
9494
|
+
const clusters = clusterNumbers(pks, this.order / 2);
|
|
9495
|
+
for (let i = 0, len = clusters.length; i < len; i++) {
|
|
9496
|
+
const cluster = clusters[i];
|
|
9497
|
+
const minPk = cluster[0];
|
|
9498
|
+
const maxPk = cluster[cluster.length - 1];
|
|
9499
|
+
if (minPk === maxPk) {
|
|
9500
|
+
const keys = await btx.keys({ equal: minPk });
|
|
9501
|
+
if (keys.size > 0) {
|
|
9502
|
+
const rid = keys.values().next().value;
|
|
9503
|
+
const index = pkIndexMap.get(minPk);
|
|
9504
|
+
if (index !== void 0) {
|
|
9505
|
+
pkArray[index] = minPk;
|
|
9506
|
+
ridArray[index] = rid;
|
|
9507
|
+
indexArray[index] = index;
|
|
9508
|
+
}
|
|
9509
|
+
}
|
|
9510
|
+
continue;
|
|
9511
|
+
}
|
|
9512
|
+
const stream = btx.whereStream({ gte: minPk, lte: maxPk });
|
|
9513
|
+
for await (const [rid, pk] of stream) {
|
|
9514
|
+
const index = pkIndexMap.get(pk);
|
|
9515
|
+
if (index !== void 0) {
|
|
9516
|
+
pkArray[index] = pk;
|
|
9517
|
+
ridArray[index] = rid;
|
|
9518
|
+
indexArray[index] = index;
|
|
9519
|
+
}
|
|
9306
9520
|
}
|
|
9307
9521
|
}
|
|
9308
|
-
return this.fetchRowsByRids(
|
|
9522
|
+
return this.fetchRowsByRids(validCount, pkArray, ridArray, indexArray, tx);
|
|
9309
9523
|
}
|
|
9310
9524
|
/**
|
|
9311
9525
|
* Fetches multiple rows by their RID and PK combinations, grouping by page ID to minimize I/O.
|
|
@@ -9313,19 +9527,21 @@ var RowTableEngine = class {
|
|
|
9313
9527
|
* @param tx Transaction
|
|
9314
9528
|
* @returns Array of row data in the same order as input PKs
|
|
9315
9529
|
*/
|
|
9316
|
-
async fetchRowsByRids(
|
|
9317
|
-
const result = new Array(
|
|
9318
|
-
if (
|
|
9530
|
+
async fetchRowsByRids(validCount, pkArray, ridArray, indexArray, tx) {
|
|
9531
|
+
const result = new Array(validCount).fill(null);
|
|
9532
|
+
if (validCount === 0) return result;
|
|
9319
9533
|
const pageGroupMap = /* @__PURE__ */ new Map();
|
|
9320
|
-
for (
|
|
9321
|
-
|
|
9322
|
-
const rid =
|
|
9534
|
+
for (let i = 0; i < validCount; i++) {
|
|
9535
|
+
const pk = pkArray[i];
|
|
9536
|
+
const rid = ridArray[i];
|
|
9537
|
+
const index = indexArray[i];
|
|
9538
|
+
if (pk === 0 && rid === 0 && index === 0) continue;
|
|
9323
9539
|
const slotIndex = rid % 65536;
|
|
9324
9540
|
const pageId = Math.floor(rid / 65536);
|
|
9325
9541
|
if (!pageGroupMap.has(pageId)) {
|
|
9326
9542
|
pageGroupMap.set(pageId, []);
|
|
9327
9543
|
}
|
|
9328
|
-
pageGroupMap.get(pageId).push({ pk
|
|
9544
|
+
pageGroupMap.get(pageId).push({ pk, slotIndex, index });
|
|
9329
9545
|
}
|
|
9330
9546
|
const sortedPageIds = Array.from(pageGroupMap.keys()).sort((a, b) => a - b);
|
|
9331
9547
|
await Promise.all(sortedPageIds.map(async (pageId) => {
|
|
@@ -9335,7 +9551,8 @@ var RowTableEngine = class {
|
|
|
9335
9551
|
throw new Error(`Page ${pageId} is not a data page`);
|
|
9336
9552
|
}
|
|
9337
9553
|
const manager = this.factory.getManager(page);
|
|
9338
|
-
for (
|
|
9554
|
+
for (let i = 0, len = items.length; i < len; i++) {
|
|
9555
|
+
const item = items[i];
|
|
9339
9556
|
const row = manager.getRow(page, item.slotIndex);
|
|
9340
9557
|
if (this.rowManager.getDeletedFlag(row)) {
|
|
9341
9558
|
result[item.index] = null;
|
|
@@ -9578,7 +9795,9 @@ var Transaction = class {
|
|
|
9578
9795
|
for (const [pageId, data] of this.dirtyPages) {
|
|
9579
9796
|
await this.pageStrategy.write(pageId, data);
|
|
9580
9797
|
}
|
|
9581
|
-
if (this.pfs.wal) {
|
|
9798
|
+
if (!this.pfs.wal) {
|
|
9799
|
+
await this.pfs.strategy.flushPages(this.dirtyPages);
|
|
9800
|
+
} else {
|
|
9582
9801
|
this.pfs.wal.incrementWrittenPages(this.dirtyPages.size);
|
|
9583
9802
|
if (this.pfs.wal.shouldCheckpoint(this.pfs.options.walCheckpointThreshold)) {
|
|
9584
9803
|
shouldTriggerCheckpoint = true;
|
|
@@ -33,6 +33,12 @@ export declare class PageMVCCStrategy {
|
|
|
33
33
|
* WAL 체크포인트 시점에 호출되어야 합니다.
|
|
34
34
|
*/
|
|
35
35
|
flush(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* 지정된 페이지들만 디스크에 기록합니다.
|
|
38
|
+
* WAL 없이 트랜잭션 커밋 시, 해당 트랜잭션의 dirty pages만 선택적으로 flush합니다.
|
|
39
|
+
* @param pages 기록할 페이지 맵 (PageID -> PageData)
|
|
40
|
+
*/
|
|
41
|
+
flushPages(pages: Map<number, Uint8Array>): Promise<void>;
|
|
36
42
|
/**
|
|
37
43
|
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
38
44
|
*/
|
|
@@ -2,4 +2,11 @@ type SupportedNumberArray = number[] | Uint8Array | Uint16Array | Uint32Array |
|
|
|
2
2
|
export declare function getMinValue(array: SupportedNumberArray): number;
|
|
3
3
|
export declare function getMaxValue(array: SupportedNumberArray): number;
|
|
4
4
|
export declare function getMinMaxValue(array: SupportedNumberArray): [number, number];
|
|
5
|
+
/**
|
|
6
|
+
* Sorts the input array and splits it into clusters based on the gaps between consecutive elements.
|
|
7
|
+
* @param numbers Array of numbers to cluster
|
|
8
|
+
* @param maxGap Optional fixed gap threshold. If not provided, it is calculated automatically.
|
|
9
|
+
* @returns Array of clusters
|
|
10
|
+
*/
|
|
11
|
+
export declare function clusterNumbers(numbers: number[] | Float64Array, maxGap?: number): Float64Array[];
|
|
5
12
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dataply",
|
|
3
|
-
"version": "0.0.24
|
|
3
|
+
"version": "0.0.24",
|
|
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,8 +47,8 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"cache-entanglement": "^1.7.1",
|
|
49
49
|
"hookall": "^2.2.0",
|
|
50
|
-
"mvcc-api": "^1.3.
|
|
50
|
+
"mvcc-api": "^1.3.5",
|
|
51
51
|
"ryoiki": "^1.2.0",
|
|
52
|
-
"serializable-bptree": "^8.3.
|
|
52
|
+
"serializable-bptree": "^8.3.5"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/readme.md
CHANGED
|
@@ -18,7 +18,7 @@ Dataply provides essential features for high-performance data management:
|
|
|
18
18
|
- **MVCC & Isolation**: Snapshot isolation via Multi-Version Concurrency Control (MVCC) enables non-blocking reads.
|
|
19
19
|
- **Reliability (WAL)**: Write-Ahead Logging (WAL) ensures data integrity and automatic crash recovery.
|
|
20
20
|
- **Atomic Transactions**: Full support for ACID-compliant Commit and Rollback operations.
|
|
21
|
-
- **Efficient Storage**: Fixed-size page management with LRU-based page caching and
|
|
21
|
+
- **Efficient Storage**: Fixed-size page management with LRU-based page caching and Free List space optimization (Bitmap-based management is deprecated).
|
|
22
22
|
- **Type Safety**: Comprehensive TypeScript definitions for a seamless developer experience.
|
|
23
23
|
|
|
24
24
|
## Installation
|
|
@@ -166,6 +166,8 @@ Opens a database file. If the file does not exist, it creates and initializes a
|
|
|
166
166
|
- `options.pageSize`: Size of a page (Default: 8192, must be a power of 2)
|
|
167
167
|
- `options.pageCacheCapacity`: Maximum number of pages to keep in memory (Default: 10000)
|
|
168
168
|
- `options.wal`: Path to the WAL file. If omitted, WAL is disabled.
|
|
169
|
+
- `options.pagePreallocationCount`: The number of pages to preallocate when creating a new page (Default: 1000).
|
|
170
|
+
- `options.walCheckpointThreshold`: The total number of pages written to the WAL before automatically clearing it (Default: 1000).
|
|
169
171
|
|
|
170
172
|
#### `async init(): Promise<void>`
|
|
171
173
|
Initializes the instance. Must be called before performing any CRUD operations.
|
|
@@ -173,12 +175,18 @@ Initializes the instance. Must be called before performing any CRUD operations.
|
|
|
173
175
|
#### `async insert(data: string | Uint8Array, tx?: Transaction): Promise<number>`
|
|
174
176
|
Inserts new data. Returns the Primary Key (PK) of the created row.
|
|
175
177
|
|
|
178
|
+
#### `async insertAsOverflow(data: string | Uint8Array, tx?: Transaction): Promise<number>`
|
|
179
|
+
Forcibly inserts data into an overflow page, even if it could fit within a standard data page. Returns the Primary Key (PK).
|
|
180
|
+
|
|
176
181
|
#### `async insertBatch(dataList: (string | Uint8Array)[], tx?: Transaction): Promise<number[]>`
|
|
177
182
|
Inserts multiple rows at once. This is significantly faster than multiple individual inserts as it minimizes internal transaction overhead.
|
|
178
183
|
|
|
179
184
|
#### `async select(pk: number, asRaw?: boolean, tx?: Transaction): Promise<string | Uint8Array | null>`
|
|
180
185
|
Retrieves data based on the PK. Returns `Uint8Array` if `asRaw` is true.
|
|
181
186
|
|
|
187
|
+
#### `async selectMany(pks: number[] | Float64Array, asRaw?: boolean, tx?: Transaction): Promise<(string | Uint8Array | null)[]>`
|
|
188
|
+
Retrieves multiple data records in batch based on the provided PKs. This is more efficient than individual `select` calls for multiple lookups.
|
|
189
|
+
|
|
182
190
|
#### `async update(pk: number, data: string | Uint8Array, tx?: Transaction): Promise<void>`
|
|
183
191
|
Updates existing data.
|
|
184
192
|
|
|
@@ -257,7 +265,7 @@ For a detailed visual guide on Dataply's internal architecture, class diagrams,
|
|
|
257
265
|
- **Fixed-size Pages**: All data is managed in fixed-size units (default 8KB) called pages.
|
|
258
266
|
- **Page Cache**: Minimizes disk I/O by caching frequently accessed pages in memory (LRU Strategy).
|
|
259
267
|
- **Dirty Page Tracking**: Tracks modified pages (Dirty) to synchronize them with disk efficiently only at the time of commit.
|
|
260
|
-
- **
|
|
268
|
+
- **Free List Management**: Efficiently tracks the allocation and deallocation of pages using a Free List (stack-like structure), facilitating fast space reclamation and reuse. (The older Bitmap-based mechanism is deprecated but remains for backward compatibility). For more details on this mechanism, see [Page Reclamation and Reuse Guide](docs/page_reclamation.md).
|
|
261
269
|
- **Detailed Structure**: For technical details on the physical layout, see [structure.md](docs/structure.md).
|
|
262
270
|
|
|
263
271
|
#### Page & Row Layout
|