document-dataply 0.0.7-alpha.2 → 0.0.8

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
@@ -10127,13 +10127,14 @@ function ngramTokenize(text, gramSize) {
10127
10127
  if (typeof text !== "string") return [];
10128
10128
  const tokens = /* @__PURE__ */ new Set();
10129
10129
  const words = text.split(/\s+/).filter(Boolean);
10130
- for (const word of words) {
10130
+ for (let i = 0, len = words.length; i < len; i++) {
10131
+ const word = words[i];
10131
10132
  if (word.length < gramSize) {
10132
10133
  if (word.length > 0) tokens.add(word);
10133
10134
  continue;
10134
10135
  }
10135
- for (let i = 0; i <= word.length - gramSize; i++) {
10136
- tokens.add(word.slice(i, i + gramSize));
10136
+ for (let j = 0, wLen = word.length; j <= wLen - gramSize; j++) {
10137
+ tokens.add(word.slice(j, j + gramSize));
10137
10138
  }
10138
10139
  }
10139
10140
  return Array.from(tokens);
@@ -10313,7 +10314,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10313
10314
  tokens = tokenize(v, indexConfig);
10314
10315
  }
10315
10316
  const batchInsertData = [];
10316
- for (const token of tokens) {
10317
+ for (let i = 0, len = tokens.length; i < len; i++) {
10318
+ const token = tokens[i];
10317
10319
  const keyToInsert = isFts ? this.getTokenKey(k, token) : k;
10318
10320
  const entry = { k, v: token };
10319
10321
  batchInsertData.push([keyToInsert, entry]);
@@ -10378,25 +10380,24 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10378
10380
  const data = JSON.parse(row);
10379
10381
  return data.magicString === "document-dataply" && data.version === 1;
10380
10382
  }
10383
+ flatten(obj, parentKey = "", result = {}) {
10384
+ for (const key in obj) {
10385
+ const newKey = parentKey ? `${parentKey}.${key}` : key;
10386
+ if (typeof obj[key] === "object" && obj[key] !== null) {
10387
+ this.flatten(obj[key], newKey, result);
10388
+ } else {
10389
+ result[newKey] = obj[key];
10390
+ }
10391
+ }
10392
+ return result;
10393
+ }
10381
10394
  /**
10382
10395
  * returns flattened document
10383
10396
  * @param document
10384
10397
  * @returns
10385
10398
  */
10386
10399
  flattenDocument(document) {
10387
- const result = {};
10388
- const flatten = (obj, parentKey = "") => {
10389
- for (const key in obj) {
10390
- const newKey = parentKey ? `${parentKey}.${key}` : key;
10391
- if (typeof obj[key] === "object" && obj[key] !== null) {
10392
- flatten(obj[key], newKey);
10393
- } else {
10394
- result[newKey] = obj[key];
10395
- }
10396
- }
10397
- };
10398
- flatten(document);
10399
- return result;
10400
+ return this.flatten(document, "", {});
10400
10401
  }
10401
10402
  async getDocumentMetadata(tx) {
10402
10403
  const metadata = await this.getMetadata(tx);
@@ -10501,6 +10502,23 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10501
10502
  };
10502
10503
  }
10503
10504
  }
10505
+ const ftsCandidate = candidates.find(
10506
+ (c) => c.isFtsMatch && c.matchTokens && c.matchTokens.length > 0
10507
+ );
10508
+ if (ftsCandidate) {
10509
+ const hasHigherPriority = candidates.some((c) => {
10510
+ if (c === ftsCandidate) return false;
10511
+ const cond = c.condition;
10512
+ return "equal" in cond || "primaryEqual" in cond;
10513
+ });
10514
+ if (!hasHigherPriority) {
10515
+ return {
10516
+ driver: ftsCandidate,
10517
+ others: candidates.filter((c) => c !== ftsCandidate),
10518
+ rollback
10519
+ };
10520
+ }
10521
+ }
10504
10522
  let res = import_dataply3.BPTreeAsync.ChooseDriver(candidates);
10505
10523
  if (!res && candidates.length > 0) {
10506
10524
  res = candidates[0];
@@ -10528,17 +10546,17 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10528
10546
  }
10529
10547
  async applyCandidateByFTS(candidate, matchedTokens, filterValues, order) {
10530
10548
  const keys = /* @__PURE__ */ new Set();
10531
- for (const token of matchedTokens) {
10549
+ for (let i = 0, len = matchedTokens.length; i < len; i++) {
10550
+ const token = matchedTokens[i];
10532
10551
  const pairs = await candidate.tree.where(
10533
10552
  { primaryEqual: { v: token } },
10534
- { order }
10535
- );
10536
- for (const c of pairs.values()) {
10537
- if (!c || typeof c.k !== "number") continue;
10538
- const dpk = c.k;
10539
- if (filterValues === void 0 || filterValues.has(dpk)) {
10540
- keys.add(dpk);
10553
+ {
10554
+ order
10541
10555
  }
10556
+ );
10557
+ for (const pair of pairs.values()) {
10558
+ if (filterValues && !filterValues.has(pair.k)) continue;
10559
+ keys.add(pair.k);
10542
10560
  }
10543
10561
  }
10544
10562
  return keys;
@@ -10571,7 +10589,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10571
10589
  const useIndexOrder = orderBy === void 0 || driver.field === orderBy;
10572
10590
  const candidates = [driver, ...others];
10573
10591
  let keys = void 0;
10574
- for (const candidate of candidates) {
10592
+ for (let i = 0, len = candidates.length; i < len; i++) {
10593
+ const candidate = candidates[i];
10575
10594
  const currentOrder = useIndexOrder ? sortOrder : void 0;
10576
10595
  if (candidate.isFtsMatch && candidate.matchTokens && candidate.matchTokens.length > 0) {
10577
10596
  keys = await this.applyCandidateByFTS(
@@ -10587,6 +10606,39 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10587
10606
  rollback();
10588
10607
  return new Float64Array(Array.from(keys || []));
10589
10608
  }
10609
+ /**
10610
+ * 드라이버 인덱스만으로 PK를 가져옵니다. (교집합 없이)
10611
+ * selectDocuments에서 사용하며, 나머지 조건(others)은 스트리밍 중 tree.verify()로 검증합니다.
10612
+ * @returns 드라이버 키 배열, others 후보 목록, rollback 함수. 또는 null.
10613
+ */
10614
+ async getDriverKeys(query, orderBy, sortOrder = "asc") {
10615
+ const isQueryEmpty = Object.keys(query).length === 0;
10616
+ const normalizedQuery = isQueryEmpty ? { _id: { gte: 0 } } : query;
10617
+ const selectivity = await this.getSelectivityCandidate(
10618
+ this.verboseQuery(normalizedQuery),
10619
+ orderBy
10620
+ );
10621
+ if (!selectivity) return null;
10622
+ const { driver, others, rollback } = selectivity;
10623
+ const useIndexOrder = orderBy === void 0 || driver.field === orderBy;
10624
+ const currentOrder = useIndexOrder ? sortOrder : void 0;
10625
+ let keys;
10626
+ if (driver.isFtsMatch && driver.matchTokens && driver.matchTokens.length > 0) {
10627
+ keys = await this.applyCandidateByFTS(
10628
+ driver,
10629
+ driver.matchTokens,
10630
+ void 0,
10631
+ currentOrder
10632
+ );
10633
+ } else {
10634
+ keys = await this.applyCandidate(driver, void 0, currentOrder);
10635
+ }
10636
+ return {
10637
+ keys: new Float64Array(Array.from(keys)),
10638
+ others,
10639
+ rollback
10640
+ };
10641
+ }
10590
10642
  async insertDocumentInternal(document, tx) {
10591
10643
  const metadata = await this.getDocumentInnerMetadata(tx);
10592
10644
  const id = ++metadata.lastId;
@@ -10622,7 +10674,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10622
10674
  if (isFts) {
10623
10675
  tokens = tokenize(v, indexConfig);
10624
10676
  }
10625
- for (const token of tokens) {
10677
+ for (let i = 0, len = tokens.length; i < len; i++) {
10678
+ const token = tokens[i];
10626
10679
  const keyToInsert = isFts ? this.getTokenKey(dpk, token) : dpk;
10627
10680
  const [error] = await catchPromise(tree.insert(keyToInsert, { k: dpk, v: token }));
10628
10681
  if (error) {
@@ -10676,7 +10729,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10676
10729
  if (isFts) {
10677
10730
  tokens = tokenize(v, indexConfig);
10678
10731
  }
10679
- for (const token of tokens) {
10732
+ for (let j = 0, len2 = tokens.length; j < len2; j++) {
10733
+ const token = tokens[j];
10680
10734
  const keyToInsert = isFts ? this.getTokenKey(item.pk, token) : item.pk;
10681
10735
  batchInsertData.push([keyToInsert, { k: item.pk, v: token }]);
10682
10736
  }
@@ -10727,7 +10781,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10727
10781
  if (isFts && typeof oldV === "string") {
10728
10782
  oldTokens = tokenize(oldV, indexConfig);
10729
10783
  }
10730
- for (const oldToken of oldTokens) {
10784
+ for (let j = 0, len2 = oldTokens.length; j < len2; j++) {
10785
+ const oldToken = oldTokens[j];
10731
10786
  const keyToDelete = isFts ? this.getTokenKey(pk, oldToken) : pk;
10732
10787
  await treeTx.delete(keyToDelete, { k: pk, v: oldToken });
10733
10788
  }
@@ -10738,7 +10793,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10738
10793
  newTokens = tokenize(newV, indexConfig);
10739
10794
  }
10740
10795
  const batchInsertData = [];
10741
- for (const newToken of newTokens) {
10796
+ for (let j = 0, len2 = newTokens.length; j < len2; j++) {
10797
+ const newToken = newTokens[j];
10742
10798
  const keyToInsert = isFts ? this.getTokenKey(pk, newToken) : pk;
10743
10799
  batchInsertData.push([keyToInsert, { k: pk, v: newToken }]);
10744
10800
  }
@@ -10816,7 +10872,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10816
10872
  if (isFts) {
10817
10873
  tokens = tokenize(v, indexConfig);
10818
10874
  }
10819
- for (const token of tokens) {
10875
+ for (let j = 0, len2 = tokens.length; j < len2; j++) {
10876
+ const token = tokens[j];
10820
10877
  const keyToDelete = isFts ? this.getTokenKey(pk, token) : pk;
10821
10878
  await tree.delete(keyToDelete, { k: pk, v: token });
10822
10879
  }
@@ -10843,10 +10900,13 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10843
10900
  * FTS 조건에 대해 문서가 유효한지 검증합니다.
10844
10901
  */
10845
10902
  verifyFts(doc, ftsConditions) {
10846
- for (const { field, matchTokens } of ftsConditions) {
10847
- const docValue = this.flattenDocument(doc)[field];
10903
+ const flatDoc = this.flattenDocument(doc);
10904
+ for (let i = 0, len = ftsConditions.length; i < len; i++) {
10905
+ const { field, matchTokens } = ftsConditions[i];
10906
+ const docValue = flatDoc[field];
10848
10907
  if (typeof docValue !== "string") return false;
10849
- for (const token of matchTokens) {
10908
+ for (let j = 0, jLen = matchTokens.length; j < jLen; j++) {
10909
+ const token = matchTokens[j];
10850
10910
  if (!docValue.includes(token)) return false;
10851
10911
  }
10852
10912
  }
@@ -10864,9 +10924,11 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10864
10924
  }
10865
10925
  /**
10866
10926
  * Prefetch 방식으로 키 배열을 청크 단위로 조회하여 문서를 순회합니다.
10867
- * FTS 검증을 통과한 문서만 yield 합니다.
10927
+ * FTS 검증 및 others 후보에 대한 tree.verify() 검증을 통과한 문서만 yield 합니다.
10928
+ * 교집합 대신 스트리밍 중 검증하여 첫 결과 반환 시간을 단축합니다.
10868
10929
  */
10869
- async *processChunkedKeys(keys, startIdx, initialChunkSize, ftsConditions, tx) {
10930
+ async *processChunkedKeysWithVerify(keys, startIdx, initialChunkSize, ftsConditions, others, tx) {
10931
+ const verifyOthers = others.filter((o) => !o.isFtsMatch);
10870
10932
  let i = startIdx;
10871
10933
  const totalKeys = keys.length;
10872
10934
  let currentChunkSize = initialChunkSize;
@@ -10890,8 +10952,26 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10890
10952
  if (!s) continue;
10891
10953
  const doc = JSON.parse(s);
10892
10954
  chunkTotalSize += s.length * 2;
10893
- if (!this.verifyFts(doc, ftsConditions)) continue;
10894
- yield { doc, rawSize: s.length * 2 };
10955
+ if (ftsConditions.length > 0 && !this.verifyFts(doc, ftsConditions)) continue;
10956
+ if (verifyOthers.length > 0) {
10957
+ const flatDoc = this.flattenDocument(doc);
10958
+ let passed = true;
10959
+ for (let k = 0, kLen = verifyOthers.length; k < kLen; k++) {
10960
+ const other = verifyOthers[k];
10961
+ const fieldValue = flatDoc[other.field];
10962
+ if (fieldValue === void 0) {
10963
+ passed = false;
10964
+ break;
10965
+ }
10966
+ const treeValue = { k: doc._id, v: fieldValue };
10967
+ if (!other.tree.verify(treeValue, other.condition)) {
10968
+ passed = false;
10969
+ break;
10970
+ }
10971
+ }
10972
+ if (!passed) continue;
10973
+ }
10974
+ yield doc;
10895
10975
  }
10896
10976
  currentChunkSize = this.adjustChunkSize(currentChunkSize, chunkTotalSize);
10897
10977
  }
@@ -10933,73 +11013,86 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10933
11013
  }
10934
11014
  }
10935
11015
  }
10936
- const keys = await self.getKeys(query, orderByField, sortOrder);
10937
- if (keys.length === 0) return;
11016
+ const driverResult = await self.getDriverKeys(query, orderByField, sortOrder);
11017
+ if (!driverResult) return;
11018
+ const { keys, others, rollback } = driverResult;
11019
+ if (keys.length === 0) {
11020
+ rollback();
11021
+ return;
11022
+ }
11023
+ const isQueryEmpty = Object.keys(query).length === 0;
11024
+ const normalizedQuery = isQueryEmpty ? { _id: { gte: 0 } } : query;
10938
11025
  const selectivity = await self.getSelectivityCandidate(
10939
- self.verboseQuery(query),
11026
+ self.verboseQuery(normalizedQuery),
10940
11027
  orderByField
10941
11028
  );
10942
11029
  const isDriverOrderByField = orderByField === void 0 || selectivity && selectivity.driver.field === orderByField;
10943
11030
  if (selectivity) selectivity.rollback();
10944
- if (!isDriverOrderByField && orderByField) {
10945
- const topK = limit === Infinity ? Infinity : offset + limit;
10946
- let heap = null;
10947
- if (topK !== Infinity) {
10948
- heap = new BinaryHeap((a, b) => {
11031
+ try {
11032
+ if (!isDriverOrderByField && orderByField) {
11033
+ const topK = limit === Infinity ? Infinity : offset + limit;
11034
+ let heap = null;
11035
+ if (topK !== Infinity) {
11036
+ heap = new BinaryHeap((a, b) => {
11037
+ const aVal = a[orderByField] ?? a._id;
11038
+ const bVal = b[orderByField] ?? b._id;
11039
+ const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
11040
+ return sortOrder === "asc" ? -cmp : cmp;
11041
+ });
11042
+ }
11043
+ const results = [];
11044
+ for await (const doc of self.processChunkedKeysWithVerify(
11045
+ keys,
11046
+ 0,
11047
+ self.options.pageSize,
11048
+ ftsConditions,
11049
+ others,
11050
+ tx2
11051
+ )) {
11052
+ if (heap) {
11053
+ if (heap.size < topK) heap.push(doc);
11054
+ else {
11055
+ const top = heap.peek();
11056
+ if (top) {
11057
+ const aVal = doc[orderByField] ?? doc._id;
11058
+ const bVal = top[orderByField] ?? top._id;
11059
+ const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
11060
+ if (sortOrder === "asc" ? cmp < 0 : cmp > 0) heap.replace(doc);
11061
+ }
11062
+ }
11063
+ } else {
11064
+ results.push(doc);
11065
+ }
11066
+ }
11067
+ const finalDocs = heap ? heap.toArray() : results;
11068
+ finalDocs.sort((a, b) => {
10949
11069
  const aVal = a[orderByField] ?? a._id;
10950
11070
  const bVal = b[orderByField] ?? b._id;
10951
11071
  const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
10952
- return sortOrder === "asc" ? -cmp : cmp;
11072
+ return sortOrder === "asc" ? cmp : -cmp;
10953
11073
  });
10954
- }
10955
- const results = [];
10956
- for await (const { doc } of self.processChunkedKeys(
10957
- keys,
10958
- 0,
10959
- self.options.pageSize,
10960
- ftsConditions,
10961
- tx2
10962
- )) {
10963
- if (heap) {
10964
- if (heap.size < topK) heap.push(doc);
10965
- else {
10966
- const top = heap.peek();
10967
- if (top) {
10968
- const aVal = doc[orderByField] ?? doc._id;
10969
- const bVal = top[orderByField] ?? top._id;
10970
- const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
10971
- if (sortOrder === "asc" ? cmp < 0 : cmp > 0) heap.replace(doc);
10972
- }
10973
- }
10974
- } else {
10975
- results.push(doc);
11074
+ const end = limit === Infinity ? void 0 : offset + limit;
11075
+ const limitedResults = finalDocs.slice(offset, end);
11076
+ for (let j = 0, len = limitedResults.length; j < len; j++) {
11077
+ yield limitedResults[j];
10976
11078
  }
10977
- }
10978
- const finalDocs = heap ? heap.toArray() : results;
10979
- finalDocs.sort((a, b) => {
10980
- const aVal = a[orderByField] ?? a._id;
10981
- const bVal = b[orderByField] ?? b._id;
10982
- const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
10983
- return sortOrder === "asc" ? cmp : -cmp;
10984
- });
10985
- const end = limit === Infinity ? void 0 : offset + limit;
10986
- const limitedResults = finalDocs.slice(offset, end);
10987
- for (let j = 0, len = limitedResults.length; j < len; j++) {
10988
- yield limitedResults[j];
10989
- }
10990
- } else {
10991
- let yieldedCount = 0;
10992
- for await (const { doc } of self.processChunkedKeys(
10993
- keys,
10994
- offset,
10995
- self.options.pageSize,
10996
- ftsConditions,
10997
- tx2
10998
- )) {
10999
- if (yieldedCount >= limit) break;
11000
- yield doc;
11001
- yieldedCount++;
11002
- }
11079
+ } else {
11080
+ let yieldedCount = 0;
11081
+ for await (const doc of self.processChunkedKeysWithVerify(
11082
+ keys,
11083
+ offset,
11084
+ self.options.pageSize,
11085
+ ftsConditions,
11086
+ others,
11087
+ tx2
11088
+ )) {
11089
+ if (yieldedCount >= limit) break;
11090
+ yield doc;
11091
+ yieldedCount++;
11092
+ }
11093
+ }
11094
+ } finally {
11095
+ rollback();
11003
11096
  }
11004
11097
  }, tx);
11005
11098
  const drain = async () => {
@@ -1,5 +1,5 @@
1
- import { ValueComparator } from 'dataply';
2
1
  import type { DataplyTreeValue, Primitive } from '../../types';
2
+ import { ValueComparator } from 'dataply';
3
3
  export declare class DocumentValueComparator<T extends DataplyTreeValue<U>, U extends Primitive> extends ValueComparator<T> {
4
4
  primaryAsc(a: T, b: T): number;
5
5
  asc(a: T, b: T): number;
@@ -26,6 +26,7 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON, IC extends Index
26
26
  createDocumentInnerMetadata(indices: DocumentDataplyInnerMetadata['indices']): DocumentDataplyInnerMetadata;
27
27
  initializeDocumentFile(tx: Transaction): Promise<void>;
28
28
  verifyDocumentFile(tx: Transaction): Promise<boolean>;
29
+ private flatten;
29
30
  /**
30
31
  * returns flattened document
31
32
  * @param document
@@ -93,6 +94,12 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON, IC extends Index
93
94
  * 쿼리 최적화를 통합하기 위한 내부 공통 메서드입니다.
94
95
  */
95
96
  getKeys(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, orderBy?: keyof IC | '_id', sortOrder?: 'asc' | 'desc'): Promise<Float64Array>;
97
+ /**
98
+ * 드라이버 인덱스만으로 PK를 가져옵니다. (교집합 없이)
99
+ * selectDocuments에서 사용하며, 나머지 조건(others)은 스트리밍 중 tree.verify()로 검증합니다.
100
+ * @returns 드라이버 키 배열, others 후보 목록, rollback 함수. 또는 null.
101
+ */
102
+ private getDriverKeys;
96
103
  private insertDocumentInternal;
97
104
  /**
98
105
  * Insert a document into the database
@@ -156,9 +163,10 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON, IC extends Index
156
163
  private adjustChunkSize;
157
164
  /**
158
165
  * Prefetch 방식으로 키 배열을 청크 단위로 조회하여 문서를 순회합니다.
159
- * FTS 검증을 통과한 문서만 yield 합니다.
166
+ * FTS 검증 및 others 후보에 대한 tree.verify() 검증을 통과한 문서만 yield 합니다.
167
+ * 교집합 대신 스트리밍 중 검증하여 첫 결과 반환 시간을 단축합니다.
160
168
  */
161
- private processChunkedKeys;
169
+ private processChunkedKeysWithVerify;
162
170
  /**
163
171
  * Select documents from the database
164
172
  * @param query The query to use (only indexed fields + _id allowed)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-dataply",
3
- "version": "0.0.7-alpha.2",
3
+ "version": "0.0.8",
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,11 +42,11 @@
42
42
  "dataply"
43
43
  ],
44
44
  "dependencies": {
45
- "dataply": "^0.0.23-alpha.2"
45
+ "dataply": "^0.0.23"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/jest": "^30.0.0",
49
- "esbuild": "^0.27.2",
49
+ "esbuild": "^0.27.3",
50
50
  "jest": "^30.2.0",
51
51
  "ts-jest": "^29.4.6",
52
52
  "typescript": "^5.9.3"
package/readme.md CHANGED
@@ -124,9 +124,13 @@ const ids = await db.insertBatch([
124
124
  | `equal`, `notEqual` | Equality check |
125
125
  | `like` | Pattern matching |
126
126
  | `or` | Matching within an array |
127
+ | `match` | Full-text search (Requires FTS Index) |
127
128
 
128
129
  For detailed operator usage, index constraints (including full scans), and sorting methods, see the [Query Guide (QUERY.md)](./docs/QUERY.md).
129
130
 
131
+ > [!IMPORTANT]
132
+ > **Full-Text Search (match)**: To use the `match` operator, you must configure the field as an FTS index (e.g., `{ type: 'fts', tokenizer: 'whitespace' }`). Standard boolean indices do not support `match`. See [QUERY.md](./docs/QUERY.md#4-full-text-search-fts-indexing) for details.
133
+
130
134
  ### Transactions
131
135
 
132
136
  Ensure data integrity with ACID-compliant transactions. Use `commit()` and `rollback()` to process multiple operations atomically.
@@ -1 +0,0 @@
1
- export declare function fastStringHash(str: string): number;