document-dataply 0.0.9-alpha.4 → 0.0.9-alpha.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
@@ -10572,8 +10572,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10572
10572
  if (metadata.lastId === 0) {
10573
10573
  return 0;
10574
10574
  }
10575
- const indexTxMap = {};
10576
- const indexEntryMap = /* @__PURE__ */ new Map();
10575
+ let indexTxMap = {};
10577
10576
  for (const indexName of backfillTargets) {
10578
10577
  const tree = this.trees.get(indexName);
10579
10578
  if (tree && indexName !== "_id") {
@@ -10581,6 +10580,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10581
10580
  }
10582
10581
  }
10583
10582
  let backfilledCount = 0;
10583
+ let chunkCount = 0;
10584
+ const CHUNK_SIZE = 1e3;
10584
10585
  const idTree = this.trees.get("_id");
10585
10586
  if (!idTree) {
10586
10587
  throw new Error("ID tree not found");
@@ -10609,10 +10610,6 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10609
10610
  const keyToInsert = this.getTokenKey(k, token);
10610
10611
  const entry = { k, v: token };
10611
10612
  batchInsertData.push([keyToInsert, entry]);
10612
- if (!indexEntryMap.has(btx)) {
10613
- indexEntryMap.set(btx, []);
10614
- }
10615
- indexEntryMap.get(btx).push({ k: keyToInsert, v: entry });
10616
10613
  }
10617
10614
  await btx.batchInsert(batchInsertData);
10618
10615
  } else {
@@ -10620,34 +10617,42 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10620
10617
  if (indexVal === void 0) continue;
10621
10618
  const entry = { k, v: indexVal };
10622
10619
  const batchInsertData = [[k, entry]];
10623
- if (!indexEntryMap.has(btx)) {
10624
- indexEntryMap.set(btx, []);
10625
- }
10626
- indexEntryMap.get(btx).push(entry);
10627
10620
  await btx.batchInsert(batchInsertData);
10628
10621
  }
10629
10622
  }
10630
10623
  backfilledCount++;
