document-dataply 0.0.5-alpha.0 → 0.0.5
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
|
@@ -5716,31 +5716,31 @@ var require_cjs = __commonJS({
|
|
|
5716
5716
|
}
|
|
5717
5717
|
return buffer;
|
|
5718
5718
|
}
|
|
5719
|
+
var tempBuffer = new ArrayBuffer(8);
|
|
5720
|
+
var tempView = new DataView(tempBuffer);
|
|
5721
|
+
var tempArray = new Uint8Array(tempBuffer);
|
|
5719
5722
|
function bytesToNumber(bytes, offset = 0, length = bytes.length) {
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5723
|
+
tempArray.set(bytes.subarray(offset, offset + length));
|
|
5724
|
+
switch (length) {
|
|
5725
|
+
case 1:
|
|
5726
|
+
return tempView.getUint8(0);
|
|
5727
|
+
case 2:
|
|
5728
|
+
return tempView.getUint16(0, true);
|
|
5729
|
+
case 3:
|
|
5730
|
+
return tempView.getUint16(0, true) + (tempView.getUint8(2) << 16);
|
|
5731
|
+
case 4:
|
|
5732
|
+
return tempView.getUint32(0, true);
|
|
5733
|
+
case 5:
|
|
5734
|
+
return tempView.getUint32(0, true) + tempView.getUint8(4) * 4294967296;
|
|
5735
|
+
case 6:
|
|
5736
|
+
return tempView.getUint32(0, true) + tempView.getUint16(4, true) * 4294967296;
|
|
5737
|
+
case 7:
|
|
5738
|
+
return tempView.getUint32(0, true) + (tempView.getUint16(4, true) + (tempView.getUint8(6) << 16)) * 4294967296;
|
|
5739
|
+
case 8:
|
|
5740
|
+
return tempView.getUint32(0, true) + tempView.getUint32(4, true) * 4294967296;
|
|
5741
|
+
default:
|
|
5742
|
+
return 0;
|
|
5727
5743
|
}
|
|
5728
|
-
let low = 0;
|
|
5729
|
-
const lenLow = length < 4 ? length : 4;
|
|
5730
|
-
for (let i = 0; i < lenLow; i++) {
|
|
5731
|
-
low |= bytes[offset + i] << i * 8;
|
|
5732
|
-
}
|
|
5733
|
-
low >>>= 0;
|
|
5734
|
-
if (length > 4) {
|
|
5735
|
-
let high = 0;
|
|
5736
|
-
const lenHigh = length < 8 ? length : 8;
|
|
5737
|
-
for (let i = 4; i < lenHigh; i++) {
|
|
5738
|
-
high |= bytes[offset + i] << (i - 4) * 8;
|
|
5739
|
-
}
|
|
5740
|
-
high >>>= 0;
|
|
5741
|
-
return low + high * 4294967296;
|
|
5742
|
-
}
|
|
5743
|
-
return low;
|
|
5744
5744
|
}
|
|
5745
5745
|
function setBit(value, bitPos, flag) {
|
|
5746
5746
|
if (flag) {
|
|
@@ -8294,7 +8294,7 @@ var require_cjs = __commonJS({
|
|
|
8294
8294
|
this.maxBodySize = this.pfs.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER;
|
|
8295
8295
|
this.order = this.getOptimalOrder(pfs.pageSize, IndexPageManager.CONSTANT.SIZE_KEY, IndexPageManager.CONSTANT.SIZE_VALUE);
|
|
8296
8296
|
this.strategy = new RowIdentifierStrategy(this.order, pfs, txContext);
|
|
8297
|
-
const budget = import_node_os.default.
|
|
8297
|
+
const budget = import_node_os.default.totalmem() * 0.1;
|
|
8298
8298
|
const nodeMemory = this.order * 24 + 256;
|
|
8299
8299
|
const capacity = Math.max(1e3, Math.min(1e6, Math.floor(budget / nodeMemory)));
|
|
8300
8300
|
this.bptree = new BPTreeAsync2(
|
|
@@ -8679,41 +8679,44 @@ var require_cjs = __commonJS({
|
|
|
8679
8679
|
if (pks.length === 0) {
|
|
8680
8680
|
return [];
|
|
8681
8681
|
}
|
|
8682
|
+
const pkIndexMap = /* @__PURE__ */ new Map();
|
|
8683
|
+
for (let i = 0, len = pks.length; i < len; i++) {
|
|
8684
|
+
pkIndexMap.set(pks[i], i);
|
|
8685
|
+
}
|
|
8682
8686
|
const minPk = Math.min(...pks);
|
|
8683
8687
|
const maxPk = Math.max(...pks);
|
|
8684
|
-
const
|
|
8685
|
-
const pkRidPairs = [];
|
|
8688
|
+
const pkRidPairs = new Array(pks.length).fill(null);
|
|
8686
8689
|
const btx = await this.getBPTreeTransaction(tx);
|
|
8687
8690
|
const stream = btx.whereStream({ gte: minPk, lte: maxPk });
|
|
8688
8691
|
for await (const [rid, pk] of stream) {
|
|
8689
|
-
|
|
8690
|
-
|
|
8692
|
+
const index = pkIndexMap.get(pk);
|
|
8693
|
+
if (index !== void 0) {
|
|
8694
|
+
pkRidPairs[index] = { pk, rid, index };
|
|
8691
8695
|
}
|
|
8692
8696
|
}
|
|
8693
|
-
|
|
8694
|
-
return pks.map((pk) => resultMap.get(pk) ?? null);
|
|
8697
|
+
return this.fetchRowsByRids(pkRidPairs, tx);
|
|
8695
8698
|
}
|
|
8696
8699
|
/**
|
|
8697
8700
|
* Fetches multiple rows by their RID and PK combinations, grouping by page ID to minimize I/O.
|
|
8698
8701
|
* @param pkRidPairs Array of {pk, rid} pairs
|
|
8699
8702
|
* @param tx Transaction
|
|
8700
|
-
* @returns
|
|
8703
|
+
* @returns Array of row data in the same order as input PKs
|
|
8701
8704
|
*/
|
|
8702
8705
|
async fetchRowsByRids(pkRidPairs, tx) {
|
|
8703
|
-
const
|
|
8704
|
-
if (pkRidPairs.length === 0) return
|
|
8706
|
+
const result = new Array(pkRidPairs.length).fill(null);
|
|
8707
|
+
if (pkRidPairs.length === 0) return result;
|
|
8705
8708
|
const pageGroupMap = /* @__PURE__ */ new Map();
|
|
8706
8709
|
for (const pair of pkRidPairs) {
|
|
8710
|
+
if (pair === null) continue;
|
|
8707
8711
|
const rid = pair.rid;
|
|
8708
8712
|
const slotIndex = rid % 65536;
|
|
8709
8713
|
const pageId = Math.floor(rid / 65536);
|
|
8710
8714
|
if (!pageGroupMap.has(pageId)) {
|
|
8711
8715
|
pageGroupMap.set(pageId, []);
|
|
8712
8716
|
}
|
|
8713
|
-
pageGroupMap.get(pageId).push({ pk: pair.pk, slotIndex });
|
|
8717
|
+
pageGroupMap.get(pageId).push({ pk: pair.pk, slotIndex, index: pair.index });
|
|
8714
8718
|
}
|
|
8715
|
-
|
|
8716
|
-
await Promise.all(sortedPageEntries.map(async ([pageId, items]) => {
|
|
8719
|
+
await Promise.all(Array.from(pageGroupMap).map(async ([pageId, items]) => {
|
|
8717
8720
|
const page = await this.pfs.get(pageId, tx);
|
|
8718
8721
|
if (!this.factory.isDataPage(page)) {
|
|
8719
8722
|
throw new Error(`Page ${pageId} is not a data page`);
|
|
@@ -8722,17 +8725,17 @@ var require_cjs = __commonJS({
|
|
|
8722
8725
|
for (const item of items) {
|
|
8723
8726
|
const row = manager.getRow(page, item.slotIndex);
|
|
8724
8727
|
if (this.rowManager.getDeletedFlag(row)) {
|
|
8725
|
-
|
|
8728
|
+
result[item.index] = null;
|
|
8726
8729
|
} else if (this.rowManager.getOverflowFlag(row)) {
|
|
8727
8730
|
const overflowPageId = bytesToNumber(this.rowManager.getBody(row));
|
|
8728
8731
|
const body = await this.pfs.getBody(overflowPageId, true, tx);
|
|
8729
|
-
|
|
8732
|
+
result[item.index] = body;
|
|
8730
8733
|
} else {
|
|
8731
|
-
|
|
8734
|
+
result[item.index] = this.rowManager.getBody(row);
|
|
8732
8735
|
}
|
|
8733
8736
|
}
|
|
8734
8737
|
}));
|
|
8735
|
-
return
|
|
8738
|
+
return result;
|
|
8736
8739
|
}
|
|
8737
8740
|
async fetchRowByRid(pk, rid, tx) {
|
|
8738
8741
|
this.keyManager.setBufferFromKey(rid, this.ridBuffer);
|
|
@@ -9522,7 +9525,7 @@ __export(src_exports, {
|
|
|
9522
9525
|
});
|
|
9523
9526
|
module.exports = __toCommonJS(src_exports);
|
|
9524
9527
|
|
|
9525
|
-
// src/core/
|
|
9528
|
+
// src/core/documentAPI.ts
|
|
9526
9529
|
var os = __toESM(require("node:os"));
|
|
9527
9530
|
var import_dataply3 = __toESM(require_cjs());
|
|
9528
9531
|
|
|
@@ -9678,17 +9681,34 @@ var BinaryHeap = class {
|
|
|
9678
9681
|
}
|
|
9679
9682
|
};
|
|
9680
9683
|
|
|
9681
|
-
// src/core/
|
|
9684
|
+
// src/core/documentAPI.ts
|
|
9682
9685
|
var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
9683
9686
|
indices = {};
|
|
9684
9687
|
trees = /* @__PURE__ */ new Map();
|
|
9685
9688
|
comparator = new DocumentValueComparator();
|
|
9686
9689
|
pendingBackfillFields = [];
|
|
9687
9690
|
lock;
|
|
9691
|
+
indexedFields;
|
|
9692
|
+
operatorConverters = {
|
|
9693
|
+
equal: "primaryEqual",
|
|
9694
|
+
notEqual: "primaryNotEqual",
|
|
9695
|
+
lt: "primaryLt",
|
|
9696
|
+
lte: "primaryLte",
|
|
9697
|
+
gt: "primaryGt",
|
|
9698
|
+
gte: "primaryGte",
|
|
9699
|
+
or: "primaryOr",
|
|
9700
|
+
like: "like"
|
|
9701
|
+
};
|
|
9688
9702
|
constructor(file, options) {
|
|
9689
9703
|
super(file, options);
|
|
9690
9704
|
this.trees = /* @__PURE__ */ new Map();
|
|
9691
9705
|
this.lock = new import_dataply3.Ryoiki();
|
|
9706
|
+
this.indexedFields = /* @__PURE__ */ new Set(["_id"]);
|
|
9707
|
+
if (options?.indices) {
|
|
9708
|
+
for (const field of Object.keys(options.indices)) {
|
|
9709
|
+
this.indexedFields.add(field);
|
|
9710
|
+
}
|
|
9711
|
+
}
|
|
9692
9712
|
this.hook.onceAfter("init", async (tx, isNewlyCreated) => {
|
|
9693
9713
|
if (isNewlyCreated) {
|
|
9694
9714
|
await this.initializeDocumentFile(tx);
|
|
@@ -9898,81 +9918,6 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
9898
9918
|
async updateDocumentInnerMetadata(metadata, tx) {
|
|
9899
9919
|
await this.update(1, JSON.stringify(metadata), tx);
|
|
9900
9920
|
}
|
|
9901
|
-
};
|
|
9902
|
-
var DocumentDataply = class _DocumentDataply {
|
|
9903
|
-
/**
|
|
9904
|
-
* Starts the database definition by setting the document type.
|
|
9905
|
-
* This is used to ensure TypeScript type inference works correctly for the document structure.
|
|
9906
|
-
* @template T The structure of the document to be stored.
|
|
9907
|
-
*/
|
|
9908
|
-
static Define() {
|
|
9909
|
-
return {
|
|
9910
|
-
/**
|
|
9911
|
-
* Sets the options for the database, such as index configurations and WAL settings.
|
|
9912
|
-
* @template IC The configuration of indices.
|
|
9913
|
-
* @param options The database initialization options.
|
|
9914
|
-
*/
|
|
9915
|
-
Options: (options) => _DocumentDataply.Options(options)
|
|
9916
|
-
};
|
|
9917
|
-
}
|
|
9918
|
-
/**
|
|
9919
|
-
* Internal method used by the Define-chain to pass options.
|
|
9920
|
-
*/
|
|
9921
|
-
static Options(options) {
|
|
9922
|
-
return {
|
|
9923
|
-
/**
|
|
9924
|
-
* Creates or opens the database instance with the specified file path.
|
|
9925
|
-
* @param file The path to the database file.
|
|
9926
|
-
*/
|
|
9927
|
-
Open: (file) => _DocumentDataply.Open(file, options)
|
|
9928
|
-
};
|
|
9929
|
-
}
|
|
9930
|
-
/**
|
|
9931
|
-
* Internal method used to finalize construction and create the instance.
|
|
9932
|
-
*/
|
|
9933
|
-
static Open(file, options) {
|
|
9934
|
-
return new _DocumentDataply(file, options);
|
|
9935
|
-
}
|
|
9936
|
-
api;
|
|
9937
|
-
indexedFields;
|
|
9938
|
-
operatorConverters = {
|
|
9939
|
-
equal: "primaryEqual",
|
|
9940
|
-
notEqual: "primaryNotEqual",
|
|
9941
|
-
lt: "primaryLt",
|
|
9942
|
-
lte: "primaryLte",
|
|
9943
|
-
gt: "primaryGt",
|
|
9944
|
-
gte: "primaryGte",
|
|
9945
|
-
or: "primaryOr",
|
|
9946
|
-
like: "like"
|
|
9947
|
-
};
|
|
9948
|
-
constructor(file, options) {
|
|
9949
|
-
this.api = new DocumentDataplyAPI(file, options ?? {});
|
|
9950
|
-
this.indexedFields = /* @__PURE__ */ new Set(["_id"]);
|
|
9951
|
-
if (options?.indices) {
|
|
9952
|
-
for (const field of Object.keys(options.indices)) {
|
|
9953
|
-
this.indexedFields.add(field);
|
|
9954
|
-
}
|
|
9955
|
-
}
|
|
9956
|
-
}
|
|
9957
|
-
/**
|
|
9958
|
-
* Initialize the document database
|
|
9959
|
-
*/
|
|
9960
|
-
async init() {
|
|
9961
|
-
await this.api.init();
|
|
9962
|
-
await this.api.backfillIndices();
|
|
9963
|
-
}
|
|
9964
|
-
/**
|
|
9965
|
-
* Get the metadata of the document database
|
|
9966
|
-
*/
|
|
9967
|
-
async getMetadata(tx) {
|
|
9968
|
-
return this.api.runWithDefault((tx2) => this.api.getDocumentMetadata(tx2), tx);
|
|
9969
|
-
}
|
|
9970
|
-
/**
|
|
9971
|
-
* Create a transaction
|
|
9972
|
-
*/
|
|
9973
|
-
createTransaction() {
|
|
9974
|
-
return this.api.createTransaction();
|
|
9975
|
-
}
|
|
9976
9921
|
verboseQuery(query) {
|
|
9977
9922
|
const result = {};
|
|
9978
9923
|
for (const field in query) {
|
|
@@ -10009,7 +9954,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10009
9954
|
async getSelectivityCandidate(query, orderByField) {
|
|
10010
9955
|
const candidates = [];
|
|
10011
9956
|
for (const field in query) {
|
|
10012
|
-
const tree = this.
|
|
9957
|
+
const tree = this.trees.get(field);
|
|
10013
9958
|
if (!tree) continue;
|
|
10014
9959
|
const condition = query[field];
|
|
10015
9960
|
const treeTx = await tree.createTransaction();
|
|
@@ -10045,6 +9990,17 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10045
9990
|
rollback
|
|
10046
9991
|
};
|
|
10047
9992
|
}
|
|
9993
|
+
/**
|
|
9994
|
+
* Get Free Memory Chunk Size
|
|
9995
|
+
* @returns { verySmallChunkSize, smallChunkSize }
|
|
9996
|
+
*/
|
|
9997
|
+
getFreeMemoryChunkSize() {
|
|
9998
|
+
const freeMem = os.freemem();
|
|
9999
|
+
const safeLimit = freeMem * 0.2;
|
|
10000
|
+
const verySmallChunkSize = safeLimit * 0.05;
|
|
10001
|
+
const smallChunkSize = safeLimit * 0.3;
|
|
10002
|
+
return { verySmallChunkSize, smallChunkSize };
|
|
10003
|
+
}
|
|
10048
10004
|
/**
|
|
10049
10005
|
* Get Primary Keys based on query and index selection.
|
|
10050
10006
|
* Internal common method to unify query optimization.
|
|
@@ -10057,33 +10013,33 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10057
10013
|
verbose,
|
|
10058
10014
|
orderBy
|
|
10059
10015
|
);
|
|
10060
|
-
if (!selectivity) return
|
|
10016
|
+
if (!selectivity) return new Float64Array(0);
|
|
10061
10017
|
const { driver, others, rollback } = selectivity;
|
|
10062
10018
|
const isDriverOrderByField = orderBy === void 0 || driver.field === orderBy;
|
|
10063
10019
|
if (isDriverOrderByField) {
|
|
10064
|
-
let
|
|
10020
|
+
let keysSet = await driver.tree.keys(driver.condition, void 0, sortOrder);
|
|
10065
10021
|
for (const { tree, condition } of others) {
|
|
10066
|
-
|
|
10022
|
+
keysSet = await tree.keys(condition, keysSet, sortOrder);
|
|
10067
10023
|
}
|
|
10068
10024
|
rollback();
|
|
10069
|
-
return
|
|
10025
|
+
return new Float64Array(keysSet);
|
|
10070
10026
|
} else {
|
|
10071
|
-
let
|
|
10027
|
+
let keysSet = await driver.tree.keys(driver.condition, void 0);
|
|
10072
10028
|
for (const { tree, condition } of others) {
|
|
10073
|
-
|
|
10029
|
+
keysSet = await tree.keys(condition, keysSet);
|
|
10074
10030
|
}
|
|
10075
10031
|
rollback();
|
|
10076
|
-
return
|
|
10032
|
+
return new Float64Array(keysSet);
|
|
10077
10033
|
}
|
|
10078
10034
|
}
|
|
10079
|
-
async
|
|
10080
|
-
const metadata = await this.
|
|
10035
|
+
async insertDocumentInternal(document, tx) {
|
|
10036
|
+
const metadata = await this.getDocumentInnerMetadata(tx);
|
|
10081
10037
|
const id = ++metadata.lastId;
|
|
10082
|
-
await this.
|
|
10038
|
+
await this.updateDocumentInnerMetadata(metadata, tx);
|
|
10083
10039
|
const dataplyDocument = Object.assign({
|
|
10084
10040
|
_id: id
|
|
10085
10041
|
}, document);
|
|
10086
|
-
const pk = await
|
|
10042
|
+
const pk = await super.insert(JSON.stringify(dataplyDocument), true, tx);
|
|
10087
10043
|
return {
|
|
10088
10044
|
pk,
|
|
10089
10045
|
id,
|
|
@@ -10096,12 +10052,12 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10096
10052
|
* @param tx The transaction to use
|
|
10097
10053
|
* @returns The primary key of the inserted document
|
|
10098
10054
|
*/
|
|
10099
|
-
async
|
|
10100
|
-
return this.
|
|
10101
|
-
const { pk, document: dataplyDocument } = await this.
|
|
10102
|
-
const flattenDocument = this.
|
|
10055
|
+
async insertSingleDocument(document, tx) {
|
|
10056
|
+
return this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10057
|
+
const { pk, document: dataplyDocument } = await this.insertDocumentInternal(document, tx2);
|
|
10058
|
+
const flattenDocument = this.flattenDocument(dataplyDocument);
|
|
10103
10059
|
for (const field in flattenDocument) {
|
|
10104
|
-
const tree = this.
|
|
10060
|
+
const tree = this.trees.get(field);
|
|
10105
10061
|
if (!tree) continue;
|
|
10106
10062
|
const v = flattenDocument[field];
|
|
10107
10063
|
const [error] = await catchPromise(tree.insert(pk, { k: pk, v }));
|
|
@@ -10118,12 +10074,12 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10118
10074
|
* @param tx The transaction to use
|
|
10119
10075
|
* @returns The primary keys of the inserted documents
|
|
10120
10076
|
*/
|
|
10121
|
-
async
|
|
10122
|
-
return this.
|
|
10123
|
-
const metadata = await this.
|
|
10077
|
+
async insertBatchDocuments(documents, tx) {
|
|
10078
|
+
return this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10079
|
+
const metadata = await this.getDocumentInnerMetadata(tx2);
|
|
10124
10080
|
const startId = metadata.lastId + 1;
|
|
10125
10081
|
metadata.lastId += documents.length;
|
|
10126
|
-
await this.
|
|
10082
|
+
await this.updateDocumentInnerMetadata(metadata, tx2);
|
|
10127
10083
|
const ids = [];
|
|
10128
10084
|
const dataplyDocuments = [];
|
|
10129
10085
|
const flattenedData = [];
|
|
@@ -10134,15 +10090,15 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10134
10090
|
}, documents[i]);
|
|
10135
10091
|
const stringified = JSON.stringify(dataplyDocument);
|
|
10136
10092
|
dataplyDocuments.push(stringified);
|
|
10137
|
-
const flattenDocument = this.
|
|
10093
|
+
const flattenDocument = this.flattenDocument(dataplyDocument);
|
|
10138
10094
|
flattenedData.push({ pk: -1, data: flattenDocument });
|
|
10139
10095
|
ids.push(id);
|
|
10140
10096
|
}
|
|
10141
|
-
const pks = await
|
|
10097
|
+
const pks = await super.insertBatch(dataplyDocuments, true, tx2);
|
|
10142
10098
|
for (let i = 0, len = pks.length; i < len; i++) {
|
|
10143
10099
|
flattenedData[i].pk = pks[i];
|
|
10144
10100
|
}
|
|
10145
|
-
for (const [field, tree] of this.
|
|
10101
|
+
for (const [field, tree] of this.trees) {
|
|
10146
10102
|
const treeTx = await tree.createTransaction();
|
|
10147
10103
|
for (let i = 0, len = flattenedData.length; i < len; i++) {
|
|
10148
10104
|
const item = flattenedData[i];
|
|
@@ -10172,16 +10128,17 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10172
10128
|
const pks = await this.getKeys(query);
|
|
10173
10129
|
let updatedCount = 0;
|
|
10174
10130
|
const treeTxs = /* @__PURE__ */ new Map();
|
|
10175
|
-
for (const [field, tree] of this.
|
|
10131
|
+
for (const [field, tree] of this.trees) {
|
|
10176
10132
|
treeTxs.set(field, await tree.createTransaction());
|
|
10177
10133
|
}
|
|
10178
10134
|
treeTxs.delete("_id");
|
|
10179
|
-
for (
|
|
10180
|
-
const
|
|
10135
|
+
for (let i = 0, len = pks.length; i < len; i++) {
|
|
10136
|
+
const pk = pks[i];
|
|
10137
|
+
const doc = await this.getDocument(pk, tx);
|
|
10181
10138
|
if (!doc) continue;
|
|
10182
10139
|
const updatedDoc = computeUpdatedDoc(doc);
|
|
10183
|
-
const oldFlatDoc = this.
|
|
10184
|
-
const newFlatDoc = this.
|
|
10140
|
+
const oldFlatDoc = this.flattenDocument(doc);
|
|
10141
|
+
const newFlatDoc = this.flattenDocument(updatedDoc);
|
|
10185
10142
|
for (const [field, treeTx] of treeTxs) {
|
|
10186
10143
|
const oldV = oldFlatDoc[field];
|
|
10187
10144
|
const newV = newFlatDoc[field];
|
|
@@ -10193,7 +10150,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10193
10150
|
await treeTx.insert(pk, { k: pk, v: newV });
|
|
10194
10151
|
}
|
|
10195
10152
|
}
|
|
10196
|
-
await this.
|
|
10153
|
+
await this.update(pk, JSON.stringify(updatedDoc), tx);
|
|
10197
10154
|
updatedCount++;
|
|
10198
10155
|
}
|
|
10199
10156
|
for (const [field, treeTx] of treeTxs) {
|
|
@@ -10215,7 +10172,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10215
10172
|
* @returns The number of updated documents
|
|
10216
10173
|
*/
|
|
10217
10174
|
async fullUpdate(query, newRecord, tx) {
|
|
10218
|
-
return await this.
|
|
10175
|
+
return await this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10219
10176
|
return this.updateInternal(query, (doc) => {
|
|
10220
10177
|
const newDoc = typeof newRecord === "function" ? newRecord(doc) : newRecord;
|
|
10221
10178
|
return { _id: doc._id, ...newDoc };
|
|
@@ -10230,11 +10187,12 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10230
10187
|
* @returns The number of updated documents
|
|
10231
10188
|
*/
|
|
10232
10189
|
async partialUpdate(query, newRecord, tx) {
|
|
10233
|
-
return this.
|
|
10190
|
+
return this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10234
10191
|
return this.updateInternal(query, (doc) => {
|
|
10235
|
-
const
|
|
10236
|
-
|
|
10237
|
-
|
|
10192
|
+
const partialUpdateContent = typeof newRecord === "function" ? newRecord(doc) : newRecord;
|
|
10193
|
+
const finalUpdate = { ...partialUpdateContent };
|
|
10194
|
+
delete finalUpdate._id;
|
|
10195
|
+
return { ...doc, ...finalUpdate };
|
|
10238
10196
|
}, tx2);
|
|
10239
10197
|
}, tx));
|
|
10240
10198
|
}
|
|
@@ -10244,20 +10202,21 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10244
10202
|
* @param tx The transaction to use
|
|
10245
10203
|
* @returns The number of deleted documents
|
|
10246
10204
|
*/
|
|
10247
|
-
async
|
|
10248
|
-
return this.
|
|
10205
|
+
async deleteDocuments(query, tx) {
|
|
10206
|
+
return this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10249
10207
|
const pks = await this.getKeys(query);
|
|
10250
10208
|
let deletedCount = 0;
|
|
10251
|
-
for (
|
|
10252
|
-
const
|
|
10209
|
+
for (let i = 0, len = pks.length; i < len; i++) {
|
|
10210
|
+
const pk = pks[i];
|
|
10211
|
+
const doc = await this.getDocument(pk, tx2);
|
|
10253
10212
|
if (!doc) continue;
|
|
10254
|
-
const flatDoc = this.
|
|
10255
|
-
for (const [field, tree] of this.
|
|
10213
|
+
const flatDoc = this.flattenDocument(doc);
|
|
10214
|
+
for (const [field, tree] of this.trees) {
|
|
10256
10215
|
const v = flatDoc[field];
|
|
10257
10216
|
if (v === void 0) continue;
|
|
10258
10217
|
await tree.delete(pk, { k: pk, v });
|
|
10259
10218
|
}
|
|
10260
|
-
await
|
|
10219
|
+
await super.delete(pk, true, tx2);
|
|
10261
10220
|
deletedCount++;
|
|
10262
10221
|
}
|
|
10263
10222
|
return deletedCount;
|
|
@@ -10269,10 +10228,10 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10269
10228
|
* @param tx The transaction to use
|
|
10270
10229
|
* @returns The number of documents that match the query
|
|
10271
10230
|
*/
|
|
10272
|
-
async
|
|
10273
|
-
return this.
|
|
10231
|
+
async countDocuments(query, tx) {
|
|
10232
|
+
return this.readLock(() => this.runWithDefault(async (tx2) => {
|
|
10274
10233
|
const pks = await this.getKeys(query);
|
|
10275
|
-
return pks.
|
|
10234
|
+
return pks.length;
|
|
10276
10235
|
}, tx));
|
|
10277
10236
|
}
|
|
10278
10237
|
/**
|
|
@@ -10283,7 +10242,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10283
10242
|
* @returns The documents that match the query
|
|
10284
10243
|
* @throws Error if query or orderBy contains non-indexed fields
|
|
10285
10244
|
*/
|
|
10286
|
-
|
|
10245
|
+
selectDocuments(query, options = {}, tx) {
|
|
10287
10246
|
for (const field of Object.keys(query)) {
|
|
10288
10247
|
if (!this.indexedFields.has(field)) {
|
|
10289
10248
|
throw new Error(`Query field "${field}" is not indexed. Available indexed fields: ${Array.from(this.indexedFields).join(", ")}`);
|
|
@@ -10300,9 +10259,8 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10300
10259
|
orderBy: orderByField
|
|
10301
10260
|
} = options;
|
|
10302
10261
|
const self = this;
|
|
10303
|
-
const stream = this.
|
|
10304
|
-
const
|
|
10305
|
-
const keys = new Uint32Array(keySet);
|
|
10262
|
+
const stream = this.streamWithDefault(async function* (tx2) {
|
|
10263
|
+
const keys = await self.getKeys(query, orderByField, sortOrder);
|
|
10306
10264
|
const totalKeys = keys.length;
|
|
10307
10265
|
if (totalKeys === 0) return;
|
|
10308
10266
|
const selectivity = await self.getSelectivityCandidate(
|
|
@@ -10313,72 +10271,73 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10313
10271
|
if (selectivity) {
|
|
10314
10272
|
selectivity.rollback();
|
|
10315
10273
|
}
|
|
10316
|
-
let
|
|
10274
|
+
let currentChunkSize = self.options.pageSize;
|
|
10317
10275
|
if (!isDriverOrderByField && orderByField) {
|
|
10318
|
-
const
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
const
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10276
|
+
const topK = limit === Infinity ? Infinity : offset + limit;
|
|
10277
|
+
let heap = null;
|
|
10278
|
+
if (topK !== Infinity) {
|
|
10279
|
+
const heapComparator = (a, b) => {
|
|
10280
|
+
const aVal = a[orderByField] ?? a._id;
|
|
10281
|
+
const bVal = b[orderByField] ?? b._id;
|
|
10282
|
+
const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
10283
|
+
return sortOrder === "asc" ? -cmp : cmp;
|
|
10284
|
+
};
|
|
10285
|
+
heap = new BinaryHeap(heapComparator);
|
|
10286
|
+
}
|
|
10327
10287
|
const results = [];
|
|
10328
10288
|
let i = 0;
|
|
10329
|
-
|
|
10330
|
-
const firstResults = await self.api.selectMany(firstChunk, false, tx2);
|
|
10331
|
-
let totalBytes = 0;
|
|
10332
|
-
let count = 0;
|
|
10333
|
-
for (const s of firstResults) {
|
|
10334
|
-
if (!s) continue;
|
|
10335
|
-
totalBytes += s.length;
|
|
10336
|
-
count++;
|
|
10337
|
-
const doc = JSON.parse(s);
|
|
10338
|
-
if (heap) {
|
|
10339
|
-
if (heap.size < heapSizeLimit) {
|
|
10340
|
-
heap.push(doc);
|
|
10341
|
-
} else if (heapComparator(doc, heap.peek()) > 0) {
|
|
10342
|
-
heap.replace(doc);
|
|
10343
|
-
}
|
|
10344
|
-
} else {
|
|
10345
|
-
results.push(doc);
|
|
10346
|
-
}
|
|
10347
|
-
}
|
|
10348
|
-
const avgSize = count > 0 ? totalBytes / count : 1024;
|
|
10349
|
-
CHUNK_SIZE = Math.max(32, Math.floor(os.freemem() * 0.1 / avgSize));
|
|
10350
|
-
i += firstChunk.length;
|
|
10351
|
-
let nextSortChunkPromise = null;
|
|
10289
|
+
let nextChunkPromise = null;
|
|
10352
10290
|
if (i < totalKeys) {
|
|
10353
|
-
const
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10291
|
+
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
10292
|
+
const chunkKeys = keys.subarray(i, endIdx);
|
|
10293
|
+
nextChunkPromise = self.selectMany(chunkKeys, false, tx2);
|
|
10294
|
+
i = endIdx;
|
|
10295
|
+
}
|
|
10296
|
+
while (nextChunkPromise) {
|
|
10297
|
+
const rawResults = await nextChunkPromise;
|
|
10298
|
+
nextChunkPromise = null;
|
|
10299
|
+
const { verySmallChunkSize, smallChunkSize } = self.getFreeMemoryChunkSize();
|
|
10359
10300
|
if (i < totalKeys) {
|
|
10360
|
-
const
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
for (
|
|
10367
|
-
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
|
|
10372
|
-
|
|
10373
|
-
|
|
10301
|
+
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
10302
|
+
const chunkKeys = keys.subarray(i, endIdx);
|
|
10303
|
+
nextChunkPromise = self.selectMany(chunkKeys, false, tx2);
|
|
10304
|
+
i = endIdx;
|
|
10305
|
+
}
|
|
10306
|
+
let chunkTotalSize = 0;
|
|
10307
|
+
for (let j = 0, len = rawResults.length; j < len; j++) {
|
|
10308
|
+
const s = rawResults[j];
|
|
10309
|
+
if (s) {
|
|
10310
|
+
const doc = JSON.parse(s);
|
|
10311
|
+
chunkTotalSize += s.length * 2;
|
|
10312
|
+
if (heap) {
|
|
10313
|
+
if (heap.size < topK) heap.push(doc);
|
|
10314
|
+
else {
|
|
10315
|
+
const top = heap.peek();
|
|
10316
|
+
if (top) {
|
|
10317
|
+
const aVal = doc[orderByField] ?? doc._id;
|
|
10318
|
+
const bVal = top[orderByField] ?? top._id;
|
|
10319
|
+
const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
10320
|
+
const isBetter = sortOrder === "asc" ? cmp < 0 : cmp > 0;
|
|
10321
|
+
if (isBetter) {
|
|
10322
|
+
heap.replace(doc);
|
|
10323
|
+
}
|
|
10324
|
+
}
|
|
10325
|
+
}
|
|
10326
|
+
} else {
|
|
10327
|
+
results.push(doc);
|
|
10374
10328
|
}
|
|
10375
|
-
}
|
|
10376
|
-
|
|
10329
|
+
}
|
|
10330
|
+
}
|
|
10331
|
+
if (chunkTotalSize > 0) {
|
|
10332
|
+
if (chunkTotalSize < verySmallChunkSize) {
|
|
10333
|
+
currentChunkSize = currentChunkSize * 2;
|
|
10334
|
+
} else if (chunkTotalSize > smallChunkSize) {
|
|
10335
|
+
currentChunkSize = Math.max(Math.floor(currentChunkSize / 2), 20);
|
|
10377
10336
|
}
|
|
10378
10337
|
}
|
|
10379
10338
|
}
|
|
10380
|
-
const
|
|
10381
|
-
|
|
10339
|
+
const finalDocs = heap ? heap.toArray() : results;
|
|
10340
|
+
finalDocs.sort((a, b) => {
|
|
10382
10341
|
const aVal = a[orderByField] ?? a._id;
|
|
10383
10342
|
const bVal = b[orderByField] ?? b._id;
|
|
10384
10343
|
const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
@@ -10386,58 +10345,46 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10386
10345
|
});
|
|
10387
10346
|
const start = offset;
|
|
10388
10347
|
const end = limit === Infinity ? void 0 : start + limit;
|
|
10389
|
-
const limitedResults =
|
|
10390
|
-
for (
|
|
10391
|
-
yield
|
|
10348
|
+
const limitedResults = finalDocs.slice(start, end);
|
|
10349
|
+
for (let j = 0, len = limitedResults.length; j < len; j++) {
|
|
10350
|
+
yield limitedResults[j];
|
|
10392
10351
|
}
|
|
10393
10352
|
} else {
|
|
10394
10353
|
let yieldedCount = 0;
|
|
10395
10354
|
let i = offset;
|
|
10396
|
-
|
|
10397
|
-
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
if (i < totalKeys && yieldedCount < limit) {
|
|
10423
|
-
const nextPksToFetchCount = Math.min(CHUNK_SIZE, limit - (yieldedCount + stringifiedResults.filter(Boolean).length));
|
|
10424
|
-
if (nextPksToFetchCount > 0) {
|
|
10425
|
-
const nextChunk = Array.from(keys.subarray(i, i + nextPksToFetchCount));
|
|
10426
|
-
nextStreamChunkPromise = self.api.selectMany(nextChunk, false, tx2);
|
|
10427
|
-
i += nextChunk.length;
|
|
10428
|
-
} else {
|
|
10429
|
-
nextStreamChunkPromise = null;
|
|
10355
|
+
let nextChunkPromise = null;
|
|
10356
|
+
if (yieldedCount < limit && i < totalKeys) {
|
|
10357
|
+
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
10358
|
+
const chunkKeys = keys.subarray(i, endIdx);
|
|
10359
|
+
nextChunkPromise = self.selectMany(chunkKeys, false, tx2);
|
|
10360
|
+
i = endIdx;
|
|
10361
|
+
}
|
|
10362
|
+
while (nextChunkPromise) {
|
|
10363
|
+
const rawResults = await nextChunkPromise;
|
|
10364
|
+
nextChunkPromise = null;
|
|
10365
|
+
const { verySmallChunkSize, smallChunkSize } = self.getFreeMemoryChunkSize();
|
|
10366
|
+
if (yieldedCount < limit && i < totalKeys) {
|
|
10367
|
+
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
10368
|
+
const chunkKeys = keys.subarray(i, endIdx);
|
|
10369
|
+
nextChunkPromise = self.selectMany(chunkKeys, false, tx2);
|
|
10370
|
+
i = endIdx;
|
|
10371
|
+
}
|
|
10372
|
+
let chunkTotalSize = 0;
|
|
10373
|
+
for (let j = 0, len = rawResults.length; j < len; j++) {
|
|
10374
|
+
const s = rawResults[j];
|
|
10375
|
+
if (s) {
|
|
10376
|
+
if (yieldedCount < limit) {
|
|
10377
|
+
yield JSON.parse(s);
|
|
10378
|
+
yieldedCount++;
|
|
10379
|
+
}
|
|
10380
|
+
chunkTotalSize += s.length * 2;
|
|
10430
10381
|
}
|
|
10431
|
-
}
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
if (
|
|
10436
|
-
|
|
10437
|
-
yieldedCount++;
|
|
10438
|
-
if (yieldedCount >= limit) {
|
|
10439
|
-
nextStreamChunkPromise = null;
|
|
10440
|
-
break;
|
|
10382
|
+
}
|
|
10383
|
+
if (chunkTotalSize > 0) {
|
|
10384
|
+
if (chunkTotalSize < verySmallChunkSize) {
|
|
10385
|
+
currentChunkSize = currentChunkSize * 2;
|
|
10386
|
+
} else if (chunkTotalSize > smallChunkSize) {
|
|
10387
|
+
currentChunkSize = Math.max(Math.floor(currentChunkSize / 2), 20);
|
|
10441
10388
|
}
|
|
10442
10389
|
}
|
|
10443
10390
|
}
|
|
@@ -10452,6 +10399,133 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10452
10399
|
};
|
|
10453
10400
|
return { stream, drain };
|
|
10454
10401
|
}
|
|
10402
|
+
};
|
|
10403
|
+
|
|
10404
|
+
// src/core/document.ts
|
|
10405
|
+
var DocumentDataply = class _DocumentDataply {
|
|
10406
|
+
/**
|
|
10407
|
+
* Starts the database definition by setting the document type.
|
|
10408
|
+
* This is used to ensure TypeScript type inference works correctly for the document structure.
|
|
10409
|
+
* @template T The structure of the document to be stored.
|
|
10410
|
+
*/
|
|
10411
|
+
static Define() {
|
|
10412
|
+
return {
|
|
10413
|
+
/**
|
|
10414
|
+
* Sets the options for the database, such as index configurations and WAL settings.
|
|
10415
|
+
* @template IC The configuration of indices.
|
|
10416
|
+
* @param options The database initialization options.
|
|
10417
|
+
*/
|
|
10418
|
+
Options: (options) => _DocumentDataply.Options(options)
|
|
10419
|
+
};
|
|
10420
|
+
}
|
|
10421
|
+
/**
|
|
10422
|
+
* Internal method used by the Define-chain to pass options.
|
|
10423
|
+
*/
|
|
10424
|
+
static Options(options) {
|
|
10425
|
+
return {
|
|
10426
|
+
/**
|
|
10427
|
+
* Creates or opens the database instance with the specified file path.
|
|
10428
|
+
* @param file The path to the database file.
|
|
10429
|
+
*/
|
|
10430
|
+
Open: (file) => _DocumentDataply.Open(file, options)
|
|
10431
|
+
};
|
|
10432
|
+
}
|
|
10433
|
+
/**
|
|
10434
|
+
* Internal method used to finalize construction and create the instance.
|
|
10435
|
+
*/
|
|
10436
|
+
static Open(file, options) {
|
|
10437
|
+
return new _DocumentDataply(file, options);
|
|
10438
|
+
}
|
|
10439
|
+
api;
|
|
10440
|
+
constructor(file, options) {
|
|
10441
|
+
this.api = new DocumentDataplyAPI(file, options ?? {});
|
|
10442
|
+
}
|
|
10443
|
+
/**
|
|
10444
|
+
* Initialize the document database
|
|
10445
|
+
*/
|
|
10446
|
+
async init() {
|
|
10447
|
+
await this.api.init();
|
|
10448
|
+
await this.api.backfillIndices();
|
|
10449
|
+
}
|
|
10450
|
+
/**
|
|
10451
|
+
* Get the metadata of the document database
|
|
10452
|
+
*/
|
|
10453
|
+
async getMetadata(tx) {
|
|
10454
|
+
return this.api.runWithDefault((tx2) => this.api.getDocumentMetadata(tx2), tx);
|
|
10455
|
+
}
|
|
10456
|
+
/**
|
|
10457
|
+
* Create a transaction
|
|
10458
|
+
*/
|
|
10459
|
+
createTransaction() {
|
|
10460
|
+
return this.api.createTransaction();
|
|
10461
|
+
}
|
|
10462
|
+
/**
|
|
10463
|
+
* Insert a document into the database
|
|
10464
|
+
* @param document The document to insert
|
|
10465
|
+
* @param tx The transaction to use
|
|
10466
|
+
* @returns The primary key of the inserted document
|
|
10467
|
+
*/
|
|
10468
|
+
async insert(document, tx) {
|
|
10469
|
+
return this.api.insertSingleDocument(document, tx);
|
|
10470
|
+
}
|
|
10471
|
+
/**
|
|
10472
|
+
* Insert a batch of documents into the database
|
|
10473
|
+
* @param documents The documents to insert
|
|
10474
|
+
* @param tx The transaction to use
|
|
10475
|
+
* @returns The primary keys of the inserted documents
|
|
10476
|
+
*/
|
|
10477
|
+
async insertBatch(documents, tx) {
|
|
10478
|
+
return this.api.insertBatchDocuments(documents, tx);
|
|
10479
|
+
}
|
|
10480
|
+
/**
|
|
10481
|
+
* Fully update documents from the database that match the query
|
|
10482
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
10483
|
+
* @param newRecord Complete document to replace with, or function that receives current document and returns new document
|
|
10484
|
+
* @param tx The transaction to use
|
|
10485
|
+
* @returns The number of updated documents
|
|
10486
|
+
*/
|
|
10487
|
+
async fullUpdate(query, newRecord, tx) {
|
|
10488
|
+
return this.api.fullUpdate(query, newRecord, tx);
|
|
10489
|
+
}
|
|
10490
|
+
/**
|
|
10491
|
+
* Partially update documents from the database that match the query
|
|
10492
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
10493
|
+
* @param newRecord Partial document to merge, or function that receives current document and returns partial update
|
|
10494
|
+
* @param tx The transaction to use
|
|
10495
|
+
* @returns The number of updated documents
|
|
10496
|
+
*/
|
|
10497
|
+
async partialUpdate(query, newRecord, tx) {
|
|
10498
|
+
return this.api.partialUpdate(query, newRecord, tx);
|
|
10499
|
+
}
|
|
10500
|
+
/**
|
|
10501
|
+
* Delete documents from the database that match the query
|
|
10502
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
10503
|
+
* @param tx The transaction to use
|
|
10504
|
+
* @returns The number of deleted documents
|
|
10505
|
+
*/
|
|
10506
|
+
async delete(query, tx) {
|
|
10507
|
+
return this.api.deleteDocuments(query, tx);
|
|
10508
|
+
}
|
|
10509
|
+
/**
|
|
10510
|
+
* Count documents from the database that match the query
|
|
10511
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
10512
|
+
* @param tx The transaction to use
|
|
10513
|
+
* @returns The number of documents that match the query
|
|
10514
|
+
*/
|
|
10515
|
+
async count(query, tx) {
|
|
10516
|
+
return this.api.countDocuments(query, tx);
|
|
10517
|
+
}
|
|
10518
|
+
/**
|
|
10519
|
+
* Select documents from the database
|
|
10520
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
10521
|
+
* @param options The options to use
|
|
10522
|
+
* @param tx The transaction to use
|
|
10523
|
+
* @returns The documents that match the query
|
|
10524
|
+
* @throws Error if query or orderBy contains non-indexed fields
|
|
10525
|
+
*/
|
|
10526
|
+
select(query, options = {}, tx) {
|
|
10527
|
+
return this.api.selectDocuments(query, options, tx);
|
|
10528
|
+
}
|
|
10455
10529
|
/**
|
|
10456
10530
|
* Close the document database
|
|
10457
10531
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DataplyTreeValue, Primitive } from '../../types';
|
|
2
2
|
import { BPTreeNode, SerializeStrategyAsync, type SerializeStrategyHead } from 'dataply';
|
|
3
|
-
import { DocumentDataplyAPI } from '../
|
|
3
|
+
import { DocumentDataplyAPI } from '../documentAPI';
|
|
4
4
|
export declare class DocumentSerializeStrategyAsync<T extends Primitive> extends SerializeStrategyAsync<number, DataplyTreeValue<T>> {
|
|
5
5
|
protected readonly api: DocumentDataplyAPI<any, any>;
|
|
6
6
|
protected readonly txContext: DocumentDataplyAPI<any, any>['txContext'];
|
|
@@ -1,39 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
export declare class DocumentDataplyAPI<T extends DocumentJSON, IC extends IndexConfig<T>> extends DataplyAPI {
|
|
5
|
-
runWithDefault: <T_1>(callback: (tx: Transaction) => Promise<T_1>, tx?: Transaction) => Promise<T_1>;
|
|
6
|
-
streamWithDefault: <T_1>(callback: (tx: Transaction) => AsyncGenerator<T_1>, tx?: Transaction) => AsyncGenerator<T_1>;
|
|
7
|
-
indices: DocumentDataplyInnerMetadata['indices'];
|
|
8
|
-
readonly trees: Map<string, BPTreeAsync<number, DataplyTreeValue<Primitive>>>;
|
|
9
|
-
readonly comparator: DocumentValueComparator<DataplyTreeValue<Primitive>, Primitive>;
|
|
10
|
-
private pendingBackfillFields;
|
|
11
|
-
private readonly lock;
|
|
12
|
-
constructor(file: string, options: DocumentDataplyOptions<T, IC>);
|
|
13
|
-
getDocument(pk: number, tx?: Transaction): Promise<DataplyDocument<T>>;
|
|
14
|
-
readLock<T>(fn: () => T): Promise<T>;
|
|
15
|
-
writeLock<T>(fn: () => T): Promise<T>;
|
|
16
|
-
/**
|
|
17
|
-
* Backfill indices for fields that were added with `true` option after data was inserted.
|
|
18
|
-
* This method should be called after `init()` if you want to index existing documents
|
|
19
|
-
* for newly added index fields.
|
|
20
|
-
*
|
|
21
|
-
* @returns Number of documents that were backfilled
|
|
22
|
-
*/
|
|
23
|
-
backfillIndices(tx?: Transaction): Promise<number>;
|
|
24
|
-
createDocumentInnerMetadata(indices: DocumentDataplyInnerMetadata['indices']): DocumentDataplyInnerMetadata;
|
|
25
|
-
initializeDocumentFile(tx: Transaction): Promise<void>;
|
|
26
|
-
verifyDocumentFile(tx: Transaction): Promise<boolean>;
|
|
27
|
-
/**
|
|
28
|
-
* returns flattened document
|
|
29
|
-
* @param document
|
|
30
|
-
* @returns
|
|
31
|
-
*/
|
|
32
|
-
flattenDocument(document: T): FlattenedDocumentJSON;
|
|
33
|
-
getDocumentMetadata(tx: Transaction): Promise<DocumentDataplyMetadata>;
|
|
34
|
-
getDocumentInnerMetadata(tx: Transaction): Promise<DocumentDataplyInnerMetadata>;
|
|
35
|
-
updateDocumentInnerMetadata(metadata: DocumentDataplyInnerMetadata, tx: Transaction): Promise<void>;
|
|
36
|
-
}
|
|
1
|
+
import type { DocumentDataplyOptions, DocumentJSON, DocumentDataplyIndexedQuery, DataplyDocument, DocumentDataplyMetadata, DocumentDataplyQueryOptions, IndexConfig } from '../types';
|
|
2
|
+
import { Transaction } from 'dataply';
|
|
3
|
+
import { DocumentDataplyAPI } from './documentAPI';
|
|
37
4
|
export declare class DocumentDataply<T extends DocumentJSON, IC extends IndexConfig<T>> {
|
|
38
5
|
/**
|
|
39
6
|
* Starts the database definition by setting the document type.
|
|
@@ -63,8 +30,6 @@ export declare class DocumentDataply<T extends DocumentJSON, IC extends IndexCon
|
|
|
63
30
|
*/
|
|
64
31
|
private static Open;
|
|
65
32
|
protected readonly api: DocumentDataplyAPI<T, IC>;
|
|
66
|
-
private readonly indexedFields;
|
|
67
|
-
private readonly operatorConverters;
|
|
68
33
|
protected constructor(file: string, options?: DocumentDataplyOptions<T, IC>);
|
|
69
34
|
/**
|
|
70
35
|
* Initialize the document database
|
|
@@ -78,32 +43,6 @@ export declare class DocumentDataply<T extends DocumentJSON, IC extends IndexCon
|
|
|
78
43
|
* Create a transaction
|
|
79
44
|
*/
|
|
80
45
|
createTransaction(): Transaction;
|
|
81
|
-
private verboseQuery;
|
|
82
|
-
/**
|
|
83
|
-
* Get the selectivity candidate for the given query
|
|
84
|
-
* @param query The query conditions
|
|
85
|
-
* @param orderByField Optional field name for orderBy optimization
|
|
86
|
-
* @returns Driver and other candidates for query execution
|
|
87
|
-
*/
|
|
88
|
-
getSelectivityCandidate<U extends Partial<DocumentDataplyIndexedQuery<T, IC>>, V extends DataplyTreeValue<U>>(query: Partial<DocumentDataplyQuery<V>>, orderByField?: string): Promise<{
|
|
89
|
-
driver: {
|
|
90
|
-
tree: BPTreeAsync<number, V>;
|
|
91
|
-
condition: Partial<DocumentDataplyCondition<U>>;
|
|
92
|
-
field: string;
|
|
93
|
-
};
|
|
94
|
-
others: {
|
|
95
|
-
tree: BPTreeAsync<number, V>;
|
|
96
|
-
condition: Partial<DocumentDataplyCondition<U>>;
|
|
97
|
-
field: string;
|
|
98
|
-
}[];
|
|
99
|
-
rollback: () => void;
|
|
100
|
-
} | null>;
|
|
101
|
-
/**
|
|
102
|
-
* Get Primary Keys based on query and index selection.
|
|
103
|
-
* Internal common method to unify query optimization.
|
|
104
|
-
*/
|
|
105
|
-
private getKeys;
|
|
106
|
-
private insertDocument;
|
|
107
46
|
/**
|
|
108
47
|
* Insert a document into the database
|
|
109
48
|
* @param document The document to insert
|
|
@@ -118,14 +57,6 @@ export declare class DocumentDataply<T extends DocumentJSON, IC extends IndexCon
|
|
|
118
57
|
* @returns The primary keys of the inserted documents
|
|
119
58
|
*/
|
|
120
59
|
insertBatch(documents: T[], tx?: Transaction): Promise<number[]>;
|
|
121
|
-
/**
|
|
122
|
-
* Internal update method used by both fullUpdate and partialUpdate
|
|
123
|
-
* @param query The query to use
|
|
124
|
-
* @param computeUpdatedDoc Function that computes the updated document from the original
|
|
125
|
-
* @param tx The transaction to use
|
|
126
|
-
* @returns The number of updated documents
|
|
127
|
-
*/
|
|
128
|
-
private updateInternal;
|
|
129
60
|
/**
|
|
130
61
|
* Fully update documents from the database that match the query
|
|
131
62
|
* @param query The query to use (only indexed fields + _id allowed)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { DataplyTreeValue, DocumentDataplyInnerMetadata, DocumentDataplyOptions, DocumentJSON, FlattenedDocumentJSON, Primitive, DataplyDocument, DocumentDataplyMetadata, IndexConfig, DocumentDataplyQuery, DocumentDataplyIndexedQuery, DocumentDataplyCondition, DocumentDataplyQueryOptions } from '../types';
|
|
2
|
+
import { DataplyAPI, Transaction, BPTreeAsync } from 'dataply';
|
|
3
|
+
import { DocumentValueComparator } from './bptree/documentComparator';
|
|
4
|
+
export declare class DocumentDataplyAPI<T extends DocumentJSON, IC extends IndexConfig<T>> extends DataplyAPI {
|
|
5
|
+
runWithDefault: <T_1>(callback: (tx: Transaction) => Promise<T_1>, tx?: Transaction) => Promise<T_1>;
|
|
6
|
+
streamWithDefault: <T_1>(callback: (tx: Transaction) => AsyncGenerator<T_1>, tx?: Transaction) => AsyncGenerator<T_1>;
|
|
7
|
+
indices: DocumentDataplyInnerMetadata['indices'];
|
|
8
|
+
readonly trees: Map<string, BPTreeAsync<number, DataplyTreeValue<Primitive>>>;
|
|
9
|
+
readonly comparator: DocumentValueComparator<DataplyTreeValue<Primitive>, Primitive>;
|
|
10
|
+
private pendingBackfillFields;
|
|
11
|
+
private readonly lock;
|
|
12
|
+
readonly indexedFields: Set<string>;
|
|
13
|
+
private readonly operatorConverters;
|
|
14
|
+
constructor(file: string, options: DocumentDataplyOptions<T, IC>);
|
|
15
|
+
getDocument(pk: number, tx?: Transaction): Promise<DataplyDocument<T>>;
|
|
16
|
+
readLock<T>(fn: () => T): Promise<T>;
|
|
17
|
+
writeLock<T>(fn: () => T): Promise<T>;
|
|
18
|
+
/**
|
|
19
|
+
* Backfill indices for fields that were added with `true` option after data was inserted.
|
|
20
|
+
* This method should be called after `init()` if you want to index existing documents
|
|
21
|
+
* for newly added index fields.
|
|
22
|
+
*
|
|
23
|
+
* @returns Number of documents that were backfilled
|
|
24
|
+
*/
|
|
25
|
+
backfillIndices(tx?: Transaction): Promise<number>;
|
|
26
|
+
createDocumentInnerMetadata(indices: DocumentDataplyInnerMetadata['indices']): DocumentDataplyInnerMetadata;
|
|
27
|
+
initializeDocumentFile(tx: Transaction): Promise<void>;
|
|
28
|
+
verifyDocumentFile(tx: Transaction): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* returns flattened document
|
|
31
|
+
* @param document
|
|
32
|
+
* @returns
|
|
33
|
+
*/
|
|
34
|
+
flattenDocument(document: T): FlattenedDocumentJSON;
|
|
35
|
+
getDocumentMetadata(tx: Transaction): Promise<DocumentDataplyMetadata>;
|
|
36
|
+
getDocumentInnerMetadata(tx: Transaction): Promise<DocumentDataplyInnerMetadata>;
|
|
37
|
+
updateDocumentInnerMetadata(metadata: DocumentDataplyInnerMetadata, tx: Transaction): Promise<void>;
|
|
38
|
+
verboseQuery<U extends Partial<DocumentDataplyIndexedQuery<T, IC>>, V extends DataplyTreeValue<U>>(query: Partial<DocumentDataplyQuery<U>>): Partial<DocumentDataplyQuery<V>>;
|
|
39
|
+
/**
|
|
40
|
+
* Get the selectivity candidate for the given query
|
|
41
|
+
* @param query The query conditions
|
|
42
|
+
* @param orderByField Optional field name for orderBy optimization
|
|
43
|
+
* @returns Driver and other candidates for query execution
|
|
44
|
+
*/
|
|
45
|
+
getSelectivityCandidate<U extends Partial<DocumentDataplyIndexedQuery<T, IC>>, V extends DataplyTreeValue<U>>(query: Partial<DocumentDataplyQuery<V>>, orderByField?: string): Promise<{
|
|
46
|
+
driver: {
|
|
47
|
+
tree: BPTreeAsync<number, V>;
|
|
48
|
+
condition: Partial<DocumentDataplyCondition<U>>;
|
|
49
|
+
field: string;
|
|
50
|
+
};
|
|
51
|
+
others: {
|
|
52
|
+
tree: BPTreeAsync<number, V>;
|
|
53
|
+
condition: Partial<DocumentDataplyCondition<U>>;
|
|
54
|
+
field: string;
|
|
55
|
+
}[];
|
|
56
|
+
rollback: () => void;
|
|
57
|
+
} | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Get Free Memory Chunk Size
|
|
60
|
+
* @returns { verySmallChunkSize, smallChunkSize }
|
|
61
|
+
*/
|
|
62
|
+
getFreeMemoryChunkSize(): {
|
|
63
|
+
verySmallChunkSize: number;
|
|
64
|
+
smallChunkSize: number;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Get Primary Keys based on query and index selection.
|
|
68
|
+
* Internal common method to unify query optimization.
|
|
69
|
+
*/
|
|
70
|
+
getKeys(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, orderBy?: keyof IC | '_id', sortOrder?: 'asc' | 'desc'): Promise<Float64Array>;
|
|
71
|
+
private insertDocumentInternal;
|
|
72
|
+
/**
|
|
73
|
+
* Insert a document into the database
|
|
74
|
+
* @param document The document to insert
|
|
75
|
+
* @param tx The transaction to use
|
|
76
|
+
* @returns The primary key of the inserted document
|
|
77
|
+
*/
|
|
78
|
+
insertSingleDocument(document: T, tx?: Transaction): Promise<number>;
|
|
79
|
+
/**
|
|
80
|
+
* Insert a batch of documents into the database
|
|
81
|
+
* @param documents The documents to insert
|
|
82
|
+
* @param tx The transaction to use
|
|
83
|
+
* @returns The primary keys of the inserted documents
|
|
84
|
+
*/
|
|
85
|
+
insertBatchDocuments(documents: T[], tx?: Transaction): Promise<number[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Internal update method used by both fullUpdate and partialUpdate
|
|
88
|
+
* @param query The query to use
|
|
89
|
+
* @param computeUpdatedDoc Function that computes the updated document from the original
|
|
90
|
+
* @param tx The transaction to use
|
|
91
|
+
* @returns The number of updated documents
|
|
92
|
+
*/
|
|
93
|
+
private updateInternal;
|
|
94
|
+
/**
|
|
95
|
+
* Fully update documents from the database that match the query
|
|
96
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
97
|
+
* @param newRecord Complete document to replace with, or function that receives current document and returns new document
|
|
98
|
+
* @param tx The transaction to use
|
|
99
|
+
* @returns The number of updated documents
|
|
100
|
+
*/
|
|
101
|
+
fullUpdate(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, newRecord: T | ((document: DataplyDocument<T>) => T), tx?: Transaction): Promise<number>;
|
|
102
|
+
/**
|
|
103
|
+
* Partially update documents from the database that match the query
|
|
104
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
105
|
+
* @param newRecord Partial document to merge, or function that receives current document and returns partial update
|
|
106
|
+
* @param tx The transaction to use
|
|
107
|
+
* @returns The number of updated documents
|
|
108
|
+
*/
|
|
109
|
+
partialUpdate(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, newRecord: Partial<DataplyDocument<T>> | ((document: DataplyDocument<T>) => Partial<DataplyDocument<T>>), tx?: Transaction): Promise<number>;
|
|
110
|
+
/**
|
|
111
|
+
* Delete documents from the database that match the query
|
|
112
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
113
|
+
* @param tx The transaction to use
|
|
114
|
+
* @returns The number of deleted documents
|
|
115
|
+
*/
|
|
116
|
+
deleteDocuments(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, tx?: Transaction): Promise<number>;
|
|
117
|
+
/**
|
|
118
|
+
* Count documents from the database that match the query
|
|
119
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
120
|
+
* @param tx The transaction to use
|
|
121
|
+
* @returns The number of documents that match the query
|
|
122
|
+
*/
|
|
123
|
+
countDocuments(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, tx?: Transaction): Promise<number>;
|
|
124
|
+
/**
|
|
125
|
+
* Select documents from the database
|
|
126
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
127
|
+
* @param options The options to use
|
|
128
|
+
* @param tx The transaction to use
|
|
129
|
+
* @returns The documents that match the query
|
|
130
|
+
* @throws Error if query or orderBy contains non-indexed fields
|
|
131
|
+
*/
|
|
132
|
+
selectDocuments(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, options?: DocumentDataplyQueryOptions<T, IC>, tx?: Transaction): {
|
|
133
|
+
stream: AsyncIterableIterator<DataplyDocument<T>>;
|
|
134
|
+
drain: () => Promise<DataplyDocument<T>[]>;
|
|
135
|
+
};
|
|
136
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document-dataply",
|
|
3
|
-
"version": "0.0.5
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Simple and powerful JSON document database supporting complex queries and flexible indexing policies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "izure <admin@izure.org>",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dataply"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"dataply": "^0.0.21
|
|
45
|
+
"dataply": "^0.0.21"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/jest": "^30.0.0",
|