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
- if (length === 4) {
5721
- return (bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24) >>> 0;
5722
- }
5723
- if (length === 8) {
5724
- const low2 = (bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24) >>> 0;
5725
- const high = (bytes[offset + 4] | bytes[offset + 5] << 8 | bytes[offset + 6] << 16 | bytes[offset + 7] << 24) >>> 0;
5726
- return low2 + high * 4294967296;
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.freemem() * 0.1;
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 pkSet = new Set(pks);
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
- if (pkSet.has(pk)) {
8690
- pkRidPairs.push({ pk, rid });
8692
+ const index = pkIndexMap.get(pk);
8693
+ if (index !== void 0) {
8694
+ pkRidPairs[index] = { pk, rid, index };
8691
8695
  }
8692
8696
  }
8693
- const resultMap = await this.fetchRowsByRids(pkRidPairs, tx);
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 Map of PK to row data
8703
+ * @returns Array of row data in the same order as input PKs
8701
8704
  */
8702
8705
  async fetchRowsByRids(pkRidPairs, tx) {
8703
- const resultMap = /* @__PURE__ */ new Map();
8704
- if (pkRidPairs.length === 0) return resultMap;
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
- const sortedPageEntries = Array.from(pageGroupMap.entries()).sort((a, b) => a[0] - b[0]);
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
- resultMap.set(item.pk, null);
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
- resultMap.set(item.pk, body);
8732
+ result[item.index] = body;
8730
8733
  } else {
8731
- resultMap.set(item.pk, this.rowManager.getBody(row));
8734
+ result[item.index] = this.rowManager.getBody(row);
8732
8735
  }
8733
8736
  }
8734
8737
  }));
8735
- return resultMap;
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/document.ts
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/document.ts
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.api.trees.get(field);
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 /* @__PURE__ */ new Set();
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 keys = await driver.tree.keys(driver.condition, void 0, sortOrder);
10020
+ let keysSet = await driver.tree.keys(driver.condition, void 0, sortOrder);
10065
10021
  for (const { tree, condition } of others) {
10066
- keys = await tree.keys(condition, keys, sortOrder);
10022
+ keysSet = await tree.keys(condition, keysSet, sortOrder);
10067
10023
  }
10068
10024
  rollback();
10069
- return keys;
10025
+ return new Float64Array(keysSet);
10070
10026
  } else {
10071
- let keys = await driver.tree.keys(driver.condition, void 0);
10027
+ let keysSet = await driver.tree.keys(driver.condition, void 0);
10072
10028
  for (const { tree, condition } of others) {
10073
- keys = await tree.keys(condition, keys);
10029
+ keysSet = await tree.keys(condition, keysSet);
10074
10030
  }
10075
10031
  rollback();
10076
- return keys;
10032
+ return new Float64Array(keysSet);
10077
10033
  }
10078
10034
  }