10631
- }
10632
- const btxs = Object.values(indexTxMap);
10633
- const success = [];
10634
- try {
10635
- for (const btx of btxs) {
10636
- await btx.commit();
10637
- success.push(btx);
10638
- }
10639
- } catch (err) {
10640
- for (const btx of btxs) {
10641
- await btx.rollback();
10624
+ chunkCount++;
10625
+ if (chunkCount >= CHUNK_SIZE) {
10626
+ try {
10627
+ for (const btx of Object.values(indexTxMap)) {
10628
+ await btx.commit();
10629
+ }
10630
+ } catch (err) {
10631
+ for (const btx of Object.values(indexTxMap)) {
10632
+ await btx.rollback();
10633
+ }
10634
+ throw err;
10635
+ }
10636
+ for (const indexName of backfillTargets) {
10637
+ const tree = this.trees.get(indexName);
10638
+ if (tree && indexName !== "_id") {
10639
+ indexTxMap[indexName] = await tree.createTransaction();
10640
+ }
10641
+ }
10642
+ chunkCount = 0;
10642
10643
  }
10643
- for (const btx of success) {
10644
- const entries = indexEntryMap.get(btx);
10645
- if (!entries) continue;
10646
- for (const entry of entries) {
10647
- await btx.delete(entry.k, entry);
10644
+ }
10645
+ if (chunkCount > 0) {
10646
+ try {
10647
+ for (const btx of Object.values(indexTxMap)) {
10648
+ await btx.commit();
10648
10649
  }
10650
+ } catch (err) {
10651
+ for (const btx of Object.values(indexTxMap)) {
10652
+ await btx.rollback();
10653
+ }
10654
+ throw err;
10649
10655
  }
10650
- throw err;
10651
10656
  }
10652
10657
  this.pendingBackfillFields = [];
10653
10658
  return backfilledCount;
@@ -10805,27 +10810,61 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10805
10810
  const condition = query[primaryField];
10806
10811
  const treeTx = await tree.createTransaction();
10807
10812
  let score = 0;
10808
- const coveredFields = config.fields.filter((f) => queryFields.has(f));
10809
- score += coveredFields.length;
10810
- if (condition) {
10811
- if (typeof condition !== "object" || condition === null) {
10812
- score += 100;
10813
- } else if ("primaryEqual" in condition || "equal" in condition) {
10814
- score += 100;
10815
- } else if ("primaryGte" in condition || "primaryLte" in condition || "primaryGt" in condition || "primaryLt" in condition || "gte" in condition || "lte" in condition || "gt" in condition || "lt" in condition) {
10816
- score += 50;
10817
- } else if ("primaryOr" in condition || "or" in condition) {
10818
- score += 20;
10819
- } else if ("like" in condition) {
10820
- score += 15;
10821
- } else {
10822
- score += 10;
10813
+ let isConsecutive = true;
10814
+ const coveredFields = [];
10815
+ const compositeVerifyFields = [];
10816
+ for (const field of config.fields) {
10817
+ if (!queryFields.has(field)) {
10818
+ isConsecutive = false;
10819
+ continue;
10820
+ }
10821
+ coveredFields.push(field);
10822
+ if (field !== primaryField) {
10823
+ compositeVerifyFields.push(field);
10824
+ }
10825
+ score += 1;
10826
+ if (isConsecutive) {
10827
+ const cond = query[field];
10828
+ if (cond !== void 0) {
10829
+ if (typeof cond !== "object" || cond === null) {
10830
+ score += 100;
10831
+ } else if ("primaryEqual" in cond || "equal" in cond) {
10832
+ score += 100;
10833
+ } else if ("primaryGte" in cond || "primaryLte" in cond || "primaryGt" in cond || "primaryLt" in cond || "gte" in cond || "lte" in cond || "gt" in cond || "lt" in cond) {
10834
+ score += 50;
10835
+ isConsecutive = false;
10836
+ } else if ("primaryOr" in cond || "or" in cond) {
10837
+ score += 20;
10838
+ isConsecutive = false;
10839
+ } else if ("like" in cond) {
10840
+ score += 15;
10841
+ isConsecutive = false;
10842
+ } else {
10843
+ score += 10;
10844
+ isConsecutive = false;
10845
+ }
10846
+ }
10823
10847
  }
10824
10848
  }
10825
- if (orderByField && primaryField === orderByField) {
10826
- score += 200;
10849
+ let isIndexOrderSupported = false;
10850
+ if (orderByField) {
10851
+ for (const field of config.fields) {
10852
+ if (field === orderByField) {
10853
+ isIndexOrderSupported = true;
10854
+ break;
10855
+ }
10856
+ const cond = query[field];
10857
+ let isExactMatch = false;
10858
+ if (cond !== void 0) {
10859
+ if (typeof cond !== "object" || cond === null) isExactMatch = true;
10860
+ else if ("primaryEqual" in cond || "equal" in cond) isExactMatch = true;
10861
+ }
10862
+ if (!isExactMatch) break;
10863
+ }
10864
+ if (isIndexOrderSupported) {
10865
+ score += 200;
10866
+ }
10827
10867
  }
10828
- const compositeVerifyFields = coveredFields.filter((f) => f !== primaryField);
10829
10868
  candidates.push({
10830
10869
  tree: treeTx,
10831
10870
  condition,
@@ -10833,7 +10872,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10833
10872
  indexName,
10834
10873
  isFtsMatch: false,
10835
10874
  score,
10836
- compositeVerifyFields
10875
+ compositeVerifyFields,
10876
+ isIndexOrderSupported
10837
10877
  });
10838
10878
  } else if (config.type === "fts") {
10839
10879
  const field = config.fields;
@@ -10851,7 +10891,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10851
10891
  isFtsMatch: true,
10852
10892
  matchTokens,
10853
10893
  score: 90,
10854
- compositeVerifyFields: []
10894
+ compositeVerifyFields: [],
10895
+ isIndexOrderSupported: false
10855
10896
  });
10856
10897
  }
10857
10898
  }
@@ -10894,33 +10935,30 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10894
10935
  getTokenKey(pk, token) {
10895
10936
  return pk + ":" + token;
10896
10937
  }
10897
- async applyCandidateByFTS(candidate, matchedTokens, filterValues, order) {
10938
+ async *applyCandidateByFTSStream(candidate, matchedTokens, filterValues, order) {
10898
10939
  const keys = /* @__PURE__ */ new Set();
10899
10940
  for (let i = 0, len = matchedTokens.length; i < len; i++) {
10900
10941
  const token = matchedTokens[i];
10901
- const pairs = await candidate.tree.where(
10942
+ for await (const pair of candidate.tree.whereStream(
10902
10943
  { primaryEqual: { v: token } },
10903
- {
10904
- order
10944
+ { order }
10945
+ )) {
10946
+ const pk = pair[1].k;
10947
+ if (filterValues && !filterValues.has(pk)) continue;
10948
+ if (!keys.has(pk)) {
10949
+ keys.add(pk);
10950
+ yield pk;
10905
10951
  }
10906
- );
10907
- for (const pair of pairs.values()) {
10908
- if (filterValues && !filterValues.has(pair.k)) continue;
10909
- keys.add(pair.k);
10910
10952
  }
10911
10953
  }
10912
- return keys;
10913
10954
  }
10914
10955
  /**
10915
10956
  * 특정 인덱스 후보를 조회하여 PK 집합을 필터링합니다.
10916
10957
  */
10917
- async applyCandidate(candidate, filterValues, order) {
10918
- return await candidate.tree.keys(
10958
+ applyCandidateStream(candidate, filterValues, order) {
10959
+ return candidate.tree.keysStream(
10919
10960
  candidate.condition,
10920
- {
10921
- filterValues,
10922
- order
10923
- }
10961
+ { filterValues, order }
10924
10962
  );
10925
10963
  }
10926
10964
  /**
@@ -10936,30 +10974,34 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10936
10974
  );
10937
10975
  if (!selectivity) return new Float64Array(0);
10938
10976
  const { driver, others, rollback } = selectivity;
10939
- const useIndexOrder = orderBy === void 0 || driver.field === orderBy;
10977
+ const useIndexOrder = orderBy === void 0 || driver.isIndexOrderSupported;
10940
10978
  const candidates = [driver, ...others];
10941
10979
  let keys = void 0;
10942
10980
  for (let i = 0, len = candidates.length; i < len; i++) {
10943
10981
  const candidate = candidates[i];
10944
10982
  const currentOrder = useIndexOrder ? sortOrder : void 0;
10945
10983
  if (candidate.isFtsMatch && candidate.matchTokens && candidate.matchTokens.length > 0) {
10946
- keys = await this.applyCandidateByFTS(
10984
+ const stream = this.applyCandidateByFTSStream(
10947
10985
  candidate,
10948
10986
  candidate.matchTokens,
10949
10987
  keys,
10950
10988
  currentOrder
10951
10989
  );
10990
+ keys = /* @__PURE__ */ new Set();
10991
+ for await (const pk of stream) keys.add(pk);
10952
10992
  } else {
10953
- keys = await this.applyCandidate(candidate, keys, currentOrder);
10993
+ const stream = this.applyCandidateStream(candidate, keys, currentOrder);
10994
+ keys = /* @__PURE__ */ new Set();
10995
+ for await (const pk of stream) keys.add(pk);
10954
10996
  }
10955
10997
  }
10956
10998
  rollback();
10957
10999
  return new Float64Array(Array.from(keys || []));
10958
11000
  }
10959
11001
  /**
10960
- * 드라이버 인덱스만으로 PK 가져옵니다. (교집합 없이)
11002
+ * 드라이버 인덱스만으로 PK 스트림을 가져옵니다. (교집합 없이)
10961
11003
  * selectDocuments에서 사용하며, 나머지 조건(others)은 스트리밍 중 tree.verify()로 검증합니다.
10962
- * @returns 드라이버 키 배열, others 후보 목록, rollback 함수. 또는 null.
11004
+ * @returns 드라이버 키 스트림, others 후보 목록, rollback 함수. 또는 null.
10963
11005
  */
10964
11006
  async getDriverKeys(query, orderBy, sortOrder = "asc") {
10965
11007
  const isQueryEmpty = Object.keys(query).length === 0;
@@ -10970,21 +11012,21 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10970
11012
  );
10971
11013
  if (!selectivity) return null;
10972
11014
  const { driver, others, compositeVerifyConditions, rollback } = selectivity;
10973
- const useIndexOrder = orderBy === void 0 || driver.field === orderBy;
11015
+ const useIndexOrder = orderBy === void 0 || driver.isIndexOrderSupported;
10974
11016
  const currentOrder = useIndexOrder ? sortOrder : void 0;
10975
- let keys;
11017
+ let keysStream;
10976
11018
  if (driver.isFtsMatch && driver.matchTokens && driver.matchTokens.length > 0) {
10977
- keys = await this.applyCandidateByFTS(
11019
+ keysStream = this.applyCandidateByFTSStream(
10978
11020
  driver,
10979
11021
  driver.matchTokens,
10980
11022
  void 0,
10981
11023
  currentOrder
10982
11024
  );
10983
11025
  } else {
10984
- keys = await this.applyCandidate(driver, void 0, currentOrder);
11026
+ keysStream = this.applyCandidateStream(driver, void 0, currentOrder);
10985
11027
  }
10986
11028
  return {
10987
- keys: new Float64Array(Array.from(keys)),
11029
+ keysStream,
10988
11030
  others,
10989
11031
  compositeVerifyConditions,
10990
11032
  isDriverOrderByField: useIndexOrder,
@@ -11334,28 +11376,17 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11334
11376
  return currentChunkSize;
11335
11377
  }
11336
11378
  /**
11337
- * Prefetch 방식으로 키 배열을 청크 단위로 조회하여 문서를 순회합니다.
11379
+ * Prefetch 방식으로 키 스트림을 청크 단위로 조회하여 문서를 순회합니다.
11338
11380
  * FTS 검증, 복합 인덱스 검증, others 후보에 대한 tree.verify() 검증을 통과한 문서만 yield 합니다.
11339
11381
  */
11340
- async *processChunkedKeysWithVerify(keys, startIdx, initialChunkSize, ftsConditions, compositeVerifyConditions, others, tx) {
11382
+ async *processChunkedKeysWithVerify(keysStream, startIdx, initialChunkSize, ftsConditions, compositeVerifyConditions, others, tx) {
11341
11383
  const verifyOthers = others.filter((o) => !o.isFtsMatch);
11342
- let i = startIdx;
11343
- const totalKeys = keys.length;
11344
11384
  let currentChunkSize = initialChunkSize;
11345
- let nextChunkPromise = null;
11346
- if (i < totalKeys) {
11347
- const endIdx = Math.min(i + currentChunkSize, totalKeys);
11348
- nextChunkPromise = this.selectMany(keys.subarray(i, endIdx), false, tx);
11349
- i = endIdx;
11350
- }
11351
- while (nextChunkPromise) {
11352
- const rawResults = await nextChunkPromise;
11353
- nextChunkPromise = null;
11354
- if (i < totalKeys) {
11355
- const endIdx = Math.min(i + currentChunkSize, totalKeys);
11356
- nextChunkPromise = this.selectMany(keys.subarray(i, endIdx), false, tx);
11357
- i = endIdx;
11358
- }
11385
+ let chunk = [];
11386
+ let dropped = 0;
11387
+ const processChunk = async (pks) => {
11388
+ const docs = [];
11389
+ const rawResults = await this.selectMany(new Float64Array(pks), false, tx);
11359
11390
  let chunkTotalSize = 0;
11360
11391
  for (let j = 0, len = rawResults.length; j < len; j++) {
11361
11392
  const s = rawResults[j];
@@ -11382,9 +11413,26 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11382
11413
  }
11383
11414
  if (!passed) continue;
11384
11415
  }
11385
- yield doc;
11416
+ docs.push(doc);
11386
11417
  }
11387
11418
  currentChunkSize = this.adjustChunkSize(currentChunkSize, chunkTotalSize);
11419
+ return docs;
11420
+ };
11421
+ for await (const pk of keysStream) {
11422
+ if (dropped < startIdx) {
11423
+ dropped++;
11424
+ continue;
11425
+ }
11426
+ chunk.push(pk);
11427
+ if (chunk.length >= currentChunkSize) {
11428
+ const docs = await processChunk(chunk);
11429
+ for (let j = 0; j < docs.length; j++) yield docs[j];
11430
+ chunk = [];
11431
+ }
11432
+ }
11433
+ if (chunk.length > 0) {
11434
+ const docs = await processChunk(chunk);
11435
+ for (let j = 0; j < docs.length; j++) yield docs[j];
11388
11436
  }
11389
11437
  }
11390
11438
  /**
@@ -11432,11 +11480,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11432
11480
  }
11433
11481
  const driverResult = await self.getDriverKeys(query, orderByField, sortOrder);
11434
11482
  if (!driverResult) return;
11435
- const { keys, others, compositeVerifyConditions, isDriverOrderByField, rollback } = driverResult;
11436
- if (keys.length === 0) {
11437
- rollback();
11438
- return;
11439
- }
11483
+ const { keysStream, others, compositeVerifyConditions, isDriverOrderByField, rollback } = driverResult;
11440
11484
  try {
11441
11485
  if (!isDriverOrderByField && orderByField) {
11442
11486
  const topK = limit === Infinity ? Infinity : offset + limit;
@@ -11451,7 +11495,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11451
11495
  }
11452
11496
  const results = [];
11453
11497
  for await (const doc of self.processChunkedKeysWithVerify(
11454
- keys,
11498
+ keysStream,
11455
11499
  0,
11456
11500
  self.options.pageSize,
11457
11501
  ftsConditions,
@@ -11487,16 +11531,23 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11487
11531
  yield limitedResults[j];
11488
11532
  }
11489
11533
  } else {
11534
+ const hasFilters = ftsConditions.length > 0 || compositeVerifyConditions.length > 0 || others.length > 0;
11535
+ const startIdx = hasFilters ? 0 : offset;
11490
11536
  let yieldedCount = 0;
11537
+ let skippedCount = hasFilters ? 0 : offset;
11491
11538
  for await (const doc of self.processChunkedKeysWithVerify(
11492
- keys,
11493
- offset,
11539
+ keysStream,
11540
+ startIdx,
11494
11541
  self.options.pageSize,
11495
11542
  ftsConditions,
11496
11543
  compositeVerifyConditions,
11497
11544
  others,
11498
11545
  tx2
11499
11546
  )) {
11547
+ if (skippedCount < offset) {
11548
+ skippedCount++;
11549
+ continue;
11550
+ }
11500
11551
  if (yieldedCount >= limit) break;
11501
11552
  yield doc;
11502
11553
  yieldedCount++;
@@ -127,6 +127,7 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON> extends DataplyA
127
127
  field: string;
128
128
  indexName: string;
129
129
  isFtsMatch: false;
130
+ isIndexOrderSupported: boolean;
130
131
  } | {
131
132
  tree: BPTreeAsync<string, V>;
132
133
  condition: Partial<DocumentDataplyCondition<U>>;
@@ -134,6 +135,7 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON> extends DataplyA
134
135
  indexName: string;
135
136
  isFtsMatch: true;
136
137
  matchTokens: string[];
138
+ isIndexOrderSupported: boolean;
137
139
  });
138
140
  others: ({
139
141
  tree: BPTreeAsync<number, V>;
@@ -141,6 +143,7 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON> extends DataplyA
141
143
  field: string;
142
144
  indexName: string;
143
145
  isFtsMatch: false;
146
+ isIndexOrderSupported: boolean;
144
147
  } | {
145
148
  tree: BPTreeAsync<string, V>;
146
149
  condition: Partial<DocumentDataplyCondition<U>>;
@@ -148,6 +151,7 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON> extends DataplyA
148
151
  indexName: string;
149
152
  isFtsMatch: true;
150
153
  matchTokens: string[];
154
+ isIndexOrderSupported: boolean;
151
155
  })[];
152
156
  compositeVerifyConditions: {
153
157
  field: string;
@@ -164,20 +168,20 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON> extends DataplyA
164
168
  smallChunkSize: number;
165
169
  };
166
170
  private getTokenKey;
167
- private applyCandidateByFTS;
171
+ private applyCandidateByFTSStream;
168
172
  /**
169
173
  * 특정 인덱스 후보를 조회하여 PK 집합을 필터링합니다.
170
174
  */
171
- private applyCandidate;
175
+ private applyCandidateStream;
172
176
  /**
173
177
  * 쿼리와 인덱스 선택을 기반으로 기본 키(Primary Keys)를 가져옵니다.
174
178
  * 쿼리 최적화를 통합하기 위한 내부 공통 메서드입니다.
175
179
  */
176
180
  getKeys(query: Partial<DocumentDataplyQuery<T>>, orderBy?: string, sortOrder?: 'asc' | 'desc'): Promise<Float64Array>;
177
181
  /**
178
- * 드라이버 인덱스만으로 PK 가져옵니다. (교집합 없이)
182
+ * 드라이버 인덱스만으로 PK 스트림을 가져옵니다. (교집합 없이)
179
183
  * selectDocuments에서 사용하며, 나머지 조건(others)은 스트리밍 중 tree.verify()로 검증합니다.
180
- * @returns 드라이버 키 배열, others 후보 목록, rollback 함수. 또는 null.
184
+ * @returns 드라이버 키 스트림, others 후보 목록, rollback 함수. 또는 null.
181
185
  */
182
186
  private getDriverKeys;
183
187
  private insertDocumentInternal;
@@ -250,7 +254,7 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON> extends DataplyA
250
254
  */
251
255
  private adjustChunkSize;
252
256
  /**
253
- * Prefetch 방식으로 키 배열을 청크 단위로 조회하여 문서를 순회합니다.
257
+ * Prefetch 방식으로 키 스트림을 청크 단위로 조회하여 문서를 순회합니다.
254
258
  * FTS 검증, 복합 인덱스 검증, others 후보에 대한 tree.verify() 검증을 통과한 문서만 yield 합니다.
255
259
  */
256
260
  private processChunkedKeysWithVerify;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-dataply",
3
- "version": "0.0.9-alpha.4",
3
+ "version": "0.0.9-alpha.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.24-alpha.0"
45
+ "dataply": "^0.0.24-alpha.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/jest": "^30.0.0",
package/readme.md CHANGED
@@ -54,16 +54,21 @@ async function main() {
54
54
  .Options({ wal: 'my-database.wal' })
55
55
  .Open('my-database.db');
56
56
 
57
- // Register indices before init (Recommended)
58
- await db.createIndex('name', { type: 'btree', fields: ['name'] });
59
- await db.createIndex('tags_0', { type: 'btree', fields: ['tags.0'] });
60
-
61
- // Composite Index support
62
- await db.createIndex('idx_name_age', { type: 'btree', fields: ['name', 'age'] });
63
-
64
57
  // Initialize database
65
58
  await db.init();
66
59
 
60
+ // Register indices
61
+ // use transaction to ensure atomicity
62
+ await db.migration(1, async (tx) => {
63
+ await db.createIndex('name', { type: 'btree', fields: ['name'] }, tx);
64
+ await db.createIndex('tags_0', { type: 'btree', fields: ['tags.0'] }, tx);
65
+
66
+ // Composite Index support
67
+ await db.createIndex('idx_name_age', { type: 'btree', fields: ['name', 'age'] }, tx);
68
+
69
+ console.log('Migration completed successfully');
70
+ });
71
+
67
72
  // Insert document
68
73
  const id = await db.insert({
69
74
  name: 'John Doe',