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 (
|
|
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
|
|
10136
|
-
tokens.add(word.slice(
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
-
{
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
10847
|
-
|
|
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 (
|
|
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 *
|
|
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
|
-
|
|
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
|
|
10937
|
-
if (
|
|
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(
|
|
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
|
-
|
|
10945
|
-
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
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" ?
|
|
11072
|
+
return sortOrder === "asc" ? cmp : -cmp;
|
|
10953
11073
|
});
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
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
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
|
|
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
|
|
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.
|
|
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
|
|
45
|
+
"dataply": "^0.0.23"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/jest": "^30.0.0",
|
|
49
|
-
"esbuild": "^0.27.
|
|
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;
|