reg-cli 0.18.16 → 0.19.0-rc0
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/README.md +155 -46
- package/dist/cli.cjs +221 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +221 -0
- package/dist/entry.cjs +119 -0
- package/dist/entry.d.cts +15 -0
- package/dist/entry.d.mts +15 -0
- package/dist/entry.mjs +118 -0
- package/dist/index.cjs +212 -0
- package/dist/index.d.cts +52 -0
- package/dist/index.d.mts +52 -0
- package/dist/index.mjs +206 -0
- package/dist/progress-BB7D-xke.mjs +28 -0
- package/dist/progress-UOiKHaTY.cjs +33 -0
- package/dist/reg.wasm +0 -0
- package/dist/rolldown-runtime-D_mwlA32.cjs +43 -0
- package/{report/ui/dist/worker.js → dist/shared/report-worker.js} +859 -247
- package/dist/tracing-7HO8hac9.mjs +378 -0
- package/dist/tracing-DlLL541f.cjs +445 -0
- package/dist/worker.cjs +143 -0
- package/dist/worker.d.cts +1 -0
- package/dist/worker.d.mts +1 -0
- package/dist/worker.mjs +142 -0
- package/dist/ximgdiff-DzkRRnkW.mjs +41 -0
- package/dist/ximgdiff-wmW1BH1H.cjs +41 -0
- package/package.json +61 -112
- package/dist/.keep +0 -0
- package/dist/cli.js +0 -214
- package/dist/diff.js +0 -80
- package/dist/icon.js +0 -12
- package/dist/image-finder.js +0 -25
- package/dist/index.js +0 -200
- package/dist/log.js +0 -21
- package/dist/process-adaptor.js +0 -40
- package/dist/report.js +0 -170
- package/dist/tracing.js +0 -169
- package/report/assets/favicon_failure.png +0 -0
- package/report/assets/favicon_success.png +0 -0
- package/report/sample/actual/sample.png +0 -0
- package/report/sample/diff/sample.png +0 -0
- package/report/sample/expected/sample.png +0 -0
- package/report/ui/dist/report.js +0 -123
- package/report/ui/dist/style.css +0 -1
- package/template/template.html +0 -20
- /package/{template → dist/shared}/worker_pre.js +0 -0
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
if (typeof value == "string") {
|
|
8
8
|
return value;
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
if (typeof value === "bigint") {
|
|
11
|
+
return value.toString();
|
|
12
|
+
}
|
|
13
|
+
const result = value + "";
|
|
11
14
|
return result == "0" && 1 / value == -Infinity ? "-0" : result;
|
|
12
15
|
}
|
|
13
16
|
function toString(value) {
|
|
@@ -49,7 +52,7 @@
|
|
|
49
52
|
this._keyMap = {};
|
|
50
53
|
let totalWeight = 0;
|
|
51
54
|
keys.forEach((key) => {
|
|
52
|
-
|
|
55
|
+
const obj = createKey(key);
|
|
53
56
|
this._keys.push(obj);
|
|
54
57
|
this._keyMap[obj.id] = obj;
|
|
55
58
|
totalWeight += obj.weight;
|
|
@@ -94,7 +97,13 @@
|
|
|
94
97
|
id = createKeyId(name);
|
|
95
98
|
getFn = key.getFn;
|
|
96
99
|
}
|
|
97
|
-
return {
|
|
100
|
+
return {
|
|
101
|
+
path,
|
|
102
|
+
id,
|
|
103
|
+
weight,
|
|
104
|
+
src,
|
|
105
|
+
getFn
|
|
106
|
+
};
|
|
98
107
|
}
|
|
99
108
|
function createKeyPath(key) {
|
|
100
109
|
return isArray(key) ? key : key.split(".");
|
|
@@ -103,29 +112,35 @@
|
|
|
103
112
|
return isArray(key) ? key.join(".") : key;
|
|
104
113
|
}
|
|
105
114
|
function get(obj, path) {
|
|
106
|
-
|
|
115
|
+
const list = [];
|
|
107
116
|
let arr = false;
|
|
108
|
-
const deepGet = (obj2, path2, index) => {
|
|
117
|
+
const deepGet = (obj2, path2, index, arrayIndex) => {
|
|
109
118
|
if (!isDefined(obj2)) {
|
|
110
119
|
return;
|
|
111
120
|
}
|
|
112
121
|
if (!path2[index]) {
|
|
113
|
-
list.push(
|
|
122
|
+
list.push(arrayIndex !== void 0 ? {
|
|
123
|
+
v: obj2,
|
|
124
|
+
i: arrayIndex
|
|
125
|
+
} : obj2);
|
|
114
126
|
} else {
|
|
115
|
-
|
|
127
|
+
const key = path2[index];
|
|
116
128
|
const value = obj2[key];
|
|
117
129
|
if (!isDefined(value)) {
|
|
118
130
|
return;
|
|
119
131
|
}
|
|
120
|
-
if (index === path2.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) {
|
|
121
|
-
list.push(
|
|
132
|
+
if (index === path2.length - 1 && (isString(value) || isNumber(value) || isBoolean(value) || typeof value === "bigint")) {
|
|
133
|
+
list.push(arrayIndex !== void 0 ? {
|
|
134
|
+
v: toString(value),
|
|
135
|
+
i: arrayIndex
|
|
136
|
+
} : toString(value));
|
|
122
137
|
} else if (isArray(value)) {
|
|
123
138
|
arr = true;
|
|
124
139
|
for (let i = 0, len = value.length; i < len; i += 1) {
|
|
125
|
-
deepGet(value[i], path2, index + 1);
|
|
140
|
+
deepGet(value[i], path2, index + 1, i);
|
|
126
141
|
}
|
|
127
142
|
} else if (path2.length) {
|
|
128
|
-
deepGet(value, path2, index + 1);
|
|
143
|
+
deepGet(value, path2, index + 1, arrayIndex);
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
146
|
};
|
|
@@ -133,67 +148,37 @@
|
|
|
133
148
|
return arr ? list : list[0];
|
|
134
149
|
}
|
|
135
150
|
const MatchOptions = {
|
|
136
|
-
// Whether the matches should be included in the result set. When `true`, each record in the result
|
|
137
|
-
// set will include the indices of the matched characters.
|
|
138
|
-
// These can consequently be used for highlighting purposes.
|
|
139
151
|
includeMatches: false,
|
|
140
|
-
// When `true`, the matching function will continue to the end of a search pattern even if
|
|
141
|
-
// a perfect match has already been located in the string.
|
|
142
152
|
findAllMatches: false,
|
|
143
|
-
// Minimum number of characters that must be matched before a result is considered a match
|
|
144
153
|
minMatchCharLength: 1
|
|
145
154
|
};
|
|
146
155
|
const BasicOptions = {
|
|
147
|
-
// When `true`, the algorithm continues searching to the end of the input even if a perfect
|
|
148
|
-
// match is found before the end of the same input.
|
|
149
156
|
isCaseSensitive: false,
|
|
150
|
-
// When `true`, the algorithm will ignore diacritics (accents) in comparisons
|
|
151
157
|
ignoreDiacritics: false,
|
|
152
|
-
// When true, the matching function will continue to the end of a search pattern even if
|
|
153
158
|
includeScore: false,
|
|
154
|
-
// List of properties that will be searched. This also supports nested properties.
|
|
155
159
|
keys: [],
|
|
156
|
-
// Whether to sort the result list, by score
|
|
157
160
|
shouldSort: true,
|
|
158
|
-
// Default sort function: sort by ascending score, ascending index
|
|
159
161
|
sortFn: (a, b) => a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1
|
|
160
162
|
};
|
|
161
163
|
const FuzzyOptions = {
|
|
162
|
-
// Approximately where in the text is the pattern expected to be found?
|
|
163
164
|
location: 0,
|
|
164
|
-
// At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match
|
|
165
|
-
// (of both letters and location), a threshold of '1.0' would match anything.
|
|
166
165
|
threshold: 0.6,
|
|
167
|
-
// Determines how close the match must be to the fuzzy location (specified above).
|
|
168
|
-
// An exact letter match which is 'distance' characters away from the fuzzy location
|
|
169
|
-
// would score as a complete mismatch. A distance of '0' requires the match be at
|
|
170
|
-
// the exact location specified, a threshold of '1000' would require a perfect match
|
|
171
|
-
// to be within 800 characters of the fuzzy location to be found using a 0.8 threshold.
|
|
172
166
|
distance: 100
|
|
173
167
|
};
|
|
174
168
|
const AdvancedOptions = {
|
|
175
|
-
// When `true`, it enables the use of unix-like search commands
|
|
176
169
|
useExtendedSearch: false,
|
|
177
|
-
|
|
178
|
-
// The default will search nested paths *ie foo.bar.baz*
|
|
170
|
+
useTokenSearch: false,
|
|
179
171
|
getFn: get,
|
|
180
|
-
// When `true`, search will ignore `location` and `distance`, so it won't matter
|
|
181
|
-
// where in the string the pattern appears.
|
|
182
|
-
// More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score
|
|
183
172
|
ignoreLocation: false,
|
|
184
|
-
// When `true`, the calculation for the relevance score (used for sorting) will
|
|
185
|
-
// ignore the field-length norm.
|
|
186
|
-
// More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm
|
|
187
173
|
ignoreFieldNorm: false,
|
|
188
|
-
// The weight to determine how much field length norm effects scoring.
|
|
189
174
|
fieldNormWeight: 1
|
|
190
175
|
};
|
|
191
|
-
|
|
176
|
+
const Config = Object.freeze({
|
|
192
177
|
...BasicOptions,
|
|
193
178
|
...MatchOptions,
|
|
194
179
|
...FuzzyOptions,
|
|
195
180
|
...AdvancedOptions
|
|
196
|
-
};
|
|
181
|
+
});
|
|
197
182
|
const SPACE = /[^ ]+/g;
|
|
198
183
|
function norm(weight = 1, mantissa = 3) {
|
|
199
184
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -222,6 +207,9 @@
|
|
|
222
207
|
this.norm = norm(fieldNormWeight, 3);
|
|
223
208
|
this.getFn = getFn;
|
|
224
209
|
this.isCreated = false;
|
|
210
|
+
this.docs = [];
|
|
211
|
+
this.keys = [];
|
|
212
|
+
this._keysMap = {};
|
|
225
213
|
this.setIndexRecords();
|
|
226
214
|
}
|
|
227
215
|
setSources(docs = []) {
|
|
@@ -269,6 +257,15 @@
|
|
|
269
257
|
this.records[i].i -= 1;
|
|
270
258
|
}
|
|
271
259
|
}
|
|
260
|
+
// Removes docs at the specified indices (must be sorted ascending)
|
|
261
|
+
removeAll(indices) {
|
|
262
|
+
for (let i = indices.length - 1; i >= 0; i -= 1) {
|
|
263
|
+
this.records.splice(indices[i], 1);
|
|
264
|
+
}
|
|
265
|
+
for (let i = 0, len = this.records.length; i < len; i += 1) {
|
|
266
|
+
this.records[i].i = i;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
272
269
|
getValueForItemAtKeyId(item, keyId) {
|
|
273
270
|
return item[this._keysMap[keyId]];
|
|
274
271
|
}
|
|
@@ -279,7 +276,7 @@
|
|
|
279
276
|
if (!isDefined(doc) || isBlank(doc)) {
|
|
280
277
|
return;
|
|
281
278
|
}
|
|
282
|
-
|
|
279
|
+
const record = {
|
|
283
280
|
v: doc,
|
|
284
281
|
i: docIndex,
|
|
285
282
|
n: this.norm.get(doc)
|
|
@@ -287,39 +284,46 @@
|
|
|
287
284
|
this.records.push(record);
|
|
288
285
|
}
|
|
289
286
|
_addObject(doc, docIndex) {
|
|
290
|
-
|
|
287
|
+
const record = {
|
|
288
|
+
i: docIndex,
|
|
289
|
+
$: {}
|
|
290
|
+
};
|
|
291
291
|
this.keys.forEach((key, keyIndex) => {
|
|
292
|
-
|
|
292
|
+
const value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
|
|
293
293
|
if (!isDefined(value)) {
|
|
294
294
|
return;
|
|
295
295
|
}
|
|
296
296
|
if (isArray(value)) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (!isDefined(value2)) {
|
|
297
|
+
const subRecords = [];
|
|
298
|
+
for (let i = 0, len = value.length; i < len; i += 1) {
|
|
299
|
+
const item = value[i];
|
|
300
|
+
if (!isDefined(item)) {
|
|
302
301
|
continue;
|
|
303
302
|
}
|
|
304
|
-
if (isString(
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
303
|
+
if (isString(item)) {
|
|
304
|
+
if (!isBlank(item)) {
|
|
305
|
+
const subRecord = {
|
|
306
|
+
v: item,
|
|
307
|
+
i,
|
|
308
|
+
n: this.norm.get(item)
|
|
309
|
+
};
|
|
310
|
+
subRecords.push(subRecord);
|
|
311
|
+
}
|
|
312
|
+
} else if (isDefined(item.v)) {
|
|
313
|
+
const text = isString(item.v) ? item.v : toString(item.v);
|
|
314
|
+
if (!isBlank(text)) {
|
|
315
|
+
const subRecord = {
|
|
316
|
+
v: text,
|
|
317
|
+
i: item.i,
|
|
318
|
+
n: this.norm.get(text)
|
|
319
|
+
};
|
|
320
|
+
subRecords.push(subRecord);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
319
323
|
}
|
|
320
324
|
record.$[keyIndex] = subRecords;
|
|
321
325
|
} else if (isString(value) && !isBlank(value)) {
|
|
322
|
-
|
|
326
|
+
const subRecord = {
|
|
323
327
|
v: value,
|
|
324
328
|
n: this.norm.get(value)
|
|
325
329
|
};
|
|
@@ -330,49 +334,51 @@
|
|
|
330
334
|
}
|
|
331
335
|
toJSON() {
|
|
332
336
|
return {
|
|
333
|
-
|
|
337
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
338
|
+
keys: this.keys.map(({
|
|
339
|
+
getFn,
|
|
340
|
+
...key
|
|
341
|
+
}) => key),
|
|
334
342
|
records: this.records
|
|
335
343
|
};
|
|
336
344
|
}
|
|
337
345
|
}
|
|
338
|
-
function createIndex(keys, docs, {
|
|
339
|
-
|
|
346
|
+
function createIndex(keys, docs, {
|
|
347
|
+
getFn = Config.getFn,
|
|
348
|
+
fieldNormWeight = Config.fieldNormWeight
|
|
349
|
+
} = {}) {
|
|
350
|
+
const myIndex = new FuseIndex({
|
|
351
|
+
getFn,
|
|
352
|
+
fieldNormWeight
|
|
353
|
+
});
|
|
340
354
|
myIndex.setKeys(keys.map(createKey));
|
|
341
355
|
myIndex.setSources(docs);
|
|
342
356
|
myIndex.create();
|
|
343
357
|
return myIndex;
|
|
344
358
|
}
|
|
345
|
-
function parseIndex(data, {
|
|
346
|
-
|
|
347
|
-
|
|
359
|
+
function parseIndex(data, {
|
|
360
|
+
getFn = Config.getFn,
|
|
361
|
+
fieldNormWeight = Config.fieldNormWeight
|
|
362
|
+
} = {}) {
|
|
363
|
+
const {
|
|
364
|
+
keys,
|
|
365
|
+
records
|
|
366
|
+
} = data;
|
|
367
|
+
const myIndex = new FuseIndex({
|
|
368
|
+
getFn,
|
|
369
|
+
fieldNormWeight
|
|
370
|
+
});
|
|
348
371
|
myIndex.setKeys(keys);
|
|
349
372
|
myIndex.setIndexRecords(records);
|
|
350
373
|
return myIndex;
|
|
351
374
|
}
|
|
352
|
-
function computeScore$1(pattern, {
|
|
353
|
-
errors = 0,
|
|
354
|
-
currentLocation = 0,
|
|
355
|
-
expectedLocation = 0,
|
|
356
|
-
distance = Config.distance,
|
|
357
|
-
ignoreLocation = Config.ignoreLocation
|
|
358
|
-
} = {}) {
|
|
359
|
-
const accuracy = errors / pattern.length;
|
|
360
|
-
if (ignoreLocation) {
|
|
361
|
-
return accuracy;
|
|
362
|
-
}
|
|
363
|
-
const proximity = Math.abs(expectedLocation - currentLocation);
|
|
364
|
-
if (!distance) {
|
|
365
|
-
return proximity ? 1 : accuracy;
|
|
366
|
-
}
|
|
367
|
-
return accuracy + proximity / distance;
|
|
368
|
-
}
|
|
369
375
|
function convertMaskToIndices(matchmask = [], minMatchCharLength = Config.minMatchCharLength) {
|
|
370
|
-
|
|
376
|
+
const indices = [];
|
|
371
377
|
let start = -1;
|
|
372
378
|
let end = -1;
|
|
373
379
|
let i = 0;
|
|
374
380
|
for (let len = matchmask.length; i < len; i += 1) {
|
|
375
|
-
|
|
381
|
+
const match = matchmask[i];
|
|
376
382
|
if (match && start === -1) {
|
|
377
383
|
start = i;
|
|
378
384
|
} else if (!match && start !== -1) {
|
|
@@ -406,16 +412,18 @@
|
|
|
406
412
|
const expectedLocation = Math.max(0, Math.min(location, textLen));
|
|
407
413
|
let currentThreshold = threshold;
|
|
408
414
|
let bestLocation = expectedLocation;
|
|
415
|
+
const calcScore = (errors, currentLocation) => {
|
|
416
|
+
const accuracy = errors / patternLen;
|
|
417
|
+
if (ignoreLocation) return accuracy;
|
|
418
|
+
const proximity = Math.abs(expectedLocation - currentLocation);
|
|
419
|
+
if (!distance) return proximity ? 1 : accuracy;
|
|
420
|
+
return accuracy + proximity / distance;
|
|
421
|
+
};
|
|
409
422
|
const computeMatches = minMatchCharLength > 1 || includeMatches;
|
|
410
423
|
const matchMask = computeMatches ? Array(textLen) : [];
|
|
411
424
|
let index;
|
|
412
425
|
while ((index = text.indexOf(pattern, bestLocation)) > -1) {
|
|
413
|
-
|
|
414
|
-
currentLocation: index,
|
|
415
|
-
expectedLocation,
|
|
416
|
-
distance,
|
|
417
|
-
ignoreLocation
|
|
418
|
-
});
|
|
426
|
+
const score = calcScore(0, index);
|
|
419
427
|
currentThreshold = Math.min(score, currentThreshold);
|
|
420
428
|
bestLocation = index + patternLen;
|
|
421
429
|
if (computeMatches) {
|
|
@@ -435,13 +443,7 @@
|
|
|
435
443
|
let binMin = 0;
|
|
436
444
|
let binMid = binMax;
|
|
437
445
|
while (binMin < binMid) {
|
|
438
|
-
const score2 =
|
|
439
|
-
errors: i,
|
|
440
|
-
currentLocation: expectedLocation + binMid,
|
|
441
|
-
expectedLocation,
|
|
442
|
-
distance,
|
|
443
|
-
ignoreLocation
|
|
444
|
-
});
|
|
446
|
+
const score2 = calcScore(i, expectedLocation + binMid);
|
|
445
447
|
if (score2 <= currentThreshold) {
|
|
446
448
|
binMin = binMid;
|
|
447
449
|
} else {
|
|
@@ -451,12 +453,12 @@
|
|
|
451
453
|
}
|
|
452
454
|
binMax = binMid;
|
|
453
455
|
let start = Math.max(1, expectedLocation - binMid + 1);
|
|
454
|
-
|
|
455
|
-
|
|
456
|
+
const finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen;
|
|
457
|
+
const bitArr = Array(finish + 2);
|
|
456
458
|
bitArr[finish + 1] = (1 << i) - 1;
|
|
457
459
|
for (let j = finish; j >= start; j -= 1) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
+
const currentLocation = j - 1;
|
|
461
|
+
const charMatch = patternAlphabet[text[currentLocation]];
|
|
460
462
|
if (computeMatches) {
|
|
461
463
|
matchMask[currentLocation] = +!!charMatch;
|
|
462
464
|
}
|
|
@@ -465,13 +467,7 @@
|
|
|
465
467
|
bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1];
|
|
466
468
|
}
|
|
467
469
|
if (bitArr[j] & mask) {
|
|
468
|
-
finalScore =
|
|
469
|
-
errors: i,
|
|
470
|
-
currentLocation,
|
|
471
|
-
expectedLocation,
|
|
472
|
-
distance,
|
|
473
|
-
ignoreLocation
|
|
474
|
-
});
|
|
470
|
+
finalScore = calcScore(i, currentLocation);
|
|
475
471
|
if (finalScore <= currentThreshold) {
|
|
476
472
|
currentThreshold = finalScore;
|
|
477
473
|
bestLocation = currentLocation;
|
|
@@ -482,13 +478,7 @@
|
|
|
482
478
|
}
|
|
483
479
|
}
|
|
484
480
|
}
|
|
485
|
-
const score =
|
|
486
|
-
errors: i + 1,
|
|
487
|
-
currentLocation: expectedLocation,
|
|
488
|
-
expectedLocation,
|
|
489
|
-
distance,
|
|
490
|
-
ignoreLocation
|
|
491
|
-
});
|
|
481
|
+
const score = calcScore(i + 1, expectedLocation);
|
|
492
482
|
if (score > currentThreshold) {
|
|
493
483
|
break;
|
|
494
484
|
}
|
|
@@ -510,14 +500,56 @@
|
|
|
510
500
|
return result;
|
|
511
501
|
}
|
|
512
502
|
function createPatternAlphabet(pattern) {
|
|
513
|
-
|
|
503
|
+
const mask = {};
|
|
514
504
|
for (let i = 0, len = pattern.length; i < len; i += 1) {
|
|
515
505
|
const char = pattern.charAt(i);
|
|
516
506
|
mask[char] = (mask[char] || 0) | 1 << len - i - 1;
|
|
517
507
|
}
|
|
518
508
|
return mask;
|
|
519
509
|
}
|
|
520
|
-
|
|
510
|
+
function mergeIndices(indices) {
|
|
511
|
+
if (indices.length <= 1) return indices;
|
|
512
|
+
indices.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
513
|
+
const merged = [indices[0]];
|
|
514
|
+
for (let i = 1, len = indices.length; i < len; i += 1) {
|
|
515
|
+
const last = merged[merged.length - 1];
|
|
516
|
+
const curr = indices[i];
|
|
517
|
+
if (curr[0] <= last[1] + 1) {
|
|
518
|
+
last[1] = Math.max(last[1], curr[1]);
|
|
519
|
+
} else {
|
|
520
|
+
merged.push(curr);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return merged;
|
|
524
|
+
}
|
|
525
|
+
const NON_DECOMPOSABLE_MAP = {
|
|
526
|
+
"ł": "l",
|
|
527
|
+
// ł
|
|
528
|
+
"Ł": "L",
|
|
529
|
+
// Ł
|
|
530
|
+
"đ": "d",
|
|
531
|
+
// đ
|
|
532
|
+
"Đ": "D",
|
|
533
|
+
// Đ
|
|
534
|
+
"ø": "o",
|
|
535
|
+
// ø
|
|
536
|
+
"Ø": "O",
|
|
537
|
+
// Ø
|
|
538
|
+
"ħ": "h",
|
|
539
|
+
// ħ
|
|
540
|
+
"Ħ": "H",
|
|
541
|
+
// Ħ
|
|
542
|
+
"ŧ": "t",
|
|
543
|
+
// ŧ
|
|
544
|
+
"Ŧ": "T",
|
|
545
|
+
// Ŧ
|
|
546
|
+
"ı": "i",
|
|
547
|
+
// ı
|
|
548
|
+
"ß": "ss"
|
|
549
|
+
// ß
|
|
550
|
+
};
|
|
551
|
+
const NON_DECOMPOSABLE_RE = new RegExp("[" + Object.keys(NON_DECOMPOSABLE_MAP).join("") + "]", "g");
|
|
552
|
+
const stripDiacritics = String.prototype.normalize ? (str) => str.normalize("NFD").replace(/[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D3-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C04\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA8FF\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F]/g, "").replace(NON_DECOMPOSABLE_RE, (ch) => NON_DECOMPOSABLE_MAP[ch]) : (str) => str;
|
|
521
553
|
class BitapSearch {
|
|
522
554
|
constructor(pattern, {
|
|
523
555
|
location = Config.location,
|
|
@@ -573,11 +605,15 @@
|
|
|
573
605
|
}
|
|
574
606
|
}
|
|
575
607
|
searchIn(text) {
|
|
576
|
-
const {
|
|
608
|
+
const {
|
|
609
|
+
isCaseSensitive,
|
|
610
|
+
ignoreDiacritics,
|
|
611
|
+
includeMatches
|
|
612
|
+
} = this.options;
|
|
577
613
|
text = isCaseSensitive ? text : text.toLowerCase();
|
|
578
614
|
text = ignoreDiacritics ? stripDiacritics(text) : text;
|
|
579
615
|
if (this.pattern === text) {
|
|
580
|
-
|
|
616
|
+
const result2 = {
|
|
581
617
|
isMatch: true,
|
|
582
618
|
score: 0
|
|
583
619
|
};
|
|
@@ -594,11 +630,19 @@
|
|
|
594
630
|
minMatchCharLength,
|
|
595
631
|
ignoreLocation
|
|
596
632
|
} = this.options;
|
|
597
|
-
|
|
633
|
+
const allIndices = [];
|
|
598
634
|
let totalScore = 0;
|
|
599
635
|
let hasMatches = false;
|
|
600
|
-
this.chunks.forEach(({
|
|
601
|
-
|
|
636
|
+
this.chunks.forEach(({
|
|
637
|
+
pattern,
|
|
638
|
+
alphabet,
|
|
639
|
+
startIndex
|
|
640
|
+
}) => {
|
|
641
|
+
const {
|
|
642
|
+
isMatch,
|
|
643
|
+
score,
|
|
644
|
+
indices
|
|
645
|
+
} = search(text, pattern, alphabet, {
|
|
602
646
|
location: location + startIndex,
|
|
603
647
|
distance,
|
|
604
648
|
threshold,
|
|
@@ -612,15 +656,15 @@
|
|
|
612
656
|
}
|
|
613
657
|
totalScore += score;
|
|
614
658
|
if (isMatch && indices) {
|
|
615
|
-
allIndices
|
|
659
|
+
allIndices.push(...indices);
|
|
616
660
|
}
|
|
617
661
|
});
|
|
618
|
-
|
|
662
|
+
const result = {
|
|
619
663
|
isMatch: hasMatches,
|
|
620
664
|
score: hasMatches ? totalScore / this.chunks.length : 1
|
|
621
665
|
};
|
|
622
666
|
if (hasMatches && includeMatches) {
|
|
623
|
-
result.indices = allIndices;
|
|
667
|
+
result.indices = mergeIndices(allIndices);
|
|
624
668
|
}
|
|
625
669
|
return result;
|
|
626
670
|
}
|
|
@@ -635,7 +679,12 @@
|
|
|
635
679
|
static isSingleMatch(pattern) {
|
|
636
680
|
return getMatch(pattern, this.singleRegex);
|
|
637
681
|
}
|
|
638
|
-
|
|
682
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
683
|
+
search(_text) {
|
|
684
|
+
return {
|
|
685
|
+
isMatch: false,
|
|
686
|
+
score: 1
|
|
687
|
+
};
|
|
639
688
|
}
|
|
640
689
|
}
|
|
641
690
|
function getMatch(pattern, exp) {
|
|
@@ -843,30 +892,58 @@
|
|
|
843
892
|
};
|
|
844
893
|
}
|
|
845
894
|
}
|
|
846
|
-
const searchers = [
|
|
847
|
-
ExactMatch,
|
|
848
|
-
IncludeMatch,
|
|
849
|
-
PrefixExactMatch,
|
|
850
|
-
InversePrefixExactMatch,
|
|
851
|
-
InverseSuffixExactMatch,
|
|
852
|
-
SuffixExactMatch,
|
|
853
|
-
InverseExactMatch,
|
|
854
|
-
FuzzyMatch
|
|
855
|
-
];
|
|
895
|
+
const searchers = [ExactMatch, IncludeMatch, PrefixExactMatch, InversePrefixExactMatch, InverseSuffixExactMatch, SuffixExactMatch, InverseExactMatch, FuzzyMatch];
|
|
856
896
|
const searchersLen = searchers.length;
|
|
857
|
-
const
|
|
897
|
+
const ESCAPED_PIPE = "\0";
|
|
858
898
|
const OR_TOKEN = "|";
|
|
899
|
+
function tokenize(pattern) {
|
|
900
|
+
const tokens = [];
|
|
901
|
+
const len = pattern.length;
|
|
902
|
+
let i = 0;
|
|
903
|
+
while (i < len) {
|
|
904
|
+
while (i < len && pattern[i] === " ") i++;
|
|
905
|
+
if (i >= len) break;
|
|
906
|
+
let j = i;
|
|
907
|
+
while (j < len && pattern[j] !== " " && pattern[j] !== '"') j++;
|
|
908
|
+
if (j < len && pattern[j] === '"') {
|
|
909
|
+
j++;
|
|
910
|
+
while (j < len) {
|
|
911
|
+
if (pattern[j] === '"') {
|
|
912
|
+
const next = j + 1;
|
|
913
|
+
if (next >= len || pattern[next] === " ") {
|
|
914
|
+
j++;
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
if (pattern[next] === "$" && (next + 1 >= len || pattern[next + 1] === " ")) {
|
|
918
|
+
j += 2;
|
|
919
|
+
break;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
j++;
|
|
923
|
+
}
|
|
924
|
+
tokens.push(pattern.substring(i, j));
|
|
925
|
+
i = j;
|
|
926
|
+
} else {
|
|
927
|
+
while (j < len && pattern[j] !== " ") j++;
|
|
928
|
+
tokens.push(pattern.substring(i, j));
|
|
929
|
+
i = j;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return tokens;
|
|
933
|
+
}
|
|
859
934
|
function parseQuery(pattern, options = {}) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
935
|
+
const escaped = pattern.replace(/\\\|/g, ESCAPED_PIPE);
|
|
936
|
+
return escaped.split(OR_TOKEN).map((item) => {
|
|
937
|
+
const restored = item.replace(/\u0000/g, "|");
|
|
938
|
+
const query = tokenize(restored.trim()).filter((item2) => item2 && !!item2.trim());
|
|
939
|
+
const results = [];
|
|
863
940
|
for (let i = 0, len = query.length; i < len; i += 1) {
|
|
864
941
|
const queryItem = query[i];
|
|
865
942
|
let found = false;
|
|
866
943
|
let idx = -1;
|
|
867
944
|
while (!found && ++idx < searchersLen) {
|
|
868
945
|
const searcher = searchers[idx];
|
|
869
|
-
|
|
946
|
+
const token = searcher.isMultiMatch(queryItem);
|
|
870
947
|
if (token) {
|
|
871
948
|
results.push(new searcher(token, options));
|
|
872
949
|
found = true;
|
|
@@ -878,7 +955,7 @@
|
|
|
878
955
|
idx = -1;
|
|
879
956
|
while (++idx < searchersLen) {
|
|
880
957
|
const searcher = searchers[idx];
|
|
881
|
-
|
|
958
|
+
const token = searcher.isSingleMatch(queryItem);
|
|
882
959
|
if (token) {
|
|
883
960
|
results.push(new searcher(token, options));
|
|
884
961
|
break;
|
|
@@ -921,6 +998,9 @@
|
|
|
921
998
|
static condition(_, options) {
|
|
922
999
|
return options.useExtendedSearch;
|
|
923
1000
|
}
|
|
1001
|
+
// Note: searchIn operates on a single text value and sets hasInverse on the
|
|
1002
|
+
// result when inverse patterns are involved. _searchObjectList uses this to
|
|
1003
|
+
// switch from "ANY key" to "ALL keys" aggregation. See #712.
|
|
924
1004
|
searchIn(text) {
|
|
925
1005
|
const query = this.query;
|
|
926
1006
|
if (!query) {
|
|
@@ -929,26 +1009,39 @@
|
|
|
929
1009
|
score: 1
|
|
930
1010
|
};
|
|
931
1011
|
}
|
|
932
|
-
const {
|
|
1012
|
+
const {
|
|
1013
|
+
includeMatches,
|
|
1014
|
+
isCaseSensitive,
|
|
1015
|
+
ignoreDiacritics
|
|
1016
|
+
} = this.options;
|
|
933
1017
|
text = isCaseSensitive ? text : text.toLowerCase();
|
|
934
1018
|
text = ignoreDiacritics ? stripDiacritics(text) : text;
|
|
935
1019
|
let numMatches = 0;
|
|
936
|
-
|
|
1020
|
+
const allIndices = [];
|
|
937
1021
|
let totalScore = 0;
|
|
1022
|
+
let hasInverse = false;
|
|
938
1023
|
for (let i = 0, qLen = query.length; i < qLen; i += 1) {
|
|
939
1024
|
const searchers2 = query[i];
|
|
940
1025
|
allIndices.length = 0;
|
|
941
1026
|
numMatches = 0;
|
|
1027
|
+
hasInverse = false;
|
|
942
1028
|
for (let j = 0, pLen = searchers2.length; j < pLen; j += 1) {
|
|
943
1029
|
const searcher = searchers2[j];
|
|
944
|
-
const {
|
|
1030
|
+
const {
|
|
1031
|
+
isMatch,
|
|
1032
|
+
indices,
|
|
1033
|
+
score
|
|
1034
|
+
} = searcher.search(text);
|
|
945
1035
|
if (isMatch) {
|
|
946
1036
|
numMatches += 1;
|
|
947
1037
|
totalScore += score;
|
|
1038
|
+
const type = searcher.constructor.type;
|
|
1039
|
+
if (type.startsWith("inverse")) {
|
|
1040
|
+
hasInverse = true;
|
|
1041
|
+
}
|
|
948
1042
|
if (includeMatches) {
|
|
949
|
-
const type = searcher.constructor.type;
|
|
950
1043
|
if (MultiMatchSet.has(type)) {
|
|
951
|
-
allIndices
|
|
1044
|
+
allIndices.push(...indices);
|
|
952
1045
|
} else {
|
|
953
1046
|
allIndices.push(indices);
|
|
954
1047
|
}
|
|
@@ -957,16 +1050,20 @@
|
|
|
957
1050
|
totalScore = 0;
|
|
958
1051
|
numMatches = 0;
|
|
959
1052
|
allIndices.length = 0;
|
|
1053
|
+
hasInverse = false;
|
|
960
1054
|
break;
|
|
961
1055
|
}
|
|
962
1056
|
}
|
|
963
1057
|
if (numMatches) {
|
|
964
|
-
|
|
1058
|
+
const result = {
|
|
965
1059
|
isMatch: true,
|
|
966
1060
|
score: totalScore / numMatches
|
|
967
1061
|
};
|
|
1062
|
+
if (hasInverse) {
|
|
1063
|
+
result.hasInverse = true;
|
|
1064
|
+
}
|
|
968
1065
|
if (includeMatches) {
|
|
969
|
-
result.indices = allIndices;
|
|
1066
|
+
result.indices = mergeIndices(allIndices);
|
|
970
1067
|
}
|
|
971
1068
|
return result;
|
|
972
1069
|
}
|
|
@@ -983,7 +1080,7 @@
|
|
|
983
1080
|
}
|
|
984
1081
|
function createSearcher(pattern, options) {
|
|
985
1082
|
for (let i = 0, len = registeredSearchers.length; i < len; i += 1) {
|
|
986
|
-
|
|
1083
|
+
const searcherClass = registeredSearchers[i];
|
|
987
1084
|
if (searcherClass.condition(pattern, options)) {
|
|
988
1085
|
return new searcherClass(pattern, options);
|
|
989
1086
|
}
|
|
@@ -1006,9 +1103,21 @@
|
|
|
1006
1103
|
[key]: query[key]
|
|
1007
1104
|
}))
|
|
1008
1105
|
});
|
|
1009
|
-
function parse(query, options, {
|
|
1106
|
+
function parse(query, options, {
|
|
1107
|
+
auto = true
|
|
1108
|
+
} = {}) {
|
|
1010
1109
|
const next = (query2) => {
|
|
1011
|
-
|
|
1110
|
+
if (isString(query2)) {
|
|
1111
|
+
const obj = {
|
|
1112
|
+
keyId: null,
|
|
1113
|
+
pattern: query2
|
|
1114
|
+
};
|
|
1115
|
+
if (auto) {
|
|
1116
|
+
obj.searcher = createSearcher(query2, options);
|
|
1117
|
+
}
|
|
1118
|
+
return obj;
|
|
1119
|
+
}
|
|
1120
|
+
const keys = Object.keys(query2);
|
|
1012
1121
|
const isQueryPath = isPath(query2);
|
|
1013
1122
|
if (!isQueryPath && keys.length > 1 && !isExpression(query2)) {
|
|
1014
1123
|
return next(convertToExplicit(query2));
|
|
@@ -1028,7 +1137,7 @@
|
|
|
1028
1137
|
}
|
|
1029
1138
|
return obj;
|
|
1030
1139
|
}
|
|
1031
|
-
|
|
1140
|
+
const node = {
|
|
1032
1141
|
children: [],
|
|
1033
1142
|
operator: keys[0]
|
|
1034
1143
|
};
|
|
@@ -1047,19 +1156,85 @@
|
|
|
1047
1156
|
}
|
|
1048
1157
|
return next(query);
|
|
1049
1158
|
}
|
|
1050
|
-
function
|
|
1159
|
+
function computeScoreSingle(matches, {
|
|
1160
|
+
ignoreFieldNorm = Config.ignoreFieldNorm
|
|
1161
|
+
}) {
|
|
1162
|
+
let totalScore = 1;
|
|
1163
|
+
matches.forEach(({
|
|
1164
|
+
key,
|
|
1165
|
+
norm: norm2,
|
|
1166
|
+
score
|
|
1167
|
+
}) => {
|
|
1168
|
+
const weight = key ? key.weight : null;
|
|
1169
|
+
totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm2));
|
|
1170
|
+
});
|
|
1171
|
+
return totalScore;
|
|
1172
|
+
}
|
|
1173
|
+
function computeScore(results, {
|
|
1174
|
+
ignoreFieldNorm = Config.ignoreFieldNorm
|
|
1175
|
+
}) {
|
|
1051
1176
|
results.forEach((result) => {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
const weight = key ? key.weight : null;
|
|
1055
|
-
totalScore *= Math.pow(
|
|
1056
|
-
score === 0 && weight ? Number.EPSILON : score,
|
|
1057
|
-
(weight || 1) * (ignoreFieldNorm ? 1 : norm2)
|
|
1058
|
-
);
|
|
1177
|
+
result.score = computeScoreSingle(result.matches, {
|
|
1178
|
+
ignoreFieldNorm
|
|
1059
1179
|
});
|
|
1060
|
-
result.score = totalScore;
|
|
1061
1180
|
});
|
|
1062
1181
|
}
|
|
1182
|
+
class MaxHeap {
|
|
1183
|
+
constructor(limit) {
|
|
1184
|
+
this.limit = limit;
|
|
1185
|
+
this.heap = [];
|
|
1186
|
+
}
|
|
1187
|
+
get size() {
|
|
1188
|
+
return this.heap.length;
|
|
1189
|
+
}
|
|
1190
|
+
shouldInsert(score) {
|
|
1191
|
+
return this.size < this.limit || score < this.heap[0].score;
|
|
1192
|
+
}
|
|
1193
|
+
insert(item) {
|
|
1194
|
+
if (this.size < this.limit) {
|
|
1195
|
+
this.heap.push(item);
|
|
1196
|
+
this._bubbleUp(this.size - 1);
|
|
1197
|
+
} else if (item.score < this.heap[0].score) {
|
|
1198
|
+
this.heap[0] = item;
|
|
1199
|
+
this._sinkDown(0);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
extractSorted(sortFn) {
|
|
1203
|
+
return this.heap.sort(sortFn);
|
|
1204
|
+
}
|
|
1205
|
+
_bubbleUp(i) {
|
|
1206
|
+
const heap = this.heap;
|
|
1207
|
+
while (i > 0) {
|
|
1208
|
+
const parent = i - 1 >> 1;
|
|
1209
|
+
if (heap[i].score <= heap[parent].score) break;
|
|
1210
|
+
const tmp = heap[i];
|
|
1211
|
+
heap[i] = heap[parent];
|
|
1212
|
+
heap[parent] = tmp;
|
|
1213
|
+
i = parent;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
_sinkDown(i) {
|
|
1217
|
+
const heap = this.heap;
|
|
1218
|
+
const len = heap.length;
|
|
1219
|
+
let largest = i;
|
|
1220
|
+
do {
|
|
1221
|
+
i = largest;
|
|
1222
|
+
const left = 2 * i + 1;
|
|
1223
|
+
const right = 2 * i + 2;
|
|
1224
|
+
if (left < len && heap[left].score > heap[largest].score) {
|
|
1225
|
+
largest = left;
|
|
1226
|
+
}
|
|
1227
|
+
if (right < len && heap[right].score > heap[largest].score) {
|
|
1228
|
+
largest = right;
|
|
1229
|
+
}
|
|
1230
|
+
if (largest !== i) {
|
|
1231
|
+
const tmp = heap[i];
|
|
1232
|
+
heap[i] = heap[largest];
|
|
1233
|
+
heap[largest] = tmp;
|
|
1234
|
+
}
|
|
1235
|
+
} while (largest !== i);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1063
1238
|
function transformMatches(result, data) {
|
|
1064
1239
|
const matches = result.matches;
|
|
1065
1240
|
data.matches = [];
|
|
@@ -1070,8 +1245,11 @@
|
|
|
1070
1245
|
if (!isDefined(match.indices) || !match.indices.length) {
|
|
1071
1246
|
return;
|
|
1072
1247
|
}
|
|
1073
|
-
const {
|
|
1074
|
-
|
|
1248
|
+
const {
|
|
1249
|
+
indices,
|
|
1250
|
+
value
|
|
1251
|
+
} = match;
|
|
1252
|
+
const obj = {
|
|
1075
1253
|
indices,
|
|
1076
1254
|
value
|
|
1077
1255
|
};
|
|
@@ -1095,7 +1273,9 @@
|
|
|
1095
1273
|
if (includeMatches) transformers.push(transformMatches);
|
|
1096
1274
|
if (includeScore) transformers.push(transformScore);
|
|
1097
1275
|
return results.map((result) => {
|
|
1098
|
-
const {
|
|
1276
|
+
const {
|
|
1277
|
+
idx
|
|
1278
|
+
} = result;
|
|
1099
1279
|
const data = {
|
|
1100
1280
|
item: docs[idx],
|
|
1101
1281
|
refIndex: idx
|
|
@@ -1108,12 +1288,174 @@
|
|
|
1108
1288
|
return data;
|
|
1109
1289
|
});
|
|
1110
1290
|
}
|
|
1291
|
+
const WORD = /\b\w+\b/g;
|
|
1292
|
+
function createAnalyzer({
|
|
1293
|
+
isCaseSensitive = false,
|
|
1294
|
+
ignoreDiacritics = false
|
|
1295
|
+
} = {}) {
|
|
1296
|
+
return {
|
|
1297
|
+
tokenize(text) {
|
|
1298
|
+
if (!isCaseSensitive) {
|
|
1299
|
+
text = text.toLowerCase();
|
|
1300
|
+
}
|
|
1301
|
+
if (ignoreDiacritics) {
|
|
1302
|
+
text = stripDiacritics(text);
|
|
1303
|
+
}
|
|
1304
|
+
return text.match(WORD) || [];
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
function buildInvertedIndex(records, keyCount, analyzer) {
|
|
1309
|
+
const terms = /* @__PURE__ */ new Map();
|
|
1310
|
+
const df = /* @__PURE__ */ new Map();
|
|
1311
|
+
let fieldCount = 0;
|
|
1312
|
+
function addField(text, docIdx, keyIdx, subIdx) {
|
|
1313
|
+
const tokens = analyzer.tokenize(text);
|
|
1314
|
+
if (!tokens.length) return;
|
|
1315
|
+
fieldCount++;
|
|
1316
|
+
const termFreqs = /* @__PURE__ */ new Map();
|
|
1317
|
+
for (const token of tokens) {
|
|
1318
|
+
termFreqs.set(token, (termFreqs.get(token) || 0) + 1);
|
|
1319
|
+
}
|
|
1320
|
+
for (const [term, tf] of termFreqs) {
|
|
1321
|
+
const posting = {
|
|
1322
|
+
docIdx,
|
|
1323
|
+
keyIdx,
|
|
1324
|
+
subIdx,
|
|
1325
|
+
tf
|
|
1326
|
+
};
|
|
1327
|
+
let postings = terms.get(term);
|
|
1328
|
+
if (!postings) {
|
|
1329
|
+
postings = [];
|
|
1330
|
+
terms.set(term, postings);
|
|
1331
|
+
}
|
|
1332
|
+
postings.push(posting);
|
|
1333
|
+
df.set(term, (df.get(term) || 0) + 1);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
for (const record of records) {
|
|
1337
|
+
const {
|
|
1338
|
+
i: docIdx,
|
|
1339
|
+
v,
|
|
1340
|
+
$: fields
|
|
1341
|
+
} = record;
|
|
1342
|
+
if (v !== void 0) {
|
|
1343
|
+
addField(v, docIdx, -1, -1);
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
if (fields) {
|
|
1347
|
+
for (let keyIdx = 0; keyIdx < keyCount; keyIdx++) {
|
|
1348
|
+
const value = fields[keyIdx];
|
|
1349
|
+
if (!value) continue;
|
|
1350
|
+
if (Array.isArray(value)) {
|
|
1351
|
+
for (const sub of value) {
|
|
1352
|
+
addField(sub.v, docIdx, keyIdx, sub.i ?? -1);
|
|
1353
|
+
}
|
|
1354
|
+
} else {
|
|
1355
|
+
addField(value.v, docIdx, keyIdx, -1);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
terms,
|
|
1362
|
+
fieldCount,
|
|
1363
|
+
df
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
function addToInvertedIndex(index, record, keyCount, analyzer) {
|
|
1367
|
+
const {
|
|
1368
|
+
i: docIdx,
|
|
1369
|
+
v,
|
|
1370
|
+
$: fields
|
|
1371
|
+
} = record;
|
|
1372
|
+
function addField(text, keyIdx, subIdx) {
|
|
1373
|
+
const tokens = analyzer.tokenize(text);
|
|
1374
|
+
if (!tokens.length) return;
|
|
1375
|
+
index.fieldCount++;
|
|
1376
|
+
const termFreqs = /* @__PURE__ */ new Map();
|
|
1377
|
+
for (const token of tokens) {
|
|
1378
|
+
termFreqs.set(token, (termFreqs.get(token) || 0) + 1);
|
|
1379
|
+
}
|
|
1380
|
+
for (const [term, tf] of termFreqs) {
|
|
1381
|
+
const posting = {
|
|
1382
|
+
docIdx,
|
|
1383
|
+
keyIdx,
|
|
1384
|
+
subIdx,
|
|
1385
|
+
tf
|
|
1386
|
+
};
|
|
1387
|
+
let postings = index.terms.get(term);
|
|
1388
|
+
if (!postings) {
|
|
1389
|
+
postings = [];
|
|
1390
|
+
index.terms.set(term, postings);
|
|
1391
|
+
}
|
|
1392
|
+
postings.push(posting);
|
|
1393
|
+
index.df.set(term, (index.df.get(term) || 0) + 1);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
if (v !== void 0) {
|
|
1397
|
+
addField(v, -1, -1);
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
if (fields) {
|
|
1401
|
+
for (let keyIdx = 0; keyIdx < keyCount; keyIdx++) {
|
|
1402
|
+
const value = fields[keyIdx];
|
|
1403
|
+
if (!value) continue;
|
|
1404
|
+
if (Array.isArray(value)) {
|
|
1405
|
+
for (const sub of value) {
|
|
1406
|
+
addField(sub.v, keyIdx, sub.i ?? -1);
|
|
1407
|
+
}
|
|
1408
|
+
} else {
|
|
1409
|
+
addField(value.v, keyIdx, -1);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
function removeFromInvertedIndex(index, docIdx) {
|
|
1415
|
+
for (const [term, postings] of index.terms) {
|
|
1416
|
+
const filtered = postings.filter((p) => p.docIdx !== docIdx);
|
|
1417
|
+
const removed = postings.length - filtered.length;
|
|
1418
|
+
if (removed > 0) {
|
|
1419
|
+
index.fieldCount -= removed;
|
|
1420
|
+
index.df.set(term, (index.df.get(term) || 0) - removed);
|
|
1421
|
+
if (filtered.length === 0) {
|
|
1422
|
+
index.terms.delete(term);
|
|
1423
|
+
index.df.delete(term);
|
|
1424
|
+
} else {
|
|
1425
|
+
index.terms.set(term, filtered);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1111
1430
|
class Fuse {
|
|
1112
|
-
|
|
1113
|
-
|
|
1431
|
+
// Statics are assigned in entry.ts
|
|
1432
|
+
constructor(docs, options, index) {
|
|
1433
|
+
this.options = {
|
|
1434
|
+
...Config,
|
|
1435
|
+
...options
|
|
1436
|
+
};
|
|
1114
1437
|
if (this.options.useExtendedSearch && false) ;
|
|
1438
|
+
if (this.options.useTokenSearch && false) ;
|
|
1115
1439
|
this._keyStore = new KeyStore(this.options.keys);
|
|
1440
|
+
this._docs = docs;
|
|
1441
|
+
this._myIndex = null;
|
|
1442
|
+
this._invertedIndex = null;
|
|
1116
1443
|
this.setCollection(docs, index);
|
|
1444
|
+
this._lastQuery = null;
|
|
1445
|
+
this._lastSearcher = null;
|
|
1446
|
+
}
|
|
1447
|
+
_getSearcher(query) {
|
|
1448
|
+
if (this._lastQuery === query) {
|
|
1449
|
+
return this._lastSearcher;
|
|
1450
|
+
}
|
|
1451
|
+
const opts = this._invertedIndex ? {
|
|
1452
|
+
...this.options,
|
|
1453
|
+
_invertedIndex: this._invertedIndex
|
|
1454
|
+
} : this.options;
|
|
1455
|
+
const searcher = createSearcher(query, opts);
|
|
1456
|
+
this._lastQuery = query;
|
|
1457
|
+
this._lastSearcher = searcher;
|
|
1458
|
+
return searcher;
|
|
1117
1459
|
}
|
|
1118
1460
|
setCollection(docs, index) {
|
|
1119
1461
|
this._docs = docs;
|
|
@@ -1124,6 +1466,13 @@
|
|
|
1124
1466
|
getFn: this.options.getFn,
|
|
1125
1467
|
fieldNormWeight: this.options.fieldNormWeight
|
|
1126
1468
|
});
|
|
1469
|
+
if (this.options.useTokenSearch) {
|
|
1470
|
+
const analyzer = createAnalyzer({
|
|
1471
|
+
isCaseSensitive: this.options.isCaseSensitive,
|
|
1472
|
+
ignoreDiacritics: this.options.ignoreDiacritics
|
|
1473
|
+
});
|
|
1474
|
+
this._invertedIndex = buildInvertedIndex(this._myIndex.records, this._myIndex.keys.length, analyzer);
|
|
1475
|
+
}
|
|
1127
1476
|
}
|
|
1128
1477
|
add(doc) {
|
|
1129
1478
|
if (!isDefined(doc)) {
|
|
@@ -1131,28 +1480,52 @@
|
|
|
1131
1480
|
}
|
|
1132
1481
|
this._docs.push(doc);
|
|
1133
1482
|
this._myIndex.add(doc);
|
|
1483
|
+
if (this._invertedIndex) {
|
|
1484
|
+
const record = this._myIndex.records[this._myIndex.records.length - 1];
|
|
1485
|
+
const analyzer = createAnalyzer({
|
|
1486
|
+
isCaseSensitive: this.options.isCaseSensitive,
|
|
1487
|
+
ignoreDiacritics: this.options.ignoreDiacritics
|
|
1488
|
+
});
|
|
1489
|
+
addToInvertedIndex(this._invertedIndex, record, this._myIndex.keys.length, analyzer);
|
|
1490
|
+
}
|
|
1134
1491
|
}
|
|
1135
1492
|
remove(predicate = () => false) {
|
|
1136
1493
|
const results = [];
|
|
1494
|
+
const indicesToRemove = [];
|
|
1137
1495
|
for (let i = 0, len = this._docs.length; i < len; i += 1) {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1496
|
+
if (predicate(this._docs[i], i)) {
|
|
1497
|
+
results.push(this._docs[i]);
|
|
1498
|
+
indicesToRemove.push(i);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
if (indicesToRemove.length) {
|
|
1502
|
+
if (this._invertedIndex) {
|
|
1503
|
+
for (const idx of indicesToRemove) {
|
|
1504
|
+
removeFromInvertedIndex(this._invertedIndex, idx);
|
|
1505
|
+
}
|
|
1144
1506
|
}
|
|
1507
|
+
for (let i = indicesToRemove.length - 1; i >= 0; i -= 1) {
|
|
1508
|
+
this._docs.splice(indicesToRemove[i], 1);
|
|
1509
|
+
}
|
|
1510
|
+
this._myIndex.removeAll(indicesToRemove);
|
|
1145
1511
|
}
|
|
1146
1512
|
return results;
|
|
1147
1513
|
}
|
|
1148
1514
|
removeAt(idx) {
|
|
1149
|
-
this.
|
|
1515
|
+
if (this._invertedIndex) {
|
|
1516
|
+
removeFromInvertedIndex(this._invertedIndex, idx);
|
|
1517
|
+
}
|
|
1518
|
+
const doc = this._docs.splice(idx, 1)[0];
|
|
1150
1519
|
this._myIndex.removeAt(idx);
|
|
1520
|
+
return doc;
|
|
1151
1521
|
}
|
|
1152
1522
|
getIndex() {
|
|
1153
1523
|
return this._myIndex;
|
|
1154
1524
|
}
|
|
1155
|
-
search(query,
|
|
1525
|
+
search(query, options) {
|
|
1526
|
+
const {
|
|
1527
|
+
limit = -1
|
|
1528
|
+
} = options || {};
|
|
1156
1529
|
const {
|
|
1157
1530
|
includeMatches,
|
|
1158
1531
|
includeScore,
|
|
@@ -1160,34 +1533,92 @@
|
|
|
1160
1533
|
sortFn,
|
|
1161
1534
|
ignoreFieldNorm
|
|
1162
1535
|
} = this.options;
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1536
|
+
if (isString(query) && !query.trim()) {
|
|
1537
|
+
let docs = this._docs.map((item, idx) => ({
|
|
1538
|
+
item,
|
|
1539
|
+
refIndex: idx
|
|
1540
|
+
}));
|
|
1541
|
+
if (isNumber(limit) && limit > -1) {
|
|
1542
|
+
docs = docs.slice(0, limit);
|
|
1543
|
+
}
|
|
1544
|
+
return docs;
|
|
1167
1545
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1546
|
+
const useHeap = isNumber(limit) && limit > 0 && isString(query);
|
|
1547
|
+
let results;
|
|
1548
|
+
if (useHeap) {
|
|
1549
|
+
const heap = new MaxHeap(limit);
|
|
1550
|
+
if (isString(this._docs[0])) {
|
|
1551
|
+
this._searchStringList(query, {
|
|
1552
|
+
heap,
|
|
1553
|
+
ignoreFieldNorm
|
|
1554
|
+
});
|
|
1555
|
+
} else {
|
|
1556
|
+
this._searchObjectList(query, {
|
|
1557
|
+
heap,
|
|
1558
|
+
ignoreFieldNorm
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
results = heap.extractSorted(sortFn);
|
|
1562
|
+
} else {
|
|
1563
|
+
results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query);
|
|
1564
|
+
computeScore(results, {
|
|
1565
|
+
ignoreFieldNorm
|
|
1566
|
+
});
|
|
1567
|
+
if (shouldSort) {
|
|
1568
|
+
results.sort(sortFn);
|
|
1569
|
+
}
|
|
1570
|
+
if (isNumber(limit) && limit > -1) {
|
|
1571
|
+
results = results.slice(0, limit);
|
|
1572
|
+
}
|
|
1170
1573
|
}
|
|
1171
1574
|
return format(results, this._docs, {
|
|
1172
1575
|
includeMatches,
|
|
1173
1576
|
includeScore
|
|
1174
1577
|
});
|
|
1175
1578
|
}
|
|
1176
|
-
_searchStringList(query
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1579
|
+
_searchStringList(query, {
|
|
1580
|
+
heap,
|
|
1581
|
+
ignoreFieldNorm
|
|
1582
|
+
} = {}) {
|
|
1583
|
+
const searcher = this._getSearcher(query);
|
|
1584
|
+
const {
|
|
1585
|
+
records
|
|
1586
|
+
} = this._myIndex;
|
|
1587
|
+
const results = heap ? null : [];
|
|
1588
|
+
records.forEach(({
|
|
1589
|
+
v: text,
|
|
1590
|
+
i: idx,
|
|
1591
|
+
n: norm2
|
|
1592
|
+
}) => {
|
|
1181
1593
|
if (!isDefined(text)) {
|
|
1182
1594
|
return;
|
|
1183
1595
|
}
|
|
1184
|
-
const {
|
|
1596
|
+
const {
|
|
1597
|
+
isMatch,
|
|
1598
|
+
score,
|
|
1599
|
+
indices
|
|
1600
|
+
} = searcher.searchIn(text);
|
|
1185
1601
|
if (isMatch) {
|
|
1186
|
-
|
|
1602
|
+
const result = {
|
|
1187
1603
|
item: text,
|
|
1188
1604
|
idx,
|
|
1189
|
-
matches: [{
|
|
1190
|
-
|
|
1605
|
+
matches: [{
|
|
1606
|
+
score,
|
|
1607
|
+
value: text,
|
|
1608
|
+
norm: norm2,
|
|
1609
|
+
indices
|
|
1610
|
+
}]
|
|
1611
|
+
};
|
|
1612
|
+
if (heap) {
|
|
1613
|
+
result.score = computeScoreSingle(result.matches, {
|
|
1614
|
+
ignoreFieldNorm
|
|
1615
|
+
});
|
|
1616
|
+
if (heap.shouldInsert(result.score)) {
|
|
1617
|
+
heap.insert(result);
|
|
1618
|
+
}
|
|
1619
|
+
} else {
|
|
1620
|
+
results.push(result);
|
|
1621
|
+
}
|
|
1191
1622
|
}
|
|
1192
1623
|
});
|
|
1193
1624
|
return results;
|
|
@@ -1195,94 +1626,171 @@
|
|
|
1195
1626
|
_searchLogical(query) {
|
|
1196
1627
|
const expression = parse(query, this.options);
|
|
1197
1628
|
const evaluate = (node, item, idx) => {
|
|
1198
|
-
if (!node
|
|
1199
|
-
const {
|
|
1200
|
-
|
|
1201
|
-
key: this._keyStore.get(keyId),
|
|
1202
|
-
value: this._myIndex.getValueForItemAtKeyId(item, keyId),
|
|
1629
|
+
if (!("children" in node)) {
|
|
1630
|
+
const {
|
|
1631
|
+
keyId,
|
|
1203
1632
|
searcher
|
|
1204
|
-
}
|
|
1633
|
+
} = node;
|
|
1634
|
+
let matches;
|
|
1635
|
+
if (keyId === null) {
|
|
1636
|
+
matches = [];
|
|
1637
|
+
this._myIndex.keys.forEach((key, keyIndex) => {
|
|
1638
|
+
matches.push(...this._findMatches({
|
|
1639
|
+
key,
|
|
1640
|
+
value: item[keyIndex],
|
|
1641
|
+
searcher
|
|
1642
|
+
}));
|
|
1643
|
+
});
|
|
1644
|
+
} else {
|
|
1645
|
+
matches = this._findMatches({
|
|
1646
|
+
key: this._keyStore.get(keyId),
|
|
1647
|
+
value: this._myIndex.getValueForItemAtKeyId(item, keyId),
|
|
1648
|
+
searcher
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1205
1651
|
if (matches && matches.length) {
|
|
1206
|
-
return [
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
}
|
|
1212
|
-
];
|
|
1652
|
+
return [{
|
|
1653
|
+
idx,
|
|
1654
|
+
item,
|
|
1655
|
+
matches
|
|
1656
|
+
}];
|
|
1213
1657
|
}
|
|
1214
1658
|
return [];
|
|
1215
1659
|
}
|
|
1660
|
+
const {
|
|
1661
|
+
children,
|
|
1662
|
+
operator
|
|
1663
|
+
} = node;
|
|
1216
1664
|
const res = [];
|
|
1217
|
-
for (let i = 0, len =
|
|
1218
|
-
const child =
|
|
1665
|
+
for (let i = 0, len = children.length; i < len; i += 1) {
|
|
1666
|
+
const child = children[i];
|
|
1219
1667
|
const result = evaluate(child, item, idx);
|
|
1220
1668
|
if (result.length) {
|
|
1221
1669
|
res.push(...result);
|
|
1222
|
-
} else if (
|
|
1670
|
+
} else if (operator === LogicalOperator.AND) {
|
|
1223
1671
|
return [];
|
|
1224
1672
|
}
|
|
1225
1673
|
}
|
|
1226
1674
|
return res;
|
|
1227
1675
|
};
|
|
1228
1676
|
const records = this._myIndex.records;
|
|
1229
|
-
const resultMap =
|
|
1677
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
1230
1678
|
const results = [];
|
|
1231
|
-
records.forEach(({
|
|
1679
|
+
records.forEach(({
|
|
1680
|
+
$: item,
|
|
1681
|
+
i: idx
|
|
1682
|
+
}) => {
|
|
1232
1683
|
if (isDefined(item)) {
|
|
1233
|
-
|
|
1684
|
+
const expResults = evaluate(expression, item, idx);
|
|
1234
1685
|
if (expResults.length) {
|
|
1235
|
-
if (!resultMap
|
|
1236
|
-
resultMap
|
|
1237
|
-
|
|
1686
|
+
if (!resultMap.has(idx)) {
|
|
1687
|
+
resultMap.set(idx, {
|
|
1688
|
+
idx,
|
|
1689
|
+
item,
|
|
1690
|
+
matches: []
|
|
1691
|
+
});
|
|
1692
|
+
results.push(resultMap.get(idx));
|
|
1238
1693
|
}
|
|
1239
|
-
expResults.forEach(({
|
|
1240
|
-
|
|
1694
|
+
expResults.forEach(({
|
|
1695
|
+
matches
|
|
1696
|
+
}) => {
|
|
1697
|
+
resultMap.get(idx).matches.push(...matches);
|
|
1241
1698
|
});
|
|
1242
1699
|
}
|
|
1243
1700
|
}
|
|
1244
1701
|
});
|
|
1245
1702
|
return results;
|
|
1246
1703
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1704
|
+
// When a search involves inverse patterns (e.g. !Syrup), the aggregation
|
|
1705
|
+
// across keys switches from "ANY key matches" to "ALL keys must match."
|
|
1706
|
+
// This is signaled by hasInverse on the SearchResult from ExtendedSearch.
|
|
1707
|
+
//
|
|
1708
|
+
// For mixed patterns like "^hello !Syrup", a key failure is ambiguous —
|
|
1709
|
+
// it could be the positive or inverse term that failed. In that case we
|
|
1710
|
+
// conservatively exclude the item, which is strictly better than the old
|
|
1711
|
+
// behavior of including it. See: https://github.com/krisk/Fuse/issues/712
|
|
1712
|
+
_searchObjectList(query, {
|
|
1713
|
+
heap,
|
|
1714
|
+
ignoreFieldNorm
|
|
1715
|
+
} = {}) {
|
|
1716
|
+
const searcher = this._getSearcher(query);
|
|
1717
|
+
const {
|
|
1718
|
+
keys,
|
|
1719
|
+
records
|
|
1720
|
+
} = this._myIndex;
|
|
1721
|
+
const results = heap ? null : [];
|
|
1722
|
+
records.forEach(({
|
|
1723
|
+
$: item,
|
|
1724
|
+
i: idx
|
|
1725
|
+
}) => {
|
|
1252
1726
|
if (!isDefined(item)) {
|
|
1253
1727
|
return;
|
|
1254
1728
|
}
|
|
1255
|
-
|
|
1729
|
+
const matches = [];
|
|
1730
|
+
let anyKeyFailed = false;
|
|
1731
|
+
let hasInverse = false;
|
|
1256
1732
|
keys.forEach((key, keyIndex) => {
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1733
|
+
const keyMatches = this._findMatches({
|
|
1734
|
+
key,
|
|
1735
|
+
value: item[keyIndex],
|
|
1736
|
+
searcher
|
|
1737
|
+
});
|
|
1738
|
+
if (keyMatches.length) {
|
|
1739
|
+
matches.push(...keyMatches);
|
|
1740
|
+
if (keyMatches[0].hasInverse) {
|
|
1741
|
+
hasInverse = true;
|
|
1742
|
+
}
|
|
1743
|
+
} else {
|
|
1744
|
+
anyKeyFailed = true;
|
|
1745
|
+
}
|
|
1264
1746
|
});
|
|
1747
|
+
if (hasInverse && anyKeyFailed) {
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1265
1750
|
if (matches.length) {
|
|
1266
|
-
|
|
1751
|
+
const result = {
|
|
1267
1752
|
idx,
|
|
1268
1753
|
item,
|
|
1269
1754
|
matches
|
|
1270
|
-
}
|
|
1755
|
+
};
|
|
1756
|
+
if (heap) {
|
|
1757
|
+
result.score = computeScoreSingle(result.matches, {
|
|
1758
|
+
ignoreFieldNorm
|
|
1759
|
+
});
|
|
1760
|
+
if (heap.shouldInsert(result.score)) {
|
|
1761
|
+
heap.insert(result);
|
|
1762
|
+
}
|
|
1763
|
+
} else {
|
|
1764
|
+
results.push(result);
|
|
1765
|
+
}
|
|
1271
1766
|
}
|
|
1272
1767
|
});
|
|
1273
1768
|
return results;
|
|
1274
1769
|
}
|
|
1275
|
-
_findMatches({
|
|
1770
|
+
_findMatches({
|
|
1771
|
+
key,
|
|
1772
|
+
value,
|
|
1773
|
+
searcher
|
|
1774
|
+
}) {
|
|
1276
1775
|
if (!isDefined(value)) {
|
|
1277
1776
|
return [];
|
|
1278
1777
|
}
|
|
1279
|
-
|
|
1778
|
+
const matches = [];
|
|
1280
1779
|
if (isArray(value)) {
|
|
1281
|
-
value.forEach(({
|
|
1780
|
+
value.forEach(({
|
|
1781
|
+
v: text,
|
|
1782
|
+
i: idx,
|
|
1783
|
+
n: norm2
|
|
1784
|
+
}) => {
|
|
1282
1785
|
if (!isDefined(text)) {
|
|
1283
1786
|
return;
|
|
1284
1787
|
}
|
|
1285
|
-
const {
|
|
1788
|
+
const {
|
|
1789
|
+
isMatch,
|
|
1790
|
+
score,
|
|
1791
|
+
indices,
|
|
1792
|
+
hasInverse
|
|
1793
|
+
} = searcher.searchIn(text);
|
|
1286
1794
|
if (isMatch) {
|
|
1287
1795
|
matches.push({
|
|
1288
1796
|
score,
|
|
@@ -1290,30 +1798,134 @@
|
|
|
1290
1798
|
value: text,
|
|
1291
1799
|
idx,
|
|
1292
1800
|
norm: norm2,
|
|
1293
|
-
indices
|
|
1801
|
+
indices,
|
|
1802
|
+
hasInverse
|
|
1294
1803
|
});
|
|
1295
1804
|
}
|
|
1296
1805
|
});
|
|
1297
1806
|
} else {
|
|
1298
|
-
const {
|
|
1299
|
-
|
|
1807
|
+
const {
|
|
1808
|
+
v: text,
|
|
1809
|
+
n: norm2
|
|
1810
|
+
} = value;
|
|
1811
|
+
const {
|
|
1812
|
+
isMatch,
|
|
1813
|
+
score,
|
|
1814
|
+
indices,
|
|
1815
|
+
hasInverse
|
|
1816
|
+
} = searcher.searchIn(text);
|
|
1300
1817
|
if (isMatch) {
|
|
1301
|
-
matches.push({
|
|
1818
|
+
matches.push({
|
|
1819
|
+
score,
|
|
1820
|
+
key,
|
|
1821
|
+
value: text,
|
|
1822
|
+
norm: norm2,
|
|
1823
|
+
indices,
|
|
1824
|
+
hasInverse
|
|
1825
|
+
});
|
|
1302
1826
|
}
|
|
1303
1827
|
}
|
|
1304
1828
|
return matches;
|
|
1305
1829
|
}
|
|
1306
1830
|
}
|
|
1307
|
-
|
|
1831
|
+
class TokenSearch {
|
|
1832
|
+
static condition(_, options) {
|
|
1833
|
+
return options.useTokenSearch;
|
|
1834
|
+
}
|
|
1835
|
+
constructor(pattern, options) {
|
|
1836
|
+
this.options = options;
|
|
1837
|
+
this.analyzer = createAnalyzer({
|
|
1838
|
+
isCaseSensitive: options.isCaseSensitive,
|
|
1839
|
+
ignoreDiacritics: options.ignoreDiacritics
|
|
1840
|
+
});
|
|
1841
|
+
const queryTerms = this.analyzer.tokenize(pattern);
|
|
1842
|
+
const invertedIndex = options._invertedIndex;
|
|
1843
|
+
const {
|
|
1844
|
+
df,
|
|
1845
|
+
fieldCount
|
|
1846
|
+
} = invertedIndex;
|
|
1847
|
+
this.termSearchers = [];
|
|
1848
|
+
this.idfWeights = [];
|
|
1849
|
+
for (const term of queryTerms) {
|
|
1850
|
+
this.termSearchers.push(new BitapSearch(term, {
|
|
1851
|
+
location: options.location,
|
|
1852
|
+
threshold: options.threshold,
|
|
1853
|
+
distance: options.distance,
|
|
1854
|
+
includeMatches: options.includeMatches,
|
|
1855
|
+
findAllMatches: options.findAllMatches,
|
|
1856
|
+
minMatchCharLength: options.minMatchCharLength,
|
|
1857
|
+
isCaseSensitive: options.isCaseSensitive,
|
|
1858
|
+
ignoreDiacritics: options.ignoreDiacritics,
|
|
1859
|
+
ignoreLocation: true
|
|
1860
|
+
}));
|
|
1861
|
+
const docFreq = df.get(term) || 0;
|
|
1862
|
+
const idf = Math.log(1 + (fieldCount - docFreq + 0.5) / (docFreq + 0.5));
|
|
1863
|
+
this.idfWeights.push(idf);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
searchIn(text) {
|
|
1867
|
+
if (!this.termSearchers.length) {
|
|
1868
|
+
return {
|
|
1869
|
+
isMatch: false,
|
|
1870
|
+
score: 1
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
const allIndices = [];
|
|
1874
|
+
let weightedScore = 0;
|
|
1875
|
+
let maxPossibleScore = 0;
|
|
1876
|
+
let matchedCount = 0;
|
|
1877
|
+
for (let i = 0; i < this.termSearchers.length; i++) {
|
|
1878
|
+
const result = this.termSearchers[i].searchIn(text);
|
|
1879
|
+
const idf = this.idfWeights[i];
|
|
1880
|
+
maxPossibleScore += idf;
|
|
1881
|
+
if (result.isMatch) {
|
|
1882
|
+
matchedCount++;
|
|
1883
|
+
weightedScore += idf * (1 - result.score);
|
|
1884
|
+
if (result.indices) {
|
|
1885
|
+
allIndices.push(...result.indices);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
if (matchedCount === 0) {
|
|
1890
|
+
return {
|
|
1891
|
+
isMatch: false,
|
|
1892
|
+
score: 1
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
1895
|
+
const normalized = maxPossibleScore > 0 ? 1 - weightedScore / maxPossibleScore : 0;
|
|
1896
|
+
const searchResult = {
|
|
1897
|
+
isMatch: true,
|
|
1898
|
+
score: Math.max(1e-3, normalized)
|
|
1899
|
+
};
|
|
1900
|
+
if (this.options.includeMatches && allIndices.length) {
|
|
1901
|
+
searchResult.indices = mergeIndices(allIndices);
|
|
1902
|
+
}
|
|
1903
|
+
return searchResult;
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
Fuse.version = "7.3.0";
|
|
1308
1907
|
Fuse.createIndex = createIndex;
|
|
1309
1908
|
Fuse.parseIndex = parseIndex;
|
|
1310
1909
|
Fuse.config = Config;
|
|
1910
|
+
Fuse.match = function(pattern, text, options) {
|
|
1911
|
+
const searcher = createSearcher(pattern, {
|
|
1912
|
+
...Config,
|
|
1913
|
+
...options
|
|
1914
|
+
});
|
|
1915
|
+
return searcher.searchIn(text);
|
|
1916
|
+
};
|
|
1311
1917
|
{
|
|
1312
1918
|
Fuse.parseQuery = parse;
|
|
1313
1919
|
}
|
|
1314
1920
|
{
|
|
1315
1921
|
register(ExtendedSearch);
|
|
1316
1922
|
}
|
|
1923
|
+
{
|
|
1924
|
+
register(TokenSearch);
|
|
1925
|
+
}
|
|
1926
|
+
Fuse.use = function(...plugins) {
|
|
1927
|
+
plugins.forEach((plugin) => register(plugin));
|
|
1928
|
+
};
|
|
1317
1929
|
const version = "0.3.5";
|
|
1318
1930
|
const packageJson = {
|
|
1319
1931
|
version
|