10079
- async insertDocument(document, tx) {
10080
- const metadata = await this.api.getDocumentInnerMetadata(tx);
10035
+ async insertDocumentInternal(document, tx) {
10036
+ const metadata = await this.getDocumentInnerMetadata(tx);
10081
10037
  const id = ++metadata.lastId;
10082
- await this.api.updateDocumentInnerMetadata(metadata, tx);
10038
+ await this.updateDocumentInnerMetadata(metadata, tx);
10083
10039
  const dataplyDocument = Object.assign({
10084
10040
  _id: id
10085
10041
  }, document);
10086
- const pk = await this.api.insert(JSON.stringify(dataplyDocument), true, tx);
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 insert(document, tx) {
10100
- return this.api.writeLock(() => this.api.runWithDefault(async (tx2) => {
10101
- const { pk, document: dataplyDocument } = await this.insertDocument(document, tx2);
10102
- const flattenDocument = this.api.flattenDocument(dataplyDocument);
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.api.trees.get(field);
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 insertBatch(documents, tx) {
10122
- return this.api.writeLock(() => this.api.runWithDefault(async (tx2) => {
10123
- const metadata = await this.api.getDocumentInnerMetadata(tx2);
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.api.updateDocumentInnerMetadata(metadata, tx2);
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.api.flattenDocument(dataplyDocument);
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 this.api.insertBatch(dataplyDocuments, true, tx2);
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.api.trees) {
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.api.trees) {
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 (const pk of pks) {
10180
- const doc = await this.api.getDocument(pk, tx);
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.api.flattenDocument(doc);
10184
- const newFlatDoc = this.api.flattenDocument(updatedDoc);
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.api.update(pk, JSON.stringify(updatedDoc), tx);
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.api.writeLock(() => this.api.runWithDefault(async (tx2) => {
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.api.writeLock(() => this.api.runWithDefault(async (tx2) => {
10190
+ return this.writeLock(() => this.runWithDefault(async (tx2) => {
10234
10191
  return this.updateInternal(query, (doc) => {
10235
- const partialUpdate = typeof newRecord === "function" ? newRecord(doc) : newRecord;
10236
- delete partialUpdate._id;
10237
- return { ...doc, ...partialUpdate };
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 delete(query, tx) {
10248
- return this.api.writeLock(() => this.api.runWithDefault(async (tx2) => {
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 (const pk of pks) {
10252
- const doc = await this.api.getDocument(pk, tx2);
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.api.flattenDocument(doc);
10255
- for (const [field, tree] of this.api.trees) {
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 this.api.delete(pk, true, tx2);
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 count(query, tx) {
10273
- return this.api.readLock(() => this.api.runWithDefault(async (tx2) => {
10231
+ async countDocuments(query, tx) {
10232
+ return this.readLock(() => this.runWithDefault(async (tx2) => {
10274
10233
  const pks = await this.getKeys(query);
10275
- return pks.size;
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
- select(query, options = {}, tx) {
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.api.streamWithDefault(async function* (tx2) {
10304
- const keySet = await self.getKeys(query, orderByField, sortOrder);
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 CHUNK_SIZE = 100;
10274
+ let currentChunkSize = self.options.pageSize;
10317
10275
  if (!isDriverOrderByField && orderByField) {
10318
- const isTopK = limit !== Infinity;
10319
- const heapSizeLimit = isTopK ? limit + offset : Infinity;
10320
- const heapComparator = (a, b) => {
10321
- const aVal = a[orderByField] ?? a._id;
10322
- const bVal = b[orderByField] ?? b._id;
10323
- const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
10324
- return sortOrder === "asc" ? -cmp : cmp;
10325
- };
10326
- const heap = isTopK ? new BinaryHeap(heapComparator) : null;
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
- const firstChunk = Array.from(keys.subarray(i, i + 100));
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 nextChunk = Array.from(keys.subarray(i, i + CHUNK_SIZE));
10354
- nextSortChunkPromise = self.api.selectMany(nextChunk, false, tx2);
10355
- i += nextChunk.length;
10356
- }
10357
- while (nextSortChunkPromise) {
10358
- const stringifiedResults = await nextSortChunkPromise;
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 nextChunk = Array.from(keys.subarray(i, i + CHUNK_SIZE));
10361
- nextSortChunkPromise = self.api.selectMany(nextChunk, false, tx2);
10362
- i += nextChunk.length;
10363
- } else {
10364
- nextSortChunkPromise = null;
10365
- }
10366
- for (const stringified of stringifiedResults) {
10367
- if (!stringified) continue;
10368
- const doc = JSON.parse(stringified);
10369
- if (heap) {
10370
- if (heap.size < heapSizeLimit) {
10371
- heap.push(doc);
10372
- } else if (heapComparator(doc, heap.peek()) > 0) {
10373
- heap.replace(doc);
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
- } else {
10376
- results.push(doc);
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 finalResults = heap ? heap.toArray() : results;
10381
- finalResults.sort((a, b) => {
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 = finalResults.slice(start, end);
10390
- for (const doc of limitedResults) {
10391
- yield doc;
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
- if (i >= totalKeys || yieldedCount >= limit) return;
10397
- const pksToFetchCount = Math.min(100, limit - yieldedCount);
10398
- const firstChunk = Array.from(keys.subarray(i, i + pksToFetchCount));
10399
- const firstResults = await self.api.selectMany(firstChunk, false, tx2);
10400
- let totalBytes = 0;
10401
- let count = 0;
10402
- for (const s of firstResults) {
10403
- if (!s) continue;
10404
- yield JSON.parse(s);
10405
- yieldedCount++;
10406
- totalBytes += s.length;
10407
- count++;
10408
- if (yieldedCount >= limit) break;
10409
- }
10410
- const avgSize = count > 0 ? totalBytes / count : 1024;
10411
- CHUNK_SIZE = Math.max(32, Math.floor(os.freemem() * 0.1 / avgSize));
10412
- i += firstChunk.length;
10413
- let nextStreamChunkPromise = null;
10414
- if (i < totalKeys && yieldedCount < limit) {
10415
- const nextPksToFetchCount = Math.min(CHUNK_SIZE, limit - yieldedCount);
10416
- const nextChunk = Array.from(keys.subarray(i, i + nextPksToFetchCount));
10417
- nextStreamChunkPromise = self.api.selectMany(nextChunk, false, tx2);
10418
- i += nextChunk.length;
10419
- }
10420
- while (nextStreamChunkPromise) {
10421
- const stringifiedResults = await nextStreamChunkPromise;
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
- } else {
10432
- nextStreamChunkPromise = null;
10433
- }
10434
- for (const stringified of stringifiedResults) {
10435
- if (!stringified) continue;
10436
- yield JSON.parse(stringified);
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 '../document';
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 { DataplyTreeValue, DocumentDataplyInnerMetadata, DocumentDataplyOptions, DocumentJSON, FlattenedDocumentJSON, Primitive, DocumentDataplyQuery, DocumentDataplyIndexedQuery, DocumentDataplyCondition, DataplyDocument, DocumentDataplyMetadata, DocumentDataplyQueryOptions, IndexConfig } 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
- 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-alpha.0",
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-alpha.0"
45
+ "dataply": "^0.0.21"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/jest": "^30.0.0",