document-dataply 0.0.9-alpha.1 → 0.0.9-alpha.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
|
@@ -873,7 +873,7 @@ var require_cjs = __commonJS({
|
|
|
873
873
|
};
|
|
874
874
|
var AsyncMVCCStrategy = class extends MVCCStrategy {
|
|
875
875
|
};
|
|
876
|
-
var
|
|
876
|
+
var Ryoiki = class _Ryoiki {
|
|
877
877
|
readings;
|
|
878
878
|
writings;
|
|
879
879
|
readQueue;
|
|
@@ -1130,7 +1130,7 @@ var require_cjs = __commonJS({
|
|
|
1130
1130
|
}
|
|
1131
1131
|
};
|
|
1132
1132
|
var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
1133
|
-
lock = new
|
|
1133
|
+
lock = new Ryoiki();
|
|
1134
1134
|
async writeLock(fn) {
|
|
1135
1135
|
let lockId;
|
|
1136
1136
|
return this.lock.writeLock(async (_lockId) => {
|
|
@@ -1580,83 +1580,217 @@ var require_cjs = __commonJS({
|
|
|
1580
1580
|
return regexp.test(nodeValue);
|
|
1581
1581
|
}
|
|
1582
1582
|
};
|
|
1583
|
-
|
|
1584
|
-
gt:
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
lte:
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1583
|
+
searchConfigs = {
|
|
1584
|
+
gt: {
|
|
1585
|
+
asc: {
|
|
1586
|
+
start: (tx, v) => tx.insertableNodeByPrimary(v[0]),
|
|
1587
|
+
end: () => null,
|
|
1588
|
+
direction: 1,
|
|
1589
|
+
earlyTerminate: false
|
|
1590
|
+
},
|
|
1591
|
+
desc: {
|
|
1592
|
+
start: (tx) => tx.rightestNode(),
|
|
1593
|
+
end: (tx, v) => tx.insertableEndNode(v[0], -1),
|
|
1594
|
+
direction: -1,
|
|
1595
|
+
earlyTerminate: true
|
|
1596
|
+
}
|
|
1597
|
+
},
|
|
1598
|
+
gte: {
|
|
1599
|
+
asc: {
|
|
1600
|
+
start: (tx, v) => tx.insertableNodeByPrimary(v[0]),
|
|
1601
|
+
end: () => null,
|
|
1602
|
+
direction: 1,
|
|
1603
|
+
earlyTerminate: false
|
|
1604
|
+
},
|
|
1605
|
+
desc: {
|
|
1606
|
+
start: (tx) => tx.rightestNode(),
|
|
1607
|
+
end: (tx, v) => tx.insertableEndNode(v[0], -1),
|
|
1608
|
+
direction: -1,
|
|
1609
|
+
earlyTerminate: true
|
|
1610
|
+
}
|
|
1611
|
+
},
|
|
1612
|
+
lt: {
|
|
1613
|
+
asc: {
|
|
1614
|
+
start: (tx) => tx.leftestNode(),
|
|
1615
|
+
end: (tx, v) => tx.insertableEndNode(v[0], 1),
|
|
1616
|
+
direction: 1,
|
|
1617
|
+
earlyTerminate: true
|
|
1618
|
+
},
|
|
1619
|
+
desc: {
|
|
1620
|
+
start: (tx, v) => tx.insertableNodeByPrimary(v[0]),
|
|
1621
|
+
end: () => null,
|
|
1622
|
+
direction: -1,
|
|
1623
|
+
earlyTerminate: false
|
|
1624
|
+
}
|
|
1625
|
+
},
|
|
1626
|
+
lte: {
|
|
1627
|
+
asc: {
|
|
1628
|
+
start: (tx) => tx.leftestNode(),
|
|
1629
|
+
end: (tx, v) => tx.insertableEndNode(v[0], 1),
|
|
1630
|
+
direction: 1,
|
|
1631
|
+
earlyTerminate: true
|
|
1632
|
+
},
|
|
1633
|
+
desc: {
|
|
1634
|
+
start: (tx, v) => tx.insertableRightestNodeByPrimary(v[0]),
|
|
1635
|
+
end: () => null,
|
|
1636
|
+
direction: -1,
|
|
1637
|
+
earlyTerminate: false
|
|
1638
|
+
}
|
|
1639
|
+
},
|
|
1640
|
+
equal: {
|
|
1641
|
+
asc: {
|
|
1642
|
+
start: (tx, v) => tx.insertableNodeByPrimary(v[0]),
|
|
1643
|
+
end: (tx, v) => tx.insertableEndNode(v[0], 1),
|
|
1644
|
+
direction: 1,
|
|
1645
|
+
earlyTerminate: true
|
|
1646
|
+
},
|
|
1647
|
+
desc: {
|
|
1648
|
+
start: (tx, v) => tx.insertableEndNode(v[0], 1),
|
|
1649
|
+
end: (tx, v) => tx.insertableEndNode(v[0], -1),
|
|
1650
|
+
direction: -1,
|
|
1651
|
+
earlyTerminate: true
|
|
1652
|
+
}
|
|
1653
|
+
},
|
|
1654
|
+
notEqual: {
|
|
1655
|
+
asc: {
|
|
1656
|
+
start: (tx) => tx.leftestNode(),
|
|
1657
|
+
end: () => null,
|
|
1658
|
+
direction: 1,
|
|
1659
|
+
earlyTerminate: false
|
|
1660
|
+
},
|
|
1661
|
+
desc: {
|
|
1662
|
+
start: (tx) => tx.rightestNode(),
|
|
1663
|
+
end: () => null,
|
|
1664
|
+
direction: -1,
|
|
1665
|
+
earlyTerminate: false
|
|
1666
|
+
}
|
|
1667
|
+
},
|
|
1668
|
+
or: {
|
|
1669
|
+
asc: {
|
|
1670
|
+
start: (tx, v) => tx.insertableNodeByPrimary(tx.lowestValue(v)),
|
|
1671
|
+
end: (tx, v) => tx.insertableEndNode(tx.highestValue(v), 1),
|
|
1672
|
+
direction: 1,
|
|
1673
|
+
earlyTerminate: false
|
|
1674
|
+
},
|
|
1675
|
+
desc: {
|
|
1676
|
+
start: (tx, v) => tx.insertableEndNode(tx.highestValue(v), 1),
|
|
1677
|
+
end: (tx, v) => tx.insertableEndNode(tx.lowestValue(v), -1),
|
|
1678
|
+
direction: -1,
|
|
1679
|
+
earlyTerminate: false
|
|
1680
|
+
}
|
|
1681
|
+
},
|
|
1682
|
+
primaryGt: {
|
|
1683
|
+
asc: {
|
|
1684
|
+
start: (tx, v) => tx.insertableRightestEndNodeByPrimary(v[0]),
|
|
1685
|
+
end: () => null,
|
|
1686
|
+
direction: 1,
|
|
1687
|
+
earlyTerminate: false
|
|
1688
|
+
},
|
|
1689
|
+
desc: {
|
|
1690
|
+
start: (tx) => tx.rightestNode(),
|
|
1691
|
+
end: (tx, v) => tx.insertableEndNode(v[0], -1),
|
|
1692
|
+
direction: -1,
|
|
1693
|
+
earlyTerminate: true
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
primaryGte: {
|
|
1697
|
+
asc: {
|
|
1698
|
+
start: (tx, v) => tx.insertableNodeByPrimary(v[0]),
|
|
1699
|
+
end: () => null,
|
|
1700
|
+
direction: 1,
|
|
1701
|
+
earlyTerminate: false
|
|
1702
|
+
},
|
|
1703
|
+
desc: {
|
|
1704
|
+
start: (tx) => tx.rightestNode(),
|
|
1705
|
+
end: (tx, v) => tx.insertableEndNode(v[0], -1),
|
|
1706
|
+
direction: -1,
|
|
1707
|
+
earlyTerminate: true
|
|
1708
|
+
}
|
|
1709
|
+
},
|
|
1710
|
+
primaryLt: {
|
|
1711
|
+
asc: {
|
|
1712
|
+
start: (tx) => tx.leftestNode(),
|
|
1713
|
+
end: (tx, v) => tx.insertableEndNode(v[0], 1),
|
|
1714
|
+
direction: 1,
|
|
1715
|
+
earlyTerminate: true
|
|
1716
|
+
},
|
|
1717
|
+
desc: {
|
|
1718
|
+
start: (tx, v) => tx.insertableNodeByPrimary(v[0]),
|
|
1719
|
+
end: () => null,
|
|
1720
|
+
direction: -1,
|
|
1721
|
+
earlyTerminate: false
|
|
1722
|
+
}
|
|
1723
|
+
},
|
|
1724
|
+
primaryLte: {
|
|
1725
|
+
asc: {
|
|
1726
|
+
start: (tx) => tx.leftestNode(),
|
|
1727
|
+
end: (tx, v) => tx.insertableEndNode(v[0], 1),
|
|
1728
|
+
direction: 1,
|
|
1729
|
+
earlyTerminate: true
|
|
1730
|
+
},
|
|
1731
|
+
desc: {
|
|
1732
|
+
start: (tx, v) => tx.insertableRightestNodeByPrimary(v[0]),
|
|
1733
|
+
end: () => null,
|
|
1734
|
+
direction: -1,
|
|
1735
|
+
earlyTerminate: false
|
|
1736
|
+
}
|
|
1737
|
+
},
|
|
1738
|
+
primaryEqual: {
|
|
1739
|
+
asc: {
|
|
1740
|
+
start: (tx, v) => tx.insertableNodeByPrimary(v[0]),
|
|
1741
|
+
end: (tx, v) => tx.insertableRightestEndNodeByPrimary(v[0]),
|
|
1742
|
+
direction: 1,
|
|
1743
|
+
earlyTerminate: true
|
|
1744
|
+
},
|
|
1745
|
+
desc: {
|
|
1746
|
+
start: (tx, v) => tx.insertableRightestEndNodeByPrimary(v[0]),
|
|
1747
|
+
end: (tx, v) => tx.insertableEndNode(v[0], -1),
|
|
1748
|
+
direction: -1,
|
|
1749
|
+
earlyTerminate: true
|
|
1750
|
+
}
|
|
1751
|
+
},
|
|
1752
|
+
primaryNotEqual: {
|
|
1753
|
+
asc: {
|
|
1754
|
+
start: (tx) => tx.leftestNode(),
|
|
1755
|
+
end: () => null,
|
|
1756
|
+
direction: 1,
|
|
1757
|
+
earlyTerminate: false
|
|
1758
|
+
},
|
|
1759
|
+
desc: {
|
|
1760
|
+
start: (tx) => tx.rightestNode(),
|
|
1761
|
+
end: () => null,
|
|
1762
|
+
direction: -1,
|
|
1763
|
+
earlyTerminate: false
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
primaryOr: {
|
|
1767
|
+
asc: {
|
|
1768
|
+
start: (tx, v) => tx.insertableNodeByPrimary(tx.lowestPrimaryValue(v)),
|
|
1769
|
+
end: (tx, v) => tx.insertableRightestEndNodeByPrimary(tx.highestPrimaryValue(v)),
|
|
1770
|
+
direction: 1,
|
|
1771
|
+
earlyTerminate: false
|
|
1772
|
+
},
|
|
1773
|
+
desc: {
|
|
1774
|
+
start: (tx, v) => tx.insertableRightestEndNodeByPrimary(tx.highestPrimaryValue(v)),
|
|
1775
|
+
end: (tx, v) => tx.insertableEndNode(tx.lowestPrimaryValue(v), -1),
|
|
1776
|
+
direction: -1,
|
|
1777
|
+
earlyTerminate: false
|
|
1778
|
+
}
|
|
1779
|
+
},
|
|
1780
|
+
like: {
|
|
1781
|
+
asc: {
|
|
1782
|
+
start: (tx) => tx.leftestNode(),
|
|
1783
|
+
end: () => null,
|
|
1784
|
+
direction: 1,
|
|
1785
|
+
earlyTerminate: false
|
|
1786
|
+
},
|
|
1787
|
+
desc: {
|
|
1788
|
+
start: (tx) => tx.rightestNode(),
|
|
1789
|
+
end: () => null,
|
|
1790
|
+
direction: -1,
|
|
1791
|
+
earlyTerminate: false
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1660
1794
|
};
|
|
1661
1795
|
/**
|
|
1662
1796
|
* Priority map for condition types.
|
|
@@ -2086,7 +2220,7 @@ var require_cjs = __commonJS({
|
|
|
2086
2220
|
return this.getNode(node.next);
|
|
2087
2221
|
}
|
|
2088
2222
|
insertableEndNode(value, direction) {
|
|
2089
|
-
const insertableNode = this.
|
|
2223
|
+
const insertableNode = direction === -1 ? this.insertableNodeByPrimary(value) : this.insertableRightestNodeByPrimary(value);
|
|
2090
2224
|
let key;
|
|
2091
2225
|
switch (direction) {
|
|
2092
2226
|
case -1:
|
|
@@ -2267,16 +2401,20 @@ var require_cjs = __commonJS({
|
|
|
2267
2401
|
const driverKey = this.getDriverKey(condition);
|
|
2268
2402
|
if (!driverKey) return;
|
|
2269
2403
|
const value = condition[driverKey];
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
let
|
|
2404
|
+
const v = this.ensureValues(value);
|
|
2405
|
+
const config = this.searchConfigs[driverKey][order];
|
|
2406
|
+
let startNode = config.start(this, v);
|
|
2407
|
+
let endNode = config.end(this, v);
|
|
2408
|
+
const direction = config.direction;
|
|
2409
|
+
const earlyTerminate = config.earlyTerminate;
|
|
2410
|
+
if (order === "desc" && !startNode) {
|
|
2411
|
+
startNode = this.rightestNode();
|
|
2412
|
+
}
|
|
2413
|
+
if (order === "asc" && !startNode) {
|
|
2414
|
+
startNode = this.leftestNode();
|
|
2415
|
+
}
|
|
2416
|
+
if (!startNode) return;
|
|
2273
2417
|
const comparator = this.verifierMap[driverKey];
|
|
2274
|
-
const earlyTerminate = this.verifierEarlyTerminate[driverKey];
|
|
2275
|
-
if (order === "desc") {
|
|
2276
|
-
startNode = endNode ?? this.rightestNode();
|
|
2277
|
-
endNode = null;
|
|
2278
|
-
direction *= -1;
|
|
2279
|
-
}
|
|
2280
2418
|
const generator = this.getPairsGenerator(
|
|
2281
2419
|
value,
|
|
2282
2420
|
startNode,
|
|
@@ -2288,7 +2426,7 @@ var require_cjs = __commonJS({
|
|
|
2288
2426
|
let count = 0;
|
|
2289
2427
|
const intersection = filterValues && filterValues.size > 0 ? filterValues : null;
|
|
2290
2428
|
for (const pair of generator) {
|
|
2291
|
-
const [k,
|
|
2429
|
+
const [k, v2] = pair;
|
|
2292
2430
|
if (intersection && !intersection.has(k)) {
|
|
2293
2431
|
continue;
|
|
2294
2432
|
}
|
|
@@ -2297,7 +2435,7 @@ var require_cjs = __commonJS({
|
|
|
2297
2435
|
if (key === driverKey) continue;
|
|
2298
2436
|
const verify = this.verifierMap[key];
|
|
2299
2437
|
const condValue = condition[key];
|
|
2300
|
-
if (!verify(
|
|
2438
|
+
if (!verify(v2, condValue)) {
|
|
2301
2439
|
isMatch = false;
|
|
2302
2440
|
break;
|
|
2303
2441
|
}
|
|
@@ -2752,7 +2890,7 @@ var require_cjs = __commonJS({
|
|
|
2752
2890
|
}
|
|
2753
2891
|
}
|
|
2754
2892
|
};
|
|
2755
|
-
var
|
|
2893
|
+
var Ryoiki2 = class _Ryoiki2 {
|
|
2756
2894
|
readings;
|
|
2757
2895
|
writings;
|
|
2758
2896
|
readQueue;
|
|
@@ -3019,7 +3157,7 @@ var require_cjs = __commonJS({
|
|
|
3019
3157
|
comparator,
|
|
3020
3158
|
option
|
|
3021
3159
|
);
|
|
3022
|
-
this.lock = new
|
|
3160
|
+
this.lock = new Ryoiki2();
|
|
3023
3161
|
}
|
|
3024
3162
|
async writeLock(id, fn) {
|
|
3025
3163
|
let lockId;
|
|
@@ -3210,7 +3348,7 @@ var require_cjs = __commonJS({
|
|
|
3210
3348
|
return await this.getNode(node.next);
|
|
3211
3349
|
}
|
|
3212
3350
|
async insertableEndNode(value, direction) {
|
|
3213
|
-
const insertableNode = await this.
|
|
3351
|
+
const insertableNode = direction === -1 ? await this.insertableNodeByPrimary(value) : await this.insertableRightestNodeByPrimary(value);
|
|
3214
3352
|
let key;
|
|
3215
3353
|
switch (direction) {
|
|
3216
3354
|
case -1:
|
|
@@ -3397,16 +3535,20 @@ var require_cjs = __commonJS({
|
|
|
3397
3535
|
const driverKey = this.getDriverKey(condition);
|
|
3398
3536
|
if (!driverKey) return;
|
|
3399
3537
|
const value = condition[driverKey];
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
let
|
|
3538
|
+
const v = this.ensureValues(value);
|
|
3539
|
+
const config = this.searchConfigs[driverKey][order];
|
|
3540
|
+
let startNode = await config.start(this, v);
|
|
3541
|
+
let endNode = await config.end(this, v);
|
|
3542
|
+
const direction = config.direction;
|
|
3543
|
+
const earlyTerminate = config.earlyTerminate;
|
|
3544
|
+
if (order === "desc" && !startNode) {
|
|
3545
|
+
startNode = await this.rightestNode();
|
|
3546
|
+
}
|
|
3547
|
+
if (order === "asc" && !startNode) {
|
|
3548
|
+
startNode = await this.leftestNode();
|
|
3549
|
+
}
|
|
3550
|
+
if (!startNode) return;
|
|
3403
3551
|
const comparator = this.verifierMap[driverKey];
|
|
3404
|
-
const earlyTerminate = this.verifierEarlyTerminate[driverKey];
|
|
3405
|
-
if (order === "desc") {
|
|
3406
|
-
startNode = endNode ?? await this.rightestNode();
|
|
3407
|
-
endNode = null;
|
|
3408
|
-
direction *= -1;
|
|
3409
|
-
}
|
|
3410
3552
|
const generator = this.getPairsGenerator(
|
|
3411
3553
|
value,
|
|
3412
3554
|
startNode,
|
|
@@ -3418,7 +3560,7 @@ var require_cjs = __commonJS({
|
|
|
3418
3560
|
let count = 0;
|
|
3419
3561
|
const intersection = filterValues && filterValues.size > 0 ? filterValues : null;
|
|
3420
3562
|
for await (const pair of generator) {
|
|
3421
|
-
const [k,
|
|
3563
|
+
const [k, v2] = pair;
|
|
3422
3564
|
if (intersection && !intersection.has(k)) {
|
|
3423
3565
|
continue;
|
|
3424
3566
|
}
|
|
@@ -3427,7 +3569,7 @@ var require_cjs = __commonJS({
|
|
|
3427
3569
|
if (key === driverKey) continue;
|
|
3428
3570
|
const verify = this.verifierMap[key];
|
|
3429
3571
|
const condValue = condition[key];
|
|
3430
|
-
if (!verify(
|
|
3572
|
+
if (!verify(v2, condValue)) {
|
|
3431
3573
|
isMatch = false;
|
|
3432
3574
|
break;
|
|
3433
3575
|
}
|
|
@@ -3957,7 +4099,7 @@ var require_cjs = __commonJS({
|
|
|
3957
4099
|
}
|
|
3958
4100
|
};
|
|
3959
4101
|
var SerializeStrategyAsync2 = class extends SerializeStrategy {
|
|
3960
|
-
lock = new
|
|
4102
|
+
lock = new Ryoiki2();
|
|
3961
4103
|
async acquireLock(action) {
|
|
3962
4104
|
let lockId;
|
|
3963
4105
|
return this.lock.writeLock((_lockId) => {
|
|
@@ -8038,9 +8180,6 @@ var require_cjs = __commonJS({
|
|
|
8038
8180
|
*/
|
|
8039
8181
|
async write(pageId, data) {
|
|
8040
8182
|
const pageStartPos = pageId * this.pageSize;
|
|
8041
|
-
if (pageStartPos + this.pageSize > 512 * 1024 * 1024) {
|
|
8042
|
-
throw new Error(`[Safety Limit] File write exceeds 512MB limit at position ${pageStartPos}`);
|
|
8043
|
-
}
|
|
8044
8183
|
const dataCopy = new Uint8Array(this.pageSize);
|
|
8045
8184
|
dataCopy.set(data);
|
|
8046
8185
|
this.dirtyPages.set(pageId, dataCopy);
|
|
@@ -8067,6 +8206,22 @@ var require_cjs = __commonJS({
|
|
|
8067
8206
|
this.dirtyPages.delete(pageId);
|
|
8068
8207
|
}
|
|
8069
8208
|
}
|
|
8209
|
+
/**
|
|
8210
|
+
* 지정된 페이지들만 디스크에 기록합니다.
|
|
8211
|
+
* WAL 없이 트랜잭션 커밋 시, 해당 트랜잭션의 dirty pages만 선택적으로 flush합니다.
|
|
8212
|
+
* @param pages 기록할 페이지 맵 (PageID -> PageData)
|
|
8213
|
+
*/
|
|
8214
|
+
async flushPages(pages) {
|
|
8215
|
+
if (pages.size === 0) {
|
|
8216
|
+
return;
|
|
8217
|
+
}
|
|
8218
|
+
const sortedPageIds = Array.from(pages.keys()).sort((a, b) => a - b);
|
|
8219
|
+
for (const pageId of sortedPageIds) {
|
|
8220
|
+
const data = pages.get(pageId);
|
|
8221
|
+
const position = pageId * this.pageSize;
|
|
8222
|
+
await this._writeToDisk(data, position);
|
|
8223
|
+
}
|
|
8224
|
+
}
|
|
8070
8225
|
/**
|
|
8071
8226
|
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
8072
8227
|
*/
|
|
@@ -8342,7 +8497,7 @@ var require_cjs = __commonJS({
|
|
|
8342
8497
|
/**
|
|
8343
8498
|
* Appends and inserts a new page.
|
|
8344
8499
|
* If a free page is available in the free list, it reuses it.
|
|
8345
|
-
* Otherwise, it
|
|
8500
|
+
* Otherwise, it preallocates `pagePreallocationCount` pages to support sequential reads.
|
|
8346
8501
|
* @returns Created or reused page ID
|
|
8347
8502
|
*/
|
|
8348
8503
|
async appendNewPage(pageType = PageManager.CONSTANT.PAGE_TYPE_EMPTY, tx) {
|
|
@@ -8357,20 +8512,32 @@ var require_cjs = __commonJS({
|
|
|
8357
8512
|
const nextFreePageId = reusedPageManager.getNextPageId(reusedPage);
|
|
8358
8513
|
metadataManager.setFreePageId(metadata, nextFreePageId);
|
|
8359
8514
|
await this.setPage(0, metadata, tx);
|
|
8360
|
-
await this.updateBitmap(reusedPageId, false, tx);
|
|
8361
8515
|
const manager2 = this.pageFactory.getManagerFromType(pageType);
|
|
8362
8516
|
const newPage2 = manager2.create(this.pageSize, reusedPageId);
|
|
8363
8517
|
await this.setPage(reusedPageId, newPage2, tx);
|
|
8364
8518
|
return reusedPageId;
|
|
8365
8519
|
}
|
|
8520
|
+
const preallocationCount = this.options.pagePreallocationCount;
|
|
8366
8521
|
const pageCount = metadataManager.getPageCount(metadata);
|
|
8367
8522
|
const newPageIndex = pageCount;
|
|
8368
|
-
const newTotalCount = pageCount +
|
|
8523
|
+
const newTotalCount = pageCount + preallocationCount;
|
|
8369
8524
|
const manager = this.pageFactory.getManagerFromType(pageType);
|
|
8370
8525
|
const newPage = manager.create(this.pageSize, newPageIndex);
|
|
8371
8526
|
await this.setPage(newPageIndex, newPage, tx);
|
|
8527
|
+
const emptyManager = this.pageFactory.getManagerFromType(PageManager.CONSTANT.PAGE_TYPE_EMPTY);
|
|
8528
|
+
const firstFreeIndex = newPageIndex + 1;
|
|
8529
|
+
const lastFreeIndex = newPageIndex + preallocationCount - 1;
|
|
8372
8530
|
metadataManager.setPageCount(metadata, newTotalCount);
|
|
8531
|
+
if (preallocationCount > 1) {
|
|
8532
|
+
metadataManager.setFreePageId(metadata, firstFreeIndex);
|
|
8533
|
+
}
|
|
8373
8534
|
await this.setPage(0, metadata, tx);
|
|
8535
|
+
for (let i = firstFreeIndex; i <= lastFreeIndex; i++) {
|
|
8536
|
+
const emptyPage = emptyManager.create(this.pageSize, i);
|
|
8537
|
+
const nextId = i < lastFreeIndex ? i + 1 : -1;
|
|
8538
|
+
emptyManager.setNextPageId(emptyPage, nextId);
|
|
8539
|
+
await this.setPage(i, emptyPage, tx);
|
|
8540
|
+
}
|
|
8374
8541
|
return newPageIndex;
|
|
8375
8542
|
}
|
|
8376
8543
|
/**
|
|
@@ -8467,7 +8634,6 @@ var require_cjs = __commonJS({
|
|
|
8467
8634
|
const emptyPage = emptyPageManager.create(this.pageSize, pageId);
|
|
8468
8635
|
emptyPageManager.setNextPageId(emptyPage, currentHeadFreePageId);
|
|
8469
8636
|
await this.setPage(pageId, emptyPage, tx);
|
|
8470
|
-
await this.updateBitmap(pageId, true, tx);
|
|
8471
8637
|
metadataManager.setFreePageId(metadata, pageId);
|
|
8472
8638
|
await this.setPage(0, metadata, tx);
|
|
8473
8639
|
}
|
|
@@ -9168,7 +9334,9 @@ var require_cjs = __commonJS({
|
|
|
9168
9334
|
}
|
|
9169
9335
|
pageGroupMap.get(pageId).push({ pk: pair.pk, slotIndex, index: pair.index });
|
|
9170
9336
|
}
|
|
9171
|
-
|
|
9337
|
+
const sortedPageIds = Array.from(pageGroupMap.keys()).sort((a, b) => a - b);
|
|
9338
|
+
await Promise.all(sortedPageIds.map(async (pageId) => {
|
|
9339
|
+
const items = pageGroupMap.get(pageId);
|
|
9172
9340
|
const page = await this.pfs.get(pageId, tx);
|
|
9173
9341
|
if (!this.factory.isDataPage(page)) {
|
|
9174
9342
|
throw new Error(`Page ${pageId} is not a data page`);
|
|
@@ -9302,6 +9470,8 @@ var require_cjs = __commonJS({
|
|
|
9302
9470
|
commitHooks = [];
|
|
9303
9471
|
/** Page MVCC Strategy for disk access */
|
|
9304
9472
|
pageStrategy;
|
|
9473
|
+
/** Release function for global write lock, set by DataplyAPI */
|
|
9474
|
+
_writeLockRelease = null;
|
|
9305
9475
|
/**
|
|
9306
9476
|
* Sets the BPTree transaction.
|
|
9307
9477
|
* @param tx BPTree transaction
|
|
@@ -9336,6 +9506,19 @@ var require_cjs = __commonJS({
|
|
|
9336
9506
|
onCommit(hook) {
|
|
9337
9507
|
this.commitHooks.push(hook);
|
|
9338
9508
|
}
|
|
9509
|
+
/**
|
|
9510
|
+
* Sets the global write lock release function.
|
|
9511
|
+
* Called by DataplyAPI.runWithDefaultWrite when acquiring the lock.
|
|
9512
|
+
*/
|
|
9513
|
+
__setWriteLockRelease(release) {
|
|
9514
|
+
this._writeLockRelease = release;
|
|
9515
|
+
}
|
|
9516
|
+
/**
|
|
9517
|
+
* Returns whether this transaction already has a write lock.
|
|
9518
|
+
*/
|
|
9519
|
+
__hasWriteLockRelease() {
|
|
9520
|
+
return this._writeLockRelease !== null;
|
|
9521
|
+
}
|
|
9339
9522
|
/**
|
|
9340
9523
|
* Reads a page. Uses dirty buffer if available, otherwise disk.
|
|
9341
9524
|
* @param pageId Page ID
|
|
@@ -9381,44 +9564,60 @@ var require_cjs = __commonJS({
|
|
|
9381
9564
|
* Commits the transaction.
|
|
9382
9565
|
*/
|
|
9383
9566
|
async commit() {
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
this.pfs.wal
|
|
9400
|
-
|
|
9401
|
-
|
|
9567
|
+
try {
|
|
9568
|
+
await this.context.run(this, async () => {
|
|
9569
|
+
for (const hook of this.commitHooks) {
|
|
9570
|
+
await hook();
|
|
9571
|
+
}
|
|
9572
|
+
});
|
|
9573
|
+
let shouldTriggerCheckpoint = false;
|
|
9574
|
+
await this.pfs.runGlobalLock(async () => {
|
|
9575
|
+
if (this.pfs.wal && this.dirtyPages.size > 0) {
|
|
9576
|
+
await this.pfs.wal.prepareCommit(this.dirtyPages);
|
|
9577
|
+
await this.pfs.wal.writeCommitMarker();
|
|
9578
|
+
}
|
|
9579
|
+
for (const [pageId, data] of this.dirtyPages) {
|
|
9580
|
+
await this.pageStrategy.write(pageId, data);
|
|
9581
|
+
}
|
|
9582
|
+
if (!this.pfs.wal) {
|
|
9583
|
+
await this.pfs.strategy.flushPages(this.dirtyPages);
|
|
9584
|
+
} else {
|
|
9585
|
+
this.pfs.wal.incrementWrittenPages(this.dirtyPages.size);
|
|
9586
|
+
if (this.pfs.wal.shouldCheckpoint(this.pfs.options.walCheckpointThreshold)) {
|
|
9587
|
+
shouldTriggerCheckpoint = true;
|
|
9588
|
+
}
|
|
9402
9589
|
}
|
|
9590
|
+
});
|
|
9591
|
+
if (shouldTriggerCheckpoint) {
|
|
9592
|
+
await this.pfs.checkpoint();
|
|
9593
|
+
}
|
|
9594
|
+
this.dirtyPages.clear();
|
|
9595
|
+
this.undoPages.clear();
|
|
9596
|
+
this.releaseAllLocks();
|
|
9597
|
+
} finally {
|
|
9598
|
+
if (this._writeLockRelease) {
|
|
9599
|
+
this._writeLockRelease();
|
|
9600
|
+
this._writeLockRelease = null;
|
|
9403
9601
|
}
|
|
9404
|
-
});
|
|
9405
|
-
if (shouldTriggerCheckpoint) {
|
|
9406
|
-
await this.pfs.checkpoint();
|
|
9407
9602
|
}
|
|
9408
|
-
this.dirtyPages.clear();
|
|
9409
|
-
this.undoPages.clear();
|
|
9410
|
-
this.releaseAllLocks();
|
|
9411
9603
|
}
|
|
9412
9604
|
/**
|
|
9413
9605
|
* Rolls back the transaction.
|
|
9414
9606
|
*/
|
|
9415
9607
|
async rollback() {
|
|
9416
|
-
|
|
9417
|
-
this.bptreeTx
|
|
9608
|
+
try {
|
|
9609
|
+
if (this.bptreeTx) {
|
|
9610
|
+
this.bptreeTx.rollback();
|
|
9611
|
+
}
|
|
9612
|
+
this.dirtyPages.clear();
|
|
9613
|
+
this.undoPages.clear();
|
|
9614
|
+
this.releaseAllLocks();
|
|
9615
|
+
} finally {
|
|
9616
|
+
if (this._writeLockRelease) {
|
|
9617
|
+
this._writeLockRelease();
|
|
9618
|
+
this._writeLockRelease = null;
|
|
9619
|
+
}
|
|
9418
9620
|
}
|
|
9419
|
-
this.dirtyPages.clear();
|
|
9420
|
-
this.undoPages.clear();
|
|
9421
|
-
this.releaseAllLocks();
|
|
9422
9621
|
}
|
|
9423
9622
|
/**
|
|
9424
9623
|
* Returns the dirty pages map.
|
|
@@ -9495,6 +9694,8 @@ var require_cjs = __commonJS({
|
|
|
9495
9694
|
/** Whether the database was created this time. */
|
|
9496
9695
|
isNewlyCreated;
|
|
9497
9696
|
txIdCounter;
|
|
9697
|
+
/** Promise-chain mutex for serializing write operations */
|
|
9698
|
+
writeQueue = Promise.resolve();
|
|
9498
9699
|
/**
|
|
9499
9700
|
* Verifies if the page file is a valid Dataply file.
|
|
9500
9701
|
* The metadata page must be located at the beginning of the Dataply file.
|
|
@@ -9519,6 +9720,7 @@ var require_cjs = __commonJS({
|
|
|
9519
9720
|
return Object.assign({
|
|
9520
9721
|
pageSize: 8192,
|
|
9521
9722
|
pageCacheCapacity: 1e4,
|
|
9723
|
+
pagePreallocationCount: 1e3,
|
|
9522
9724
|
wal: null,
|
|
9523
9725
|
walCheckpointThreshold: 1e3
|
|
9524
9726
|
}, options);
|
|
@@ -9651,6 +9853,52 @@ var require_cjs = __commonJS({
|
|
|
9651
9853
|
* @param tx The transaction to use. If not provided, a new transaction is created.
|
|
9652
9854
|
* @returns The result of the callback function.
|
|
9653
9855
|
*/
|
|
9856
|
+
/**
|
|
9857
|
+
* Acquires the global write lock.
|
|
9858
|
+
* Returns a release function that MUST be called to unlock.
|
|
9859
|
+
* Used internally by runWithDefaultWrite.
|
|
9860
|
+
* @returns A release function
|
|
9861
|
+
*/
|
|
9862
|
+
acquireWriteLock() {
|
|
9863
|
+
const previous = this.writeQueue;
|
|
9864
|
+
let release;
|
|
9865
|
+
this.writeQueue = new Promise((resolve) => {
|
|
9866
|
+
release = resolve;
|
|
9867
|
+
});
|
|
9868
|
+
return previous.then(() => release);
|
|
9869
|
+
}
|
|
9870
|
+
/**
|
|
9871
|
+
* Runs a write callback within a transaction context with global write serialization.
|
|
9872
|
+
* If no transaction is provided, a new transaction is created, committed on success, rolled back on error.
|
|
9873
|
+
* If a transaction is provided (external), the write lock is acquired on first call and held until commit/rollback.
|
|
9874
|
+
* Subclasses MUST use this method for all write operations instead of runWithDefault.
|
|
9875
|
+
* @param callback The callback function to run.
|
|
9876
|
+
* @param tx Optional external transaction.
|
|
9877
|
+
* @returns The result of the callback.
|
|
9878
|
+
*/
|
|
9879
|
+
async runWithDefaultWrite(callback, tx) {
|
|
9880
|
+
if (!tx) {
|
|
9881
|
+
const release = await this.acquireWriteLock();
|
|
9882
|
+
const internalTx = this.createTransaction();
|
|
9883
|
+
internalTx.__setWriteLockRelease(release);
|
|
9884
|
+
const [error2, result2] = await catchPromise2(this.txContext.run(internalTx, () => callback(internalTx)));
|
|
9885
|
+
if (error2) {
|
|
9886
|
+
await internalTx.rollback();
|
|
9887
|
+
throw error2;
|
|
9888
|
+
}
|
|
9889
|
+
await internalTx.commit();
|
|
9890
|
+
return result2;
|
|
9891
|
+
}
|
|
9892
|
+
if (!tx.__hasWriteLockRelease()) {
|
|
9893
|
+
const release = await this.acquireWriteLock();
|
|
9894
|
+
tx.__setWriteLockRelease(release);
|
|
9895
|
+
}
|
|
9896
|
+
const [error, result] = await catchPromise2(this.txContext.run(tx, () => callback(tx)));
|
|
9897
|
+
if (error) {
|
|
9898
|
+
throw error;
|
|
9899
|
+
}
|
|
9900
|
+
return result;
|
|
9901
|
+
}
|
|
9654
9902
|
async runWithDefault(callback, tx) {
|
|
9655
9903
|
const isInternalTx = !tx;
|
|
9656
9904
|
if (!tx) {
|
|
@@ -9722,7 +9970,7 @@ var require_cjs = __commonJS({
|
|
|
9722
9970
|
if (!this.initialized) {
|
|
9723
9971
|
throw new Error("Dataply instance is not initialized");
|
|
9724
9972
|
}
|
|
9725
|
-
return this.
|
|
9973
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
9726
9974
|
incrementRowCount = incrementRowCount ?? true;
|
|
9727
9975
|
if (typeof data === "string") {
|
|
9728
9976
|
data = this.textCodec.encode(data);
|
|
@@ -9742,7 +9990,7 @@ var require_cjs = __commonJS({
|
|
|
9742
9990
|
if (!this.initialized) {
|
|
9743
9991
|
throw new Error("Dataply instance is not initialized");
|
|
9744
9992
|
}
|
|
9745
|
-
return this.
|
|
9993
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
9746
9994
|
incrementRowCount = incrementRowCount ?? true;
|
|
9747
9995
|
if (typeof data === "string") {
|
|
9748
9996
|
data = this.textCodec.encode(data);
|
|
@@ -9763,7 +10011,7 @@ var require_cjs = __commonJS({
|
|
|
9763
10011
|
if (!this.initialized) {
|
|
9764
10012
|
throw new Error("Dataply instance is not initialized");
|
|
9765
10013
|
}
|
|
9766
|
-
return this.
|
|
10014
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
9767
10015
|
incrementRowCount = incrementRowCount ?? true;
|
|
9768
10016
|
const encodedList = dataList.map(
|
|
9769
10017
|
(data) => typeof data === "string" ? this.textCodec.encode(data) : data
|
|
@@ -9781,7 +10029,7 @@ var require_cjs = __commonJS({
|
|
|
9781
10029
|
if (!this.initialized) {
|
|
9782
10030
|
throw new Error("Dataply instance is not initialized");
|
|
9783
10031
|
}
|
|
9784
|
-
return this.
|
|
10032
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
9785
10033
|
if (typeof data === "string") {
|
|
9786
10034
|
data = this.textCodec.encode(data);
|
|
9787
10035
|
}
|
|
@@ -9798,7 +10046,7 @@ var require_cjs = __commonJS({
|
|
|
9798
10046
|
if (!this.initialized) {
|
|
9799
10047
|
throw new Error("Dataply instance is not initialized");
|
|
9800
10048
|
}
|
|
9801
|
-
return this.
|
|
10049
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
9802
10050
|
decrementRowCount = decrementRowCount ?? true;
|
|
9803
10051
|
await this.rowTableEngine.delete(pk, decrementRowCount, tx2);
|
|
9804
10052
|
}, tx);
|
|
@@ -9990,6 +10238,11 @@ var DocumentSerializeStrategyAsync = class extends import_dataply.SerializeStrat
|
|
|
9990
10238
|
this.txContext = txContext;
|
|
9991
10239
|
this.treeKey = treeKey;
|
|
9992
10240
|
}
|
|
10241
|
+
/**
|
|
10242
|
+
* readHead에서 할당된 headPk를 캐싱하여
|
|
10243
|
+
* writeHead에서 AsyncLocalStorage 컨텍스트 유실 시에도 사용할 수 있도록 함
|
|
10244
|
+
*/
|
|
10245
|
+
cachedHeadPk = null;
|
|
9993
10246
|
async id(isLeaf) {
|
|
9994
10247
|
const tx = this.txContext.get();
|
|
9995
10248
|
const pk = await this.api.insertAsOverflow("__BPTREE_NODE_PLACEHOLDER__", false, tx);
|
|
@@ -10022,20 +10275,25 @@ var DocumentSerializeStrategyAsync = class extends import_dataply.SerializeStrat
|
|
|
10022
10275
|
const pk = await this.api.insertAsOverflow("__BPTREE_HEAD_PLACEHOLDER__", false, tx);
|
|
10023
10276
|
metadata.indices[this.treeKey][0] = pk;
|
|
10024
10277
|
await this.api.updateDocumentInnerMetadata(metadata, tx);
|
|
10278
|
+
this.cachedHeadPk = pk;
|
|
10025
10279
|
return null;
|
|
10026
10280
|
}
|
|
10281
|
+
this.cachedHeadPk = headPk;
|
|
10027
10282
|
const row = await this.api.select(headPk, false, tx);
|
|
10028
10283
|
if (row === null || row === "" || row.startsWith("__BPTREE_")) return null;
|
|
10029
10284
|
return JSON.parse(row);
|
|
10030
10285
|
}
|
|
10031
10286
|
async writeHead(head) {
|
|
10032
10287
|
const tx = this.txContext.get();
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10288
|
+
let headPk = this.cachedHeadPk;
|
|
10289
|
+
if (headPk === null) {
|
|
10290
|
+
const metadata = await this.api.getDocumentInnerMetadata(tx);
|
|
10291
|
+
const indexInfo = metadata.indices[this.treeKey];
|
|
10292
|
+
if (!indexInfo) {
|
|
10293
|
+
throw new Error(`Index info not found for tree: ${this.treeKey}. Initialization should be handled outside.`);
|
|
10294
|
+
}
|
|
10295
|
+
headPk = indexInfo[0];
|
|
10037
10296
|
}
|
|
10038
|
-
const headPk = indexInfo[0];
|
|
10039
10297
|
const json = JSON.stringify(head);
|
|
10040
10298
|
await this.api.update(headPk, json, tx);
|
|
10041
10299
|
}
|
|
@@ -10071,9 +10329,24 @@ function compareValue(a, b) {
|
|
|
10071
10329
|
}
|
|
10072
10330
|
return aList.length - bList.length;
|
|
10073
10331
|
}
|
|
10332
|
+
function comparePrimaryValue(a, b) {
|
|
10333
|
+
const aArr = Array.isArray(a);
|
|
10334
|
+
const bArr = Array.isArray(b);
|
|
10335
|
+
if (!aArr && !bArr) {
|
|
10336
|
+
return comparePrimitive(a, b);
|
|
10337
|
+
}
|
|
10338
|
+
const aList = aArr ? a : [a];
|
|
10339
|
+
const bList = bArr ? b : [b];
|
|
10340
|
+
const len = Math.min(aList.length, bList.length);
|
|
10341
|
+
for (let i = 0; i < len; i++) {
|
|
10342
|
+
const diff = comparePrimitive(aList[i], bList[i]);
|
|
10343
|
+
if (diff !== 0) return diff;
|
|
10344
|
+
}
|
|
10345
|
+
return 0;
|
|
10346
|
+
}
|
|
10074
10347
|
var DocumentValueComparator = class extends import_dataply2.ValueComparator {
|
|
10075
10348
|
primaryAsc(a, b) {
|
|
10076
|
-
return
|
|
10349
|
+
return comparePrimaryValue(a.v, b.v);
|
|
10077
10350
|
}
|
|
10078
10351
|
asc(a, b) {
|
|
10079
10352
|
const diff = compareValue(a.v, b.v);
|
|
@@ -10194,7 +10467,6 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10194
10467
|
trees = /* @__PURE__ */ new Map();
|
|
10195
10468
|
comparator = new DocumentValueComparator();
|
|
10196
10469
|
pendingBackfillFields = [];
|
|
10197
|
-
lock;
|
|
10198
10470
|
_initialized = false;
|
|
10199
10471
|
indexedFields;
|
|
10200
10472
|
/**
|
|
@@ -10225,7 +10497,6 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10225
10497
|
constructor(file, options) {
|
|
10226
10498
|
super(file, options);
|
|
10227
10499
|
this.trees = /* @__PURE__ */ new Map();
|
|
10228
|
-
this.lock = new import_dataply3.Ryoiki();
|
|
10229
10500
|
this.indexedFields = /* @__PURE__ */ new Set(["_id"]);
|
|
10230
10501
|
this.hook.onceAfter("init", async (tx, isNewlyCreated) => {
|
|
10231
10502
|
if (isNewlyCreated) {
|
|
@@ -10235,8 +10506,12 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10235
10506
|
throw new Error("Document metadata verification failed");
|
|
10236
10507
|
}
|
|
10237
10508
|
const metadata = await this.getDocumentInnerMetadata(tx);
|
|
10238
|
-
const targetIndices = /* @__PURE__ */ new Map(
|
|
10239
|
-
|
|
10509
|
+
const targetIndices = /* @__PURE__ */ new Map([
|
|
10510
|
+
["_id", { type: "btree", fields: ["_id"] }]
|
|
10511
|
+
]);
|
|
10512
|
+
for (const [name, info] of Object.entries(metadata.indices)) {
|
|
10513
|
+
targetIndices.set(name, info[1]);
|
|
10514
|
+
}
|
|
10240
10515
|
for (const [name, option] of this.pendingCreateIndices) {
|
|
10241
10516
|
const config = this.toIndexMetaConfig(option);
|
|
10242
10517
|
targetIndices.set(name, config);
|
|
@@ -10326,7 +10601,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10326
10601
|
const existing = this.registeredIndices.get(name);
|
|
10327
10602
|
if (JSON.stringify(existing) === JSON.stringify(config)) return;
|
|
10328
10603
|
}
|
|
10329
|
-
await this.
|
|
10604
|
+
await this.runWithDefaultWrite(async (tx2) => {
|
|
10330
10605
|
const metadata = await this.getDocumentInnerMetadata(tx2);
|
|
10331
10606
|
metadata.indices[name] = [-1, config];
|
|
10332
10607
|
await this.updateDocumentInnerMetadata(metadata, tx2);
|
|
@@ -10374,7 +10649,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10374
10649
|
if (!this.registeredIndices.has(name)) {
|
|
10375
10650
|
throw new Error(`Index '${name}' does not exist`);
|
|
10376
10651
|
}
|
|
10377
|
-
await this.
|
|
10652
|
+
await this.runWithDefaultWrite(async (tx2) => {
|
|
10378
10653
|
const config = this.registeredIndices.get(name);
|
|
10379
10654
|
const metadata = await this.getDocumentInnerMetadata(tx2);
|
|
10380
10655
|
delete metadata.indices[name];
|
|
@@ -10403,19 +10678,39 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10403
10678
|
* Convert CreateIndexOption to IndexMetaConfig for metadata storage.
|
|
10404
10679
|
*/
|
|
10405
10680
|
toIndexMetaConfig(option) {
|
|
10681
|
+
if (!option || typeof option !== "object") {
|
|
10682
|
+
throw new Error("Index option must be a non-null object");
|
|
10683
|
+
}
|
|
10684
|
+
if (!option.type) {
|
|
10685
|
+
throw new Error('Index option must have a "type" property ("btree" or "fts")');
|
|
10686
|
+
}
|
|
10406
10687
|
if (option.type === "btree") {
|
|
10688
|
+
if (!Array.isArray(option.fields) || option.fields.length === 0) {
|
|
10689
|
+
throw new Error('btree index requires a non-empty "fields" array');
|
|
10690
|
+
}
|
|
10691
|
+
for (let i = 0; i < option.fields.length; i++) {
|
|
10692
|
+
if (typeof option.fields[i] !== "string" || option.fields[i].length === 0) {
|
|
10693
|
+
throw new Error(`btree index "fields[${i}]" must be a non-empty string, got: ${JSON.stringify(option.fields[i])}`);
|
|
10694
|
+
}
|
|
10695
|
+
}
|
|
10407
10696
|
return {
|
|
10408
10697
|
type: "btree",
|
|
10409
10698
|
fields: option.fields
|
|
10410
10699
|
};
|
|
10411
10700
|
}
|
|
10412
10701
|
if (option.type === "fts") {
|
|
10702
|
+
if (typeof option.fields !== "string" || option.fields.length === 0) {
|
|
10703
|
+
throw new Error(`fts index requires a non-empty string "fields", got: ${JSON.stringify(option.fields)}`);
|
|
10704
|
+
}
|
|
10413
10705
|
if (option.tokenizer === "ngram") {
|
|
10706
|
+
if (typeof option.gramSize !== "number" || option.gramSize < 1) {
|
|
10707
|
+
throw new Error(`fts ngram index requires a positive "gramSize" number, got: ${JSON.stringify(option.gramSize)}`);
|
|
10708
|
+
}
|
|
10414
10709
|
return {
|
|
10415
10710
|
type: "fts",
|
|
10416
10711
|
fields: option.fields,
|
|
10417
10712
|
tokenizer: "ngram",
|
|
10418
|
-
gramSize: option.
|
|
10713
|
+
gramSize: option.gramSize
|
|
10419
10714
|
};
|
|
10420
10715
|
}
|
|
10421
10716
|
return {
|
|
@@ -10489,24 +10784,6 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10489
10784
|
return JSON.parse(row);
|
|
10490
10785
|
}, tx);
|
|
10491
10786
|
}
|
|
10492
|
-
async readLock(fn) {
|
|
10493
|
-
let lockId;
|
|
10494
|
-
return this.lock.readLock(async (_lockId) => {
|
|
10495
|
-
lockId = _lockId;
|
|
10496
|
-
return await fn();
|
|
10497
|
-
}).finally(() => {
|
|
10498
|
-
this.lock.readUnlock(lockId);
|
|
10499
|
-
});
|
|
10500
|
-
}
|
|
10501
|
-
async writeLock(fn) {
|
|
10502
|
-
let lockId;
|
|
10503
|
-
return this.lock.writeLock(async (_lockId) => {
|
|
10504
|
-
lockId = _lockId;
|
|
10505
|
-
return await fn();
|
|
10506
|
-
}).finally(() => {
|
|
10507
|
-
this.lock.writeUnlock(lockId);
|
|
10508
|
-
});
|
|
10509
|
-
}
|
|
10510
10787
|
/**
|
|
10511
10788
|
* Backfill indices for newly created indices after data was inserted.
|
|
10512
10789
|
* This method should be called after `init()`.
|
|
@@ -10514,7 +10791,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10514
10791
|
* @returns Number of documents that were backfilled
|
|
10515
10792
|
*/
|
|
10516
10793
|
async backfillIndices(tx) {
|
|
10517
|
-
return this.
|
|
10794
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
10518
10795
|
if (this.pendingBackfillFields.length === 0) {
|
|
10519
10796
|
return 0;
|
|
10520
10797
|
}
|
|
@@ -10523,8 +10800,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10523
10800
|
if (metadata.lastId === 0) {
|
|
10524
10801
|
return 0;
|
|
10525
10802
|
}
|
|
10526
|
-
|
|
10527
|
-
const indexEntryMap = /* @__PURE__ */ new Map();
|
|
10803
|
+
let indexTxMap = {};
|
|
10528
10804
|
for (const indexName of backfillTargets) {
|
|
10529
10805
|
const tree = this.trees.get(indexName);
|
|
10530
10806
|
if (tree && indexName !== "_id") {
|
|
@@ -10532,6 +10808,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10532
10808
|
}
|
|
10533
10809
|
}
|
|
10534
10810
|
let backfilledCount = 0;
|
|
10811
|
+
let chunkCount = 0;
|
|
10812
|
+
const CHUNK_SIZE = 1e3;
|
|
10535
10813
|
const idTree = this.trees.get("_id");
|
|
10536
10814
|
if (!idTree) {
|
|
10537
10815
|
throw new Error("ID tree not found");
|
|
@@ -10560,10 +10838,6 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10560
10838
|
const keyToInsert = this.getTokenKey(k, token);
|
|
10561
10839
|
const entry = { k, v: token };
|
|
10562
10840
|
batchInsertData.push([keyToInsert, entry]);
|
|
10563
|
-
if (!indexEntryMap.has(btx)) {
|
|
10564
|
-
indexEntryMap.set(btx, []);
|
|
10565
|
-
}
|
|
10566
|
-
indexEntryMap.get(btx).push({ k: keyToInsert, v: entry });
|
|
10567
10841
|
}
|
|
10568
10842
|
await btx.batchInsert(batchInsertData);
|
|
10569
10843
|
} else {
|
|
@@ -10571,34 +10845,42 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10571
10845
|
if (indexVal === void 0) continue;
|
|
10572
10846
|
const entry = { k, v: indexVal };
|
|
10573
10847
|
const batchInsertData = [[k, entry]];
|
|
10574
|
-
if (!indexEntryMap.has(btx)) {
|
|
10575
|
-
indexEntryMap.set(btx, []);
|
|
10576
|
-
}
|
|
10577
|
-
indexEntryMap.get(btx).push(entry);
|
|
10578
10848
|
await btx.batchInsert(batchInsertData);
|
|
10579
10849
|
}
|
|
10580
10850
|
}
|
|
10581
10851
|
backfilledCount++;
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10852
|
+
chunkCount++;
|
|
10853
|
+
if (chunkCount >= CHUNK_SIZE) {
|
|
10854
|
+
try {
|
|
10855
|
+
for (const btx of Object.values(indexTxMap)) {
|
|
10856
|
+
await btx.commit();
|
|
10857
|
+
}
|
|
10858
|
+
} catch (err) {
|
|
10859
|
+
for (const btx of Object.values(indexTxMap)) {
|
|
10860
|
+
await btx.rollback();
|
|
10861
|
+
}
|
|
10862
|
+
throw err;
|
|
10863
|
+
}
|
|
10864
|
+
for (const indexName of backfillTargets) {
|
|
10865
|
+
const tree = this.trees.get(indexName);
|
|
10866
|
+
if (tree && indexName !== "_id") {
|
|
10867
|
+
indexTxMap[indexName] = await tree.createTransaction();
|
|
10868
|
+
}
|
|
10869
|
+
}
|
|
10870
|
+
chunkCount = 0;
|
|
10593
10871
|
}
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
for (const
|
|
10598
|
-
await btx.
|
|
10872
|
+
}
|
|
10873
|
+
if (chunkCount > 0) {
|
|
10874
|
+
try {
|
|
10875
|
+
for (const btx of Object.values(indexTxMap)) {
|
|
10876
|
+
await btx.commit();
|
|
10877
|
+
}
|
|
10878
|
+
} catch (err) {
|
|
10879
|
+
for (const btx of Object.values(indexTxMap)) {
|
|
10880
|
+
await btx.rollback();
|
|
10599
10881
|
}
|
|
10882
|
+
throw err;
|
|
10600
10883
|
}
|
|
10601
|
-
throw err;
|
|
10602
10884
|
}
|
|
10603
10885
|
this.pendingBackfillFields = [];
|
|
10604
10886
|
return backfilledCount;
|
|
@@ -10687,14 +10969,15 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10687
10969
|
* @param tx Optional transaction
|
|
10688
10970
|
*/
|
|
10689
10971
|
async migration(version, callback, tx) {
|
|
10690
|
-
await this.
|
|
10972
|
+
await this.runWithDefaultWrite(async (tx2) => {
|
|
10691
10973
|
const innerMetadata = await this.getDocumentInnerMetadata(tx2);
|
|
10692
10974
|
const currentVersion = innerMetadata.schemeVersion ?? 0;
|
|
10693
10975
|
if (currentVersion < version) {
|
|
10694
10976
|
await callback(tx2);
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10977
|
+
const freshMetadata = await this.getDocumentInnerMetadata(tx2);
|
|
10978
|
+
freshMetadata.schemeVersion = version;
|
|
10979
|
+
freshMetadata.updatedAt = Date.now();
|
|
10980
|
+
await this.updateDocumentInnerMetadata(freshMetadata, tx2);
|
|
10698
10981
|
}
|
|
10699
10982
|
}, tx);
|
|
10700
10983
|
}
|
|
@@ -10755,27 +11038,61 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10755
11038
|
const condition = query[primaryField];
|
|
10756
11039
|
const treeTx = await tree.createTransaction();
|
|
10757
11040
|
let score = 0;
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
}
|
|
10766
|
-
|
|
10767
|
-
|
|
10768
|
-
|
|
10769
|
-
}
|
|
10770
|
-
|
|
10771
|
-
|
|
10772
|
-
|
|
11041
|
+
let isConsecutive = true;
|
|
11042
|
+
const coveredFields = [];
|
|
11043
|
+
const compositeVerifyFields = [];
|
|
11044
|
+
for (const field of config.fields) {
|
|
11045
|
+
if (!queryFields.has(field)) {
|
|
11046
|
+
isConsecutive = false;
|
|
11047
|
+
continue;
|
|
11048
|
+
}
|
|
11049
|
+
coveredFields.push(field);
|
|
11050
|
+
if (field !== primaryField) {
|
|
11051
|
+
compositeVerifyFields.push(field);
|
|
11052
|
+
}
|
|
11053
|
+
score += 1;
|
|
11054
|
+
if (isConsecutive) {
|
|
11055
|
+
const cond = query[field];
|
|
11056
|
+
if (cond !== void 0) {
|
|
11057
|
+
if (typeof cond !== "object" || cond === null) {
|
|
11058
|
+
score += 100;
|
|
11059
|
+
} else if ("primaryEqual" in cond || "equal" in cond) {
|
|
11060
|
+
score += 100;
|
|
11061
|
+
} else if ("primaryGte" in cond || "primaryLte" in cond || "primaryGt" in cond || "primaryLt" in cond || "gte" in cond || "lte" in cond || "gt" in cond || "lt" in cond) {
|
|
11062
|
+
score += 50;
|
|
11063
|
+
isConsecutive = false;
|
|
11064
|
+
} else if ("primaryOr" in cond || "or" in cond) {
|
|
11065
|
+
score += 20;
|
|
11066
|
+
isConsecutive = false;
|
|
11067
|
+
} else if ("like" in cond) {
|
|
11068
|
+
score += 15;
|
|
11069
|
+
isConsecutive = false;
|
|
11070
|
+
} else {
|
|
11071
|
+
score += 10;
|
|
11072
|
+
isConsecutive = false;
|
|
11073
|
+
}
|
|
11074
|
+
}
|
|
10773
11075
|
}
|
|
10774
11076
|
}
|
|
10775
|
-
|
|
10776
|
-
|
|
11077
|
+
let isIndexOrderSupported = false;
|
|
11078
|
+
if (orderByField) {
|
|
11079
|
+
for (const field of config.fields) {
|
|
11080
|
+
if (field === orderByField) {
|
|
11081
|
+
isIndexOrderSupported = true;
|
|
11082
|
+
break;
|
|
11083
|
+
}
|
|
11084
|
+
const cond = query[field];
|
|
11085
|
+
let isExactMatch = false;
|
|
11086
|
+
if (cond !== void 0) {
|
|
11087
|
+
if (typeof cond !== "object" || cond === null) isExactMatch = true;
|
|
11088
|
+
else if ("primaryEqual" in cond || "equal" in cond) isExactMatch = true;
|
|
11089
|
+
}
|
|
11090
|
+
if (!isExactMatch) break;
|
|
11091
|
+
}
|
|
11092
|
+
if (isIndexOrderSupported) {
|
|
11093
|
+
score += 200;
|
|
11094
|
+
}
|
|
10777
11095
|
}
|
|
10778
|
-
const compositeVerifyFields = coveredFields.filter((f) => f !== primaryField);
|
|
10779
11096
|
candidates.push({
|
|
10780
11097
|
tree: treeTx,
|
|
10781
11098
|
condition,
|
|
@@ -10783,7 +11100,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10783
11100
|
indexName,
|
|
10784
11101
|
isFtsMatch: false,
|
|
10785
11102
|
score,
|
|
10786
|
-
compositeVerifyFields
|
|
11103
|
+
compositeVerifyFields,
|
|
11104
|
+
isIndexOrderSupported
|
|
10787
11105
|
});
|
|
10788
11106
|
} else if (config.type === "fts") {
|
|
10789
11107
|
const field = config.fields;
|
|
@@ -10801,7 +11119,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10801
11119
|
isFtsMatch: true,
|
|
10802
11120
|
matchTokens,
|
|
10803
11121
|
score: 90,
|
|
10804
|
-
compositeVerifyFields: []
|
|
11122
|
+
compositeVerifyFields: [],
|
|
11123
|
+
isIndexOrderSupported: false
|
|
10805
11124
|
});
|
|
10806
11125
|
}
|
|
10807
11126
|
}
|
|
@@ -10844,33 +11163,30 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10844
11163
|
getTokenKey(pk, token) {
|
|
10845
11164
|
return pk + ":" + token;
|
|
10846
11165
|
}
|
|
10847
|
-
async
|
|
11166
|
+
async *applyCandidateByFTSStream(candidate, matchedTokens, filterValues, order) {
|
|
10848
11167
|
const keys = /* @__PURE__ */ new Set();
|
|
10849
11168
|
for (let i = 0, len = matchedTokens.length; i < len; i++) {
|
|
10850
11169
|
const token = matchedTokens[i];
|
|
10851
|
-
const
|
|
11170
|
+
for await (const pair of candidate.tree.whereStream(
|
|
10852
11171
|
{ primaryEqual: { v: token } },
|
|
10853
|
-
{
|
|
10854
|
-
|
|
11172
|
+
{ order }
|
|
11173
|
+
)) {
|
|
11174
|
+
const pk = pair[1].k;
|
|
11175
|
+
if (filterValues && !filterValues.has(pk)) continue;
|
|
11176
|
+
if (!keys.has(pk)) {
|
|
11177
|
+
keys.add(pk);
|
|
11178
|
+
yield pk;
|
|
10855
11179
|
}
|
|
10856
|
-
);
|
|
10857
|
-
for (const pair of pairs.values()) {
|
|
10858
|
-
if (filterValues && !filterValues.has(pair.k)) continue;
|
|
10859
|
-
keys.add(pair.k);
|
|
10860
11180
|
}
|
|
10861
11181
|
}
|
|
10862
|
-
return keys;
|
|
10863
11182
|
}
|
|
10864
11183
|
/**
|
|
10865
11184
|
* 특정 인덱스 후보를 조회하여 PK 집합을 필터링합니다.
|
|
10866
11185
|
*/
|
|
10867
|
-
|
|
10868
|
-
return
|
|
11186
|
+
applyCandidateStream(candidate, filterValues, order) {
|
|
11187
|
+
return candidate.tree.keysStream(
|
|
10869
11188
|
candidate.condition,
|
|
10870
|
-
{
|
|
10871
|
-
filterValues,
|
|
10872
|
-
order
|
|
10873
|
-
}
|
|
11189
|
+
{ filterValues, order }
|
|
10874
11190
|
);
|
|
10875
11191
|
}
|
|
10876
11192
|
/**
|
|
@@ -10886,30 +11202,34 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10886
11202
|
);
|
|
10887
11203
|
if (!selectivity) return new Float64Array(0);
|
|
10888
11204
|
const { driver, others, rollback } = selectivity;
|
|
10889
|
-
const useIndexOrder = orderBy === void 0 || driver.
|
|
11205
|
+
const useIndexOrder = orderBy === void 0 || driver.isIndexOrderSupported;
|
|
10890
11206
|
const candidates = [driver, ...others];
|
|
10891
11207
|
let keys = void 0;
|
|
10892
11208
|
for (let i = 0, len = candidates.length; i < len; i++) {
|
|
10893
11209
|
const candidate = candidates[i];
|
|
10894
11210
|
const currentOrder = useIndexOrder ? sortOrder : void 0;
|
|
10895
11211
|
if (candidate.isFtsMatch && candidate.matchTokens && candidate.matchTokens.length > 0) {
|
|
10896
|
-
|
|
11212
|
+
const stream = this.applyCandidateByFTSStream(
|
|
10897
11213
|
candidate,
|
|
10898
11214
|
candidate.matchTokens,
|
|
10899
11215
|
keys,
|
|
10900
11216
|
currentOrder
|
|
10901
11217
|
);
|
|
11218
|
+
keys = /* @__PURE__ */ new Set();
|
|
11219
|
+
for await (const pk of stream) keys.add(pk);
|
|
10902
11220
|
} else {
|
|
10903
|
-
|
|
11221
|
+
const stream = this.applyCandidateStream(candidate, keys, currentOrder);
|
|
11222
|
+
keys = /* @__PURE__ */ new Set();
|
|
11223
|
+
for await (const pk of stream) keys.add(pk);
|
|
10904
11224
|
}
|
|
10905
11225
|
}
|
|
10906
11226
|
rollback();
|
|
10907
11227
|
return new Float64Array(Array.from(keys || []));
|
|
10908
11228
|
}
|
|
10909
11229
|
/**
|
|
10910
|
-
* 드라이버 인덱스만으로 PK
|
|
11230
|
+
* 드라이버 인덱스만으로 PK 스트림을 가져옵니다. (교집합 없이)
|
|
10911
11231
|
* selectDocuments에서 사용하며, 나머지 조건(others)은 스트리밍 중 tree.verify()로 검증합니다.
|
|
10912
|
-
* @returns 드라이버 키
|
|
11232
|
+
* @returns 드라이버 키 스트림, others 후보 목록, rollback 함수. 또는 null.
|
|
10913
11233
|
*/
|
|
10914
11234
|
async getDriverKeys(query, orderBy, sortOrder = "asc") {
|
|
10915
11235
|
const isQueryEmpty = Object.keys(query).length === 0;
|
|
@@ -10920,21 +11240,21 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10920
11240
|
);
|
|
10921
11241
|
if (!selectivity) return null;
|
|
10922
11242
|
const { driver, others, compositeVerifyConditions, rollback } = selectivity;
|
|
10923
|
-
const useIndexOrder = orderBy === void 0 || driver.
|
|
11243
|
+
const useIndexOrder = orderBy === void 0 || driver.isIndexOrderSupported;
|
|
10924
11244
|
const currentOrder = useIndexOrder ? sortOrder : void 0;
|
|
10925
|
-
let
|
|
11245
|
+
let keysStream;
|
|
10926
11246
|
if (driver.isFtsMatch && driver.matchTokens && driver.matchTokens.length > 0) {
|
|
10927
|
-
|
|
11247
|
+
keysStream = this.applyCandidateByFTSStream(
|
|
10928
11248
|
driver,
|
|
10929
11249
|
driver.matchTokens,
|
|
10930
11250
|
void 0,
|
|
10931
11251
|
currentOrder
|
|
10932
11252
|
);
|
|
10933
11253
|
} else {
|
|
10934
|
-
|
|
11254
|
+
keysStream = this.applyCandidateStream(driver, void 0, currentOrder);
|
|
10935
11255
|
}
|
|
10936
11256
|
return {
|
|
10937
|
-
|
|
11257
|
+
keysStream,
|
|
10938
11258
|
others,
|
|
10939
11259
|
compositeVerifyConditions,
|
|
10940
11260
|
isDriverOrderByField: useIndexOrder,
|
|
@@ -10962,7 +11282,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10962
11282
|
* @returns The primary key of the inserted document
|
|
10963
11283
|
*/
|
|
10964
11284
|
async insertSingleDocument(document, tx) {
|
|
10965
|
-
return this.
|
|
11285
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
10966
11286
|
const { pk: dpk, document: dataplyDocument } = await this.insertDocumentInternal(document, tx2);
|
|
10967
11287
|
const flattenDocument = this.flattenDocument(dataplyDocument);
|
|
10968
11288
|
for (const [indexName, config] of this.registeredIndices) {
|
|
@@ -10988,7 +11308,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10988
11308
|
}
|
|
10989
11309
|
}
|
|
10990
11310
|
return dataplyDocument._id;
|
|
10991
|
-
}, tx)
|
|
11311
|
+
}, tx);
|
|
10992
11312
|
}
|
|
10993
11313
|
/**
|
|
10994
11314
|
* Insert a batch of documents into the database
|
|
@@ -10997,7 +11317,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
10997
11317
|
* @returns The primary keys of the inserted documents
|
|
10998
11318
|
*/
|
|
10999
11319
|
async insertBatchDocuments(documents, tx) {
|
|
11000
|
-
return this.
|
|
11320
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
11001
11321
|
const metadata = await this.getDocumentInnerMetadata(tx2);
|
|
11002
11322
|
const startId = metadata.lastId + 1;
|
|
11003
11323
|
metadata.lastId += documents.length;
|
|
@@ -11056,7 +11376,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11056
11376
|
}
|
|
11057
11377
|
}
|
|
11058
11378
|
return ids;
|
|
11059
|
-
}, tx)
|
|
11379
|
+
}, tx);
|
|
11060
11380
|
}
|
|
11061
11381
|
/**
|
|
11062
11382
|
* Internal update method used by both fullUpdate and partialUpdate
|
|
@@ -11137,12 +11457,12 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11137
11457
|
* @returns The number of updated documents
|
|
11138
11458
|
*/
|
|
11139
11459
|
async fullUpdate(query, newRecord, tx) {
|
|
11140
|
-
return
|
|
11460
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
11141
11461
|
return this.updateInternal(query, (doc) => {
|
|
11142
11462
|
const newDoc = typeof newRecord === "function" ? newRecord(doc) : newRecord;
|
|
11143
11463
|
return { _id: doc._id, ...newDoc };
|
|
11144
11464
|
}, tx2);
|
|
11145
|
-
}, tx)
|
|
11465
|
+
}, tx);
|
|
11146
11466
|
}
|
|
11147
11467
|
/**
|
|
11148
11468
|
* Partially update documents from the database that match the query
|
|
@@ -11152,14 +11472,14 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11152
11472
|
* @returns The number of updated documents
|
|
11153
11473
|
*/
|
|
11154
11474
|
async partialUpdate(query, newRecord, tx) {
|
|
11155
|
-
return this.
|
|
11475
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
11156
11476
|
return this.updateInternal(query, (doc) => {
|
|
11157
11477
|
const partialUpdateContent = typeof newRecord === "function" ? newRecord(doc) : newRecord;
|
|
11158
11478
|
const finalUpdate = { ...partialUpdateContent };
|
|
11159
11479
|
delete finalUpdate._id;
|
|
11160
11480
|
return { ...doc, ...finalUpdate };
|
|
11161
11481
|
}, tx2);
|
|
11162
|
-
}, tx)
|
|
11482
|
+
}, tx);
|
|
11163
11483
|
}
|
|
11164
11484
|
/**
|
|
11165
11485
|
* Delete documents from the database that match the query
|
|
@@ -11168,7 +11488,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11168
11488
|
* @returns The number of deleted documents
|
|
11169
11489
|
*/
|
|
11170
11490
|
async deleteDocuments(query, tx) {
|
|
11171
|
-
return this.
|
|
11491
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
11172
11492
|
const pks = await this.getKeys(query);
|
|
11173
11493
|
let deletedCount = 0;
|
|
11174
11494
|
for (let i = 0, len = pks.length; i < len; i++) {
|
|
@@ -11198,7 +11518,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11198
11518
|
deletedCount++;
|
|
11199
11519
|
}
|
|
11200
11520
|
return deletedCount;
|
|
11201
|
-
}, tx)
|
|
11521
|
+
}, tx);
|
|
11202
11522
|
}
|
|
11203
11523
|
/**
|
|
11204
11524
|
* Count documents from the database that match the query
|
|
@@ -11207,10 +11527,10 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11207
11527
|
* @returns The number of documents that match the query
|
|
11208
11528
|
*/
|
|
11209
11529
|
async countDocuments(query, tx) {
|
|
11210
|
-
return this.
|
|
11530
|
+
return this.runWithDefault(async (tx2) => {
|
|
11211
11531
|
const pks = await this.getKeys(query);
|
|
11212
11532
|
return pks.length;
|
|
11213
|
-
}, tx)
|
|
11533
|
+
}, tx);
|
|
11214
11534
|
}
|
|
11215
11535
|
/**
|
|
11216
11536
|
* FTS 조건에 대해 문서가 유효한지 검증합니다.
|
|
@@ -11284,28 +11604,17 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11284
11604
|
return currentChunkSize;
|
|
11285
11605
|
}
|
|
11286
11606
|
/**
|
|
11287
|
-
* Prefetch 방식으로 키
|
|
11607
|
+
* Prefetch 방식으로 키 스트림을 청크 단위로 조회하여 문서를 순회합니다.
|
|
11288
11608
|
* FTS 검증, 복합 인덱스 검증, others 후보에 대한 tree.verify() 검증을 통과한 문서만 yield 합니다.
|
|
11289
11609
|
*/
|
|
11290
|
-
async *processChunkedKeysWithVerify(
|
|
11610
|
+
async *processChunkedKeysWithVerify(keysStream, startIdx, initialChunkSize, ftsConditions, compositeVerifyConditions, others, tx) {
|
|
11291
11611
|
const verifyOthers = others.filter((o) => !o.isFtsMatch);
|
|
11292
|
-
let i = startIdx;
|
|
11293
|
-
const totalKeys = keys.length;
|
|
11294
11612
|
let currentChunkSize = initialChunkSize;
|
|
11295
|
-
let
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
}
|
|
11301
|
-
while (nextChunkPromise) {
|
|
11302
|
-
const rawResults = await nextChunkPromise;
|
|
11303
|
-
nextChunkPromise = null;
|
|
11304
|
-
if (i < totalKeys) {
|
|
11305
|
-
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
11306
|
-
nextChunkPromise = this.selectMany(keys.subarray(i, endIdx), false, tx);
|
|
11307
|
-
i = endIdx;
|
|
11308
|
-
}
|
|
11613
|
+
let chunk = [];
|
|
11614
|
+
let dropped = 0;
|
|
11615
|
+
const processChunk = async (pks) => {
|
|
11616
|
+
const docs = [];
|
|
11617
|
+
const rawResults = await this.selectMany(new Float64Array(pks), false, tx);
|
|
11309
11618
|
let chunkTotalSize = 0;
|
|
11310
11619
|
for (let j = 0, len = rawResults.length; j < len; j++) {
|
|
11311
11620
|
const s = rawResults[j];
|
|
@@ -11332,9 +11641,26 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11332
11641
|
}
|
|
11333
11642
|
if (!passed) continue;
|
|
11334
11643
|
}
|
|
11335
|
-
|
|
11644
|
+
docs.push(doc);
|
|
11336
11645
|
}
|
|
11337
11646
|
currentChunkSize = this.adjustChunkSize(currentChunkSize, chunkTotalSize);
|
|
11647
|
+
return docs;
|
|
11648
|
+
};
|
|
11649
|
+
for await (const pk of keysStream) {
|
|
11650
|
+
if (dropped < startIdx) {
|
|
11651
|
+
dropped++;
|
|
11652
|
+
continue;
|
|
11653
|
+
}
|
|
11654
|
+
chunk.push(pk);
|
|
11655
|
+
if (chunk.length >= currentChunkSize) {
|
|
11656
|
+
const docs = await processChunk(chunk);
|
|
11657
|
+
for (let j = 0; j < docs.length; j++) yield docs[j];
|
|
11658
|
+
chunk = [];
|
|
11659
|
+
}
|
|
11660
|
+
}
|
|
11661
|
+
if (chunk.length > 0) {
|
|
11662
|
+
const docs = await processChunk(chunk);
|
|
11663
|
+
for (let j = 0; j < docs.length; j++) yield docs[j];
|
|
11338
11664
|
}
|
|
11339
11665
|
}
|
|
11340
11666
|
/**
|
|
@@ -11382,11 +11708,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11382
11708
|
}
|
|
11383
11709
|
const driverResult = await self.getDriverKeys(query, orderByField, sortOrder);
|
|
11384
11710
|
if (!driverResult) return;
|
|
11385
|
-
const {
|
|
11386
|
-
if (keys.length === 0) {
|
|
11387
|
-
rollback();
|
|
11388
|
-
return;
|
|
11389
|
-
}
|
|
11711
|
+
const { keysStream, others, compositeVerifyConditions, isDriverOrderByField, rollback } = driverResult;
|
|
11390
11712
|
try {
|
|
11391
11713
|
if (!isDriverOrderByField && orderByField) {
|
|
11392
11714
|
const topK = limit === Infinity ? Infinity : offset + limit;
|
|
@@ -11401,7 +11723,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11401
11723
|
}
|
|
11402
11724
|
const results = [];
|
|
11403
11725
|
for await (const doc of self.processChunkedKeysWithVerify(
|
|
11404
|
-
|
|
11726
|
+
keysStream,
|
|
11405
11727
|
0,
|
|
11406
11728
|
self.options.pageSize,
|
|
11407
11729
|
ftsConditions,
|
|
@@ -11437,16 +11759,23 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
11437
11759
|
yield limitedResults[j];
|
|
11438
11760
|
}
|
|
11439
11761
|
} else {
|
|
11762
|
+
const hasFilters = ftsConditions.length > 0 || compositeVerifyConditions.length > 0 || others.length > 0;
|
|
11763
|
+
const startIdx = hasFilters ? 0 : offset;
|
|
11440
11764
|
let yieldedCount = 0;
|
|
11765
|
+
let skippedCount = hasFilters ? 0 : offset;
|
|
11441
11766
|
for await (const doc of self.processChunkedKeysWithVerify(
|
|
11442
|
-
|
|
11443
|
-
|
|
11767
|
+
keysStream,
|
|
11768
|
+
startIdx,
|
|
11444
11769
|
self.options.pageSize,
|
|
11445
11770
|
ftsConditions,
|
|
11446
11771
|
compositeVerifyConditions,
|
|
11447
11772
|
others,
|
|
11448
11773
|
tx2
|
|
11449
11774
|
)) {
|
|
11775
|
+
if (skippedCount < offset) {
|
|
11776
|
+
skippedCount++;
|
|
11777
|
+
continue;
|
|
11778
|
+
}
|
|
11450
11779
|
if (yieldedCount >= limit) break;
|
|
11451
11780
|
yield doc;
|
|
11452
11781
|
yieldedCount++;
|