document-dataply 0.0.5-alpha.1 → 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) {
|
|
@@ -8681,8 +8681,7 @@ var require_cjs = __commonJS({
|
|
|
8681
8681
|
}
|
|
8682
8682
|
const pkIndexMap = /* @__PURE__ */ new Map();
|
|
8683
8683
|
for (let i = 0, len = pks.length; i < len; i++) {
|
|
8684
|
-
|
|
8685
|
-
pkIndexMap.set(pk, i);
|
|
8684
|
+
pkIndexMap.set(pks[i], i);
|
|
8686
8685
|
}
|
|
8687
8686
|
const minPk = Math.min(...pks);
|
|
8688
8687
|
const maxPk = Math.max(...pks);
|
|
@@ -9526,7 +9525,7 @@ __export(src_exports, {
|
|
|
9526
9525
|
});
|
|
9527
9526
|
module.exports = __toCommonJS(src_exports);
|
|
9528
9527
|
|
|
9529
|
-
// src/core/
|
|
9528
|
+
// src/core/documentAPI.ts
|
|
9530
9529
|
var os = __toESM(require("node:os"));
|
|
9531
9530
|
var import_dataply3 = __toESM(require_cjs());
|
|
9532
9531
|
|
|
@@ -9617,17 +9616,99 @@ async function catchPromise(promise) {
|
|
|
9617
9616
|
return promise.then((res) => [void 0, res]).catch((reason) => [reason]);
|
|
9618
9617
|
}
|
|
9619
9618
|
|
|
9620
|
-
// src/
|
|
9619
|
+
// src/utils/heap.ts
|
|
9620
|
+
var BinaryHeap = class {
|
|
9621
|
+
constructor(comparator) {
|
|
9622
|
+
this.comparator = comparator;
|
|
9623
|
+
}
|
|
9624
|
+
heap = [];
|
|
9625
|
+
get size() {
|
|
9626
|
+
return this.heap.length;
|
|
9627
|
+
}
|
|
9628
|
+
peek() {
|
|
9629
|
+
return this.heap[0];
|
|
9630
|
+
}
|
|
9631
|
+
push(value) {
|
|
9632
|
+
this.heap.push(value);
|
|
9633
|
+
this.bubbleUp(this.heap.length - 1);
|
|
9634
|
+
}
|
|
9635
|
+
pop() {
|
|
9636
|
+
if (this.size === 0) return void 0;
|
|
9637
|
+
const top = this.heap[0];
|
|
9638
|
+
const bottom = this.heap.pop();
|
|
9639
|
+
if (this.size > 0) {
|
|
9640
|
+
this.heap[0] = bottom;
|
|
9641
|
+
this.sinkDown(0);
|
|
9642
|
+
}
|
|
9643
|
+
return top;
|
|
9644
|
+
}
|
|
9645
|
+
/**
|
|
9646
|
+
* Replace the root element with a new value and re-heapify.
|
|
9647
|
+
* Faster than pop() followed by push().
|
|
9648
|
+
*/
|
|
9649
|
+
replace(value) {
|
|
9650
|
+
const top = this.heap[0];
|
|
9651
|
+
this.heap[0] = value;
|
|
9652
|
+
this.sinkDown(0);
|
|
9653
|
+
return top;
|
|
9654
|
+
}
|
|
9655
|
+
toArray() {
|
|
9656
|
+
return [...this.heap];
|
|
9657
|
+
}
|
|
9658
|
+
bubbleUp(index) {
|
|
9659
|
+
while (index > 0) {
|
|
9660
|
+
const parentIndex = Math.floor((index - 1) / 2);
|
|
9661
|
+
if (this.comparator(this.heap[index], this.heap[parentIndex]) >= 0) break;
|
|
9662
|
+
[this.heap[index], this.heap[parentIndex]] = [this.heap[parentIndex], this.heap[index]];
|
|
9663
|
+
index = parentIndex;
|
|
9664
|
+
}
|
|
9665
|
+
}
|
|
9666
|
+
sinkDown(index) {
|
|
9667
|
+
while (true) {
|
|
9668
|
+
let smallest = index;
|
|
9669
|
+
const left = 2 * index + 1;
|
|
9670
|
+
const right = 2 * index + 2;
|
|
9671
|
+
if (left < this.size && this.comparator(this.heap[left], this.heap[smallest]) < 0) {
|
|
9672
|
+
smallest = left;
|
|
9673
|
+
}
|
|
9674
|
+
if (right < this.size && this.comparator(this.heap[right], this.heap[smallest]) < 0) {
|
|
9675
|
+
smallest = right;
|
|
9676
|
+
}
|
|
9677
|
+
if (smallest === index) break;
|
|
9678
|
+
[this.heap[index], this.heap[smallest]] = [this.heap[smallest], this.heap[index]];
|
|
9679
|
+
index = smallest;
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9682
|
+
};
|
|
9683
|
+
|
|
9684
|
+
// src/core/documentAPI.ts
|
|
9621
9685
|
var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
9622
9686
|
indices = {};
|
|
9623
9687
|
trees = /* @__PURE__ */ new Map();
|
|
9624
9688
|
comparator = new DocumentValueComparator();
|
|
9625
9689
|
pendingBackfillFields = [];
|
|
9626
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
|
+
};
|
|
9627
9702
|
constructor(file, options) {
|
|
9628
9703
|
super(file, options);
|
|
9629
9704
|
this.trees = /* @__PURE__ */ new Map();
|
|
9630
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
|
+
}
|
|
9631
9712
|
this.hook.onceAfter("init", async (tx, isNewlyCreated) => {
|
|
9632
9713
|
if (isNewlyCreated) {
|
|
9633
9714
|
await this.initializeDocumentFile(tx);
|
|
@@ -9837,81 +9918,6 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
9837
9918
|
async updateDocumentInnerMetadata(metadata, tx) {
|
|
9838
9919
|
await this.update(1, JSON.stringify(metadata), tx);
|
|
9839
9920
|
}
|
|
9840
|
-
};
|
|
9841
|
-
var DocumentDataply = class _DocumentDataply {
|
|
9842
|
-
/**
|
|
9843
|
-
* Starts the database definition by setting the document type.
|
|
9844
|
-
* This is used to ensure TypeScript type inference works correctly for the document structure.
|
|
9845
|
-
* @template T The structure of the document to be stored.
|
|
9846
|
-
*/
|
|
9847
|
-
static Define() {
|
|
9848
|
-
return {
|
|
9849
|
-
/**
|
|
9850
|
-
* Sets the options for the database, such as index configurations and WAL settings.
|
|
9851
|
-
* @template IC The configuration of indices.
|
|
9852
|
-
* @param options The database initialization options.
|
|
9853
|
-
*/
|
|
9854
|
-
Options: (options) => _DocumentDataply.Options(options)
|
|
9855
|
-
};
|
|
9856
|
-
}
|
|
9857
|
-
/**
|
|
9858
|
-
* Internal method used by the Define-chain to pass options.
|
|
9859
|
-
*/
|
|
9860
|
-
static Options(options) {
|
|
9861
|
-
return {
|
|
9862
|
-
/**
|
|
9863
|
-
* Creates or opens the database instance with the specified file path.
|
|
9864
|
-
* @param file The path to the database file.
|
|
9865
|
-
*/
|
|
9866
|
-
Open: (file) => _DocumentDataply.Open(file, options)
|
|
9867
|
-
};
|
|
9868
|
-
}
|
|
9869
|
-
/**
|
|
9870
|
-
* Internal method used to finalize construction and create the instance.
|
|
9871
|
-
*/
|
|
9872
|
-
static Open(file, options) {
|
|
9873
|
-
return new _DocumentDataply(file, options);
|
|
9874
|
-
}
|
|
9875
|
-
api;
|
|
9876
|
-
indexedFields;
|
|
9877
|
-
operatorConverters = {
|
|
9878
|
-
equal: "primaryEqual",
|
|
9879
|
-
notEqual: "primaryNotEqual",
|
|
9880
|
-
lt: "primaryLt",
|
|
9881
|
-
lte: "primaryLte",
|
|
9882
|
-
gt: "primaryGt",
|
|
9883
|
-
gte: "primaryGte",
|
|
9884
|
-
or: "primaryOr",
|
|
9885
|
-
like: "like"
|
|
9886
|
-
};
|
|
9887
|
-
constructor(file, options) {
|
|
9888
|
-
this.api = new DocumentDataplyAPI(file, options ?? {});
|
|
9889
|
-
this.indexedFields = /* @__PURE__ */ new Set(["_id"]);
|
|
9890
|
-
if (options?.indices) {
|
|
9891
|
-
for (const field of Object.keys(options.indices)) {
|
|
9892
|
-
this.indexedFields.add(field);
|
|
9893
|
-
}
|
|
9894
|
-
}
|
|
9895
|
-
}
|
|
9896
|
-
/**
|
|
9897
|
-
* Initialize the document database
|
|
9898
|
-
*/
|
|
9899
|
-
async init() {
|
|
9900
|
-
await this.api.init();
|
|
9901
|
-
await this.api.backfillIndices();
|
|
9902
|
-
}
|
|
9903
|
-
/**
|
|
9904
|
-
* Get the metadata of the document database
|
|
9905
|
-
*/
|
|
9906
|
-
async getMetadata(tx) {
|
|
9907
|
-
return this.api.runWithDefault((tx2) => this.api.getDocumentMetadata(tx2), tx);
|
|
9908
|
-
}
|
|
9909
|
-
/**
|
|
9910
|
-
* Create a transaction
|
|
9911
|
-
*/
|
|
9912
|
-
createTransaction() {
|
|
9913
|
-
return this.api.createTransaction();
|
|
9914
|
-
}
|
|
9915
9921
|
verboseQuery(query) {
|
|
9916
9922
|
const result = {};
|
|
9917
9923
|
for (const field in query) {
|
|
@@ -9948,7 +9954,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
9948
9954
|
async getSelectivityCandidate(query, orderByField) {
|
|
9949
9955
|
const candidates = [];
|
|
9950
9956
|
for (const field in query) {
|
|
9951
|
-
const tree = this.
|
|
9957
|
+
const tree = this.trees.get(field);
|
|
9952
9958
|
if (!tree) continue;
|
|
9953
9959
|
const condition = query[field];
|
|
9954
9960
|
const treeTx = await tree.createTransaction();
|
|
@@ -9984,6 +9990,17 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
9984
9990
|
rollback
|
|
9985
9991
|
};
|
|
9986
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
|
+
}
|
|
9987
10004
|
/**
|
|
9988
10005
|
* Get Primary Keys based on query and index selection.
|
|
9989
10006
|
* Internal common method to unify query optimization.
|
|
@@ -10015,14 +10032,14 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10015
10032
|
return new Float64Array(keysSet);
|
|
10016
10033
|
}
|
|
10017
10034
|
}
|
|
10018
|
-
async
|
|
10019
|
-
const metadata = await this.
|
|
10035
|
+
async insertDocumentInternal(document, tx) {
|
|
10036
|
+
const metadata = await this.getDocumentInnerMetadata(tx);
|
|
10020
10037
|
const id = ++metadata.lastId;
|
|
10021
|
-
await this.
|
|
10038
|
+
await this.updateDocumentInnerMetadata(metadata, tx);
|
|
10022
10039
|
const dataplyDocument = Object.assign({
|
|
10023
10040
|
_id: id
|
|
10024
10041
|
}, document);
|
|
10025
|
-
const pk = await
|
|
10042
|
+
const pk = await super.insert(JSON.stringify(dataplyDocument), true, tx);
|
|
10026
10043
|
return {
|
|
10027
10044
|
pk,
|
|
10028
10045
|
id,
|
|
@@ -10035,12 +10052,12 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10035
10052
|
* @param tx The transaction to use
|
|
10036
10053
|
* @returns The primary key of the inserted document
|
|
10037
10054
|
*/
|
|
10038
|
-
async
|
|
10039
|
-
return this.
|
|
10040
|
-
const { pk, document: dataplyDocument } = await this.
|
|
10041
|
-
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);
|
|
10042
10059
|
for (const field in flattenDocument) {
|
|
10043
|
-
const tree = this.
|
|
10060
|
+
const tree = this.trees.get(field);
|
|
10044
10061
|
if (!tree) continue;
|
|
10045
10062
|
const v = flattenDocument[field];
|
|
10046
10063
|
const [error] = await catchPromise(tree.insert(pk, { k: pk, v }));
|
|
@@ -10057,12 +10074,12 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10057
10074
|
* @param tx The transaction to use
|
|
10058
10075
|
* @returns The primary keys of the inserted documents
|
|
10059
10076
|
*/
|
|
10060
|
-
async
|
|
10061
|
-
return this.
|
|
10062
|
-
const metadata = await this.
|
|
10077
|
+
async insertBatchDocuments(documents, tx) {
|
|
10078
|
+
return this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10079
|
+
const metadata = await this.getDocumentInnerMetadata(tx2);
|
|
10063
10080
|
const startId = metadata.lastId + 1;
|
|
10064
10081
|
metadata.lastId += documents.length;
|
|
10065
|
-
await this.
|
|
10082
|
+
await this.updateDocumentInnerMetadata(metadata, tx2);
|
|
10066
10083
|
const ids = [];
|
|
10067
10084
|
const dataplyDocuments = [];
|
|
10068
10085
|
const flattenedData = [];
|
|
@@ -10073,15 +10090,15 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10073
10090
|
}, documents[i]);
|
|
10074
10091
|
const stringified = JSON.stringify(dataplyDocument);
|
|
10075
10092
|
dataplyDocuments.push(stringified);
|
|
10076
|
-
const flattenDocument = this.
|
|
10093
|
+
const flattenDocument = this.flattenDocument(dataplyDocument);
|
|
10077
10094
|
flattenedData.push({ pk: -1, data: flattenDocument });
|
|
10078
10095
|
ids.push(id);
|
|
10079
10096
|
}
|
|
10080
|
-
const pks = await
|
|
10097
|
+
const pks = await super.insertBatch(dataplyDocuments, true, tx2);
|
|
10081
10098
|
for (let i = 0, len = pks.length; i < len; i++) {
|
|
10082
10099
|
flattenedData[i].pk = pks[i];
|
|
10083
10100
|
}
|
|
10084
|
-
for (const [field, tree] of this.
|
|
10101
|
+
for (const [field, tree] of this.trees) {
|
|
10085
10102
|
const treeTx = await tree.createTransaction();
|
|
10086
10103
|
for (let i = 0, len = flattenedData.length; i < len; i++) {
|
|
10087
10104
|
const item = flattenedData[i];
|
|
@@ -10111,17 +10128,17 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10111
10128
|
const pks = await this.getKeys(query);
|
|
10112
10129
|
let updatedCount = 0;
|
|
10113
10130
|
const treeTxs = /* @__PURE__ */ new Map();
|
|
10114
|
-
for (const [field, tree] of this.
|
|
10131
|
+
for (const [field, tree] of this.trees) {
|
|
10115
10132
|
treeTxs.set(field, await tree.createTransaction());
|
|
10116
10133
|
}
|
|
10117
10134
|
treeTxs.delete("_id");
|
|
10118
10135
|
for (let i = 0, len = pks.length; i < len; i++) {
|
|
10119
10136
|
const pk = pks[i];
|
|
10120
|
-
const doc = await this.
|
|
10137
|
+
const doc = await this.getDocument(pk, tx);
|
|
10121
10138
|
if (!doc) continue;
|
|
10122
10139
|
const updatedDoc = computeUpdatedDoc(doc);
|
|
10123
|
-
const oldFlatDoc = this.
|
|
10124
|
-
const newFlatDoc = this.
|
|
10140
|
+
const oldFlatDoc = this.flattenDocument(doc);
|
|
10141
|
+
const newFlatDoc = this.flattenDocument(updatedDoc);
|
|
10125
10142
|
for (const [field, treeTx] of treeTxs) {
|
|
10126
10143
|
const oldV = oldFlatDoc[field];
|
|
10127
10144
|
const newV = newFlatDoc[field];
|
|
@@ -10133,7 +10150,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10133
10150
|
await treeTx.insert(pk, { k: pk, v: newV });
|
|
10134
10151
|
}
|
|
10135
10152
|
}
|
|
10136
|
-
await this.
|
|
10153
|
+
await this.update(pk, JSON.stringify(updatedDoc), tx);
|
|
10137
10154
|
updatedCount++;
|
|
10138
10155
|
}
|
|
10139
10156
|
for (const [field, treeTx] of treeTxs) {
|
|
@@ -10155,7 +10172,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10155
10172
|
* @returns The number of updated documents
|
|
10156
10173
|
*/
|
|
10157
10174
|
async fullUpdate(query, newRecord, tx) {
|
|
10158
|
-
return await this.
|
|
10175
|
+
return await this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10159
10176
|
return this.updateInternal(query, (doc) => {
|
|
10160
10177
|
const newDoc = typeof newRecord === "function" ? newRecord(doc) : newRecord;
|
|
10161
10178
|
return { _id: doc._id, ...newDoc };
|
|
@@ -10170,11 +10187,12 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10170
10187
|
* @returns The number of updated documents
|
|
10171
10188
|
*/
|
|
10172
10189
|
async partialUpdate(query, newRecord, tx) {
|
|
10173
|
-
return this.
|
|
10190
|
+
return this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10174
10191
|
return this.updateInternal(query, (doc) => {
|
|
10175
|
-
const
|
|
10176
|
-
|
|
10177
|
-
|
|
10192
|
+
const partialUpdateContent = typeof newRecord === "function" ? newRecord(doc) : newRecord;
|
|
10193
|
+
const finalUpdate = { ...partialUpdateContent };
|
|
10194
|
+
delete finalUpdate._id;
|
|
10195
|
+
return { ...doc, ...finalUpdate };
|
|
10178
10196
|
}, tx2);
|
|
10179
10197
|
}, tx));
|
|
10180
10198
|
}
|
|
@@ -10184,21 +10202,21 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10184
10202
|
* @param tx The transaction to use
|
|
10185
10203
|
* @returns The number of deleted documents
|
|
10186
10204
|
*/
|
|
10187
|
-
async
|
|
10188
|
-
return this.
|
|
10205
|
+
async deleteDocuments(query, tx) {
|
|
10206
|
+
return this.writeLock(() => this.runWithDefault(async (tx2) => {
|
|
10189
10207
|
const pks = await this.getKeys(query);
|
|
10190
10208
|
let deletedCount = 0;
|
|
10191
10209
|
for (let i = 0, len = pks.length; i < len; i++) {
|
|
10192
10210
|
const pk = pks[i];
|
|
10193
|
-
const doc = await this.
|
|
10211
|
+
const doc = await this.getDocument(pk, tx2);
|
|
10194
10212
|
if (!doc) continue;
|
|
10195
|
-
const flatDoc = this.
|
|
10196
|
-
for (const [field, tree] of this.
|
|
10213
|
+
const flatDoc = this.flattenDocument(doc);
|
|
10214
|
+
for (const [field, tree] of this.trees) {
|
|
10197
10215
|
const v = flatDoc[field];
|
|
10198
10216
|
if (v === void 0) continue;
|
|
10199
10217
|
await tree.delete(pk, { k: pk, v });
|
|
10200
10218
|
}
|
|
10201
|
-
await
|
|
10219
|
+
await super.delete(pk, true, tx2);
|
|
10202
10220
|
deletedCount++;
|
|
10203
10221
|
}
|
|
10204
10222
|
return deletedCount;
|
|
@@ -10210,8 +10228,8 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10210
10228
|
* @param tx The transaction to use
|
|
10211
10229
|
* @returns The number of documents that match the query
|
|
10212
10230
|
*/
|
|
10213
|
-
async
|
|
10214
|
-
return this.
|
|
10231
|
+
async countDocuments(query, tx) {
|
|
10232
|
+
return this.readLock(() => this.runWithDefault(async (tx2) => {
|
|
10215
10233
|
const pks = await this.getKeys(query);
|
|
10216
10234
|
return pks.length;
|
|
10217
10235
|
}, tx));
|
|
@@ -10224,7 +10242,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10224
10242
|
* @returns The documents that match the query
|
|
10225
10243
|
* @throws Error if query or orderBy contains non-indexed fields
|
|
10226
10244
|
*/
|
|
10227
|
-
|
|
10245
|
+
selectDocuments(query, options = {}, tx) {
|
|
10228
10246
|
for (const field of Object.keys(query)) {
|
|
10229
10247
|
if (!this.indexedFields.has(field)) {
|
|
10230
10248
|
throw new Error(`Query field "${field}" is not indexed. Available indexed fields: ${Array.from(this.indexedFields).join(", ")}`);
|
|
@@ -10241,7 +10259,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10241
10259
|
orderBy: orderByField
|
|
10242
10260
|
} = options;
|
|
10243
10261
|
const self = this;
|
|
10244
|
-
const stream = this.
|
|
10262
|
+
const stream = this.streamWithDefault(async function* (tx2) {
|
|
10245
10263
|
const keys = await self.getKeys(query, orderByField, sortOrder);
|
|
10246
10264
|
const totalKeys = keys.length;
|
|
10247
10265
|
if (totalKeys === 0) return;
|
|
@@ -10253,42 +10271,73 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10253
10271
|
if (selectivity) {
|
|
10254
10272
|
selectivity.rollback();
|
|
10255
10273
|
}
|
|
10256
|
-
let currentChunkSize = self.
|
|
10274
|
+
let currentChunkSize = self.options.pageSize;
|
|
10257
10275
|
if (!isDriverOrderByField && orderByField) {
|
|
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
|
+
}
|
|
10258
10287
|
const results = [];
|
|
10259
10288
|
let i = 0;
|
|
10260
10289
|
let nextChunkPromise = null;
|
|
10261
10290
|
if (i < totalKeys) {
|
|
10262
10291
|
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
10263
10292
|
const chunkKeys = keys.subarray(i, endIdx);
|
|
10264
|
-
nextChunkPromise = self.
|
|
10293
|
+
nextChunkPromise = self.selectMany(chunkKeys, false, tx2);
|
|
10265
10294
|
i = endIdx;
|
|
10266
10295
|
}
|
|
10267
10296
|
while (nextChunkPromise) {
|
|
10268
10297
|
const rawResults = await nextChunkPromise;
|
|
10269
10298
|
nextChunkPromise = null;
|
|
10270
|
-
const
|
|
10271
|
-
const safeLimit = freeMem * 0.2;
|
|
10299
|
+
const { verySmallChunkSize, smallChunkSize } = self.getFreeMemoryChunkSize();
|
|
10272
10300
|
if (i < totalKeys) {
|
|
10273
10301
|
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
10274
10302
|
const chunkKeys = keys.subarray(i, endIdx);
|
|
10275
|
-
nextChunkPromise = self.
|
|
10303
|
+
nextChunkPromise = self.selectMany(chunkKeys, false, tx2);
|
|
10276
10304
|
i = endIdx;
|
|
10277
10305
|
}
|
|
10278
10306
|
let chunkTotalSize = 0;
|
|
10279
10307
|
for (let j = 0, len = rawResults.length; j < len; j++) {
|
|
10280
10308
|
const s = rawResults[j];
|
|
10281
10309
|
if (s) {
|
|
10282
|
-
|
|
10310
|
+
const doc = JSON.parse(s);
|
|
10283
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);
|
|
10328
|
+
}
|
|
10284
10329
|
}
|
|
10285
10330
|
}
|
|
10286
10331
|
if (chunkTotalSize > 0) {
|
|
10287
|
-
if (chunkTotalSize <
|
|
10288
|
-
|
|
10332
|
+
if (chunkTotalSize < verySmallChunkSize) {
|
|
10333
|
+
currentChunkSize = currentChunkSize * 2;
|
|
10334
|
+
} else if (chunkTotalSize > smallChunkSize) {
|
|
10335
|
+
currentChunkSize = Math.max(Math.floor(currentChunkSize / 2), 20);
|
|
10336
|
+
}
|
|
10289
10337
|
}
|
|
10290
10338
|
}
|
|
10291
|
-
|
|
10339
|
+
const finalDocs = heap ? heap.toArray() : results;
|
|
10340
|
+
finalDocs.sort((a, b) => {
|
|
10292
10341
|
const aVal = a[orderByField] ?? a._id;
|
|
10293
10342
|
const bVal = b[orderByField] ?? b._id;
|
|
10294
10343
|
const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
@@ -10296,7 +10345,7 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10296
10345
|
});
|
|
10297
10346
|
const start = offset;
|
|
10298
10347
|
const end = limit === Infinity ? void 0 : start + limit;
|
|
10299
|
-
const limitedResults =
|
|
10348
|
+
const limitedResults = finalDocs.slice(start, end);
|
|
10300
10349
|
for (let j = 0, len = limitedResults.length; j < len; j++) {
|
|
10301
10350
|
yield limitedResults[j];
|
|
10302
10351
|
}
|
|
@@ -10307,18 +10356,17 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10307
10356
|
if (yieldedCount < limit && i < totalKeys) {
|
|
10308
10357
|
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
10309
10358
|
const chunkKeys = keys.subarray(i, endIdx);
|
|
10310
|
-
nextChunkPromise = self.
|
|
10359
|
+
nextChunkPromise = self.selectMany(chunkKeys, false, tx2);
|
|
10311
10360
|
i = endIdx;
|
|
10312
10361
|
}
|
|
10313
10362
|
while (nextChunkPromise) {
|
|
10314
10363
|
const rawResults = await nextChunkPromise;
|
|
10315
10364
|
nextChunkPromise = null;
|
|
10316
|
-
const
|
|
10317
|
-
const safeLimit = freeMem * 0.2;
|
|
10365
|
+
const { verySmallChunkSize, smallChunkSize } = self.getFreeMemoryChunkSize();
|
|
10318
10366
|
if (yieldedCount < limit && i < totalKeys) {
|
|
10319
10367
|
const endIdx = Math.min(i + currentChunkSize, totalKeys);
|
|
10320
10368
|
const chunkKeys = keys.subarray(i, endIdx);
|
|
10321
|
-
nextChunkPromise = self.
|
|
10369
|
+
nextChunkPromise = self.selectMany(chunkKeys, false, tx2);
|
|
10322
10370
|
i = endIdx;
|
|
10323
10371
|
}
|
|
10324
10372
|
let chunkTotalSize = 0;
|
|
@@ -10333,8 +10381,11 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10333
10381
|
}
|
|
10334
10382
|
}
|
|
10335
10383
|
if (chunkTotalSize > 0) {
|
|
10336
|
-
if (chunkTotalSize <
|
|
10337
|
-
|
|
10384
|
+
if (chunkTotalSize < verySmallChunkSize) {
|
|
10385
|
+
currentChunkSize = currentChunkSize * 2;
|
|
10386
|
+
} else if (chunkTotalSize > smallChunkSize) {
|
|
10387
|
+
currentChunkSize = Math.max(Math.floor(currentChunkSize / 2), 20);
|
|
10388
|
+
}
|
|
10338
10389
|
}
|
|
10339
10390
|
}
|
|
10340
10391
|
}
|
|
@@ -10348,6 +10399,133 @@ var DocumentDataply = class _DocumentDataply {
|
|
|
10348
10399
|
};
|
|
10349
10400
|
return { stream, drain };
|
|
10350
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
|
+
}
|
|
10351
10529
|
/**
|
|
10352
10530
|
* Close the document database
|
|
10353
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",
|