@delali/narsil 0.1.11 → 0.1.12
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 +21 -2
- package/dist/{chunk-Q4OVNESD.mjs → chunk-HMO6OMZ3.mjs} +1766 -1131
- package/dist/{chunk-CU6BVZEU.mjs → chunk-MEW7HD75.mjs} +87 -155
- package/dist/{config-DX--2Owg.d.ts → config-CojEPOgS.d.ts} +10 -1
- package/dist/{direct-executor-6FOKJKLD.mjs → direct-executor-E52FFGDE.mjs} +1 -1
- package/dist/distribution/coordinator/etcd.d.ts +2 -2
- package/dist/distribution/coordinator/in-memory.d.ts +2 -2
- package/dist/distribution.d.ts +4 -4
- package/dist/distribution.mjs +4 -3
- package/dist/index.browser.mjs +1868 -1301
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/{narsil-B3jKyXCr.d.ts → narsil-D-84Nvm4.d.ts} +2 -2
- package/dist/persistence/durability/checkpoint-worker.d.ts +2 -0
- package/dist/persistence/durability/checkpoint-worker.mjs +2 -2
- package/dist/{schema-C3VhNvfw.d.ts → schema-DCgkO1j4.d.ts} +5 -0
- package/dist/server.d.ts +4 -3
- package/dist/server.mjs +1 -0
- package/dist/{types-CTVVm4GG.d.ts → types-DHX6zGeD.d.ts} +1 -1
- package/dist/workers/entry.mjs +1 -1
- package/package.json +1 -1
package/dist/index.browser.mjs
CHANGED
|
@@ -129,6 +129,7 @@ function computeDefaultCacheSize() {
|
|
|
129
129
|
return Math.max(CACHE_SIZE_FLOOR, Math.min(2e5, CACHE_SIZE_CEILING));
|
|
130
130
|
}
|
|
131
131
|
var normalizationCache = /* @__PURE__ */ new Map();
|
|
132
|
+
var normalizationCacheSize = 0;
|
|
132
133
|
var maxCacheSize = computeDefaultCacheSize();
|
|
133
134
|
function configureNormalizationCache(maxSize) {
|
|
134
135
|
if (!Number.isFinite(maxSize)) {
|
|
@@ -151,19 +152,13 @@ function configureNormalizationCache(maxSize) {
|
|
|
151
152
|
);
|
|
152
153
|
}
|
|
153
154
|
maxCacheSize = Math.max(CACHE_SIZE_FLOOR, Math.min(Math.floor(maxSize), CACHE_SIZE_CEILING));
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
let count = 0;
|
|
157
|
-
for (const key of normalizationCache.keys()) {
|
|
158
|
-
if (count >= excess) break;
|
|
159
|
-
normalizationCache.delete(key);
|
|
160
|
-
count++;
|
|
161
|
-
}
|
|
155
|
+
if (normalizationCacheSize > maxCacheSize) {
|
|
156
|
+
evictOldestEntries(normalizationCacheSize - maxCacheSize);
|
|
162
157
|
}
|
|
163
158
|
}
|
|
164
159
|
var cachedLangName = "";
|
|
165
160
|
var cachedFlags = "";
|
|
166
|
-
var
|
|
161
|
+
var cachedBucket = null;
|
|
167
162
|
function stripDiacritics(text) {
|
|
168
163
|
return text.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
169
164
|
}
|
|
@@ -172,18 +167,44 @@ function resolveStopWords(language, override) {
|
|
|
172
167
|
if (override instanceof Set) return override;
|
|
173
168
|
return override(language.stopWords);
|
|
174
169
|
}
|
|
175
|
-
function
|
|
176
|
-
if (language.name === cachedLangName && flags === cachedFlags) return
|
|
170
|
+
function getCacheBucket(language, flags) {
|
|
171
|
+
if (cachedBucket && language.name === cachedLangName && flags === cachedFlags) return cachedBucket;
|
|
172
|
+
const key = `${language.name}:${flags}`;
|
|
173
|
+
let bucket = normalizationCache.get(key);
|
|
174
|
+
if (!bucket) {
|
|
175
|
+
bucket = /* @__PURE__ */ new Map();
|
|
176
|
+
normalizationCache.set(key, bucket);
|
|
177
|
+
}
|
|
177
178
|
cachedLangName = language.name;
|
|
178
179
|
cachedFlags = flags;
|
|
179
|
-
|
|
180
|
-
return
|
|
180
|
+
cachedBucket = bucket;
|
|
181
|
+
return bucket;
|
|
182
|
+
}
|
|
183
|
+
function evictOldestEntries(count) {
|
|
184
|
+
let remaining = count;
|
|
185
|
+
for (const [key, bucket] of normalizationCache) {
|
|
186
|
+
if (remaining <= 0) break;
|
|
187
|
+
if (bucket.size <= remaining) {
|
|
188
|
+
remaining -= bucket.size;
|
|
189
|
+
normalizationCacheSize -= bucket.size;
|
|
190
|
+
normalizationCache.delete(key);
|
|
191
|
+
if (bucket === cachedBucket) cachedBucket = null;
|
|
192
|
+
} else {
|
|
193
|
+
let deleted = 0;
|
|
194
|
+
for (const raw of bucket.keys()) {
|
|
195
|
+
if (deleted >= remaining) break;
|
|
196
|
+
bucket.delete(raw);
|
|
197
|
+
deleted++;
|
|
198
|
+
}
|
|
199
|
+
normalizationCacheSize -= deleted;
|
|
200
|
+
remaining = 0;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
181
203
|
}
|
|
182
204
|
function transformToken(raw, language, stem, removeDiacritics) {
|
|
183
205
|
const flags = (stem ? "s" : "") + (removeDiacritics ? "d" : "");
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
const cached = normalizationCache.get(cacheKey);
|
|
206
|
+
let bucket = getCacheBucket(language, flags);
|
|
207
|
+
const cached = bucket.get(raw);
|
|
187
208
|
if (cached !== void 0) return cached;
|
|
188
209
|
let normalized = raw;
|
|
189
210
|
if (removeDiacritics) {
|
|
@@ -192,16 +213,12 @@ function transformToken(raw, language, stem, removeDiacritics) {
|
|
|
192
213
|
if (stem && language.stemmer) {
|
|
193
214
|
normalized = language.stemmer(normalized);
|
|
194
215
|
}
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
for (const key of normalizationCache.keys()) {
|
|
199
|
-
if (count >= evictCount) break;
|
|
200
|
-
normalizationCache.delete(key);
|
|
201
|
-
count++;
|
|
202
|
-
}
|
|
216
|
+
if (normalizationCacheSize >= maxCacheSize) {
|
|
217
|
+
evictOldestEntries(Math.max(1, maxCacheSize >>> 2));
|
|
218
|
+
bucket = getCacheBucket(language, flags);
|
|
203
219
|
}
|
|
204
|
-
|
|
220
|
+
bucket.set(raw, normalized);
|
|
221
|
+
normalizationCacheSize++;
|
|
205
222
|
return normalized;
|
|
206
223
|
}
|
|
207
224
|
function splitText(text, language) {
|
|
@@ -231,24 +248,28 @@ function tokenize(text, language, options) {
|
|
|
231
248
|
stem = true,
|
|
232
249
|
removeStopWords = true,
|
|
233
250
|
removeDiacritics = false,
|
|
251
|
+
collectSurfaces = false,
|
|
234
252
|
customTokenizer,
|
|
235
253
|
stopWordOverride
|
|
236
254
|
} = options ?? {};
|
|
237
255
|
if (customTokenizer) {
|
|
238
256
|
const customResult = customTokenizer.tokenize(text);
|
|
257
|
+
const originals = customResult.map((t) => t.token);
|
|
239
258
|
return {
|
|
240
259
|
tokens: customResult,
|
|
241
|
-
originalTokens:
|
|
260
|
+
originalTokens: originals
|
|
242
261
|
};
|
|
243
262
|
}
|
|
244
263
|
const normalized = isAscii(text) ? text.toLowerCase() : text.normalize("NFC").toLowerCase();
|
|
245
264
|
const rawParts = splitText(normalized, language);
|
|
246
265
|
const minLength = language.tokenizer?.minTokenLength ?? DEFAULT_MIN_TOKEN_LENGTH;
|
|
247
266
|
const effectiveDiacritics = removeDiacritics || (language.tokenizer?.normalizeDiacritics ?? false);
|
|
267
|
+
const stemsTokens = stem && language.stemmer !== void 0;
|
|
248
268
|
const stopWords2 = removeStopWords ? resolveStopWords(language, stopWordOverride) : /* @__PURE__ */ new Set();
|
|
249
269
|
const stripPossessives = language.tokenizer?.stripPossessive ?? false;
|
|
250
270
|
const tokens = [];
|
|
251
271
|
const originalTokens = [];
|
|
272
|
+
const surfaces = collectSurfaces && stemsTokens ? [] : void 0;
|
|
252
273
|
let position = 0;
|
|
253
274
|
for (const part of rawParts) {
|
|
254
275
|
const candidate = stripPossessives ? stripPossessive(part) : part;
|
|
@@ -264,16 +285,21 @@ function tokenize(text, language, options) {
|
|
|
264
285
|
if (processed.length > 0) {
|
|
265
286
|
tokens.push({ token: processed, position });
|
|
266
287
|
originalTokens.push(part);
|
|
288
|
+
if (surfaces) {
|
|
289
|
+
const surface = effectiveDiacritics ? transformToken(candidate, language, false, true) : candidate;
|
|
290
|
+
surfaces.push(surface === processed ? void 0 : surface);
|
|
291
|
+
}
|
|
267
292
|
}
|
|
268
293
|
position++;
|
|
269
294
|
}
|
|
270
|
-
return { tokens, originalTokens };
|
|
295
|
+
return { tokens, originalTokens, surfaces };
|
|
271
296
|
}
|
|
272
297
|
function* tokenizeIterator(text, language, options) {
|
|
273
298
|
const {
|
|
274
299
|
stem = true,
|
|
275
300
|
removeStopWords = true,
|
|
276
301
|
removeDiacritics = false,
|
|
302
|
+
collectSurfaces = false,
|
|
277
303
|
customTokenizer,
|
|
278
304
|
stopWordOverride
|
|
279
305
|
} = options ?? {};
|
|
@@ -288,6 +314,8 @@ function* tokenizeIterator(text, language, options) {
|
|
|
288
314
|
const rawParts = splitText(normalized, language);
|
|
289
315
|
const minLength = language.tokenizer?.minTokenLength ?? DEFAULT_MIN_TOKEN_LENGTH;
|
|
290
316
|
const effectiveDiacritics = removeDiacritics || (language.tokenizer?.normalizeDiacritics ?? false);
|
|
317
|
+
const stemsTokens = stem && language.stemmer !== void 0;
|
|
318
|
+
const wantSurfaces = collectSurfaces && stemsTokens;
|
|
291
319
|
const stopWords2 = removeStopWords ? resolveStopWords(language, stopWordOverride) : /* @__PURE__ */ new Set();
|
|
292
320
|
const stripPossessives = language.tokenizer?.stripPossessive ?? false;
|
|
293
321
|
let position = 0;
|
|
@@ -303,20 +331,27 @@ function* tokenizeIterator(text, language, options) {
|
|
|
303
331
|
}
|
|
304
332
|
const processed = transformToken(candidate, language, stem, effectiveDiacritics);
|
|
305
333
|
if (processed.length > 0) {
|
|
306
|
-
|
|
334
|
+
if (wantSurfaces) {
|
|
335
|
+
const surface = effectiveDiacritics ? transformToken(candidate, language, false, true) : candidate;
|
|
336
|
+
yield { token: processed, position, surface: surface === processed ? void 0 : surface };
|
|
337
|
+
} else {
|
|
338
|
+
yield { token: processed, position };
|
|
339
|
+
}
|
|
307
340
|
}
|
|
308
341
|
position++;
|
|
309
342
|
}
|
|
310
343
|
}
|
|
311
344
|
function clearNormalizationCache() {
|
|
312
345
|
normalizationCache.clear();
|
|
346
|
+
normalizationCacheSize = 0;
|
|
347
|
+
cachedBucket = null;
|
|
313
348
|
}
|
|
314
349
|
function resetNormalizationCache() {
|
|
315
|
-
|
|
350
|
+
clearNormalizationCache();
|
|
316
351
|
maxCacheSize = computeDefaultCacheSize();
|
|
317
352
|
}
|
|
318
353
|
function getNormalizationCacheSize() {
|
|
319
|
-
return
|
|
354
|
+
return normalizationCacheSize;
|
|
320
355
|
}
|
|
321
356
|
|
|
322
357
|
// src/languages/english.ts
|
|
@@ -1081,8 +1116,20 @@ function validateVectorPromotion(config) {
|
|
|
1081
1116
|
fail("vectorPromotion.hnswConfig.metric must be 'cosine', 'dotProduct', or 'euclidean'", { metric });
|
|
1082
1117
|
}
|
|
1083
1118
|
}
|
|
1084
|
-
function
|
|
1085
|
-
|
|
1119
|
+
function sanitizeSurfaceForms(raw) {
|
|
1120
|
+
if (raw === void 0 || raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
1121
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
1122
|
+
let count = 0;
|
|
1123
|
+
for (const [surface, value] of Object.entries(raw)) {
|
|
1124
|
+
if (typeof value === "number") {
|
|
1125
|
+
result[surface] = value;
|
|
1126
|
+
count++;
|
|
1127
|
+
} else if (Array.isArray(value) && typeof value[0] === "number" && typeof value[1] === "string") {
|
|
1128
|
+
result[surface] = [value[0], value[1]];
|
|
1129
|
+
count++;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
return count > 0 ? result : void 0;
|
|
1086
1133
|
}
|
|
1087
1134
|
var VALID_HNSW_METRICS = /* @__PURE__ */ new Set(["cosine", "dotProduct", "euclidean"]);
|
|
1088
1135
|
function validateHnswMetric(value) {
|
|
@@ -1092,6 +1139,153 @@ function validateHnswMetric(value) {
|
|
|
1092
1139
|
}
|
|
1093
1140
|
return void 0;
|
|
1094
1141
|
}
|
|
1142
|
+
function wireToPartition(raw) {
|
|
1143
|
+
const documents = {};
|
|
1144
|
+
for (const [docId, doc] of Object.entries(raw.documents ?? {})) {
|
|
1145
|
+
documents[docId] = {
|
|
1146
|
+
fields: doc.fields,
|
|
1147
|
+
fieldLengths: doc.field_lengths ?? {}
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
const invertedIndex = {};
|
|
1151
|
+
for (const [token, list] of Object.entries(raw.inverted_index ?? {})) {
|
|
1152
|
+
invertedIndex[token] = {
|
|
1153
|
+
docFrequency: list.doc_freq,
|
|
1154
|
+
postings: (list.postings ?? []).map((p) => ({
|
|
1155
|
+
docId: p.doc_id,
|
|
1156
|
+
termFrequency: p.term_freq,
|
|
1157
|
+
field: p.field,
|
|
1158
|
+
positions: p.positions ?? []
|
|
1159
|
+
}))
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
const numeric = {};
|
|
1163
|
+
for (const [field, entries] of Object.entries(raw.field_indexes?.numeric ?? {})) {
|
|
1164
|
+
numeric[field] = entries.map((e) => ({ value: e.value, docId: e.doc_id }));
|
|
1165
|
+
}
|
|
1166
|
+
const boolean = {};
|
|
1167
|
+
for (const [field, idx] of Object.entries(raw.field_indexes?.boolean ?? {})) {
|
|
1168
|
+
boolean[field] = { trueDocs: idx.true_docs ?? [], falseDocs: idx.false_docs ?? [] };
|
|
1169
|
+
}
|
|
1170
|
+
const geopoint = {};
|
|
1171
|
+
for (const [field, entries] of Object.entries(raw.field_indexes?.geopoint ?? {})) {
|
|
1172
|
+
geopoint[field] = entries.map((e) => ({ lat: e.lat, lon: e.lon, docId: e.doc_id }));
|
|
1173
|
+
}
|
|
1174
|
+
const vectorData = {};
|
|
1175
|
+
for (const [field, data] of Object.entries(raw.vector_data ?? {})) {
|
|
1176
|
+
vectorData[field] = {
|
|
1177
|
+
dimension: data.dimension,
|
|
1178
|
+
vectors: (data.vectors ?? []).map((v) => ({ docId: v.doc_id, vector: v.vector })),
|
|
1179
|
+
hnswGraph: data.hnsw_graph ? {
|
|
1180
|
+
entryPoint: data.hnsw_graph.entry_point,
|
|
1181
|
+
maxLayer: data.hnsw_graph.max_layer,
|
|
1182
|
+
m: data.hnsw_graph.m,
|
|
1183
|
+
efConstruction: data.hnsw_graph.ef_construction,
|
|
1184
|
+
metric: validateHnswMetric(data.hnsw_graph.metric),
|
|
1185
|
+
nodes: data.hnsw_graph.nodes
|
|
1186
|
+
} : null,
|
|
1187
|
+
sq8: data.sq8 ? {
|
|
1188
|
+
alpha: data.sq8.alpha,
|
|
1189
|
+
offset: data.sq8.offset,
|
|
1190
|
+
quantizedVectors: data.sq8.quantized_vectors,
|
|
1191
|
+
vectorSums: data.sq8.vector_sums,
|
|
1192
|
+
vectorSumSqs: data.sq8.vector_sum_sqs
|
|
1193
|
+
} : null
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
return {
|
|
1197
|
+
indexName: raw.index_name,
|
|
1198
|
+
partitionId: raw.partition_id,
|
|
1199
|
+
totalPartitions: raw.total_partitions,
|
|
1200
|
+
language: raw.language ?? "english",
|
|
1201
|
+
schema: raw.schema ?? {},
|
|
1202
|
+
docCount: raw.doc_count ?? 0,
|
|
1203
|
+
avgDocLength: raw.avg_doc_length ?? 0,
|
|
1204
|
+
documents,
|
|
1205
|
+
invertedIndex,
|
|
1206
|
+
fieldIndexes: {
|
|
1207
|
+
numeric,
|
|
1208
|
+
boolean,
|
|
1209
|
+
enum: raw.field_indexes?.enum ?? {},
|
|
1210
|
+
geopoint
|
|
1211
|
+
},
|
|
1212
|
+
surfaceForms: sanitizeSurfaceForms(raw.surface_forms),
|
|
1213
|
+
vectorData,
|
|
1214
|
+
statistics: {
|
|
1215
|
+
totalDocuments: raw.statistics?.total_documents ?? 0,
|
|
1216
|
+
totalFieldLengths: raw.statistics?.total_field_lengths ?? {},
|
|
1217
|
+
averageFieldLengths: raw.statistics?.average_field_lengths ?? {},
|
|
1218
|
+
docFrequencies: raw.statistics?.doc_frequencies ?? {}
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
function deserializePayloadV1(data) {
|
|
1223
|
+
const raw = decode(data);
|
|
1224
|
+
return wireToPartition(raw);
|
|
1225
|
+
}
|
|
1226
|
+
function metadataToWire(meta) {
|
|
1227
|
+
const wire = {
|
|
1228
|
+
index_name: meta.indexName,
|
|
1229
|
+
schema: meta.schema,
|
|
1230
|
+
language: meta.language,
|
|
1231
|
+
partition_count: meta.partitionCount,
|
|
1232
|
+
bm25_params: meta.bm25Params,
|
|
1233
|
+
created_at: meta.createdAt,
|
|
1234
|
+
engine_version: meta.engineVersion
|
|
1235
|
+
};
|
|
1236
|
+
if (meta.vectorFields) {
|
|
1237
|
+
wire.vector_fields = meta.vectorFields;
|
|
1238
|
+
}
|
|
1239
|
+
if (meta.embedding) {
|
|
1240
|
+
wire.embedding = meta.embedding.adapter !== void 0 ? { adapter: meta.embedding.adapter, fields: meta.embedding.fields } : { fields: meta.embedding.fields };
|
|
1241
|
+
}
|
|
1242
|
+
if (meta.surfaceForms === true) {
|
|
1243
|
+
wire.surface_forms_enabled = true;
|
|
1244
|
+
}
|
|
1245
|
+
return wire;
|
|
1246
|
+
}
|
|
1247
|
+
function wireToMetadata(raw) {
|
|
1248
|
+
const meta = {
|
|
1249
|
+
indexName: raw.index_name,
|
|
1250
|
+
schema: raw.schema ?? {},
|
|
1251
|
+
language: raw.language ?? "english",
|
|
1252
|
+
partitionCount: raw.partition_count ?? 1,
|
|
1253
|
+
bm25Params: raw.bm25_params ?? { k1: 1.2, b: 0.75 },
|
|
1254
|
+
createdAt: raw.created_at ?? 0,
|
|
1255
|
+
engineVersion: raw.engine_version ?? "0.0.0"
|
|
1256
|
+
};
|
|
1257
|
+
if (raw.vector_fields) {
|
|
1258
|
+
meta.vectorFields = raw.vector_fields;
|
|
1259
|
+
}
|
|
1260
|
+
if (raw.embedding && typeof raw.embedding === "object" && typeof raw.embedding.fields === "object") {
|
|
1261
|
+
meta.embedding = typeof raw.embedding.adapter === "string" ? { adapter: raw.embedding.adapter, fields: raw.embedding.fields } : { fields: raw.embedding.fields };
|
|
1262
|
+
}
|
|
1263
|
+
if (raw.surface_forms_enabled === true) {
|
|
1264
|
+
meta.surfaceForms = true;
|
|
1265
|
+
}
|
|
1266
|
+
return meta;
|
|
1267
|
+
}
|
|
1268
|
+
function serializeMetadata(meta) {
|
|
1269
|
+
const wire = metadataToWire(meta);
|
|
1270
|
+
return encode(wire);
|
|
1271
|
+
}
|
|
1272
|
+
function deserializeMetadata(data) {
|
|
1273
|
+
const raw = decode(data);
|
|
1274
|
+
return wireToMetadata(raw);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
// src/serialization/payload-v2.ts
|
|
1278
|
+
function encodeRawPayloadV2(wire) {
|
|
1279
|
+
return encode(wire);
|
|
1280
|
+
}
|
|
1281
|
+
var VALID_HNSW_METRICS2 = /* @__PURE__ */ new Set(["cosine", "dotProduct", "euclidean"]);
|
|
1282
|
+
function validateHnswMetric2(value) {
|
|
1283
|
+
if (value === void 0 || value === null) return void 0;
|
|
1284
|
+
if (typeof value === "string" && VALID_HNSW_METRICS2.has(value)) {
|
|
1285
|
+
return value;
|
|
1286
|
+
}
|
|
1287
|
+
return void 0;
|
|
1288
|
+
}
|
|
1095
1289
|
function deserializePayloadV2(data) {
|
|
1096
1290
|
const raw = decode(data);
|
|
1097
1291
|
const documents = {};
|
|
@@ -1141,7 +1335,7 @@ function deserializePayloadV2(data) {
|
|
|
1141
1335
|
maxLayer: data2.hnsw_graph.max_layer,
|
|
1142
1336
|
m: data2.hnsw_graph.m,
|
|
1143
1337
|
efConstruction: data2.hnsw_graph.ef_construction,
|
|
1144
|
-
metric:
|
|
1338
|
+
metric: validateHnswMetric2(data2.hnsw_graph.metric),
|
|
1145
1339
|
nodes: data2.hnsw_graph.nodes
|
|
1146
1340
|
} : null,
|
|
1147
1341
|
sq8: data2.sq8 ? {
|
|
@@ -1169,6 +1363,7 @@ function deserializePayloadV2(data) {
|
|
|
1169
1363
|
enum: raw.field_indexes?.enum ?? {},
|
|
1170
1364
|
geopoint
|
|
1171
1365
|
},
|
|
1366
|
+
surfaceForms: sanitizeSurfaceForms(raw.surface_forms),
|
|
1172
1367
|
vectorData,
|
|
1173
1368
|
statistics: {
|
|
1174
1369
|
totalDocuments: raw.statistics?.total_documents ?? 0,
|
|
@@ -1384,7 +1579,8 @@ function createInvertedIndex(fieldNameTable) {
|
|
|
1384
1579
|
fieldNameIndices: new Uint8Array(INITIAL_CAPACITY),
|
|
1385
1580
|
positions: null,
|
|
1386
1581
|
docIdSet: /* @__PURE__ */ new Set(),
|
|
1387
|
-
deletedDocs: /* @__PURE__ */ new Set()
|
|
1582
|
+
deletedDocs: /* @__PURE__ */ new Set(),
|
|
1583
|
+
totalTermFrequency: 0
|
|
1388
1584
|
};
|
|
1389
1585
|
index.set(token, list);
|
|
1390
1586
|
trackToken(token);
|
|
@@ -1414,6 +1610,8 @@ function createInvertedIndex(fieldNameTable) {
|
|
|
1414
1610
|
}
|
|
1415
1611
|
}
|
|
1416
1612
|
writeIdx++;
|
|
1613
|
+
} else {
|
|
1614
|
+
list.totalTermFrequency -= list.termFrequencies[i];
|
|
1417
1615
|
}
|
|
1418
1616
|
}
|
|
1419
1617
|
list.docIds.length = writeIdx;
|
|
@@ -1434,6 +1632,8 @@ function createInvertedIndex(fieldNameTable) {
|
|
|
1434
1632
|
}
|
|
1435
1633
|
}
|
|
1436
1634
|
writeIdx++;
|
|
1635
|
+
} else {
|
|
1636
|
+
list.totalTermFrequency -= list.termFrequencies[i];
|
|
1437
1637
|
}
|
|
1438
1638
|
}
|
|
1439
1639
|
list.docIds.length = writeIdx;
|
|
@@ -1454,6 +1654,7 @@ function createInvertedIndex(fieldNameTable) {
|
|
|
1454
1654
|
list.docIds.push(internalId);
|
|
1455
1655
|
list.termFrequencies[idx] = termFrequency > MAX_TERM_FREQUENCY ? MAX_TERM_FREQUENCY : termFrequency;
|
|
1456
1656
|
list.fieldNameIndices[idx] = fieldNameIndex;
|
|
1657
|
+
list.totalTermFrequency += list.termFrequencies[idx];
|
|
1457
1658
|
if (positions !== null) {
|
|
1458
1659
|
if (!list.positions) {
|
|
1459
1660
|
list.positions = [];
|
|
@@ -1581,12 +1782,14 @@ function createInvertedIndex(fieldNameTable) {
|
|
|
1581
1782
|
const fieldNameIndices = new Uint8Array(count);
|
|
1582
1783
|
let hasPositions = false;
|
|
1583
1784
|
let validCount = 0;
|
|
1785
|
+
let totalTermFrequency = 0;
|
|
1584
1786
|
for (let i = 0; i < count; i++) {
|
|
1585
1787
|
const p = src.postings[i];
|
|
1586
1788
|
const internalId = resolver.toInternal(p.docId);
|
|
1587
1789
|
if (internalId === void 0) continue;
|
|
1588
1790
|
docIds[validCount] = internalId;
|
|
1589
1791
|
termFrequencies[validCount] = p.termFrequency > MAX_TERM_FREQUENCY ? MAX_TERM_FREQUENCY : p.termFrequency;
|
|
1792
|
+
totalTermFrequency += termFrequencies[validCount];
|
|
1590
1793
|
let fnIndex = fieldNameTable.indexMap.get(p.fieldName);
|
|
1591
1794
|
if (fnIndex === void 0) {
|
|
1592
1795
|
fnIndex = fieldNameTable.names.length;
|
|
@@ -1620,7 +1823,8 @@ function createInvertedIndex(fieldNameTable) {
|
|
|
1620
1823
|
fieldNameIndices: finalFNI,
|
|
1621
1824
|
positions,
|
|
1622
1825
|
docIdSet,
|
|
1623
|
-
deletedDocs: /* @__PURE__ */ new Set()
|
|
1826
|
+
deletedDocs: /* @__PURE__ */ new Set(),
|
|
1827
|
+
totalTermFrequency
|
|
1624
1828
|
});
|
|
1625
1829
|
trackToken(token);
|
|
1626
1830
|
}
|
|
@@ -1762,6 +1966,115 @@ function createPartitionStats() {
|
|
|
1762
1966
|
return stats;
|
|
1763
1967
|
}
|
|
1764
1968
|
|
|
1969
|
+
// src/core/surface-registry.ts
|
|
1970
|
+
function createSurfaceRegistry() {
|
|
1971
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1972
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1973
|
+
let totalsByToken = null;
|
|
1974
|
+
function track(surface) {
|
|
1975
|
+
const ch = surface[0];
|
|
1976
|
+
let bucket = buckets.get(ch);
|
|
1977
|
+
if (!bucket) {
|
|
1978
|
+
bucket = /* @__PURE__ */ new Set();
|
|
1979
|
+
buckets.set(ch, bucket);
|
|
1980
|
+
}
|
|
1981
|
+
bucket.add(surface);
|
|
1982
|
+
}
|
|
1983
|
+
function untrack(surface) {
|
|
1984
|
+
const ch = surface[0];
|
|
1985
|
+
const bucket = buckets.get(ch);
|
|
1986
|
+
if (bucket) {
|
|
1987
|
+
bucket.delete(surface);
|
|
1988
|
+
if (bucket.size === 0) buckets.delete(ch);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
function adjustTotals(token, delta) {
|
|
1992
|
+
if (!totalsByToken || delta === 0) return;
|
|
1993
|
+
const next = (totalsByToken.get(token) ?? 0) + delta;
|
|
1994
|
+
if (next <= 0) totalsByToken.delete(token);
|
|
1995
|
+
else totalsByToken.set(token, next);
|
|
1996
|
+
}
|
|
1997
|
+
return {
|
|
1998
|
+
add(surface, token, occurrences) {
|
|
1999
|
+
if (surface.length === 0 || token.length === 0 || occurrences <= 0) return;
|
|
2000
|
+
if (surface === token) return;
|
|
2001
|
+
const existing = entries.get(surface);
|
|
2002
|
+
if (existing) {
|
|
2003
|
+
existing.count += occurrences;
|
|
2004
|
+
adjustTotals(existing.token, occurrences);
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
entries.set(surface, { token, count: occurrences });
|
|
2008
|
+
track(surface);
|
|
2009
|
+
adjustTotals(token, occurrences);
|
|
2010
|
+
},
|
|
2011
|
+
subtract(surface, occurrences) {
|
|
2012
|
+
if (occurrences <= 0) return;
|
|
2013
|
+
const existing = entries.get(surface);
|
|
2014
|
+
if (!existing) return;
|
|
2015
|
+
const removed = Math.min(occurrences, existing.count);
|
|
2016
|
+
existing.count -= occurrences;
|
|
2017
|
+
adjustTotals(existing.token, -removed);
|
|
2018
|
+
if (existing.count <= 0) {
|
|
2019
|
+
entries.delete(surface);
|
|
2020
|
+
untrack(surface);
|
|
2021
|
+
}
|
|
2022
|
+
},
|
|
2023
|
+
candidatesForPrefix(prefix) {
|
|
2024
|
+
if (prefix.length === 0) return [];
|
|
2025
|
+
const bucket = buckets.get(prefix[0]);
|
|
2026
|
+
if (!bucket) return [];
|
|
2027
|
+
const results = [];
|
|
2028
|
+
for (const surface of bucket) {
|
|
2029
|
+
if (surface.length < prefix.length || !surface.startsWith(prefix)) continue;
|
|
2030
|
+
const entry = entries.get(surface);
|
|
2031
|
+
if (!entry) continue;
|
|
2032
|
+
results.push({ surface, token: entry.token, occurrences: entry.count });
|
|
2033
|
+
}
|
|
2034
|
+
return results;
|
|
2035
|
+
},
|
|
2036
|
+
stemChangedTotalFor(token) {
|
|
2037
|
+
if (!totalsByToken) {
|
|
2038
|
+
totalsByToken = /* @__PURE__ */ new Map();
|
|
2039
|
+
for (const entry of entries.values()) {
|
|
2040
|
+
totalsByToken.set(entry.token, (totalsByToken.get(entry.token) ?? 0) + entry.count);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
return totalsByToken.get(token) ?? 0;
|
|
2044
|
+
},
|
|
2045
|
+
size() {
|
|
2046
|
+
return entries.size;
|
|
2047
|
+
},
|
|
2048
|
+
clear() {
|
|
2049
|
+
entries.clear();
|
|
2050
|
+
buckets.clear();
|
|
2051
|
+
totalsByToken = null;
|
|
2052
|
+
},
|
|
2053
|
+
serialize() {
|
|
2054
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
2055
|
+
for (const [surface, entry] of entries) {
|
|
2056
|
+
result[surface] = [entry.count, entry.token];
|
|
2057
|
+
}
|
|
2058
|
+
return result;
|
|
2059
|
+
},
|
|
2060
|
+
deserialize(data) {
|
|
2061
|
+
entries.clear();
|
|
2062
|
+
buckets.clear();
|
|
2063
|
+
totalsByToken = null;
|
|
2064
|
+
for (const surface of Object.keys(data)) {
|
|
2065
|
+
if (surface.length === 0) continue;
|
|
2066
|
+
const value = data[surface];
|
|
2067
|
+
if (!Array.isArray(value) || typeof value[0] !== "number" || typeof value[1] !== "string") continue;
|
|
2068
|
+
const count = value[0];
|
|
2069
|
+
const token = value[1];
|
|
2070
|
+
if (!Number.isFinite(count) || count <= 0 || token.length === 0 || token === surface) continue;
|
|
2071
|
+
entries.set(surface, { token, count: Math.floor(count) });
|
|
2072
|
+
track(surface);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
|
|
1765
2078
|
// src/core/partition/utils.ts
|
|
1766
2079
|
function getOrCreateFieldNameIndex(table, fieldName) {
|
|
1767
2080
|
const existing = table.indexMap.get(fieldName);
|
|
@@ -1806,6 +2119,7 @@ function tokenizeOptions(options) {
|
|
|
1806
2119
|
return {
|
|
1807
2120
|
stem: true,
|
|
1808
2121
|
removeStopWords: true,
|
|
2122
|
+
collectSurfaces: options?.collectSurfaces === true,
|
|
1809
2123
|
stopWordOverride: options?.stopWordOverride,
|
|
1810
2124
|
customTokenizer: options?.customTokenizer
|
|
1811
2125
|
};
|
|
@@ -1891,32 +2205,177 @@ function computeFacets(state, docIds, config, schema) {
|
|
|
1891
2205
|
return result;
|
|
1892
2206
|
}
|
|
1893
2207
|
|
|
1894
|
-
// src/
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
const
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
const
|
|
1906
|
-
|
|
1907
|
-
return (bs[word] & 1 << (index & 31)) !== 0;
|
|
2208
|
+
// src/geo/haversine.ts
|
|
2209
|
+
var EARTH_RADIUS_METERS = 63710088e-1;
|
|
2210
|
+
var DEG_TO_RAD = Math.PI / 180;
|
|
2211
|
+
function haversineDistance(lat1, lon1, lat2, lon2) {
|
|
2212
|
+
const phi1 = lat1 * DEG_TO_RAD;
|
|
2213
|
+
const phi2 = lat2 * DEG_TO_RAD;
|
|
2214
|
+
const deltaPhi = (lat2 - lat1) * DEG_TO_RAD;
|
|
2215
|
+
const deltaLambda = (lon2 - lon1) * DEG_TO_RAD;
|
|
2216
|
+
const halfDeltaPhi = Math.sin(deltaPhi / 2);
|
|
2217
|
+
const halfDeltaLambda = Math.sin(deltaLambda / 2);
|
|
2218
|
+
const a = halfDeltaPhi * halfDeltaPhi + Math.cos(phi1) * Math.cos(phi2) * halfDeltaLambda * halfDeltaLambda;
|
|
2219
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
2220
|
+
return EARTH_RADIUS_METERS * c;
|
|
1908
2221
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
2222
|
+
|
|
2223
|
+
// src/geo/polygon.ts
|
|
2224
|
+
function isPointInPolygon(lat, lon, polygon) {
|
|
2225
|
+
if (polygon.length < 3) return false;
|
|
2226
|
+
let inside = false;
|
|
2227
|
+
const n = polygon.length;
|
|
2228
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
2229
|
+
const lonI = polygon[i].lon;
|
|
2230
|
+
const latI = polygon[i].lat;
|
|
2231
|
+
const lonJ = polygon[j].lon;
|
|
2232
|
+
const latJ = polygon[j].lat;
|
|
2233
|
+
if (lonI > lon !== lonJ > lon) {
|
|
2234
|
+
const slope = (latJ - latI) / (lonJ - lonI);
|
|
2235
|
+
const intersectLat = latI + slope * (lon - lonI);
|
|
2236
|
+
if (lat < intersectLat) {
|
|
2237
|
+
inside = !inside;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
1914
2240
|
}
|
|
1915
|
-
return
|
|
2241
|
+
return inside;
|
|
1916
2242
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2243
|
+
|
|
2244
|
+
// src/geo/vincenty.ts
|
|
2245
|
+
var WGS84_A = 6378137;
|
|
2246
|
+
var WGS84_F = 1 / 298.257223563;
|
|
2247
|
+
var WGS84_B = WGS84_A * (1 - WGS84_F);
|
|
2248
|
+
var DEG_TO_RAD2 = Math.PI / 180;
|
|
2249
|
+
var MAX_ITERATIONS = 200;
|
|
2250
|
+
var CONVERGENCE_THRESHOLD = 1e-12;
|
|
2251
|
+
function vincentyDistance(lat1, lon1, lat2, lon2) {
|
|
2252
|
+
const phi1 = lat1 * DEG_TO_RAD2;
|
|
2253
|
+
const phi2 = lat2 * DEG_TO_RAD2;
|
|
2254
|
+
const L = (lon2 - lon1) * DEG_TO_RAD2;
|
|
2255
|
+
const U1 = Math.atan((1 - WGS84_F) * Math.tan(phi1));
|
|
2256
|
+
const U2 = Math.atan((1 - WGS84_F) * Math.tan(phi2));
|
|
2257
|
+
const sinU1 = Math.sin(U1);
|
|
2258
|
+
const cosU1 = Math.cos(U1);
|
|
2259
|
+
const sinU2 = Math.sin(U2);
|
|
2260
|
+
const cosU2 = Math.cos(U2);
|
|
2261
|
+
let lambda = L;
|
|
2262
|
+
let prevLambda;
|
|
2263
|
+
let sinSigma = 0;
|
|
2264
|
+
let cosSigma = 0;
|
|
2265
|
+
let sigma = 0;
|
|
2266
|
+
let sinAlpha = 0;
|
|
2267
|
+
let cos2Alpha = 0;
|
|
2268
|
+
let cos2SigmaM = 0;
|
|
2269
|
+
let iterations = MAX_ITERATIONS;
|
|
2270
|
+
do {
|
|
2271
|
+
const sinLambda = Math.sin(lambda);
|
|
2272
|
+
const cosLambda = Math.cos(lambda);
|
|
2273
|
+
const term1 = cosU2 * sinLambda;
|
|
2274
|
+
const term2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
|
|
2275
|
+
sinSigma = Math.sqrt(term1 * term1 + term2 * term2);
|
|
2276
|
+
if (sinSigma === 0) return 0;
|
|
2277
|
+
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
|
|
2278
|
+
sigma = Math.atan2(sinSigma, cosSigma);
|
|
2279
|
+
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
|
|
2280
|
+
cos2Alpha = 1 - sinAlpha * sinAlpha;
|
|
2281
|
+
cos2SigmaM = cos2Alpha === 0 ? 0 : cosSigma - 2 * sinU1 * sinU2 / cos2Alpha;
|
|
2282
|
+
const C = WGS84_F / 16 * cos2Alpha * (4 + WGS84_F * (4 - 3 * cos2Alpha));
|
|
2283
|
+
prevLambda = lambda;
|
|
2284
|
+
lambda = L + (1 - C) * WGS84_F * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
|
|
2285
|
+
} while (Math.abs(lambda - prevLambda) > CONVERGENCE_THRESHOLD && --iterations > 0);
|
|
2286
|
+
if (iterations === 0) {
|
|
2287
|
+
return haversineDistance(lat1, lon1, lat2, lon2);
|
|
2288
|
+
}
|
|
2289
|
+
const uSq = cos2Alpha * (WGS84_A * WGS84_A - WGS84_B * WGS84_B) / (WGS84_B * WGS84_B);
|
|
2290
|
+
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
|
|
2291
|
+
const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
|
|
2292
|
+
const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
|
|
2293
|
+
return WGS84_B * A * (sigma - deltaSigma);
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
// src/geo/geo-index.ts
|
|
2297
|
+
function createGeoIndex() {
|
|
2298
|
+
const entries = [];
|
|
2299
|
+
return {
|
|
2300
|
+
get entries() {
|
|
2301
|
+
return entries;
|
|
2302
|
+
},
|
|
2303
|
+
insert(internalId, lat, lon) {
|
|
2304
|
+
entries.push({ lat, lon, docId: internalId });
|
|
2305
|
+
},
|
|
2306
|
+
remove(internalId) {
|
|
2307
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
2308
|
+
if (entries[i].docId === internalId) {
|
|
2309
|
+
entries.splice(i, 1);
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
},
|
|
2314
|
+
radiusQuery(lat, lon, distanceMeters, inside, highPrecision) {
|
|
2315
|
+
const result = /* @__PURE__ */ new Set();
|
|
2316
|
+
const distanceFn = highPrecision ? vincentyDistance : haversineDistance;
|
|
2317
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2318
|
+
const entry = entries[i];
|
|
2319
|
+
const dist = distanceFn(lat, lon, entry.lat, entry.lon);
|
|
2320
|
+
const withinRadius = dist <= distanceMeters;
|
|
2321
|
+
if (inside ? withinRadius : !withinRadius) {
|
|
2322
|
+
result.add(entry.docId);
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
return result;
|
|
2326
|
+
},
|
|
2327
|
+
polygonQuery(points, inside) {
|
|
2328
|
+
const result = /* @__PURE__ */ new Set();
|
|
2329
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2330
|
+
const entry = entries[i];
|
|
2331
|
+
const withinPolygon = isPointInPolygon(entry.lat, entry.lon, points);
|
|
2332
|
+
if (inside ? withinPolygon : !withinPolygon) {
|
|
2333
|
+
result.add(entry.docId);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
return result;
|
|
2337
|
+
},
|
|
2338
|
+
clear() {
|
|
2339
|
+
entries.length = 0;
|
|
2340
|
+
},
|
|
2341
|
+
serialize() {
|
|
2342
|
+
return entries.map((e) => ({ ...e }));
|
|
2343
|
+
},
|
|
2344
|
+
deserialize(data) {
|
|
2345
|
+
entries.length = 0;
|
|
2346
|
+
for (const entry of data) {
|
|
2347
|
+
entries.push({ lat: entry.lat, lon: entry.lon, docId: entry.docId });
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
// src/core/bitset.ts
|
|
2354
|
+
function createBitSet(capacity) {
|
|
2355
|
+
return new Uint32Array(capacity + 31 >>> 5 || 1);
|
|
2356
|
+
}
|
|
2357
|
+
function bitsetSet(bs, index) {
|
|
2358
|
+
const word = index >>> 5;
|
|
2359
|
+
if (word < bs.length) {
|
|
2360
|
+
bs[word] |= 1 << (index & 31);
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
function bitsetHas(bs, index) {
|
|
2364
|
+
const word = index >>> 5;
|
|
2365
|
+
if (word >= bs.length) return false;
|
|
2366
|
+
return (bs[word] & 1 << (index & 31)) !== 0;
|
|
2367
|
+
}
|
|
2368
|
+
function bitsetAnd(a, b) {
|
|
2369
|
+
const len = Math.min(a.length, b.length);
|
|
2370
|
+
const result = new Uint32Array(len);
|
|
2371
|
+
for (let i = 0; i < len; i++) {
|
|
2372
|
+
result[i] = a[i] & b[i];
|
|
2373
|
+
}
|
|
2374
|
+
return result;
|
|
2375
|
+
}
|
|
2376
|
+
function bitsetOr(a, b) {
|
|
2377
|
+
const longer = a.length >= b.length ? a : b;
|
|
2378
|
+
const shorter = a.length >= b.length ? b : a;
|
|
1920
2379
|
const result = new Uint32Array(longer.length);
|
|
1921
2380
|
for (let i = 0; i < shorter.length; i++) {
|
|
1922
2381
|
result[i] = longer[i] | shorter[i];
|
|
@@ -1952,1197 +2411,1096 @@ function bitsetFromSet(set, capacity) {
|
|
|
1952
2411
|
return bs;
|
|
1953
2412
|
}
|
|
1954
2413
|
|
|
1955
|
-
// src/
|
|
1956
|
-
function
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
return distance;
|
|
1985
|
-
}
|
|
1986
|
-
function matchesNumericComparison(value, filter) {
|
|
1987
|
-
if (filter.eq !== void 0 && value !== filter.eq) return false;
|
|
1988
|
-
if (filter.ne !== void 0 && value === filter.ne) return false;
|
|
1989
|
-
if (filter.gt !== void 0 && !(value > filter.gt)) return false;
|
|
1990
|
-
if (filter.lt !== void 0 && !(value < filter.lt)) return false;
|
|
1991
|
-
if (filter.gte !== void 0 && !(value >= filter.gte)) return false;
|
|
1992
|
-
if (filter.lte !== void 0 && !(value <= filter.lte)) return false;
|
|
1993
|
-
if (filter.between !== void 0 && !(value >= filter.between[0] && value <= filter.between[1])) return false;
|
|
1994
|
-
return true;
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
// src/filters/operators/bitset.ts
|
|
1998
|
-
function resolveBitset(bs) {
|
|
1999
|
-
return typeof bs === "function" ? bs() : bs;
|
|
2000
|
-
}
|
|
2001
|
-
function scanToBitset(docIds, capacity, getValue, predicate) {
|
|
2002
|
-
const resolved = resolveBitset(docIds);
|
|
2003
|
-
const result = createBitSet(capacity);
|
|
2004
|
-
for (let wi = 0; wi < resolved.length; wi++) {
|
|
2005
|
-
let word = resolved[wi];
|
|
2006
|
-
if (word === 0) continue;
|
|
2007
|
-
const base = wi << 5;
|
|
2008
|
-
while (word !== 0) {
|
|
2009
|
-
const tz = Math.clz32(word & -word) ^ 31;
|
|
2010
|
-
const id = base + tz;
|
|
2011
|
-
if (predicate(getValue(id))) {
|
|
2012
|
-
bitsetSet(result, id);
|
|
2414
|
+
// src/core/field-index/boolean.ts
|
|
2415
|
+
function createBooleanIndex() {
|
|
2416
|
+
const trueDocs = /* @__PURE__ */ new Set();
|
|
2417
|
+
const falseDocs = /* @__PURE__ */ new Set();
|
|
2418
|
+
return {
|
|
2419
|
+
insert(internalId, value) {
|
|
2420
|
+
if (value) trueDocs.add(internalId);
|
|
2421
|
+
else falseDocs.add(internalId);
|
|
2422
|
+
},
|
|
2423
|
+
remove(internalId, value) {
|
|
2424
|
+
if (value) trueDocs.delete(internalId);
|
|
2425
|
+
else falseDocs.delete(internalId);
|
|
2426
|
+
},
|
|
2427
|
+
queryEq(value) {
|
|
2428
|
+
return new Set(value ? trueDocs : falseDocs);
|
|
2429
|
+
},
|
|
2430
|
+
queryNe(value) {
|
|
2431
|
+
return new Set(value ? falseDocs : trueDocs);
|
|
2432
|
+
},
|
|
2433
|
+
getAllDocIds() {
|
|
2434
|
+
const all = new Set(trueDocs);
|
|
2435
|
+
for (const id of falseDocs) all.add(id);
|
|
2436
|
+
return all;
|
|
2437
|
+
},
|
|
2438
|
+
queryEqBitset(value, capacity) {
|
|
2439
|
+
const source = value ? trueDocs : falseDocs;
|
|
2440
|
+
const bs = createBitSet(capacity);
|
|
2441
|
+
for (const id of source) {
|
|
2442
|
+
bitsetSet(bs, id);
|
|
2013
2443
|
}
|
|
2014
|
-
|
|
2444
|
+
return bs;
|
|
2445
|
+
},
|
|
2446
|
+
getAllDocIdsBitset(capacity) {
|
|
2447
|
+
const bs = createBitSet(capacity);
|
|
2448
|
+
for (const id of trueDocs) bitsetSet(bs, id);
|
|
2449
|
+
for (const id of falseDocs) bitsetSet(bs, id);
|
|
2450
|
+
return bs;
|
|
2451
|
+
},
|
|
2452
|
+
count() {
|
|
2453
|
+
return trueDocs.size + falseDocs.size;
|
|
2454
|
+
},
|
|
2455
|
+
clear() {
|
|
2456
|
+
trueDocs.clear();
|
|
2457
|
+
falseDocs.clear();
|
|
2458
|
+
},
|
|
2459
|
+
serialize() {
|
|
2460
|
+
return {
|
|
2461
|
+
trueDocs: Array.from(trueDocs),
|
|
2462
|
+
falseDocs: Array.from(falseDocs)
|
|
2463
|
+
};
|
|
2464
|
+
},
|
|
2465
|
+
deserialize(data) {
|
|
2466
|
+
trueDocs.clear();
|
|
2467
|
+
falseDocs.clear();
|
|
2468
|
+
for (const id of data.trueDocs) trueDocs.add(id);
|
|
2469
|
+
for (const id of data.falseDocs) falseDocs.add(id);
|
|
2015
2470
|
}
|
|
2016
|
-
}
|
|
2017
|
-
return result;
|
|
2471
|
+
};
|
|
2018
2472
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => v === value);
|
|
2030
|
-
}
|
|
2031
|
-
function applyNeBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2032
|
-
if (fieldIndex?.type === "numeric" && typeof value === "number") {
|
|
2033
|
-
const all = fieldIndex.index.allDocIdsBitset(capacity);
|
|
2034
|
-
const eq = fieldIndex.index.eqBitset(value, capacity);
|
|
2035
|
-
return bitsetAnd(all, bitsetNot(eq, capacity));
|
|
2036
|
-
}
|
|
2037
|
-
if (fieldIndex?.type === "boolean" && typeof value === "boolean") {
|
|
2038
|
-
return value ? fieldIndex.index.getFalseBitset(capacity) : fieldIndex.index.getTrueBitset(capacity);
|
|
2039
|
-
}
|
|
2040
|
-
if (fieldIndex?.type === "enum" && typeof value === "string") {
|
|
2041
|
-
const all = fieldIndex.index.allDocIdsBitset(capacity);
|
|
2042
|
-
const eq = fieldIndex.index.getDocIdsBitset(value, capacity);
|
|
2043
|
-
return bitsetAnd(all, bitsetNot(eq, capacity));
|
|
2044
|
-
}
|
|
2045
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => v !== void 0 && v !== null && v !== value);
|
|
2046
|
-
}
|
|
2047
|
-
function applyGtBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2048
|
-
if (fieldIndex?.type === "numeric") {
|
|
2049
|
-
return fieldIndex.index.gtBitset(value, capacity);
|
|
2050
|
-
}
|
|
2051
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v > value);
|
|
2052
|
-
}
|
|
2053
|
-
function applyLtBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2054
|
-
if (fieldIndex?.type === "numeric") {
|
|
2055
|
-
return fieldIndex.index.ltBitset(value, capacity);
|
|
2056
|
-
}
|
|
2057
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v < value);
|
|
2058
|
-
}
|
|
2059
|
-
function applyGteBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2060
|
-
if (fieldIndex?.type === "numeric") {
|
|
2061
|
-
return fieldIndex.index.gteBitset(value, capacity);
|
|
2473
|
+
|
|
2474
|
+
// src/core/field-index/enum.ts
|
|
2475
|
+
function createEnumIndex() {
|
|
2476
|
+
const valueMap = /* @__PURE__ */ new Map();
|
|
2477
|
+
function allDocs() {
|
|
2478
|
+
const result = /* @__PURE__ */ new Set();
|
|
2479
|
+
for (const docSet of valueMap.values()) {
|
|
2480
|
+
for (const id of docSet) result.add(id);
|
|
2481
|
+
}
|
|
2482
|
+
return result;
|
|
2062
2483
|
}
|
|
2063
|
-
return
|
|
2484
|
+
return {
|
|
2485
|
+
insert(internalId, value) {
|
|
2486
|
+
let docSet = valueMap.get(value);
|
|
2487
|
+
if (!docSet) {
|
|
2488
|
+
docSet = /* @__PURE__ */ new Set();
|
|
2489
|
+
valueMap.set(value, docSet);
|
|
2490
|
+
}
|
|
2491
|
+
docSet.add(internalId);
|
|
2492
|
+
},
|
|
2493
|
+
remove(internalId, value) {
|
|
2494
|
+
const docSet = valueMap.get(value);
|
|
2495
|
+
if (!docSet) return;
|
|
2496
|
+
docSet.delete(internalId);
|
|
2497
|
+
if (docSet.size === 0) valueMap.delete(value);
|
|
2498
|
+
},
|
|
2499
|
+
queryEq(value) {
|
|
2500
|
+
const docSet = valueMap.get(value);
|
|
2501
|
+
return docSet ? new Set(docSet) : /* @__PURE__ */ new Set();
|
|
2502
|
+
},
|
|
2503
|
+
queryNe(value) {
|
|
2504
|
+
const excluded = valueMap.get(value);
|
|
2505
|
+
const result = allDocs();
|
|
2506
|
+
if (excluded) {
|
|
2507
|
+
for (const id of excluded) result.delete(id);
|
|
2508
|
+
}
|
|
2509
|
+
return result;
|
|
2510
|
+
},
|
|
2511
|
+
queryIn(values) {
|
|
2512
|
+
const result = /* @__PURE__ */ new Set();
|
|
2513
|
+
for (const val of values) {
|
|
2514
|
+
const docSet = valueMap.get(val);
|
|
2515
|
+
if (docSet) {
|
|
2516
|
+
for (const id of docSet) result.add(id);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
return result;
|
|
2520
|
+
},
|
|
2521
|
+
queryNin(values) {
|
|
2522
|
+
const excluded = this.queryIn(values);
|
|
2523
|
+
const result = allDocs();
|
|
2524
|
+
for (const id of excluded) result.delete(id);
|
|
2525
|
+
return result;
|
|
2526
|
+
},
|
|
2527
|
+
getAllDocIds() {
|
|
2528
|
+
return allDocs();
|
|
2529
|
+
},
|
|
2530
|
+
queryEqBitset(value, capacity) {
|
|
2531
|
+
const bs = createBitSet(capacity);
|
|
2532
|
+
const docSet = valueMap.get(value);
|
|
2533
|
+
if (docSet) {
|
|
2534
|
+
for (const id of docSet) bitsetSet(bs, id);
|
|
2535
|
+
}
|
|
2536
|
+
return bs;
|
|
2537
|
+
},
|
|
2538
|
+
queryInBitset(values, capacity) {
|
|
2539
|
+
const bs = createBitSet(capacity);
|
|
2540
|
+
for (const val of values) {
|
|
2541
|
+
const docSet = valueMap.get(val);
|
|
2542
|
+
if (docSet) {
|
|
2543
|
+
for (const id of docSet) bitsetSet(bs, id);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
return bs;
|
|
2547
|
+
},
|
|
2548
|
+
getAllDocIdsBitset(capacity) {
|
|
2549
|
+
const bs = createBitSet(capacity);
|
|
2550
|
+
for (const docSet of valueMap.values()) {
|
|
2551
|
+
for (const id of docSet) bitsetSet(bs, id);
|
|
2552
|
+
}
|
|
2553
|
+
return bs;
|
|
2554
|
+
},
|
|
2555
|
+
count() {
|
|
2556
|
+
let total = 0;
|
|
2557
|
+
for (const docSet of valueMap.values()) total += docSet.size;
|
|
2558
|
+
return total;
|
|
2559
|
+
},
|
|
2560
|
+
clear() {
|
|
2561
|
+
valueMap.clear();
|
|
2562
|
+
},
|
|
2563
|
+
serialize() {
|
|
2564
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
2565
|
+
for (const [value, docSet] of valueMap) {
|
|
2566
|
+
result[value] = Array.from(docSet);
|
|
2567
|
+
}
|
|
2568
|
+
return result;
|
|
2569
|
+
},
|
|
2570
|
+
deserialize(data) {
|
|
2571
|
+
valueMap.clear();
|
|
2572
|
+
for (const value of Object.keys(data)) {
|
|
2573
|
+
valueMap.set(value, new Set(data[value]));
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
};
|
|
2064
2577
|
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2578
|
+
|
|
2579
|
+
// src/core/field-index/numeric.ts
|
|
2580
|
+
function lowerBound(entries, value) {
|
|
2581
|
+
let lo = 0;
|
|
2582
|
+
let hi = entries.length;
|
|
2583
|
+
while (lo < hi) {
|
|
2584
|
+
const mid = lo + hi >>> 1;
|
|
2585
|
+
if (entries[mid].value < value) lo = mid + 1;
|
|
2586
|
+
else hi = mid;
|
|
2068
2587
|
}
|
|
2069
|
-
return
|
|
2588
|
+
return lo;
|
|
2070
2589
|
}
|
|
2071
|
-
function
|
|
2072
|
-
|
|
2073
|
-
|
|
2590
|
+
function upperBound(entries, value) {
|
|
2591
|
+
let lo = 0;
|
|
2592
|
+
let hi = entries.length;
|
|
2593
|
+
while (lo < hi) {
|
|
2594
|
+
const mid = lo + hi >>> 1;
|
|
2595
|
+
if (entries[mid].value <= value) lo = mid + 1;
|
|
2596
|
+
else hi = mid;
|
|
2074
2597
|
}
|
|
2075
|
-
return
|
|
2598
|
+
return lo;
|
|
2076
2599
|
}
|
|
2077
|
-
function
|
|
2078
|
-
|
|
2079
|
-
|
|
2600
|
+
function collectDocIds(entries, from, to) {
|
|
2601
|
+
const result = /* @__PURE__ */ new Set();
|
|
2602
|
+
for (let i = from; i < to; i++) {
|
|
2603
|
+
result.add(entries[i].docId);
|
|
2080
2604
|
}
|
|
2081
|
-
|
|
2082
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "string" && valSet.has(v));
|
|
2605
|
+
return result;
|
|
2083
2606
|
}
|
|
2084
|
-
function
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
return bitsetAnd(all, bitsetNot(matched, capacity));
|
|
2607
|
+
function collectDocIdsBitset(entries, from, to, capacity) {
|
|
2608
|
+
const bs = createBitSet(capacity);
|
|
2609
|
+
for (let i = from; i < to; i++) {
|
|
2610
|
+
bitsetSet(bs, entries[i].docId);
|
|
2089
2611
|
}
|
|
2090
|
-
|
|
2091
|
-
return scanToBitset(
|
|
2092
|
-
allDocsBitset,
|
|
2093
|
-
capacity,
|
|
2094
|
-
getValue,
|
|
2095
|
-
(v) => v !== void 0 && v !== null && typeof v === "string" && !valSet.has(v)
|
|
2096
|
-
);
|
|
2097
|
-
}
|
|
2098
|
-
function applyStartsWithBitset(prefix, allDocsBitset, capacity, getValue) {
|
|
2099
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "string" && v.startsWith(prefix));
|
|
2100
|
-
}
|
|
2101
|
-
function applyEndsWithBitset(suffix, allDocsBitset, capacity, getValue) {
|
|
2102
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "string" && v.endsWith(suffix));
|
|
2103
|
-
}
|
|
2104
|
-
function applyContainsAllBitset(values, allDocsBitset, capacity, getValue) {
|
|
2105
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
|
|
2106
|
-
if (!Array.isArray(v)) return false;
|
|
2107
|
-
const arr = v;
|
|
2108
|
-
for (const val of values) {
|
|
2109
|
-
if (!arr.includes(val)) return false;
|
|
2110
|
-
}
|
|
2111
|
-
return true;
|
|
2112
|
-
});
|
|
2612
|
+
return bs;
|
|
2113
2613
|
}
|
|
2114
|
-
function
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2614
|
+
function createNumericIndex() {
|
|
2615
|
+
let entries = [];
|
|
2616
|
+
let sorted = true;
|
|
2617
|
+
function ensureSorted() {
|
|
2618
|
+
if (!sorted) {
|
|
2619
|
+
entries.sort((a, b) => a.value - b.value);
|
|
2620
|
+
sorted = true;
|
|
2120
2621
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
}
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2622
|
+
}
|
|
2623
|
+
return {
|
|
2624
|
+
insert(internalId, value) {
|
|
2625
|
+
entries.push({ value, docId: internalId });
|
|
2626
|
+
sorted = false;
|
|
2627
|
+
},
|
|
2628
|
+
remove(internalId, value) {
|
|
2629
|
+
ensureSorted();
|
|
2630
|
+
const start = lowerBound(entries, value);
|
|
2631
|
+
const end = upperBound(entries, value);
|
|
2632
|
+
for (let i = start; i < end; i++) {
|
|
2633
|
+
if (entries[i].docId === internalId) {
|
|
2634
|
+
entries.splice(i, 1);
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
},
|
|
2639
|
+
queryEq(value) {
|
|
2640
|
+
ensureSorted();
|
|
2641
|
+
return collectDocIds(entries, lowerBound(entries, value), upperBound(entries, value));
|
|
2642
|
+
},
|
|
2643
|
+
queryNe(value) {
|
|
2644
|
+
ensureSorted();
|
|
2645
|
+
const excluded = this.queryEq(value);
|
|
2646
|
+
const result = /* @__PURE__ */ new Set();
|
|
2647
|
+
for (const entry of entries) {
|
|
2648
|
+
if (!excluded.has(entry.docId)) result.add(entry.docId);
|
|
2649
|
+
}
|
|
2650
|
+
return result;
|
|
2651
|
+
},
|
|
2652
|
+
queryGt(value) {
|
|
2653
|
+
ensureSorted();
|
|
2654
|
+
return collectDocIds(entries, upperBound(entries, value), entries.length);
|
|
2655
|
+
},
|
|
2656
|
+
queryGte(value) {
|
|
2657
|
+
ensureSorted();
|
|
2658
|
+
return collectDocIds(entries, lowerBound(entries, value), entries.length);
|
|
2659
|
+
},
|
|
2660
|
+
queryLt(value) {
|
|
2661
|
+
ensureSorted();
|
|
2662
|
+
return collectDocIds(entries, 0, lowerBound(entries, value));
|
|
2663
|
+
},
|
|
2664
|
+
queryLte(value) {
|
|
2665
|
+
ensureSorted();
|
|
2666
|
+
return collectDocIds(entries, 0, upperBound(entries, value));
|
|
2667
|
+
},
|
|
2668
|
+
queryBetween(min, max) {
|
|
2669
|
+
ensureSorted();
|
|
2670
|
+
return collectDocIds(entries, lowerBound(entries, min), upperBound(entries, max));
|
|
2671
|
+
},
|
|
2672
|
+
getAllDocIds() {
|
|
2673
|
+
return collectDocIds(entries, 0, entries.length);
|
|
2674
|
+
},
|
|
2675
|
+
queryEqBitset(value, capacity) {
|
|
2676
|
+
ensureSorted();
|
|
2677
|
+
return collectDocIdsBitset(entries, lowerBound(entries, value), upperBound(entries, value), capacity);
|
|
2678
|
+
},
|
|
2679
|
+
queryGtBitset(value, capacity) {
|
|
2680
|
+
ensureSorted();
|
|
2681
|
+
return collectDocIdsBitset(entries, upperBound(entries, value), entries.length, capacity);
|
|
2682
|
+
},
|
|
2683
|
+
queryGteBitset(value, capacity) {
|
|
2684
|
+
ensureSorted();
|
|
2685
|
+
return collectDocIdsBitset(entries, lowerBound(entries, value), entries.length, capacity);
|
|
2686
|
+
},
|
|
2687
|
+
queryLtBitset(value, capacity) {
|
|
2688
|
+
ensureSorted();
|
|
2689
|
+
return collectDocIdsBitset(entries, 0, lowerBound(entries, value), capacity);
|
|
2690
|
+
},
|
|
2691
|
+
queryLteBitset(value, capacity) {
|
|
2692
|
+
ensureSorted();
|
|
2693
|
+
return collectDocIdsBitset(entries, 0, upperBound(entries, value), capacity);
|
|
2694
|
+
},
|
|
2695
|
+
queryBetweenBitset(min, max, capacity) {
|
|
2696
|
+
ensureSorted();
|
|
2697
|
+
return collectDocIdsBitset(entries, lowerBound(entries, min), upperBound(entries, max), capacity);
|
|
2698
|
+
},
|
|
2699
|
+
getAllDocIdsBitset(capacity) {
|
|
2700
|
+
return collectDocIdsBitset(entries, 0, entries.length, capacity);
|
|
2701
|
+
},
|
|
2702
|
+
count() {
|
|
2703
|
+
return entries.length;
|
|
2704
|
+
},
|
|
2705
|
+
clear() {
|
|
2706
|
+
entries = [];
|
|
2707
|
+
sorted = true;
|
|
2708
|
+
},
|
|
2709
|
+
serialize() {
|
|
2710
|
+
ensureSorted();
|
|
2711
|
+
return entries.slice();
|
|
2712
|
+
},
|
|
2713
|
+
deserialize(data) {
|
|
2714
|
+
entries = data.slice();
|
|
2715
|
+
sorted = true;
|
|
2716
|
+
}
|
|
2717
|
+
};
|
|
2168
2718
|
}
|
|
2169
2719
|
|
|
2170
|
-
// src/
|
|
2171
|
-
function
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
if (
|
|
2179
|
-
|
|
2180
|
-
bitsets.push(applyAndBitset(andBitsets));
|
|
2181
|
-
}
|
|
2182
|
-
if (expression.or?.length) {
|
|
2183
|
-
const orBitsets = expression.or.map((expr) => evaluateFilters(expr, context));
|
|
2184
|
-
bitsets.push(applyOrBitset(orBitsets));
|
|
2185
|
-
}
|
|
2186
|
-
if (expression.not) {
|
|
2187
|
-
const excluded = evaluateFilters(expression.not, context);
|
|
2188
|
-
const universe = context.allDocIdsBitset;
|
|
2189
|
-
bitsets.push(applyAndBitset([universe, applyNotBitset(excluded, context.capacity)]));
|
|
2720
|
+
// src/core/partition/indexing.ts
|
|
2721
|
+
function ensureFieldIndex(state, fieldPath, fieldType) {
|
|
2722
|
+
if ((fieldType === "number" || fieldType === "number[]") && !state.numericIndexes.has(fieldPath)) {
|
|
2723
|
+
state.numericIndexes.set(fieldPath, createNumericIndex());
|
|
2724
|
+
} else if ((fieldType === "boolean" || fieldType === "boolean[]") && !state.booleanIndexes.has(fieldPath)) {
|
|
2725
|
+
state.booleanIndexes.set(fieldPath, createBooleanIndex());
|
|
2726
|
+
} else if ((fieldType === "enum" || fieldType === "enum[]") && !state.enumIndexes.has(fieldPath)) {
|
|
2727
|
+
state.enumIndexes.set(fieldPath, createEnumIndex());
|
|
2728
|
+
} else if (fieldType === "geopoint" && !state.geoIndexes.has(fieldPath)) {
|
|
2729
|
+
state.geoIndexes.set(fieldPath, createGeoIndex());
|
|
2190
2730
|
}
|
|
2191
|
-
if (bitsets.length === 0) return context.allDocIdsBitset;
|
|
2192
|
-
if (bitsets.length === 1) return bitsets[0];
|
|
2193
|
-
return applyAndBitset(bitsets);
|
|
2194
2731
|
}
|
|
2195
|
-
function
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
const { capacity } = context;
|
|
2201
|
-
let allDocsBitset = null;
|
|
2202
|
-
const getAllDocsBitset = () => {
|
|
2203
|
-
if (allDocsBitset === null) {
|
|
2204
|
-
allDocsBitset = context.allDocIdsBitset;
|
|
2205
|
-
}
|
|
2206
|
-
return allDocsBitset;
|
|
2207
|
-
};
|
|
2208
|
-
if ("radius" in f && f.radius) {
|
|
2209
|
-
const geoIndex = resolveGeoIndex(fieldPath, fieldIndex);
|
|
2210
|
-
bitsets.push(applyGeoRadiusBitset(filter.radius, geoIndex, capacity));
|
|
2211
|
-
}
|
|
2212
|
-
if ("polygon" in f && f.polygon) {
|
|
2213
|
-
const geoIndex = resolveGeoIndex(fieldPath, fieldIndex);
|
|
2214
|
-
bitsets.push(applyGeoPolygonBitset(filter.polygon, geoIndex, capacity));
|
|
2215
|
-
}
|
|
2216
|
-
if ("eq" in f && f.eq !== void 0) {
|
|
2217
|
-
bitsets.push(applyEqBitset(f.eq, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2218
|
-
}
|
|
2219
|
-
if ("ne" in f && f.ne !== void 0) {
|
|
2220
|
-
bitsets.push(applyNeBitset(f.ne, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2221
|
-
}
|
|
2222
|
-
if ("gt" in f && f.gt !== void 0) {
|
|
2223
|
-
bitsets.push(applyGtBitset(f.gt, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2224
|
-
}
|
|
2225
|
-
if ("lt" in f && f.lt !== void 0) {
|
|
2226
|
-
bitsets.push(applyLtBitset(f.lt, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2227
|
-
}
|
|
2228
|
-
if ("gte" in f && f.gte !== void 0) {
|
|
2229
|
-
bitsets.push(applyGteBitset(f.gte, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2230
|
-
}
|
|
2231
|
-
if ("lte" in f && f.lte !== void 0) {
|
|
2232
|
-
bitsets.push(applyLteBitset(f.lte, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2233
|
-
}
|
|
2234
|
-
if ("between" in f && f.between !== void 0) {
|
|
2235
|
-
bitsets.push(applyBetweenBitset(f.between, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2236
|
-
}
|
|
2237
|
-
if ("in" in f && f.in !== void 0) {
|
|
2238
|
-
bitsets.push(applyInBitset(f.in, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2239
|
-
}
|
|
2240
|
-
if ("nin" in f && f.nin !== void 0) {
|
|
2241
|
-
bitsets.push(applyNinBitset(f.nin, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2242
|
-
}
|
|
2243
|
-
if ("startsWith" in f && f.startsWith !== void 0) {
|
|
2244
|
-
bitsets.push(applyStartsWithBitset(f.startsWith, getAllDocsBitset, capacity, getValue));
|
|
2245
|
-
}
|
|
2246
|
-
if ("endsWith" in f && f.endsWith !== void 0) {
|
|
2247
|
-
bitsets.push(applyEndsWithBitset(f.endsWith, getAllDocsBitset, capacity, getValue));
|
|
2248
|
-
}
|
|
2249
|
-
if ("containsAll" in f && f.containsAll !== void 0) {
|
|
2250
|
-
bitsets.push(
|
|
2251
|
-
applyContainsAllBitset(f.containsAll, getAllDocsBitset, capacity, getValue)
|
|
2252
|
-
);
|
|
2253
|
-
}
|
|
2254
|
-
if ("matchesAny" in f && f.matchesAny !== void 0) {
|
|
2255
|
-
bitsets.push(
|
|
2256
|
-
applyMatchesAnyBitset(f.matchesAny, getAllDocsBitset, capacity, getValue)
|
|
2257
|
-
);
|
|
2258
|
-
}
|
|
2259
|
-
if ("size" in f && f.size !== void 0) {
|
|
2260
|
-
bitsets.push(applySizeBitset(f.size, getAllDocsBitset, capacity, getValue));
|
|
2261
|
-
}
|
|
2262
|
-
if ("exists" in f && f.exists !== void 0) {
|
|
2263
|
-
bitsets.push(
|
|
2264
|
-
f.exists ? applyExistsBitset(getAllDocsBitset, capacity, getValue) : applyNotExistsBitset(getAllDocsBitset, capacity, getValue)
|
|
2265
|
-
);
|
|
2732
|
+
function getOrCreateNumericIndex(state, fieldPath) {
|
|
2733
|
+
let idx = state.numericIndexes.get(fieldPath);
|
|
2734
|
+
if (!idx) {
|
|
2735
|
+
idx = createNumericIndex();
|
|
2736
|
+
state.numericIndexes.set(fieldPath, idx);
|
|
2266
2737
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2738
|
+
return idx;
|
|
2739
|
+
}
|
|
2740
|
+
function getOrCreateBooleanIndex(state, fieldPath) {
|
|
2741
|
+
let idx = state.booleanIndexes.get(fieldPath);
|
|
2742
|
+
if (!idx) {
|
|
2743
|
+
idx = createBooleanIndex();
|
|
2744
|
+
state.booleanIndexes.set(fieldPath, idx);
|
|
2271
2745
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2746
|
+
return idx;
|
|
2747
|
+
}
|
|
2748
|
+
function resolveInternalId(state, docId) {
|
|
2749
|
+
const internalId = state.docStore.getInternalId(docId);
|
|
2750
|
+
if (internalId === void 0) {
|
|
2751
|
+
throw new Error(`Internal ID not found for document "${docId}"`);
|
|
2276
2752
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2753
|
+
return internalId;
|
|
2754
|
+
}
|
|
2755
|
+
function countSurface(counts, surface, token) {
|
|
2756
|
+
if (surface === void 0 || surface.length === 0) return;
|
|
2757
|
+
const existing = counts.get(surface);
|
|
2758
|
+
if (existing) {
|
|
2759
|
+
existing.count++;
|
|
2760
|
+
} else {
|
|
2761
|
+
counts.set(surface, { token, count: 1 });
|
|
2281
2762
|
}
|
|
2282
|
-
if (bitsets.length === 0) return context.allDocIdsBitset;
|
|
2283
|
-
if (bitsets.length === 1) return bitsets[0];
|
|
2284
|
-
return applyAndBitset(bitsets);
|
|
2285
2763
|
}
|
|
2286
|
-
function
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2764
|
+
function applySurfaceCounts(state, counts, direction) {
|
|
2765
|
+
for (const [surface, entry] of counts) {
|
|
2766
|
+
if (direction === 1) {
|
|
2767
|
+
state.surfaceRegistry.add(surface, entry.token, entry.count);
|
|
2768
|
+
} else {
|
|
2769
|
+
state.surfaceRegistry.subtract(surface, entry.count);
|
|
2770
|
+
}
|
|
2293
2771
|
}
|
|
2294
|
-
return fieldIndex.index;
|
|
2295
2772
|
}
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
const
|
|
2300
|
-
const
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
const
|
|
2305
|
-
if (
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
gt: (v) => numIdx.queryGt(v),
|
|
2311
|
-
gte: (v) => numIdx.queryGte(v),
|
|
2312
|
-
lt: (v) => numIdx.queryLt(v),
|
|
2313
|
-
lte: (v) => numIdx.queryLte(v),
|
|
2314
|
-
between: (min, max) => numIdx.queryBetween(min, max),
|
|
2315
|
-
allDocIds: () => numIdx.getAllDocIds(),
|
|
2316
|
-
eqBitset: (v, cap) => numIdx.queryEqBitset(v, cap),
|
|
2317
|
-
gtBitset: (v, cap) => numIdx.queryGtBitset(v, cap),
|
|
2318
|
-
gteBitset: (v, cap) => numIdx.queryGteBitset(v, cap),
|
|
2319
|
-
ltBitset: (v, cap) => numIdx.queryLtBitset(v, cap),
|
|
2320
|
-
lteBitset: (v, cap) => numIdx.queryLteBitset(v, cap),
|
|
2321
|
-
betweenBitset: (min, max, cap) => numIdx.queryBetweenBitset(min, max, cap),
|
|
2322
|
-
allDocIdsBitset: (cap) => numIdx.getAllDocIdsBitset(cap)
|
|
2323
|
-
}
|
|
2324
|
-
};
|
|
2325
|
-
}
|
|
2326
|
-
} else if (fieldType === "boolean" || fieldType === "boolean[]") {
|
|
2327
|
-
const boolIdx = state.booleanIndexes.get(fieldPath);
|
|
2328
|
-
if (boolIdx) {
|
|
2329
|
-
fieldIndexes[fieldPath] = {
|
|
2330
|
-
type: "boolean",
|
|
2331
|
-
index: {
|
|
2332
|
-
getTrue: () => boolIdx.queryEq(true),
|
|
2333
|
-
getFalse: () => boolIdx.queryEq(false),
|
|
2334
|
-
allDocIds: () => boolIdx.getAllDocIds(),
|
|
2335
|
-
getTrueBitset: (cap) => boolIdx.queryEqBitset(true, cap),
|
|
2336
|
-
getFalseBitset: (cap) => boolIdx.queryEqBitset(false, cap),
|
|
2337
|
-
allDocIdsBitset: (cap) => boolIdx.getAllDocIdsBitset(cap)
|
|
2338
|
-
}
|
|
2339
|
-
};
|
|
2340
|
-
}
|
|
2341
|
-
} else if (fieldType === "enum" || fieldType === "enum[]") {
|
|
2342
|
-
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
2343
|
-
if (enumIdx) {
|
|
2344
|
-
fieldIndexes[fieldPath] = {
|
|
2345
|
-
type: "enum",
|
|
2346
|
-
index: {
|
|
2347
|
-
getDocIds: (v) => enumIdx.queryEq(v),
|
|
2348
|
-
allDocIds: () => enumIdx.getAllDocIds(),
|
|
2349
|
-
getDocIdsBitset: (v, cap) => enumIdx.queryEqBitset(v, cap),
|
|
2350
|
-
getDocIdsInBitset: (values, cap) => enumIdx.queryInBitset(values, cap),
|
|
2351
|
-
allDocIdsBitset: (cap) => enumIdx.getAllDocIdsBitset(cap)
|
|
2352
|
-
}
|
|
2353
|
-
};
|
|
2354
|
-
}
|
|
2355
|
-
} else if (fieldType === "geopoint") {
|
|
2356
|
-
const geoIdx = state.geoIndexes.get(fieldPath);
|
|
2357
|
-
if (geoIdx) {
|
|
2358
|
-
fieldIndexes[fieldPath] = {
|
|
2359
|
-
type: "geopoint",
|
|
2360
|
-
index: geoIdx
|
|
2361
|
-
};
|
|
2773
|
+
function indexStringField(state, internalId, fieldPath, text, language, options, fieldLengths, tokensByField) {
|
|
2774
|
+
const fieldTokenList = [];
|
|
2775
|
+
const fieldNameIndex = getOrCreateFieldNameIndex(state.fieldNameTable, fieldPath);
|
|
2776
|
+
const opts = tokenizeOptions(options);
|
|
2777
|
+
const surfaceCounts = /* @__PURE__ */ new Map();
|
|
2778
|
+
if (state.trackPositions) {
|
|
2779
|
+
const tokenFreqs = /* @__PURE__ */ new Map();
|
|
2780
|
+
for (const t of tokenizeIterator(text, language, opts)) {
|
|
2781
|
+
const existing = tokenFreqs.get(t.token);
|
|
2782
|
+
if (existing) {
|
|
2783
|
+
existing.count++;
|
|
2784
|
+
existing.positions.push(t.position);
|
|
2785
|
+
} else {
|
|
2786
|
+
tokenFreqs.set(t.token, { count: 1, positions: [t.position] });
|
|
2362
2787
|
}
|
|
2788
|
+
fieldTokenList.push(t.token);
|
|
2789
|
+
countSurface(surfaceCounts, t.surface, t.token);
|
|
2790
|
+
}
|
|
2791
|
+
for (const [token, freq] of tokenFreqs) {
|
|
2792
|
+
state.invertedIdx.insert(token, internalId, freq.count, fieldNameIndex, freq.positions);
|
|
2793
|
+
}
|
|
2794
|
+
} else {
|
|
2795
|
+
const tokenCounts = /* @__PURE__ */ new Map();
|
|
2796
|
+
for (const t of tokenizeIterator(text, language, opts)) {
|
|
2797
|
+
tokenCounts.set(t.token, (tokenCounts.get(t.token) ?? 0) + 1);
|
|
2798
|
+
fieldTokenList.push(t.token);
|
|
2799
|
+
countSurface(surfaceCounts, t.surface, t.token);
|
|
2800
|
+
}
|
|
2801
|
+
for (const [token, count] of tokenCounts) {
|
|
2802
|
+
state.invertedIdx.insert(token, internalId, count, fieldNameIndex, null);
|
|
2363
2803
|
}
|
|
2364
2804
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2805
|
+
applySurfaceCounts(state, surfaceCounts, 1);
|
|
2806
|
+
fieldLengths[fieldPath] = fieldTokenList.length;
|
|
2807
|
+
tokensByField[fieldPath] = fieldTokenList;
|
|
2808
|
+
}
|
|
2809
|
+
function indexStringArrayField(state, internalId, fieldPath, arr, language, options, fieldLengths, tokensByField) {
|
|
2810
|
+
const fieldTokenList = [];
|
|
2811
|
+
const fieldNameIndex = getOrCreateFieldNameIndex(state.fieldNameTable, fieldPath);
|
|
2812
|
+
const opts = tokenizeOptions(options);
|
|
2813
|
+
const surfaceCounts = /* @__PURE__ */ new Map();
|
|
2814
|
+
if (state.trackPositions) {
|
|
2815
|
+
const tokenFreqs = /* @__PURE__ */ new Map();
|
|
2816
|
+
let positionOffset = 0;
|
|
2817
|
+
for (const item of arr) {
|
|
2818
|
+
let itemTokenCount = 0;
|
|
2819
|
+
for (const t of tokenizeIterator(item, language, opts)) {
|
|
2820
|
+
const existing = tokenFreqs.get(t.token);
|
|
2821
|
+
if (existing) {
|
|
2822
|
+
existing.count++;
|
|
2823
|
+
existing.positions.push(positionOffset + t.position);
|
|
2824
|
+
} else {
|
|
2825
|
+
tokenFreqs.set(t.token, { count: 1, positions: [positionOffset + t.position] });
|
|
2826
|
+
}
|
|
2827
|
+
fieldTokenList.push(t.token);
|
|
2828
|
+
countSurface(surfaceCounts, t.surface, t.token);
|
|
2829
|
+
itemTokenCount++;
|
|
2380
2830
|
}
|
|
2381
|
-
|
|
2831
|
+
positionOffset += itemTokenCount;
|
|
2382
2832
|
}
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
while (word !== 0) {
|
|
2395
|
-
const tz = Math.clz32(word & -word) ^ 31;
|
|
2396
|
-
const internalId = base + tz;
|
|
2397
|
-
const externalId = resolver.toExternal(internalId);
|
|
2398
|
-
if (externalId !== void 0) {
|
|
2399
|
-
externalResult.add(externalId);
|
|
2833
|
+
fieldLengths[fieldPath] = fieldTokenList.length;
|
|
2834
|
+
for (const [token, freq] of tokenFreqs) {
|
|
2835
|
+
state.invertedIdx.insert(token, internalId, freq.count, fieldNameIndex, freq.positions);
|
|
2836
|
+
}
|
|
2837
|
+
} else {
|
|
2838
|
+
const tokenCounts = /* @__PURE__ */ new Map();
|
|
2839
|
+
for (const item of arr) {
|
|
2840
|
+
for (const t of tokenizeIterator(item, language, opts)) {
|
|
2841
|
+
tokenCounts.set(t.token, (tokenCounts.get(t.token) ?? 0) + 1);
|
|
2842
|
+
fieldTokenList.push(t.token);
|
|
2843
|
+
countSurface(surfaceCounts, t.surface, t.token);
|
|
2400
2844
|
}
|
|
2401
|
-
|
|
2845
|
+
}
|
|
2846
|
+
fieldLengths[fieldPath] = fieldTokenList.length;
|
|
2847
|
+
for (const [token, count] of tokenCounts) {
|
|
2848
|
+
state.invertedIdx.insert(token, internalId, count, fieldNameIndex, null);
|
|
2402
2849
|
}
|
|
2403
2850
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
function applyPartitionFiltersBitset(state, filters, schema) {
|
|
2407
|
-
const context = buildFilterContext(state, schema);
|
|
2408
|
-
return evaluateFilters(filters, context);
|
|
2409
|
-
}
|
|
2410
|
-
|
|
2411
|
-
// src/geo/haversine.ts
|
|
2412
|
-
var EARTH_RADIUS_METERS = 63710088e-1;
|
|
2413
|
-
var DEG_TO_RAD = Math.PI / 180;
|
|
2414
|
-
function haversineDistance(lat1, lon1, lat2, lon2) {
|
|
2415
|
-
const phi1 = lat1 * DEG_TO_RAD;
|
|
2416
|
-
const phi2 = lat2 * DEG_TO_RAD;
|
|
2417
|
-
const deltaPhi = (lat2 - lat1) * DEG_TO_RAD;
|
|
2418
|
-
const deltaLambda = (lon2 - lon1) * DEG_TO_RAD;
|
|
2419
|
-
const halfDeltaPhi = Math.sin(deltaPhi / 2);
|
|
2420
|
-
const halfDeltaLambda = Math.sin(deltaLambda / 2);
|
|
2421
|
-
const a = halfDeltaPhi * halfDeltaPhi + Math.cos(phi1) * Math.cos(phi2) * halfDeltaLambda * halfDeltaLambda;
|
|
2422
|
-
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
2423
|
-
return EARTH_RADIUS_METERS * c;
|
|
2851
|
+
applySurfaceCounts(state, surfaceCounts, 1);
|
|
2852
|
+
tokensByField[fieldPath] = fieldTokenList;
|
|
2424
2853
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2854
|
+
function indexDocument(state, docId, document, flatSchema, language, options) {
|
|
2855
|
+
const fieldLengths = {};
|
|
2856
|
+
const tokensByField = {};
|
|
2857
|
+
const internalId = resolveInternalId(state, docId);
|
|
2858
|
+
for (const [fieldPath, fieldType] of Object.entries(flatSchema)) {
|
|
2859
|
+
const value = getNestedValue(document, fieldPath);
|
|
2860
|
+
if (value === void 0 || value === null) continue;
|
|
2861
|
+
ensureFieldIndex(state, fieldPath, fieldType);
|
|
2862
|
+
if (fieldType === "string") {
|
|
2863
|
+
indexStringField(state, internalId, fieldPath, value, language, options, fieldLengths, tokensByField);
|
|
2864
|
+
} else if (fieldType === "number") {
|
|
2865
|
+
getOrCreateNumericIndex(state, fieldPath).insert(internalId, value);
|
|
2866
|
+
} else if (fieldType === "boolean") {
|
|
2867
|
+
getOrCreateBooleanIndex(state, fieldPath).insert(internalId, value);
|
|
2868
|
+
} else if (fieldType === "enum") {
|
|
2869
|
+
state.enumIndexes.get(fieldPath)?.insert(internalId, value);
|
|
2870
|
+
} else if (fieldType === "geopoint") {
|
|
2871
|
+
const geo = value;
|
|
2872
|
+
state.geoIndexes.get(fieldPath)?.insert(internalId, geo.lat, geo.lon);
|
|
2873
|
+
} else if (fieldType === "string[]") {
|
|
2874
|
+
indexStringArrayField(
|
|
2875
|
+
state,
|
|
2876
|
+
internalId,
|
|
2877
|
+
fieldPath,
|
|
2878
|
+
value,
|
|
2879
|
+
language,
|
|
2880
|
+
options,
|
|
2881
|
+
fieldLengths,
|
|
2882
|
+
tokensByField
|
|
2883
|
+
);
|
|
2884
|
+
} else if (fieldType === "number[]") {
|
|
2885
|
+
const numIdx = getOrCreateNumericIndex(state, fieldPath);
|
|
2886
|
+
for (const num of value) numIdx.insert(internalId, num);
|
|
2887
|
+
} else if (fieldType === "boolean[]") {
|
|
2888
|
+
const boolIdx = getOrCreateBooleanIndex(state, fieldPath);
|
|
2889
|
+
for (const b of value) boolIdx.insert(internalId, b);
|
|
2890
|
+
} else if (fieldType === "enum[]") {
|
|
2891
|
+
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
2892
|
+
if (enumIdx) {
|
|
2893
|
+
for (const e of value) enumIdx.insert(internalId, e);
|
|
2441
2894
|
}
|
|
2442
2895
|
}
|
|
2443
2896
|
}
|
|
2444
|
-
return
|
|
2445
|
-
}
|
|
2446
|
-
|
|
2447
|
-
// src/geo/vincenty.ts
|
|
2448
|
-
var WGS84_A = 6378137;
|
|
2449
|
-
var WGS84_F = 1 / 298.257223563;
|
|
2450
|
-
var WGS84_B = WGS84_A * (1 - WGS84_F);
|
|
2451
|
-
var DEG_TO_RAD2 = Math.PI / 180;
|
|
2452
|
-
var MAX_ITERATIONS = 200;
|
|
2453
|
-
var CONVERGENCE_THRESHOLD = 1e-12;
|
|
2454
|
-
function vincentyDistance(lat1, lon1, lat2, lon2) {
|
|
2455
|
-
const phi1 = lat1 * DEG_TO_RAD2;
|
|
2456
|
-
const phi2 = lat2 * DEG_TO_RAD2;
|
|
2457
|
-
const L = (lon2 - lon1) * DEG_TO_RAD2;
|
|
2458
|
-
const U1 = Math.atan((1 - WGS84_F) * Math.tan(phi1));
|
|
2459
|
-
const U2 = Math.atan((1 - WGS84_F) * Math.tan(phi2));
|
|
2460
|
-
const sinU1 = Math.sin(U1);
|
|
2461
|
-
const cosU1 = Math.cos(U1);
|
|
2462
|
-
const sinU2 = Math.sin(U2);
|
|
2463
|
-
const cosU2 = Math.cos(U2);
|
|
2464
|
-
let lambda = L;
|
|
2465
|
-
let prevLambda;
|
|
2466
|
-
let sinSigma = 0;
|
|
2467
|
-
let cosSigma = 0;
|
|
2468
|
-
let sigma = 0;
|
|
2469
|
-
let sinAlpha = 0;
|
|
2470
|
-
let cos2Alpha = 0;
|
|
2471
|
-
let cos2SigmaM = 0;
|
|
2472
|
-
let iterations = MAX_ITERATIONS;
|
|
2473
|
-
do {
|
|
2474
|
-
const sinLambda = Math.sin(lambda);
|
|
2475
|
-
const cosLambda = Math.cos(lambda);
|
|
2476
|
-
const term1 = cosU2 * sinLambda;
|
|
2477
|
-
const term2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
|
|
2478
|
-
sinSigma = Math.sqrt(term1 * term1 + term2 * term2);
|
|
2479
|
-
if (sinSigma === 0) return 0;
|
|
2480
|
-
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
|
|
2481
|
-
sigma = Math.atan2(sinSigma, cosSigma);
|
|
2482
|
-
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
|
|
2483
|
-
cos2Alpha = 1 - sinAlpha * sinAlpha;
|
|
2484
|
-
cos2SigmaM = cos2Alpha === 0 ? 0 : cosSigma - 2 * sinU1 * sinU2 / cos2Alpha;
|
|
2485
|
-
const C = WGS84_F / 16 * cos2Alpha * (4 + WGS84_F * (4 - 3 * cos2Alpha));
|
|
2486
|
-
prevLambda = lambda;
|
|
2487
|
-
lambda = L + (1 - C) * WGS84_F * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
|
|
2488
|
-
} while (Math.abs(lambda - prevLambda) > CONVERGENCE_THRESHOLD && --iterations > 0);
|
|
2489
|
-
if (iterations === 0) {
|
|
2490
|
-
return haversineDistance(lat1, lon1, lat2, lon2);
|
|
2491
|
-
}
|
|
2492
|
-
const uSq = cos2Alpha * (WGS84_A * WGS84_A - WGS84_B * WGS84_B) / (WGS84_B * WGS84_B);
|
|
2493
|
-
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
|
|
2494
|
-
const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
|
|
2495
|
-
const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
|
|
2496
|
-
return WGS84_B * A * (sigma - deltaSigma);
|
|
2897
|
+
return { fieldLengths, tokensByField };
|
|
2497
2898
|
}
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2899
|
+
function removeFromIndexes(state, docId, storedDoc, flatSchema, language, options) {
|
|
2900
|
+
const fieldLengths = {};
|
|
2901
|
+
const tokensByField = {};
|
|
2902
|
+
const fields = storedDoc.fields;
|
|
2903
|
+
const internalId = resolveInternalId(state, docId);
|
|
2904
|
+
const opts = tokenizeOptions(options);
|
|
2905
|
+
for (const [fieldPath, fieldType] of Object.entries(flatSchema)) {
|
|
2906
|
+
const value = getNestedValue(fields, fieldPath);
|
|
2907
|
+
if (value === void 0 || value === null) continue;
|
|
2908
|
+
if (fieldType === "string") {
|
|
2909
|
+
const result = tokenize(value, language, opts);
|
|
2910
|
+
fieldLengths[fieldPath] = result.tokens.length;
|
|
2911
|
+
const uniqueTokens = /* @__PURE__ */ new Set();
|
|
2912
|
+
const fieldTokenList = [];
|
|
2913
|
+
const surfaceCounts = /* @__PURE__ */ new Map();
|
|
2914
|
+
for (let i = 0; i < result.tokens.length; i++) {
|
|
2915
|
+
const t = result.tokens[i];
|
|
2916
|
+
uniqueTokens.add(t.token);
|
|
2917
|
+
fieldTokenList.push(t.token);
|
|
2918
|
+
countSurface(surfaceCounts, result.surfaces?.[i], t.token);
|
|
2515
2919
|
}
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2920
|
+
for (const token of uniqueTokens) state.invertedIdx.remove(token, internalId);
|
|
2921
|
+
applySurfaceCounts(state, surfaceCounts, -1);
|
|
2922
|
+
tokensByField[fieldPath] = fieldTokenList;
|
|
2923
|
+
} else if (fieldType === "number") {
|
|
2924
|
+
state.numericIndexes.get(fieldPath)?.remove(internalId, value);
|
|
2925
|
+
} else if (fieldType === "boolean") {
|
|
2926
|
+
state.booleanIndexes.get(fieldPath)?.remove(internalId, value);
|
|
2927
|
+
} else if (fieldType === "enum") {
|
|
2928
|
+
state.enumIndexes.get(fieldPath)?.remove(internalId, value);
|
|
2929
|
+
} else if (fieldType === "geopoint") {
|
|
2930
|
+
state.geoIndexes.get(fieldPath)?.remove(internalId);
|
|
2931
|
+
} else if (fieldType === "string[]") {
|
|
2932
|
+
const arr = value;
|
|
2933
|
+
const uniqueTokens = /* @__PURE__ */ new Set();
|
|
2934
|
+
const fieldTokenList = [];
|
|
2935
|
+
const surfaceCounts = /* @__PURE__ */ new Map();
|
|
2936
|
+
for (const item of arr) {
|
|
2937
|
+
const result = tokenize(item, language, opts);
|
|
2938
|
+
for (let i = 0; i < result.tokens.length; i++) {
|
|
2939
|
+
const t = result.tokens[i];
|
|
2940
|
+
uniqueTokens.add(t.token);
|
|
2941
|
+
fieldTokenList.push(t.token);
|
|
2942
|
+
countSurface(surfaceCounts, result.surfaces?.[i], t.token);
|
|
2526
2943
|
}
|
|
2527
2944
|
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
result.add(entry.docId);
|
|
2537
|
-
}
|
|
2945
|
+
fieldLengths[fieldPath] = fieldTokenList.length;
|
|
2946
|
+
for (const token of uniqueTokens) state.invertedIdx.remove(token, internalId);
|
|
2947
|
+
applySurfaceCounts(state, surfaceCounts, -1);
|
|
2948
|
+
tokensByField[fieldPath] = fieldTokenList;
|
|
2949
|
+
} else if (fieldType === "number[]") {
|
|
2950
|
+
const numIdx = state.numericIndexes.get(fieldPath);
|
|
2951
|
+
if (numIdx) {
|
|
2952
|
+
for (const num of value) numIdx.remove(internalId, num);
|
|
2538
2953
|
}
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
},
|
|
2544
|
-
serialize() {
|
|
2545
|
-
return entries.map((e) => ({ ...e }));
|
|
2546
|
-
},
|
|
2547
|
-
deserialize(data) {
|
|
2548
|
-
entries.length = 0;
|
|
2549
|
-
for (const entry of data) {
|
|
2550
|
-
entries.push({ lat: entry.lat, lon: entry.lon, docId: entry.docId });
|
|
2954
|
+
} else if (fieldType === "boolean[]") {
|
|
2955
|
+
const boolIdx = state.booleanIndexes.get(fieldPath);
|
|
2956
|
+
if (boolIdx) {
|
|
2957
|
+
for (const b of value) boolIdx.remove(internalId, b);
|
|
2551
2958
|
}
|
|
2552
|
-
}
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
// src/core/field-index/boolean.ts
|
|
2557
|
-
function createBooleanIndex() {
|
|
2558
|
-
const trueDocs = /* @__PURE__ */ new Set();
|
|
2559
|
-
const falseDocs = /* @__PURE__ */ new Set();
|
|
2560
|
-
return {
|
|
2561
|
-
insert(internalId, value) {
|
|
2562
|
-
if (value) trueDocs.add(internalId);
|
|
2563
|
-
else falseDocs.add(internalId);
|
|
2564
|
-
},
|
|
2565
|
-
remove(internalId, value) {
|
|
2566
|
-
if (value) trueDocs.delete(internalId);
|
|
2567
|
-
else falseDocs.delete(internalId);
|
|
2568
|
-
},
|
|
2569
|
-
queryEq(value) {
|
|
2570
|
-
return new Set(value ? trueDocs : falseDocs);
|
|
2571
|
-
},
|
|
2572
|
-
queryNe(value) {
|
|
2573
|
-
return new Set(value ? falseDocs : trueDocs);
|
|
2574
|
-
},
|
|
2575
|
-
getAllDocIds() {
|
|
2576
|
-
const all = new Set(trueDocs);
|
|
2577
|
-
for (const id of falseDocs) all.add(id);
|
|
2578
|
-
return all;
|
|
2579
|
-
},
|
|
2580
|
-
queryEqBitset(value, capacity) {
|
|
2581
|
-
const source = value ? trueDocs : falseDocs;
|
|
2582
|
-
const bs = createBitSet(capacity);
|
|
2583
|
-
for (const id of source) {
|
|
2584
|
-
bitsetSet(bs, id);
|
|
2959
|
+
} else if (fieldType === "enum[]") {
|
|
2960
|
+
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
2961
|
+
if (enumIdx) {
|
|
2962
|
+
for (const e of value) enumIdx.remove(internalId, e);
|
|
2585
2963
|
}
|
|
2586
|
-
return bs;
|
|
2587
|
-
},
|
|
2588
|
-
getAllDocIdsBitset(capacity) {
|
|
2589
|
-
const bs = createBitSet(capacity);
|
|
2590
|
-
for (const id of trueDocs) bitsetSet(bs, id);
|
|
2591
|
-
for (const id of falseDocs) bitsetSet(bs, id);
|
|
2592
|
-
return bs;
|
|
2593
|
-
},
|
|
2594
|
-
count() {
|
|
2595
|
-
return trueDocs.size + falseDocs.size;
|
|
2596
|
-
},
|
|
2597
|
-
clear() {
|
|
2598
|
-
trueDocs.clear();
|
|
2599
|
-
falseDocs.clear();
|
|
2600
|
-
},
|
|
2601
|
-
serialize() {
|
|
2602
|
-
return {
|
|
2603
|
-
trueDocs: Array.from(trueDocs),
|
|
2604
|
-
falseDocs: Array.from(falseDocs)
|
|
2605
|
-
};
|
|
2606
|
-
},
|
|
2607
|
-
deserialize(data) {
|
|
2608
|
-
trueDocs.clear();
|
|
2609
|
-
falseDocs.clear();
|
|
2610
|
-
for (const id of data.trueDocs) trueDocs.add(id);
|
|
2611
|
-
for (const id of data.falseDocs) falseDocs.add(id);
|
|
2612
2964
|
}
|
|
2613
|
-
}
|
|
2965
|
+
}
|
|
2966
|
+
return { fieldLengths, tokensByField };
|
|
2614
2967
|
}
|
|
2615
2968
|
|
|
2616
|
-
// src/core/field-
|
|
2617
|
-
function
|
|
2618
|
-
const
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
for (const docSet of valueMap.values()) {
|
|
2622
|
-
for (const id of docSet) result.add(id);
|
|
2623
|
-
}
|
|
2624
|
-
return result;
|
|
2969
|
+
// src/core/partition/field-updates.ts
|
|
2970
|
+
function resolveInternalId2(state, docId) {
|
|
2971
|
+
const internalId = state.docStore.getInternalId(docId);
|
|
2972
|
+
if (internalId === void 0) {
|
|
2973
|
+
throw new Error(`Internal ID not found for document "${docId}"`);
|
|
2625
2974
|
}
|
|
2626
|
-
return
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
queryNe(value) {
|
|
2646
|
-
const excluded = valueMap.get(value);
|
|
2647
|
-
const result = allDocs();
|
|
2648
|
-
if (excluded) {
|
|
2649
|
-
for (const id of excluded) result.delete(id);
|
|
2975
|
+
return internalId;
|
|
2976
|
+
}
|
|
2977
|
+
function updateFieldIndexOnly(state, docId, oldFields, newDoc, flatSchema) {
|
|
2978
|
+
const internalId = resolveInternalId2(state, docId);
|
|
2979
|
+
for (const [fieldPath, fieldType] of Object.entries(flatSchema)) {
|
|
2980
|
+
if (fieldType === "string" || fieldType === "string[]") continue;
|
|
2981
|
+
const oldVal = getNestedValue(oldFields, fieldPath);
|
|
2982
|
+
const newVal = getNestedValue(newDoc, fieldPath);
|
|
2983
|
+
if (oldVal === newVal) continue;
|
|
2984
|
+
if (oldVal !== void 0 && oldVal !== null && newVal !== void 0 && newVal !== null && typeof oldVal === typeof newVal && JSON.stringify(oldVal) === JSON.stringify(newVal))
|
|
2985
|
+
continue;
|
|
2986
|
+
if (newVal !== void 0 && newVal !== null) {
|
|
2987
|
+
ensureFieldIndex(state, fieldPath, fieldType);
|
|
2988
|
+
}
|
|
2989
|
+
if (fieldType === "number") {
|
|
2990
|
+
const numIdx = state.numericIndexes.get(fieldPath);
|
|
2991
|
+
if (numIdx) {
|
|
2992
|
+
if (oldVal !== void 0 && oldVal !== null) numIdx.remove(internalId, oldVal);
|
|
2993
|
+
if (newVal !== void 0 && newVal !== null) numIdx.insert(internalId, newVal);
|
|
2650
2994
|
}
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
const docSet = valueMap.get(val);
|
|
2657
|
-
if (docSet) {
|
|
2658
|
-
for (const id of docSet) result.add(id);
|
|
2659
|
-
}
|
|
2995
|
+
} else if (fieldType === "boolean") {
|
|
2996
|
+
const boolIdx = state.booleanIndexes.get(fieldPath);
|
|
2997
|
+
if (boolIdx) {
|
|
2998
|
+
if (oldVal !== void 0 && oldVal !== null) boolIdx.remove(internalId, oldVal);
|
|
2999
|
+
if (newVal !== void 0 && newVal !== null) boolIdx.insert(internalId, newVal);
|
|
2660
3000
|
}
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
for (const id of excluded) result.delete(id);
|
|
2667
|
-
return result;
|
|
2668
|
-
},
|
|
2669
|
-
getAllDocIds() {
|
|
2670
|
-
return allDocs();
|
|
2671
|
-
},
|
|
2672
|
-
queryEqBitset(value, capacity) {
|
|
2673
|
-
const bs = createBitSet(capacity);
|
|
2674
|
-
const docSet = valueMap.get(value);
|
|
2675
|
-
if (docSet) {
|
|
2676
|
-
for (const id of docSet) bitsetSet(bs, id);
|
|
3001
|
+
} else if (fieldType === "enum") {
|
|
3002
|
+
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
3003
|
+
if (enumIdx) {
|
|
3004
|
+
if (oldVal !== void 0 && oldVal !== null) enumIdx.remove(internalId, oldVal);
|
|
3005
|
+
if (newVal !== void 0 && newVal !== null) enumIdx.insert(internalId, newVal);
|
|
2677
3006
|
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
for (const id of docSet) bitsetSet(bs, id);
|
|
3007
|
+
} else if (fieldType === "geopoint") {
|
|
3008
|
+
const geoIdx = state.geoIndexes.get(fieldPath);
|
|
3009
|
+
if (geoIdx) {
|
|
3010
|
+
if (oldVal !== void 0 && oldVal !== null) geoIdx.remove(internalId);
|
|
3011
|
+
if (newVal !== void 0 && newVal !== null) {
|
|
3012
|
+
const geo = newVal;
|
|
3013
|
+
geoIdx.insert(internalId, geo.lat, geo.lon);
|
|
2686
3014
|
}
|
|
2687
3015
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
3016
|
+
} else if (fieldType === "number[]") {
|
|
3017
|
+
const numIdx = state.numericIndexes.get(fieldPath);
|
|
3018
|
+
if (numIdx) {
|
|
3019
|
+
if (oldVal !== void 0 && oldVal !== null) {
|
|
3020
|
+
for (const n of oldVal) numIdx.remove(internalId, n);
|
|
3021
|
+
}
|
|
3022
|
+
if (newVal !== void 0 && newVal !== null) {
|
|
3023
|
+
for (const n of newVal) numIdx.insert(internalId, n);
|
|
3024
|
+
}
|
|
2694
3025
|
}
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
},
|
|
2705
|
-
serialize() {
|
|
2706
|
-
const result = /* @__PURE__ */ Object.create(null);
|
|
2707
|
-
for (const [value, docSet] of valueMap) {
|
|
2708
|
-
result[value] = Array.from(docSet);
|
|
3026
|
+
} else if (fieldType === "boolean[]") {
|
|
3027
|
+
const boolIdx = state.booleanIndexes.get(fieldPath);
|
|
3028
|
+
if (boolIdx) {
|
|
3029
|
+
if (oldVal !== void 0 && oldVal !== null) {
|
|
3030
|
+
for (const b of oldVal) boolIdx.remove(internalId, b);
|
|
3031
|
+
}
|
|
3032
|
+
if (newVal !== void 0 && newVal !== null) {
|
|
3033
|
+
for (const b of newVal) boolIdx.insert(internalId, b);
|
|
3034
|
+
}
|
|
2709
3035
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
3036
|
+
} else if (fieldType === "enum[]") {
|
|
3037
|
+
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
3038
|
+
if (enumIdx) {
|
|
3039
|
+
if (oldVal !== void 0 && oldVal !== null) {
|
|
3040
|
+
for (const e of oldVal) enumIdx.remove(internalId, e);
|
|
3041
|
+
}
|
|
3042
|
+
if (newVal !== void 0 && newVal !== null) {
|
|
3043
|
+
for (const e of newVal) enumIdx.insert(internalId, e);
|
|
3044
|
+
}
|
|
2716
3045
|
}
|
|
2717
3046
|
}
|
|
2718
|
-
}
|
|
3047
|
+
}
|
|
2719
3048
|
}
|
|
2720
3049
|
|
|
2721
|
-
// src/
|
|
2722
|
-
function
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
else hi = mid;
|
|
3050
|
+
// src/filters/combinators.ts
|
|
3051
|
+
function applyAndBitset(bitsets) {
|
|
3052
|
+
if (bitsets.length === 0) return new Uint32Array(0);
|
|
3053
|
+
if (bitsets.length === 1) return bitsets[0];
|
|
3054
|
+
let result = bitsets[0];
|
|
3055
|
+
for (let i = 1; i < bitsets.length; i++) {
|
|
3056
|
+
result = bitsetAnd(result, bitsets[i]);
|
|
2729
3057
|
}
|
|
2730
|
-
return
|
|
3058
|
+
return result;
|
|
2731
3059
|
}
|
|
2732
|
-
function
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
else hi = mid;
|
|
3060
|
+
function applyOrBitset(bitsets) {
|
|
3061
|
+
if (bitsets.length === 0) return new Uint32Array(0);
|
|
3062
|
+
if (bitsets.length === 1) return bitsets[0];
|
|
3063
|
+
let result = bitsets[0];
|
|
3064
|
+
for (let i = 1; i < bitsets.length; i++) {
|
|
3065
|
+
result = bitsetOr(result, bitsets[i]);
|
|
2739
3066
|
}
|
|
2740
|
-
return
|
|
3067
|
+
return result;
|
|
2741
3068
|
}
|
|
2742
|
-
function
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
3069
|
+
function applyNotBitset(excluded, capacity) {
|
|
3070
|
+
return bitsetNot(excluded, capacity);
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
// src/filters/operators/shared.ts
|
|
3074
|
+
var METERS_PER_KM = 1e3;
|
|
3075
|
+
var METERS_PER_MI = 1609.344;
|
|
3076
|
+
function convertToMeters(distance, unit) {
|
|
3077
|
+
if (unit === "km") return distance * METERS_PER_KM;
|
|
3078
|
+
if (unit === "mi") return distance * METERS_PER_MI;
|
|
3079
|
+
return distance;
|
|
3080
|
+
}
|
|
3081
|
+
function matchesNumericComparison(value, filter) {
|
|
3082
|
+
if (filter.eq !== void 0 && value !== filter.eq) return false;
|
|
3083
|
+
if (filter.ne !== void 0 && value === filter.ne) return false;
|
|
3084
|
+
if (filter.gt !== void 0 && !(value > filter.gt)) return false;
|
|
3085
|
+
if (filter.lt !== void 0 && !(value < filter.lt)) return false;
|
|
3086
|
+
if (filter.gte !== void 0 && !(value >= filter.gte)) return false;
|
|
3087
|
+
if (filter.lte !== void 0 && !(value <= filter.lte)) return false;
|
|
3088
|
+
if (filter.between !== void 0 && !(value >= filter.between[0] && value <= filter.between[1])) return false;
|
|
3089
|
+
return true;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
// src/filters/operators/bitset.ts
|
|
3093
|
+
function resolveBitset(bs) {
|
|
3094
|
+
return typeof bs === "function" ? bs() : bs;
|
|
3095
|
+
}
|
|
3096
|
+
function scanToBitset(docIds, capacity, getValue, predicate) {
|
|
3097
|
+
const resolved = resolveBitset(docIds);
|
|
3098
|
+
const result = createBitSet(capacity);
|
|
3099
|
+
for (let wi = 0; wi < resolved.length; wi++) {
|
|
3100
|
+
let word = resolved[wi];
|
|
3101
|
+
if (word === 0) continue;
|
|
3102
|
+
const base = wi << 5;
|
|
3103
|
+
while (word !== 0) {
|
|
3104
|
+
const tz = Math.clz32(word & -word) ^ 31;
|
|
3105
|
+
const id = base + tz;
|
|
3106
|
+
if (predicate(getValue(id))) {
|
|
3107
|
+
bitsetSet(result, id);
|
|
3108
|
+
}
|
|
3109
|
+
word &= word - 1;
|
|
3110
|
+
}
|
|
2746
3111
|
}
|
|
2747
3112
|
return result;
|
|
2748
3113
|
}
|
|
2749
|
-
function
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
bitsetSet(bs, entries[i].docId);
|
|
3114
|
+
function applyEqBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3115
|
+
if (fieldIndex?.type === "numeric" && typeof value === "number") {
|
|
3116
|
+
return fieldIndex.index.eqBitset(value, capacity);
|
|
2753
3117
|
}
|
|
2754
|
-
|
|
3118
|
+
if (fieldIndex?.type === "boolean" && typeof value === "boolean") {
|
|
3119
|
+
return value ? fieldIndex.index.getTrueBitset(capacity) : fieldIndex.index.getFalseBitset(capacity);
|
|
3120
|
+
}
|
|
3121
|
+
if (fieldIndex?.type === "enum" && typeof value === "string") {
|
|
3122
|
+
return fieldIndex.index.getDocIdsBitset(value, capacity);
|
|
3123
|
+
}
|
|
3124
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => v === value);
|
|
2755
3125
|
}
|
|
2756
|
-
function
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
entries.sort((a, b) => a.value - b.value);
|
|
2762
|
-
sorted = true;
|
|
2763
|
-
}
|
|
3126
|
+
function applyNeBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3127
|
+
if (fieldIndex?.type === "numeric" && typeof value === "number") {
|
|
3128
|
+
const all = fieldIndex.index.allDocIdsBitset(capacity);
|
|
3129
|
+
const eq = fieldIndex.index.eqBitset(value, capacity);
|
|
3130
|
+
return bitsetAnd(all, bitsetNot(eq, capacity));
|
|
2764
3131
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
for (let i = start; i < end; i++) {
|
|
2775
|
-
if (entries[i].docId === internalId) {
|
|
2776
|
-
entries.splice(i, 1);
|
|
2777
|
-
return;
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
|
-
},
|
|
2781
|
-
queryEq(value) {
|
|
2782
|
-
ensureSorted();
|
|
2783
|
-
return collectDocIds(entries, lowerBound(entries, value), upperBound(entries, value));
|
|
2784
|
-
},
|
|
2785
|
-
queryNe(value) {
|
|
2786
|
-
ensureSorted();
|
|
2787
|
-
const excluded = this.queryEq(value);
|
|
2788
|
-
const result = /* @__PURE__ */ new Set();
|
|
2789
|
-
for (const entry of entries) {
|
|
2790
|
-
if (!excluded.has(entry.docId)) result.add(entry.docId);
|
|
2791
|
-
}
|
|
2792
|
-
return result;
|
|
2793
|
-
},
|
|
2794
|
-
queryGt(value) {
|
|
2795
|
-
ensureSorted();
|
|
2796
|
-
return collectDocIds(entries, upperBound(entries, value), entries.length);
|
|
2797
|
-
},
|
|
2798
|
-
queryGte(value) {
|
|
2799
|
-
ensureSorted();
|
|
2800
|
-
return collectDocIds(entries, lowerBound(entries, value), entries.length);
|
|
2801
|
-
},
|
|
2802
|
-
queryLt(value) {
|
|
2803
|
-
ensureSorted();
|
|
2804
|
-
return collectDocIds(entries, 0, lowerBound(entries, value));
|
|
2805
|
-
},
|
|
2806
|
-
queryLte(value) {
|
|
2807
|
-
ensureSorted();
|
|
2808
|
-
return collectDocIds(entries, 0, upperBound(entries, value));
|
|
2809
|
-
},
|
|
2810
|
-
queryBetween(min, max) {
|
|
2811
|
-
ensureSorted();
|
|
2812
|
-
return collectDocIds(entries, lowerBound(entries, min), upperBound(entries, max));
|
|
2813
|
-
},
|
|
2814
|
-
getAllDocIds() {
|
|
2815
|
-
return collectDocIds(entries, 0, entries.length);
|
|
2816
|
-
},
|
|
2817
|
-
queryEqBitset(value, capacity) {
|
|
2818
|
-
ensureSorted();
|
|
2819
|
-
return collectDocIdsBitset(entries, lowerBound(entries, value), upperBound(entries, value), capacity);
|
|
2820
|
-
},
|
|
2821
|
-
queryGtBitset(value, capacity) {
|
|
2822
|
-
ensureSorted();
|
|
2823
|
-
return collectDocIdsBitset(entries, upperBound(entries, value), entries.length, capacity);
|
|
2824
|
-
},
|
|
2825
|
-
queryGteBitset(value, capacity) {
|
|
2826
|
-
ensureSorted();
|
|
2827
|
-
return collectDocIdsBitset(entries, lowerBound(entries, value), entries.length, capacity);
|
|
2828
|
-
},
|
|
2829
|
-
queryLtBitset(value, capacity) {
|
|
2830
|
-
ensureSorted();
|
|
2831
|
-
return collectDocIdsBitset(entries, 0, lowerBound(entries, value), capacity);
|
|
2832
|
-
},
|
|
2833
|
-
queryLteBitset(value, capacity) {
|
|
2834
|
-
ensureSorted();
|
|
2835
|
-
return collectDocIdsBitset(entries, 0, upperBound(entries, value), capacity);
|
|
2836
|
-
},
|
|
2837
|
-
queryBetweenBitset(min, max, capacity) {
|
|
2838
|
-
ensureSorted();
|
|
2839
|
-
return collectDocIdsBitset(entries, lowerBound(entries, min), upperBound(entries, max), capacity);
|
|
2840
|
-
},
|
|
2841
|
-
getAllDocIdsBitset(capacity) {
|
|
2842
|
-
return collectDocIdsBitset(entries, 0, entries.length, capacity);
|
|
2843
|
-
},
|
|
2844
|
-
count() {
|
|
2845
|
-
return entries.length;
|
|
2846
|
-
},
|
|
2847
|
-
clear() {
|
|
2848
|
-
entries = [];
|
|
2849
|
-
sorted = true;
|
|
2850
|
-
},
|
|
2851
|
-
serialize() {
|
|
2852
|
-
ensureSorted();
|
|
2853
|
-
return entries.slice();
|
|
2854
|
-
},
|
|
2855
|
-
deserialize(data) {
|
|
2856
|
-
entries = data.slice();
|
|
2857
|
-
sorted = true;
|
|
2858
|
-
}
|
|
2859
|
-
};
|
|
3132
|
+
if (fieldIndex?.type === "boolean" && typeof value === "boolean") {
|
|
3133
|
+
return value ? fieldIndex.index.getFalseBitset(capacity) : fieldIndex.index.getTrueBitset(capacity);
|
|
3134
|
+
}
|
|
3135
|
+
if (fieldIndex?.type === "enum" && typeof value === "string") {
|
|
3136
|
+
const all = fieldIndex.index.allDocIdsBitset(capacity);
|
|
3137
|
+
const eq = fieldIndex.index.getDocIdsBitset(value, capacity);
|
|
3138
|
+
return bitsetAnd(all, bitsetNot(eq, capacity));
|
|
3139
|
+
}
|
|
3140
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => v !== void 0 && v !== null && v !== value);
|
|
2860
3141
|
}
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
}
|
|
2871
|
-
|
|
3142
|
+
function applyGtBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3143
|
+
if (fieldIndex?.type === "numeric") {
|
|
3144
|
+
return fieldIndex.index.gtBitset(value, capacity);
|
|
3145
|
+
}
|
|
3146
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v > value);
|
|
3147
|
+
}
|
|
3148
|
+
function applyLtBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3149
|
+
if (fieldIndex?.type === "numeric") {
|
|
3150
|
+
return fieldIndex.index.ltBitset(value, capacity);
|
|
3151
|
+
}
|
|
3152
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v < value);
|
|
3153
|
+
}
|
|
3154
|
+
function applyGteBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3155
|
+
if (fieldIndex?.type === "numeric") {
|
|
3156
|
+
return fieldIndex.index.gteBitset(value, capacity);
|
|
3157
|
+
}
|
|
3158
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v >= value);
|
|
3159
|
+
}
|
|
3160
|
+
function applyLteBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3161
|
+
if (fieldIndex?.type === "numeric") {
|
|
3162
|
+
return fieldIndex.index.lteBitset(value, capacity);
|
|
3163
|
+
}
|
|
3164
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v <= value);
|
|
3165
|
+
}
|
|
3166
|
+
function applyBetweenBitset(range, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3167
|
+
if (fieldIndex?.type === "numeric") {
|
|
3168
|
+
return fieldIndex.index.betweenBitset(range[0], range[1], capacity);
|
|
2872
3169
|
}
|
|
3170
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v >= range[0] && v <= range[1]);
|
|
2873
3171
|
}
|
|
2874
|
-
function
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
idx = createNumericIndex();
|
|
2878
|
-
state.numericIndexes.set(fieldPath, idx);
|
|
3172
|
+
function applyInBitset(values, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3173
|
+
if (fieldIndex?.type === "enum") {
|
|
3174
|
+
return fieldIndex.index.getDocIdsInBitset(values, capacity);
|
|
2879
3175
|
}
|
|
2880
|
-
|
|
3176
|
+
const valSet = new Set(values);
|
|
3177
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "string" && valSet.has(v));
|
|
2881
3178
|
}
|
|
2882
|
-
function
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
3179
|
+
function applyNinBitset(values, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
3180
|
+
if (fieldIndex?.type === "enum") {
|
|
3181
|
+
const all = fieldIndex.index.allDocIdsBitset(capacity);
|
|
3182
|
+
const matched = fieldIndex.index.getDocIdsInBitset(values, capacity);
|
|
3183
|
+
return bitsetAnd(all, bitsetNot(matched, capacity));
|
|
2887
3184
|
}
|
|
2888
|
-
|
|
3185
|
+
const valSet = new Set(values);
|
|
3186
|
+
return scanToBitset(
|
|
3187
|
+
allDocsBitset,
|
|
3188
|
+
capacity,
|
|
3189
|
+
getValue,
|
|
3190
|
+
(v) => v !== void 0 && v !== null && typeof v === "string" && !valSet.has(v)
|
|
3191
|
+
);
|
|
2889
3192
|
}
|
|
2890
|
-
function
|
|
2891
|
-
|
|
2892
|
-
if (internalId === void 0) {
|
|
2893
|
-
throw new Error(`Internal ID not found for document "${docId}"`);
|
|
2894
|
-
}
|
|
2895
|
-
return internalId;
|
|
3193
|
+
function applyStartsWithBitset(prefix, allDocsBitset, capacity, getValue) {
|
|
3194
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "string" && v.startsWith(prefix));
|
|
2896
3195
|
}
|
|
2897
|
-
function
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
if (
|
|
2906
|
-
existing.count++;
|
|
2907
|
-
existing.positions.push(t.position);
|
|
2908
|
-
} else {
|
|
2909
|
-
tokenFreqs.set(t.token, { count: 1, positions: [t.position] });
|
|
2910
|
-
}
|
|
2911
|
-
fieldTokenList.push(t.token);
|
|
2912
|
-
}
|
|
2913
|
-
for (const [token, freq] of tokenFreqs) {
|
|
2914
|
-
state.invertedIdx.insert(token, internalId, freq.count, fieldNameIndex, freq.positions);
|
|
3196
|
+
function applyEndsWithBitset(suffix, allDocsBitset, capacity, getValue) {
|
|
3197
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "string" && v.endsWith(suffix));
|
|
3198
|
+
}
|
|
3199
|
+
function applyContainsAllBitset(values, allDocsBitset, capacity, getValue) {
|
|
3200
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
|
|
3201
|
+
if (!Array.isArray(v)) return false;
|
|
3202
|
+
const arr = v;
|
|
3203
|
+
for (const val of values) {
|
|
3204
|
+
if (!arr.includes(val)) return false;
|
|
2915
3205
|
}
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
3206
|
+
return true;
|
|
3207
|
+
});
|
|
3208
|
+
}
|
|
3209
|
+
function applyMatchesAnyBitset(values, allDocsBitset, capacity, getValue) {
|
|
3210
|
+
const valSet = new Set(values);
|
|
3211
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
|
|
3212
|
+
if (!Array.isArray(v)) return false;
|
|
3213
|
+
for (const item of v) {
|
|
3214
|
+
if (valSet.has(item)) return true;
|
|
2921
3215
|
}
|
|
2922
|
-
|
|
2923
|
-
|
|
3216
|
+
return false;
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3219
|
+
function applySizeBitset(sizeFilter, allDocsBitset, capacity, getValue) {
|
|
3220
|
+
return scanToBitset(
|
|
3221
|
+
allDocsBitset,
|
|
3222
|
+
capacity,
|
|
3223
|
+
getValue,
|
|
3224
|
+
(v) => Array.isArray(v) && matchesNumericComparison(v.length, sizeFilter)
|
|
3225
|
+
);
|
|
3226
|
+
}
|
|
3227
|
+
function applyExistsBitset(allDocsBitset, capacity, getValue) {
|
|
3228
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => v !== void 0 && v !== null);
|
|
3229
|
+
}
|
|
3230
|
+
function applyNotExistsBitset(allDocsBitset, capacity, getValue) {
|
|
3231
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => v === void 0 || v === null);
|
|
3232
|
+
}
|
|
3233
|
+
function applyIsEmptyBitset(allDocsBitset, capacity, getValue) {
|
|
3234
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
|
|
3235
|
+
if (v === void 0 || v === null) return true;
|
|
3236
|
+
if (typeof v === "string" && v === "") return true;
|
|
3237
|
+
if (Array.isArray(v) && v.length === 0) return true;
|
|
3238
|
+
return false;
|
|
3239
|
+
});
|
|
3240
|
+
}
|
|
3241
|
+
function applyIsNotEmptyBitset(allDocsBitset, capacity, getValue) {
|
|
3242
|
+
return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
|
|
3243
|
+
if (v === void 0 || v === null) return false;
|
|
3244
|
+
if (typeof v === "string" && v === "") return false;
|
|
3245
|
+
if (Array.isArray(v) && v.length === 0) return false;
|
|
3246
|
+
return true;
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
function applyGeoRadiusBitset(filter, geoIndex, capacity) {
|
|
3250
|
+
const distanceMeters = convertToMeters(filter.distance, filter.unit);
|
|
3251
|
+
const setResult = geoIndex.radiusQuery(
|
|
3252
|
+
filter.lat,
|
|
3253
|
+
filter.lon,
|
|
3254
|
+
distanceMeters,
|
|
3255
|
+
filter.inside ?? true,
|
|
3256
|
+
filter.highPrecision ?? false
|
|
3257
|
+
);
|
|
3258
|
+
return bitsetFromSet(setResult, capacity);
|
|
3259
|
+
}
|
|
3260
|
+
function applyGeoPolygonBitset(filter, geoIndex, capacity) {
|
|
3261
|
+
const setResult = geoIndex.polygonQuery(filter.points, filter.inside ?? true);
|
|
3262
|
+
return bitsetFromSet(setResult, capacity);
|
|
3263
|
+
}
|
|
3264
|
+
|
|
3265
|
+
// src/filters/evaluator.ts
|
|
3266
|
+
function evaluateFilters(expression, context) {
|
|
3267
|
+
const bitsets = [];
|
|
3268
|
+
if (expression.fields) {
|
|
3269
|
+
for (const [fieldPath, filter] of Object.entries(expression.fields)) {
|
|
3270
|
+
bitsets.push(evaluateFieldFilter(fieldPath, filter, context));
|
|
2924
3271
|
}
|
|
2925
3272
|
}
|
|
2926
|
-
|
|
2927
|
-
|
|
3273
|
+
if (expression.and?.length) {
|
|
3274
|
+
const andBitsets = expression.and.map((expr) => evaluateFilters(expr, context));
|
|
3275
|
+
bitsets.push(applyAndBitset(andBitsets));
|
|
3276
|
+
}
|
|
3277
|
+
if (expression.or?.length) {
|
|
3278
|
+
const orBitsets = expression.or.map((expr) => evaluateFilters(expr, context));
|
|
3279
|
+
bitsets.push(applyOrBitset(orBitsets));
|
|
3280
|
+
}
|
|
3281
|
+
if (expression.not) {
|
|
3282
|
+
const excluded = evaluateFilters(expression.not, context);
|
|
3283
|
+
const universe = context.allDocIdsBitset;
|
|
3284
|
+
bitsets.push(applyAndBitset([universe, applyNotBitset(excluded, context.capacity)]));
|
|
3285
|
+
}
|
|
3286
|
+
if (bitsets.length === 0) return context.allDocIdsBitset;
|
|
3287
|
+
if (bitsets.length === 1) return bitsets[0];
|
|
3288
|
+
return applyAndBitset(bitsets);
|
|
2928
3289
|
}
|
|
2929
|
-
function
|
|
2930
|
-
const
|
|
2931
|
-
const
|
|
2932
|
-
const
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
const existing = tokenFreqs.get(t.token);
|
|
2940
|
-
if (existing) {
|
|
2941
|
-
existing.count++;
|
|
2942
|
-
existing.positions.push(positionOffset + t.position);
|
|
2943
|
-
} else {
|
|
2944
|
-
tokenFreqs.set(t.token, { count: 1, positions: [positionOffset + t.position] });
|
|
2945
|
-
}
|
|
2946
|
-
fieldTokenList.push(t.token);
|
|
2947
|
-
itemTokenCount++;
|
|
2948
|
-
}
|
|
2949
|
-
positionOffset += itemTokenCount;
|
|
2950
|
-
}
|
|
2951
|
-
fieldLengths[fieldPath] = fieldTokenList.length;
|
|
2952
|
-
for (const [token, freq] of tokenFreqs) {
|
|
2953
|
-
state.invertedIdx.insert(token, internalId, freq.count, fieldNameIndex, freq.positions);
|
|
2954
|
-
}
|
|
2955
|
-
} else {
|
|
2956
|
-
const tokenCounts = /* @__PURE__ */ new Map();
|
|
2957
|
-
for (const item of arr) {
|
|
2958
|
-
for (const t of tokenizeIterator(item, language, opts)) {
|
|
2959
|
-
tokenCounts.set(t.token, (tokenCounts.get(t.token) ?? 0) + 1);
|
|
2960
|
-
fieldTokenList.push(t.token);
|
|
2961
|
-
}
|
|
2962
|
-
}
|
|
2963
|
-
fieldLengths[fieldPath] = fieldTokenList.length;
|
|
2964
|
-
for (const [token, count] of tokenCounts) {
|
|
2965
|
-
state.invertedIdx.insert(token, internalId, count, fieldNameIndex, null);
|
|
3290
|
+
function evaluateFieldFilter(fieldPath, filter, context) {
|
|
3291
|
+
const fieldIndex = context.fieldIndexes[fieldPath];
|
|
3292
|
+
const getValue = (internalId) => context.getFieldValue(internalId, fieldPath);
|
|
3293
|
+
const f = filter;
|
|
3294
|
+
const bitsets = [];
|
|
3295
|
+
const { capacity } = context;
|
|
3296
|
+
let allDocsBitset = null;
|
|
3297
|
+
const getAllDocsBitset = () => {
|
|
3298
|
+
if (allDocsBitset === null) {
|
|
3299
|
+
allDocsBitset = context.allDocIdsBitset;
|
|
2966
3300
|
}
|
|
3301
|
+
return allDocsBitset;
|
|
3302
|
+
};
|
|
3303
|
+
if ("radius" in f && f.radius) {
|
|
3304
|
+
const geoIndex = resolveGeoIndex(fieldPath, fieldIndex);
|
|
3305
|
+
bitsets.push(applyGeoRadiusBitset(filter.radius, geoIndex, capacity));
|
|
3306
|
+
}
|
|
3307
|
+
if ("polygon" in f && f.polygon) {
|
|
3308
|
+
const geoIndex = resolveGeoIndex(fieldPath, fieldIndex);
|
|
3309
|
+
bitsets.push(applyGeoPolygonBitset(filter.polygon, geoIndex, capacity));
|
|
3310
|
+
}
|
|
3311
|
+
if ("eq" in f && f.eq !== void 0) {
|
|
3312
|
+
bitsets.push(applyEqBitset(f.eq, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3313
|
+
}
|
|
3314
|
+
if ("ne" in f && f.ne !== void 0) {
|
|
3315
|
+
bitsets.push(applyNeBitset(f.ne, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3316
|
+
}
|
|
3317
|
+
if ("gt" in f && f.gt !== void 0) {
|
|
3318
|
+
bitsets.push(applyGtBitset(f.gt, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3319
|
+
}
|
|
3320
|
+
if ("lt" in f && f.lt !== void 0) {
|
|
3321
|
+
bitsets.push(applyLtBitset(f.lt, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3322
|
+
}
|
|
3323
|
+
if ("gte" in f && f.gte !== void 0) {
|
|
3324
|
+
bitsets.push(applyGteBitset(f.gte, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3325
|
+
}
|
|
3326
|
+
if ("lte" in f && f.lte !== void 0) {
|
|
3327
|
+
bitsets.push(applyLteBitset(f.lte, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3328
|
+
}
|
|
3329
|
+
if ("between" in f && f.between !== void 0) {
|
|
3330
|
+
bitsets.push(applyBetweenBitset(f.between, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3331
|
+
}
|
|
3332
|
+
if ("in" in f && f.in !== void 0) {
|
|
3333
|
+
bitsets.push(applyInBitset(f.in, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3334
|
+
}
|
|
3335
|
+
if ("nin" in f && f.nin !== void 0) {
|
|
3336
|
+
bitsets.push(applyNinBitset(f.nin, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
3337
|
+
}
|
|
3338
|
+
if ("startsWith" in f && f.startsWith !== void 0) {
|
|
3339
|
+
bitsets.push(applyStartsWithBitset(f.startsWith, getAllDocsBitset, capacity, getValue));
|
|
3340
|
+
}
|
|
3341
|
+
if ("endsWith" in f && f.endsWith !== void 0) {
|
|
3342
|
+
bitsets.push(applyEndsWithBitset(f.endsWith, getAllDocsBitset, capacity, getValue));
|
|
3343
|
+
}
|
|
3344
|
+
if ("containsAll" in f && f.containsAll !== void 0) {
|
|
3345
|
+
bitsets.push(
|
|
3346
|
+
applyContainsAllBitset(f.containsAll, getAllDocsBitset, capacity, getValue)
|
|
3347
|
+
);
|
|
3348
|
+
}
|
|
3349
|
+
if ("matchesAny" in f && f.matchesAny !== void 0) {
|
|
3350
|
+
bitsets.push(
|
|
3351
|
+
applyMatchesAnyBitset(f.matchesAny, getAllDocsBitset, capacity, getValue)
|
|
3352
|
+
);
|
|
3353
|
+
}
|
|
3354
|
+
if ("size" in f && f.size !== void 0) {
|
|
3355
|
+
bitsets.push(applySizeBitset(f.size, getAllDocsBitset, capacity, getValue));
|
|
2967
3356
|
}
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
const tokensByField = {};
|
|
2973
|
-
const internalId = resolveInternalId(state, docId);
|
|
2974
|
-
for (const [fieldPath, fieldType] of Object.entries(flatSchema)) {
|
|
2975
|
-
const value = getNestedValue(document, fieldPath);
|
|
2976
|
-
if (value === void 0 || value === null) continue;
|
|
2977
|
-
ensureFieldIndex(state, fieldPath, fieldType);
|
|
2978
|
-
if (fieldType === "string") {
|
|
2979
|
-
indexStringField(state, internalId, fieldPath, value, language, options, fieldLengths, tokensByField);
|
|
2980
|
-
} else if (fieldType === "number") {
|
|
2981
|
-
getOrCreateNumericIndex(state, fieldPath).insert(internalId, value);
|
|
2982
|
-
} else if (fieldType === "boolean") {
|
|
2983
|
-
getOrCreateBooleanIndex(state, fieldPath).insert(internalId, value);
|
|
2984
|
-
} else if (fieldType === "enum") {
|
|
2985
|
-
state.enumIndexes.get(fieldPath)?.insert(internalId, value);
|
|
2986
|
-
} else if (fieldType === "geopoint") {
|
|
2987
|
-
const geo = value;
|
|
2988
|
-
state.geoIndexes.get(fieldPath)?.insert(internalId, geo.lat, geo.lon);
|
|
2989
|
-
} else if (fieldType === "string[]") {
|
|
2990
|
-
indexStringArrayField(
|
|
2991
|
-
state,
|
|
2992
|
-
internalId,
|
|
2993
|
-
fieldPath,
|
|
2994
|
-
value,
|
|
2995
|
-
language,
|
|
2996
|
-
options,
|
|
2997
|
-
fieldLengths,
|
|
2998
|
-
tokensByField
|
|
2999
|
-
);
|
|
3000
|
-
} else if (fieldType === "number[]") {
|
|
3001
|
-
const numIdx = getOrCreateNumericIndex(state, fieldPath);
|
|
3002
|
-
for (const num of value) numIdx.insert(internalId, num);
|
|
3003
|
-
} else if (fieldType === "boolean[]") {
|
|
3004
|
-
const boolIdx = getOrCreateBooleanIndex(state, fieldPath);
|
|
3005
|
-
for (const b of value) boolIdx.insert(internalId, b);
|
|
3006
|
-
} else if (fieldType === "enum[]") {
|
|
3007
|
-
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
3008
|
-
if (enumIdx) {
|
|
3009
|
-
for (const e of value) enumIdx.insert(internalId, e);
|
|
3010
|
-
}
|
|
3011
|
-
}
|
|
3357
|
+
if ("exists" in f && f.exists !== void 0) {
|
|
3358
|
+
bitsets.push(
|
|
3359
|
+
f.exists ? applyExistsBitset(getAllDocsBitset, capacity, getValue) : applyNotExistsBitset(getAllDocsBitset, capacity, getValue)
|
|
3360
|
+
);
|
|
3012
3361
|
}
|
|
3013
|
-
|
|
3362
|
+
if ("notExists" in f && f.notExists !== void 0) {
|
|
3363
|
+
bitsets.push(
|
|
3364
|
+
f.notExists ? applyNotExistsBitset(getAllDocsBitset, capacity, getValue) : applyExistsBitset(getAllDocsBitset, capacity, getValue)
|
|
3365
|
+
);
|
|
3366
|
+
}
|
|
3367
|
+
if ("isEmpty" in f && f.isEmpty !== void 0) {
|
|
3368
|
+
bitsets.push(
|
|
3369
|
+
f.isEmpty ? applyIsEmptyBitset(getAllDocsBitset, capacity, getValue) : applyIsNotEmptyBitset(getAllDocsBitset, capacity, getValue)
|
|
3370
|
+
);
|
|
3371
|
+
}
|
|
3372
|
+
if ("isNotEmpty" in f && f.isNotEmpty !== void 0) {
|
|
3373
|
+
bitsets.push(
|
|
3374
|
+
f.isNotEmpty ? applyIsNotEmptyBitset(getAllDocsBitset, capacity, getValue) : applyIsEmptyBitset(getAllDocsBitset, capacity, getValue)
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3377
|
+
if (bitsets.length === 0) return context.allDocIdsBitset;
|
|
3378
|
+
if (bitsets.length === 1) return bitsets[0];
|
|
3379
|
+
return applyAndBitset(bitsets);
|
|
3014
3380
|
}
|
|
3015
|
-
function
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
if (value === void 0 || value === null) continue;
|
|
3023
|
-
if (fieldType === "string") {
|
|
3024
|
-
const result = tokenize(value, language, tokenizeOptions(options));
|
|
3025
|
-
fieldLengths[fieldPath] = result.tokens.length;
|
|
3026
|
-
const uniqueTokens = /* @__PURE__ */ new Set();
|
|
3027
|
-
const fieldTokenList = [];
|
|
3028
|
-
for (const t of result.tokens) {
|
|
3029
|
-
uniqueTokens.add(t.token);
|
|
3030
|
-
fieldTokenList.push(t.token);
|
|
3031
|
-
}
|
|
3032
|
-
for (const token of uniqueTokens) state.invertedIdx.remove(token, internalId);
|
|
3033
|
-
tokensByField[fieldPath] = fieldTokenList;
|
|
3034
|
-
} else if (fieldType === "number") {
|
|
3035
|
-
state.numericIndexes.get(fieldPath)?.remove(internalId, value);
|
|
3036
|
-
} else if (fieldType === "boolean") {
|
|
3037
|
-
state.booleanIndexes.get(fieldPath)?.remove(internalId, value);
|
|
3038
|
-
} else if (fieldType === "enum") {
|
|
3039
|
-
state.enumIndexes.get(fieldPath)?.remove(internalId, value);
|
|
3040
|
-
} else if (fieldType === "geopoint") {
|
|
3041
|
-
state.geoIndexes.get(fieldPath)?.remove(internalId);
|
|
3042
|
-
} else if (fieldType === "string[]") {
|
|
3043
|
-
const arr = value;
|
|
3044
|
-
const uniqueTokens = /* @__PURE__ */ new Set();
|
|
3045
|
-
const fieldTokenList = [];
|
|
3046
|
-
for (const item of arr) {
|
|
3047
|
-
const result = tokenize(item, language, tokenizeOptions(options));
|
|
3048
|
-
for (const t of result.tokens) {
|
|
3049
|
-
uniqueTokens.add(t.token);
|
|
3050
|
-
fieldTokenList.push(t.token);
|
|
3051
|
-
}
|
|
3052
|
-
}
|
|
3053
|
-
fieldLengths[fieldPath] = fieldTokenList.length;
|
|
3054
|
-
for (const token of uniqueTokens) state.invertedIdx.remove(token, internalId);
|
|
3055
|
-
tokensByField[fieldPath] = fieldTokenList;
|
|
3056
|
-
} else if (fieldType === "number[]") {
|
|
3057
|
-
const numIdx = state.numericIndexes.get(fieldPath);
|
|
3058
|
-
if (numIdx) {
|
|
3059
|
-
for (const num of value) numIdx.remove(internalId, num);
|
|
3060
|
-
}
|
|
3061
|
-
} else if (fieldType === "boolean[]") {
|
|
3062
|
-
const boolIdx = state.booleanIndexes.get(fieldPath);
|
|
3063
|
-
if (boolIdx) {
|
|
3064
|
-
for (const b of value) boolIdx.remove(internalId, b);
|
|
3065
|
-
}
|
|
3066
|
-
} else if (fieldType === "enum[]") {
|
|
3067
|
-
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
3068
|
-
if (enumIdx) {
|
|
3069
|
-
for (const e of value) enumIdx.remove(internalId, e);
|
|
3070
|
-
}
|
|
3071
|
-
}
|
|
3381
|
+
function resolveGeoIndex(fieldPath, fieldIndex) {
|
|
3382
|
+
if (fieldIndex?.type !== "geopoint") {
|
|
3383
|
+
throw new NarsilError(
|
|
3384
|
+
ErrorCodes.SEARCH_INVALID_FILTER,
|
|
3385
|
+
`Field "${fieldPath}" requires a geopoint index for geo filters`,
|
|
3386
|
+
{ fieldPath }
|
|
3387
|
+
);
|
|
3072
3388
|
}
|
|
3073
|
-
return
|
|
3389
|
+
return fieldIndex.index;
|
|
3074
3390
|
}
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
if (
|
|
3083
|
-
continue;
|
|
3084
|
-
if (newVal !== void 0 && newVal !== null) {
|
|
3085
|
-
ensureFieldIndex(state, fieldPath, fieldType);
|
|
3086
|
-
}
|
|
3087
|
-
if (fieldType === "number") {
|
|
3391
|
+
|
|
3392
|
+
// src/core/partition/filters.ts
|
|
3393
|
+
function buildFilterContext(state, schema) {
|
|
3394
|
+
const flat = getFlatSchema(state, schema);
|
|
3395
|
+
const fieldIndexes = {};
|
|
3396
|
+
const capacity = state.docStore.internalIdCapacity();
|
|
3397
|
+
for (const [fieldPath, fieldType] of Object.entries(flat)) {
|
|
3398
|
+
if (fieldType === "number" || fieldType === "number[]") {
|
|
3088
3399
|
const numIdx = state.numericIndexes.get(fieldPath);
|
|
3089
3400
|
if (numIdx) {
|
|
3090
|
-
|
|
3091
|
-
|
|
3401
|
+
fieldIndexes[fieldPath] = {
|
|
3402
|
+
type: "numeric",
|
|
3403
|
+
index: {
|
|
3404
|
+
eq: (v) => numIdx.queryEq(v),
|
|
3405
|
+
gt: (v) => numIdx.queryGt(v),
|
|
3406
|
+
gte: (v) => numIdx.queryGte(v),
|
|
3407
|
+
lt: (v) => numIdx.queryLt(v),
|
|
3408
|
+
lte: (v) => numIdx.queryLte(v),
|
|
3409
|
+
between: (min, max) => numIdx.queryBetween(min, max),
|
|
3410
|
+
allDocIds: () => numIdx.getAllDocIds(),
|
|
3411
|
+
eqBitset: (v, cap) => numIdx.queryEqBitset(v, cap),
|
|
3412
|
+
gtBitset: (v, cap) => numIdx.queryGtBitset(v, cap),
|
|
3413
|
+
gteBitset: (v, cap) => numIdx.queryGteBitset(v, cap),
|
|
3414
|
+
ltBitset: (v, cap) => numIdx.queryLtBitset(v, cap),
|
|
3415
|
+
lteBitset: (v, cap) => numIdx.queryLteBitset(v, cap),
|
|
3416
|
+
betweenBitset: (min, max, cap) => numIdx.queryBetweenBitset(min, max, cap),
|
|
3417
|
+
allDocIdsBitset: (cap) => numIdx.getAllDocIdsBitset(cap)
|
|
3418
|
+
}
|
|
3419
|
+
};
|
|
3092
3420
|
}
|
|
3093
|
-
} else if (fieldType === "boolean") {
|
|
3421
|
+
} else if (fieldType === "boolean" || fieldType === "boolean[]") {
|
|
3094
3422
|
const boolIdx = state.booleanIndexes.get(fieldPath);
|
|
3095
3423
|
if (boolIdx) {
|
|
3096
|
-
|
|
3097
|
-
|
|
3424
|
+
fieldIndexes[fieldPath] = {
|
|
3425
|
+
type: "boolean",
|
|
3426
|
+
index: {
|
|
3427
|
+
getTrue: () => boolIdx.queryEq(true),
|
|
3428
|
+
getFalse: () => boolIdx.queryEq(false),
|
|
3429
|
+
allDocIds: () => boolIdx.getAllDocIds(),
|
|
3430
|
+
getTrueBitset: (cap) => boolIdx.queryEqBitset(true, cap),
|
|
3431
|
+
getFalseBitset: (cap) => boolIdx.queryEqBitset(false, cap),
|
|
3432
|
+
allDocIdsBitset: (cap) => boolIdx.getAllDocIdsBitset(cap)
|
|
3433
|
+
}
|
|
3434
|
+
};
|
|
3098
3435
|
}
|
|
3099
|
-
} else if (fieldType === "enum") {
|
|
3436
|
+
} else if (fieldType === "enum" || fieldType === "enum[]") {
|
|
3100
3437
|
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
3101
3438
|
if (enumIdx) {
|
|
3102
|
-
|
|
3103
|
-
|
|
3439
|
+
fieldIndexes[fieldPath] = {
|
|
3440
|
+
type: "enum",
|
|
3441
|
+
index: {
|
|
3442
|
+
getDocIds: (v) => enumIdx.queryEq(v),
|
|
3443
|
+
allDocIds: () => enumIdx.getAllDocIds(),
|
|
3444
|
+
getDocIdsBitset: (v, cap) => enumIdx.queryEqBitset(v, cap),
|
|
3445
|
+
getDocIdsInBitset: (values, cap) => enumIdx.queryInBitset(values, cap),
|
|
3446
|
+
allDocIdsBitset: (cap) => enumIdx.getAllDocIdsBitset(cap)
|
|
3447
|
+
}
|
|
3448
|
+
};
|
|
3104
3449
|
}
|
|
3105
3450
|
} else if (fieldType === "geopoint") {
|
|
3106
3451
|
const geoIdx = state.geoIndexes.get(fieldPath);
|
|
3107
3452
|
if (geoIdx) {
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
}
|
|
3453
|
+
fieldIndexes[fieldPath] = {
|
|
3454
|
+
type: "geopoint",
|
|
3455
|
+
index: geoIdx
|
|
3456
|
+
};
|
|
3113
3457
|
}
|
|
3114
|
-
}
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
let cachedAllDocIds = null;
|
|
3461
|
+
let cachedAllDocIdsBitset = null;
|
|
3462
|
+
return {
|
|
3463
|
+
fieldIndexes,
|
|
3464
|
+
getFieldValue: (internalId, fieldPath) => getFieldValueByInternalId(state.docStore, internalId, fieldPath),
|
|
3465
|
+
get allDocIds() {
|
|
3466
|
+
if (!cachedAllDocIds) {
|
|
3467
|
+
cachedAllDocIds = getAllInternalDocIds(state.docStore);
|
|
3123
3468
|
}
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
if (newVal !== void 0 && newVal !== null) {
|
|
3131
|
-
for (const b of newVal) boolIdx.insert(internalId, b);
|
|
3132
|
-
}
|
|
3469
|
+
return cachedAllDocIds;
|
|
3470
|
+
},
|
|
3471
|
+
capacity,
|
|
3472
|
+
get allDocIdsBitset() {
|
|
3473
|
+
if (!cachedAllDocIdsBitset) {
|
|
3474
|
+
cachedAllDocIdsBitset = bitsetFromSet(this.allDocIds, capacity);
|
|
3133
3475
|
}
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3476
|
+
return cachedAllDocIdsBitset;
|
|
3477
|
+
}
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
function applyPartitionFilters(state, filters, schema) {
|
|
3481
|
+
const context = buildFilterContext(state, schema);
|
|
3482
|
+
const resultBitset = evaluateFilters(filters, context);
|
|
3483
|
+
const resolver = state.docStore.resolver();
|
|
3484
|
+
const externalResult = /* @__PURE__ */ new Set();
|
|
3485
|
+
for (let wi = 0; wi < resultBitset.length; wi++) {
|
|
3486
|
+
let word = resultBitset[wi];
|
|
3487
|
+
if (word === 0) continue;
|
|
3488
|
+
const base = wi << 5;
|
|
3489
|
+
while (word !== 0) {
|
|
3490
|
+
const tz = Math.clz32(word & -word) ^ 31;
|
|
3491
|
+
const internalId = base + tz;
|
|
3492
|
+
const externalId = resolver.toExternal(internalId);
|
|
3493
|
+
if (externalId !== void 0) {
|
|
3494
|
+
externalResult.add(externalId);
|
|
3143
3495
|
}
|
|
3496
|
+
word &= word - 1;
|
|
3144
3497
|
}
|
|
3145
3498
|
}
|
|
3499
|
+
return externalResult;
|
|
3500
|
+
}
|
|
3501
|
+
function applyPartitionFiltersBitset(state, filters, schema) {
|
|
3502
|
+
const context = buildFilterContext(state, schema);
|
|
3503
|
+
return evaluateFilters(filters, context);
|
|
3146
3504
|
}
|
|
3147
3505
|
|
|
3148
3506
|
// src/core/scorer.ts
|
|
@@ -3200,6 +3558,7 @@ function accumulateTermScore(docScores, internalId, termScore, collect, fieldNam
|
|
|
3200
3558
|
function searchFulltext(state, params) {
|
|
3201
3559
|
const {
|
|
3202
3560
|
queryTokens,
|
|
3561
|
+
prefixExpansion,
|
|
3203
3562
|
fields,
|
|
3204
3563
|
boost,
|
|
3205
3564
|
tolerance = 0,
|
|
@@ -3224,9 +3583,132 @@ function searchFulltext(state, params) {
|
|
|
3224
3583
|
const useIntersection = termMatch === "all" && queryTokens.length > 1;
|
|
3225
3584
|
const fieldNames = state.fieldNameTable.names;
|
|
3226
3585
|
const resolver = state.docStore.resolver();
|
|
3586
|
+
function resolveFieldLength(internalId, fieldName, avgLen) {
|
|
3587
|
+
let cachedLengths = fieldLengthCache.get(internalId);
|
|
3588
|
+
if (cachedLengths === void 0) {
|
|
3589
|
+
const externalId = resolver.toExternal(internalId);
|
|
3590
|
+
const storedDoc = externalId !== void 0 ? state.docStore.get(externalId) : void 0;
|
|
3591
|
+
cachedLengths = storedDoc?.fieldLengths ?? null;
|
|
3592
|
+
fieldLengthCache.set(internalId, cachedLengths);
|
|
3593
|
+
}
|
|
3594
|
+
return cachedLengths?.[fieldName] ?? avgLen;
|
|
3595
|
+
}
|
|
3596
|
+
function resolvePrefixMatches(token, expansionTerms) {
|
|
3597
|
+
const found = [];
|
|
3598
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3599
|
+
for (const term of [token, ...expansionTerms]) {
|
|
3600
|
+
if (seen.has(term)) continue;
|
|
3601
|
+
seen.add(term);
|
|
3602
|
+
const postingList = state.invertedIdx.lookup(term);
|
|
3603
|
+
if (!postingList) continue;
|
|
3604
|
+
const docFreq = globalStats ? globalDocFreqs[term] ?? postingList.docIdSet.size : postingList.docIdSet.size;
|
|
3605
|
+
found.push({ token: term, postingList, docFreq });
|
|
3606
|
+
}
|
|
3607
|
+
if (found.length === 0) return [];
|
|
3608
|
+
let blendedDf = 0;
|
|
3609
|
+
for (const f of found) {
|
|
3610
|
+
if (f.docFreq > blendedDf) blendedDf = f.docFreq;
|
|
3611
|
+
}
|
|
3612
|
+
const blendedIdf = computeIDF(blendedDf, totalDocs);
|
|
3613
|
+
return found.map((f) => ({
|
|
3614
|
+
token: f.token,
|
|
3615
|
+
factor: Math.min(1, token.length / f.token.length),
|
|
3616
|
+
postingList: f.postingList,
|
|
3617
|
+
docFreq: blendedDf,
|
|
3618
|
+
idf: blendedIdf
|
|
3619
|
+
}));
|
|
3620
|
+
}
|
|
3621
|
+
function computePrefixContributions(matches, collect) {
|
|
3622
|
+
const best = /* @__PURE__ */ new Map();
|
|
3623
|
+
for (const match of matches) {
|
|
3624
|
+
const perTerm = /* @__PURE__ */ new Map();
|
|
3625
|
+
const list = match.postingList;
|
|
3626
|
+
const hasDeleted = list.deletedDocs.size > 0;
|
|
3627
|
+
for (let pi = 0; pi < list.length; pi++) {
|
|
3628
|
+
const internalId = list.docIds[pi];
|
|
3629
|
+
if (hasDeleted && list.deletedDocs.has(internalId)) continue;
|
|
3630
|
+
if (filterBitset && !bitsetHas(filterBitset, internalId)) continue;
|
|
3631
|
+
const fieldName = fieldNames[list.fieldNameIndices[pi]];
|
|
3632
|
+
if (fields && !fields.includes(fieldName)) continue;
|
|
3633
|
+
const termFrequency = list.termFrequencies[pi];
|
|
3634
|
+
const fieldBoost = boost?.[fieldName] ?? 1;
|
|
3635
|
+
const avgLen = avgFieldLengths[fieldName] ?? 1;
|
|
3636
|
+
const actualFieldLength = resolveFieldLength(internalId, fieldName, avgLen);
|
|
3637
|
+
const termScore = scoreFn(termFrequency, match.docFreq, totalDocs, actualFieldLength, avgLen, bm25Params) * fieldBoost * match.factor;
|
|
3638
|
+
const existing = perTerm.get(internalId);
|
|
3639
|
+
if (existing) {
|
|
3640
|
+
existing.score += termScore;
|
|
3641
|
+
if (collect) {
|
|
3642
|
+
existing.termFrequencies[`${fieldName}:${match.token}`] = termFrequency;
|
|
3643
|
+
existing.fieldLengths[fieldName] = actualFieldLength;
|
|
3644
|
+
}
|
|
3645
|
+
} else if (collect) {
|
|
3646
|
+
perTerm.set(internalId, {
|
|
3647
|
+
score: termScore,
|
|
3648
|
+
token: match.token,
|
|
3649
|
+
idf: match.idf,
|
|
3650
|
+
termFrequencies: { [`${fieldName}:${match.token}`]: termFrequency },
|
|
3651
|
+
fieldLengths: { [fieldName]: actualFieldLength }
|
|
3652
|
+
});
|
|
3653
|
+
} else {
|
|
3654
|
+
perTerm.set(internalId, {
|
|
3655
|
+
score: termScore,
|
|
3656
|
+
token: match.token,
|
|
3657
|
+
idf: match.idf,
|
|
3658
|
+
termFrequencies: EMPTY_COMPONENTS,
|
|
3659
|
+
fieldLengths: EMPTY_COMPONENTS
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
for (const [internalId, contribution] of perTerm) {
|
|
3664
|
+
const current = best.get(internalId);
|
|
3665
|
+
if (!current || contribution.score > current.score) {
|
|
3666
|
+
best.set(internalId, contribution);
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
return best;
|
|
3671
|
+
}
|
|
3672
|
+
function mergePrefixContribution(internalId, contribution, collect) {
|
|
3673
|
+
const existing = docScores.get(internalId);
|
|
3674
|
+
if (existing) {
|
|
3675
|
+
existing.score += contribution.score;
|
|
3676
|
+
if (collect) {
|
|
3677
|
+
Object.assign(existing.termFrequencies, contribution.termFrequencies);
|
|
3678
|
+
Object.assign(existing.fieldLengths, contribution.fieldLengths);
|
|
3679
|
+
existing.idf[contribution.token] = contribution.idf;
|
|
3680
|
+
}
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
if (collect) {
|
|
3684
|
+
docScores.set(internalId, {
|
|
3685
|
+
score: contribution.score,
|
|
3686
|
+
termFrequencies: contribution.termFrequencies,
|
|
3687
|
+
fieldLengths: contribution.fieldLengths,
|
|
3688
|
+
idf: { [contribution.token]: contribution.idf }
|
|
3689
|
+
});
|
|
3690
|
+
} else {
|
|
3691
|
+
docScores.set(internalId, {
|
|
3692
|
+
score: contribution.score,
|
|
3693
|
+
termFrequencies: EMPTY_COMPONENTS,
|
|
3694
|
+
fieldLengths: EMPTY_COMPONENTS,
|
|
3695
|
+
idf: EMPTY_COMPONENTS
|
|
3696
|
+
});
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3227
3699
|
if (useIntersection) {
|
|
3228
3700
|
const resolved = [];
|
|
3701
|
+
let prefixMatches = [];
|
|
3229
3702
|
for (const qt of queryTokens) {
|
|
3703
|
+
if (prefixExpansion && qt.token === prefixExpansion.token) {
|
|
3704
|
+
prefixMatches = resolvePrefixMatches(qt.token, prefixExpansion.terms);
|
|
3705
|
+
let totalPostings2 = 0;
|
|
3706
|
+
for (const m of prefixMatches) {
|
|
3707
|
+
totalPostings2 += m.postingList.length;
|
|
3708
|
+
}
|
|
3709
|
+
resolved.push({ token: qt.token, matches: [], totalPostings: totalPostings2, isPrefix: true });
|
|
3710
|
+
continue;
|
|
3711
|
+
}
|
|
3230
3712
|
const rawMatches = exact ? (() => {
|
|
3231
3713
|
const postingList = state.invertedIdx.lookup(qt.token);
|
|
3232
3714
|
return postingList ? [{ token: qt.token, postingList }] : [];
|
|
@@ -3243,6 +3725,14 @@ function searchFulltext(state, params) {
|
|
|
3243
3725
|
}
|
|
3244
3726
|
resolved.sort((a, b) => a.totalPostings - b.totalPostings);
|
|
3245
3727
|
for (let tokenIndex = 0; tokenIndex < resolved.length; tokenIndex++) {
|
|
3728
|
+
if (resolved[tokenIndex].isPrefix) {
|
|
3729
|
+
const contributions = computePrefixContributions(prefixMatches, collectComponents);
|
|
3730
|
+
for (const [internalId, contribution] of contributions) {
|
|
3731
|
+
if (tokenIndex > 0 && !docScores.has(internalId)) continue;
|
|
3732
|
+
mergePrefixContribution(internalId, contribution, collectComponents);
|
|
3733
|
+
}
|
|
3734
|
+
continue;
|
|
3735
|
+
}
|
|
3246
3736
|
for (const match of resolved[tokenIndex].matches) {
|
|
3247
3737
|
const list = match.postingList;
|
|
3248
3738
|
const hasDeleted = list.deletedDocs.size > 0;
|
|
@@ -3282,6 +3772,16 @@ function searchFulltext(state, params) {
|
|
|
3282
3772
|
}
|
|
3283
3773
|
} else {
|
|
3284
3774
|
for (const qt of queryTokens) {
|
|
3775
|
+
if (prefixExpansion && qt.token === prefixExpansion.token) {
|
|
3776
|
+
const contributions = computePrefixContributions(
|
|
3777
|
+
resolvePrefixMatches(qt.token, prefixExpansion.terms),
|
|
3778
|
+
collectComponents
|
|
3779
|
+
);
|
|
3780
|
+
for (const [internalId, contribution] of contributions) {
|
|
3781
|
+
mergePrefixContribution(internalId, contribution, collectComponents);
|
|
3782
|
+
}
|
|
3783
|
+
continue;
|
|
3784
|
+
}
|
|
3285
3785
|
const matchingPostings = exact ? (() => {
|
|
3286
3786
|
const postingList = state.invertedIdx.lookup(qt.token);
|
|
3287
3787
|
return postingList ? [{ token: qt.token, postingList }] : [];
|
|
@@ -3468,6 +3968,7 @@ function serializePartition(state, partitionId, indexName, totalPartitions, lang
|
|
|
3468
3968
|
enum: serializedEnum,
|
|
3469
3969
|
geopoint: serializedGeo
|
|
3470
3970
|
},
|
|
3971
|
+
surfaceForms: state.surfaceRegistry.serialize(),
|
|
3471
3972
|
statistics: serializedStats
|
|
3472
3973
|
};
|
|
3473
3974
|
}
|
|
@@ -3527,6 +4028,7 @@ function deserializePartition(state, data, clearFn, schema) {
|
|
|
3527
4028
|
geoIdx.deserialize(converted);
|
|
3528
4029
|
state.geoIndexes.set(path, geoIdx);
|
|
3529
4030
|
}
|
|
4031
|
+
state.surfaceRegistry.deserialize(data.surfaceForms ?? {});
|
|
3530
4032
|
state.stats.deserialize(data.statistics);
|
|
3531
4033
|
const flatSchema = getFlatSchema(state, schema);
|
|
3532
4034
|
const serializedFields = data.schema;
|
|
@@ -3544,6 +4046,72 @@ function deserializePartition(state, data, clearFn, schema) {
|
|
|
3544
4046
|
}
|
|
3545
4047
|
}
|
|
3546
4048
|
|
|
4049
|
+
// src/core/partition/suggestions.ts
|
|
4050
|
+
function verbatimOccurrences(state, token) {
|
|
4051
|
+
const postingList = state.invertedIdx.lookup(token);
|
|
4052
|
+
if (!postingList) return 0;
|
|
4053
|
+
const derived = postingList.totalTermFrequency - state.surfaceRegistry.stemChangedTotalFor(token);
|
|
4054
|
+
return derived > 0 ? derived : 0;
|
|
4055
|
+
}
|
|
4056
|
+
function suggestDisplayTerms(state, surfacePrefix, stemmedPrefix, limit) {
|
|
4057
|
+
if (limit <= 0 || surfacePrefix.length === 0) return [];
|
|
4058
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4059
|
+
for (const candidate of state.surfaceRegistry.candidatesForPrefix(surfacePrefix)) {
|
|
4060
|
+
const postingList = state.invertedIdx.lookup(candidate.token);
|
|
4061
|
+
if (!postingList || postingList.docIdSet.size === 0) continue;
|
|
4062
|
+
let group = groups.get(candidate.token);
|
|
4063
|
+
if (!group) {
|
|
4064
|
+
group = { documentFrequency: postingList.docIdSet.size, surfaces: [] };
|
|
4065
|
+
groups.set(candidate.token, group);
|
|
4066
|
+
}
|
|
4067
|
+
group.surfaces.push({ surface: candidate.surface, occurrences: candidate.occurrences });
|
|
4068
|
+
}
|
|
4069
|
+
const termPrefixes = state.surfaceRegistry.size() === 0 && stemmedPrefix !== surfacePrefix && stemmedPrefix.length > 0 ? [surfacePrefix, stemmedPrefix] : [surfacePrefix];
|
|
4070
|
+
for (const prefix of termPrefixes) {
|
|
4071
|
+
for (const suggestion of state.invertedIdx.prefixSearch(prefix, Number.MAX_SAFE_INTEGER)) {
|
|
4072
|
+
const occurrences = verbatimOccurrences(state, suggestion.term);
|
|
4073
|
+
if (occurrences === 0) continue;
|
|
4074
|
+
let group = groups.get(suggestion.term);
|
|
4075
|
+
if (!group) {
|
|
4076
|
+
group = { documentFrequency: suggestion.documentFrequency, surfaces: [] };
|
|
4077
|
+
groups.set(suggestion.term, group);
|
|
4078
|
+
} else if (group.surfaces.some((s) => s.surface === suggestion.term)) {
|
|
4079
|
+
continue;
|
|
4080
|
+
}
|
|
4081
|
+
group.surfaces.push({ surface: suggestion.term, occurrences });
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
const results = [];
|
|
4085
|
+
for (const [token, group] of groups) {
|
|
4086
|
+
results.push({ token, documentFrequency: group.documentFrequency, surfaces: group.surfaces });
|
|
4087
|
+
}
|
|
4088
|
+
results.sort((a, b) => b.documentFrequency - a.documentFrequency || (a.token < b.token ? -1 : 1));
|
|
4089
|
+
if (results.length > limit) results.length = limit;
|
|
4090
|
+
return results;
|
|
4091
|
+
}
|
|
4092
|
+
function expandTermPrefix(state, surfacePrefix, stemmedToken, maxExpansions) {
|
|
4093
|
+
if (maxExpansions <= 0 || surfacePrefix.length === 0) return [];
|
|
4094
|
+
const dfByToken = /* @__PURE__ */ new Map();
|
|
4095
|
+
for (const candidate of state.surfaceRegistry.candidatesForPrefix(surfacePrefix)) {
|
|
4096
|
+
if (dfByToken.has(candidate.token)) continue;
|
|
4097
|
+
const postingList = state.invertedIdx.lookup(candidate.token);
|
|
4098
|
+
if (!postingList || postingList.docIdSet.size === 0) continue;
|
|
4099
|
+
dfByToken.set(candidate.token, postingList.docIdSet.size);
|
|
4100
|
+
}
|
|
4101
|
+
const termPrefixes = state.surfaceRegistry.size() === 0 && stemmedToken !== surfacePrefix && stemmedToken.length > 0 ? [surfacePrefix, stemmedToken] : [surfacePrefix];
|
|
4102
|
+
for (const prefix of termPrefixes) {
|
|
4103
|
+
for (const suggestion of state.invertedIdx.prefixSearch(prefix, Number.MAX_SAFE_INTEGER)) {
|
|
4104
|
+
if (dfByToken.has(suggestion.term)) continue;
|
|
4105
|
+
if (verbatimOccurrences(state, suggestion.term) === 0) continue;
|
|
4106
|
+
dfByToken.set(suggestion.term, suggestion.documentFrequency);
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
const entries = Array.from(dfByToken.entries());
|
|
4110
|
+
entries.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1));
|
|
4111
|
+
if (entries.length > maxExpansions) entries.length = maxExpansions;
|
|
4112
|
+
return entries.map((e) => e[0]);
|
|
4113
|
+
}
|
|
4114
|
+
|
|
3547
4115
|
// src/core/partition/wire-payload.ts
|
|
3548
4116
|
function serializePartitionToWirePayloadV2(state, partitionId, indexName, totalPartitions, language, schema) {
|
|
3549
4117
|
const flatSchema = getFlatSchema(state, schema);
|
|
@@ -3640,6 +4208,7 @@ function serializePartitionToWirePayloadV2(state, partitionId, indexName, totalP
|
|
|
3640
4208
|
enum: wireEnum,
|
|
3641
4209
|
geopoint: wireGeo
|
|
3642
4210
|
},
|
|
4211
|
+
surface_forms: state.surfaceRegistry.serialize(),
|
|
3643
4212
|
statistics: {
|
|
3644
4213
|
total_documents: serializedStats.totalDocuments,
|
|
3645
4214
|
total_field_lengths: serializedStats.totalFieldLengths,
|
|
@@ -3656,6 +4225,7 @@ function createPartitionIndex(partitionId, trackPositions = true) {
|
|
|
3656
4225
|
invertedIdx: createInvertedIndex(fieldNameTable),
|
|
3657
4226
|
docStore: createDocumentStore(),
|
|
3658
4227
|
stats: createPartitionStats(),
|
|
4228
|
+
surfaceRegistry: createSurfaceRegistry(),
|
|
3659
4229
|
numericIndexes: /* @__PURE__ */ new Map(),
|
|
3660
4230
|
booleanIndexes: /* @__PURE__ */ new Map(),
|
|
3661
4231
|
enumIndexes: /* @__PURE__ */ new Map(),
|
|
@@ -3668,6 +4238,7 @@ function createPartitionIndex(partitionId, trackPositions = true) {
|
|
|
3668
4238
|
function clearAll() {
|
|
3669
4239
|
state.invertedIdx.clear();
|
|
3670
4240
|
state.docStore.clear();
|
|
4241
|
+
state.surfaceRegistry.clear();
|
|
3671
4242
|
for (const idx of state.numericIndexes.values()) idx.clear();
|
|
3672
4243
|
for (const idx of state.booleanIndexes.values()) idx.clear();
|
|
3673
4244
|
for (const idx of state.enumIndexes.values()) idx.clear();
|
|
@@ -3818,6 +4389,8 @@ function createPartitionIndex(partitionId, trackPositions = true) {
|
|
|
3818
4389
|
}
|
|
3819
4390
|
bytes += totalPostings * POSTING_ENTRY_SIZE;
|
|
3820
4391
|
bytes += termCount * PER_TERM_OVERHEAD;
|
|
4392
|
+
const SURFACE_ENTRY_OVERHEAD = 140;
|
|
4393
|
+
bytes += state.surfaceRegistry.size() * SURFACE_ENTRY_OVERHEAD;
|
|
3821
4394
|
const FIELD_ENTRY_OVERHEAD = 42;
|
|
3822
4395
|
bytes += docCount * state.numericIndexes.size * FIELD_ENTRY_OVERHEAD;
|
|
3823
4396
|
bytes += docCount * state.booleanIndexes.size * FIELD_ENTRY_OVERHEAD;
|
|
@@ -3837,8 +4410,11 @@ function createPartitionIndex(partitionId, trackPositions = true) {
|
|
|
3837
4410
|
computeFacets(docIds, config, schema) {
|
|
3838
4411
|
return computeFacets(state, docIds, config, schema);
|
|
3839
4412
|
},
|
|
3840
|
-
suggestTerms(
|
|
3841
|
-
return state
|
|
4413
|
+
suggestTerms(surfacePrefix, stemmedPrefix, limit) {
|
|
4414
|
+
return suggestDisplayTerms(state, surfacePrefix, stemmedPrefix, limit);
|
|
4415
|
+
},
|
|
4416
|
+
expandTermPrefix(surfacePrefix, stemmedToken, maxExpansions) {
|
|
4417
|
+
return expandTermPrefix(state, surfacePrefix, stemmedToken, maxExpansions);
|
|
3842
4418
|
},
|
|
3843
4419
|
serialize(indexName, totalPartitions, language, schema) {
|
|
3844
4420
|
return serializePartition(state, partitionId, indexName, totalPartitions, language, schema);
|
|
@@ -3899,9 +4475,15 @@ function createRebalancer() {
|
|
|
3899
4475
|
});
|
|
3900
4476
|
const newPartitions = [];
|
|
3901
4477
|
for (let i = 0; i < newPartitionCount; i++) {
|
|
3902
|
-
newPartitions.push(createPartitionIndex(i));
|
|
4478
|
+
newPartitions.push(createPartitionIndex(i, manager.config.trackPositions ?? true));
|
|
3903
4479
|
}
|
|
3904
4480
|
let documentsProcessed = 0;
|
|
4481
|
+
const insertOptions = {
|
|
4482
|
+
validate: false,
|
|
4483
|
+
stopWordOverride: manager.config.stopWords,
|
|
4484
|
+
customTokenizer: manager.config.tokenizer,
|
|
4485
|
+
collectSurfaces: manager.config.surfaceForms === true
|
|
4486
|
+
};
|
|
3905
4487
|
for (let chunkStart = 0; chunkStart < collectedDocs.length; chunkStart += CHUNK_SIZE) {
|
|
3906
4488
|
const chunkEnd = Math.min(chunkStart + CHUNK_SIZE, collectedDocs.length);
|
|
3907
4489
|
for (let i = chunkStart; i < chunkEnd; i++) {
|
|
@@ -3911,7 +4493,7 @@ function createRebalancer() {
|
|
|
3911
4493
|
if (targetPartition.has(docId)) {
|
|
3912
4494
|
continue;
|
|
3913
4495
|
}
|
|
3914
|
-
targetPartition.insert(docId, document, manager.schema, manager.language,
|
|
4496
|
+
targetPartition.insert(docId, document, manager.schema, manager.language, insertOptions);
|
|
3915
4497
|
documentsProcessed++;
|
|
3916
4498
|
}
|
|
3917
4499
|
onProgress?.({
|
|
@@ -4168,6 +4750,36 @@ function mergeFacets(partitionFacets) {
|
|
|
4168
4750
|
}
|
|
4169
4751
|
|
|
4170
4752
|
// src/search/fulltext.ts
|
|
4753
|
+
var PREFIX_MAX_EXPANSIONS = 50;
|
|
4754
|
+
function resolvePrefixExpansion(partition, params, language, options, queryTokens, lastToken) {
|
|
4755
|
+
const term = params.term;
|
|
4756
|
+
if (term === void 0) return void 0;
|
|
4757
|
+
const unstemmed = tokenize(term, language, {
|
|
4758
|
+
stem: false,
|
|
4759
|
+
removeStopWords: true,
|
|
4760
|
+
customTokenizer: options?.customTokenizer,
|
|
4761
|
+
stopWordOverride: options?.stopWords
|
|
4762
|
+
});
|
|
4763
|
+
let surfacePrefix;
|
|
4764
|
+
for (const t of unstemmed.tokens) {
|
|
4765
|
+
if (t.position === lastToken.position) {
|
|
4766
|
+
surfacePrefix = t.token;
|
|
4767
|
+
break;
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
if (surfacePrefix === void 0 || surfacePrefix.length === 0) return void 0;
|
|
4771
|
+
const expansions = partition.expandTermPrefix(surfacePrefix, lastToken.token, PREFIX_MAX_EXPANSIONS);
|
|
4772
|
+
const terms = [];
|
|
4773
|
+
for (const expanded of expansions) {
|
|
4774
|
+
if (expanded !== lastToken.token) terms.push(expanded);
|
|
4775
|
+
}
|
|
4776
|
+
for (const qt of queryTokens) {
|
|
4777
|
+
if (qt.token === lastToken.token) {
|
|
4778
|
+
return { token: qt.token, terms };
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
return void 0;
|
|
4782
|
+
}
|
|
4171
4783
|
function fulltextSearch(partition, params, language, schema, options) {
|
|
4172
4784
|
if (!params.term || params.term.trim().length === 0) {
|
|
4173
4785
|
return { scored: [], totalMatched: 0 };
|
|
@@ -4189,6 +4801,11 @@ function fulltextSearch(partition, params, language, schema, options) {
|
|
|
4189
4801
|
return { scored: [], totalMatched: 0 };
|
|
4190
4802
|
}
|
|
4191
4803
|
const queryTokens = deduplicateTokens(queryTokenResult.tokens);
|
|
4804
|
+
let prefixExpansion;
|
|
4805
|
+
if (params.prefix === true && params.exact !== true) {
|
|
4806
|
+
const lastToken = queryTokenResult.tokens[queryTokenResult.tokens.length - 1];
|
|
4807
|
+
prefixExpansion = resolvePrefixExpansion(partition, params, language, options, queryTokens, lastToken);
|
|
4808
|
+
}
|
|
4192
4809
|
let filterBitset;
|
|
4193
4810
|
if (params.filters) {
|
|
4194
4811
|
filterBitset = partition.applyFiltersBitset(params.filters, schema);
|
|
@@ -4202,6 +4819,7 @@ function fulltextSearch(partition, params, language, schema, options) {
|
|
|
4202
4819
|
const collectComponents = params.includeScoreComponents === true || params.termMatch !== void 0 && params.termMatch !== "any";
|
|
4203
4820
|
const rawResult = partition.searchFulltext({
|
|
4204
4821
|
queryTokens,
|
|
4822
|
+
prefixExpansion,
|
|
4205
4823
|
fields: params.fields,
|
|
4206
4824
|
boost: params.boost,
|
|
4207
4825
|
tolerance: params.tolerance ?? 0,
|
|
@@ -4217,7 +4835,14 @@ function fulltextSearch(partition, params, language, schema, options) {
|
|
|
4217
4835
|
let scored = rawResult.scored;
|
|
4218
4836
|
const termMatch = params.termMatch ?? "any";
|
|
4219
4837
|
if (termMatch !== "any") {
|
|
4220
|
-
scored = filterByTermCoverage(
|
|
4838
|
+
scored = filterByTermCoverage(
|
|
4839
|
+
scored,
|
|
4840
|
+
queryTokens,
|
|
4841
|
+
termMatch,
|
|
4842
|
+
params.tolerance ?? 0,
|
|
4843
|
+
params.exact ?? false,
|
|
4844
|
+
prefixExpansion
|
|
4845
|
+
);
|
|
4221
4846
|
}
|
|
4222
4847
|
if (params.minScore !== void 0 && params.minScore > 0) {
|
|
4223
4848
|
const threshold = params.minScore;
|
|
@@ -4252,26 +4877,40 @@ function deduplicateTokens(tokens) {
|
|
|
4252
4877
|
}
|
|
4253
4878
|
return result;
|
|
4254
4879
|
}
|
|
4255
|
-
function filterByTermCoverage(scored, queryTokens, policy, tolerance, exact) {
|
|
4880
|
+
function filterByTermCoverage(scored, queryTokens, policy, tolerance, exact, prefixExpansion) {
|
|
4256
4881
|
const requiredCount = policy === "all" ? queryTokens.length : policy;
|
|
4257
4882
|
if (requiredCount <= 0) return scored;
|
|
4258
4883
|
if (requiredCount > queryTokens.length) return [];
|
|
4884
|
+
const expansionTerms = prefixExpansion ? new Set(prefixExpansion.terms) : void 0;
|
|
4259
4885
|
return scored.filter((doc) => {
|
|
4260
|
-
const matched = countDocTermMatches(doc, queryTokens, tolerance, exact);
|
|
4886
|
+
const matched = countDocTermMatches(doc, queryTokens, tolerance, exact, prefixExpansion?.token, expansionTerms);
|
|
4261
4887
|
return matched >= requiredCount;
|
|
4262
4888
|
});
|
|
4263
4889
|
}
|
|
4264
|
-
function countDocTermMatches(doc, queryTokens, tolerance, exact) {
|
|
4890
|
+
function countDocTermMatches(doc, queryTokens, tolerance, exact, prefixToken, expansionTerms) {
|
|
4265
4891
|
const indexTokens = Object.keys(doc.idf);
|
|
4266
4892
|
if (indexTokens.length === 0) return 0;
|
|
4267
4893
|
let count = 0;
|
|
4268
4894
|
for (const qt of queryTokens) {
|
|
4895
|
+
if (prefixToken !== void 0 && qt.token === prefixToken) {
|
|
4896
|
+
if (prefixTokenSatisfied(qt.token, indexTokens, expansionTerms)) {
|
|
4897
|
+
count++;
|
|
4898
|
+
}
|
|
4899
|
+
continue;
|
|
4900
|
+
}
|
|
4269
4901
|
if (queryTermSatisfied(qt.token, indexTokens, tolerance, exact)) {
|
|
4270
4902
|
count++;
|
|
4271
4903
|
}
|
|
4272
4904
|
}
|
|
4273
4905
|
return count;
|
|
4274
4906
|
}
|
|
4907
|
+
function prefixTokenSatisfied(prefixToken, indexTokens, expansionTerms) {
|
|
4908
|
+
for (const indexToken of indexTokens) {
|
|
4909
|
+
if (indexToken === prefixToken) return true;
|
|
4910
|
+
if (expansionTerms?.has(indexToken)) return true;
|
|
4911
|
+
}
|
|
4912
|
+
return false;
|
|
4913
|
+
}
|
|
4275
4914
|
function queryTermSatisfied(queryToken, indexTokens, tolerance, exact) {
|
|
4276
4915
|
if (exact || tolerance === 0) {
|
|
4277
4916
|
return indexTokens.includes(queryToken);
|
|
@@ -4531,13 +5170,15 @@ function createPartitionManager(indexName, config, language, router, initialPart
|
|
|
4531
5170
|
function resolveInsertOptions(options) {
|
|
4532
5171
|
const applyStrict = config.strict === true;
|
|
4533
5172
|
const applyAnalyzer = config.stopWords !== void 0 || config.tokenizer !== void 0;
|
|
4534
|
-
|
|
5173
|
+
const applySurfaces = config.surfaceForms === true;
|
|
5174
|
+
if (!applyStrict && !applyAnalyzer && !applySurfaces) return options;
|
|
4535
5175
|
const resolved = { ...options };
|
|
4536
5176
|
if (applyStrict) resolved.strict = true;
|
|
4537
5177
|
if (applyAnalyzer) {
|
|
4538
5178
|
resolved.stopWordOverride = options?.stopWordOverride ?? config.stopWords;
|
|
4539
5179
|
resolved.customTokenizer = options?.customTokenizer ?? config.tokenizer;
|
|
4540
5180
|
}
|
|
5181
|
+
if (applySurfaces) resolved.collectSurfaces = true;
|
|
4541
5182
|
return resolved;
|
|
4542
5183
|
}
|
|
4543
5184
|
function rebuildDocPartitionMap() {
|
|
@@ -4634,7 +5275,7 @@ function createPartitionManager(indexName, config, language, router, initialPart
|
|
|
4634
5275
|
if (pid === void 0) {
|
|
4635
5276
|
throw new NarsilError(ErrorCodes.DOC_NOT_FOUND, `Document "${docId}" not found in any partition`, { docId });
|
|
4636
5277
|
}
|
|
4637
|
-
partitions[pid].remove(docId, config.schema, language);
|
|
5278
|
+
partitions[pid].remove(docId, config.schema, language, resolveInsertOptions());
|
|
4638
5279
|
docPartitionMap.delete(docId);
|
|
4639
5280
|
},
|
|
4640
5281
|
beginBatchRemove() {
|
|
@@ -7625,141 +8266,6 @@ function readHeader(data) {
|
|
|
7625
8266
|
reserved
|
|
7626
8267
|
};
|
|
7627
8268
|
}
|
|
7628
|
-
var VALID_HNSW_METRICS2 = /* @__PURE__ */ new Set(["cosine", "dotProduct", "euclidean"]);
|
|
7629
|
-
function validateHnswMetric2(value) {
|
|
7630
|
-
if (value === void 0 || value === null) return void 0;
|
|
7631
|
-
if (typeof value === "string" && VALID_HNSW_METRICS2.has(value)) {
|
|
7632
|
-
return value;
|
|
7633
|
-
}
|
|
7634
|
-
return void 0;
|
|
7635
|
-
}
|
|
7636
|
-
function wireToPartition(raw) {
|
|
7637
|
-
const documents = {};
|
|
7638
|
-
for (const [docId, doc] of Object.entries(raw.documents ?? {})) {
|
|
7639
|
-
documents[docId] = {
|
|
7640
|
-
fields: doc.fields,
|
|
7641
|
-
fieldLengths: doc.field_lengths ?? {}
|
|
7642
|
-
};
|
|
7643
|
-
}
|
|
7644
|
-
const invertedIndex = {};
|
|
7645
|
-
for (const [token, list] of Object.entries(raw.inverted_index ?? {})) {
|
|
7646
|
-
invertedIndex[token] = {
|
|
7647
|
-
docFrequency: list.doc_freq,
|
|
7648
|
-
postings: (list.postings ?? []).map((p) => ({
|
|
7649
|
-
docId: p.doc_id,
|
|
7650
|
-
termFrequency: p.term_freq,
|
|
7651
|
-
field: p.field,
|
|
7652
|
-
positions: p.positions ?? []
|
|
7653
|
-
}))
|
|
7654
|
-
};
|
|
7655
|
-
}
|
|
7656
|
-
const numeric = {};
|
|
7657
|
-
for (const [field, entries] of Object.entries(raw.field_indexes?.numeric ?? {})) {
|
|
7658
|
-
numeric[field] = entries.map((e) => ({ value: e.value, docId: e.doc_id }));
|
|
7659
|
-
}
|
|
7660
|
-
const boolean = {};
|
|
7661
|
-
for (const [field, idx] of Object.entries(raw.field_indexes?.boolean ?? {})) {
|
|
7662
|
-
boolean[field] = { trueDocs: idx.true_docs ?? [], falseDocs: idx.false_docs ?? [] };
|
|
7663
|
-
}
|
|
7664
|
-
const geopoint = {};
|
|
7665
|
-
for (const [field, entries] of Object.entries(raw.field_indexes?.geopoint ?? {})) {
|
|
7666
|
-
geopoint[field] = entries.map((e) => ({ lat: e.lat, lon: e.lon, docId: e.doc_id }));
|
|
7667
|
-
}
|
|
7668
|
-
const vectorData = {};
|
|
7669
|
-
for (const [field, data] of Object.entries(raw.vector_data ?? {})) {
|
|
7670
|
-
vectorData[field] = {
|
|
7671
|
-
dimension: data.dimension,
|
|
7672
|
-
vectors: (data.vectors ?? []).map((v) => ({ docId: v.doc_id, vector: v.vector })),
|
|
7673
|
-
hnswGraph: data.hnsw_graph ? {
|
|
7674
|
-
entryPoint: data.hnsw_graph.entry_point,
|
|
7675
|
-
maxLayer: data.hnsw_graph.max_layer,
|
|
7676
|
-
m: data.hnsw_graph.m,
|
|
7677
|
-
efConstruction: data.hnsw_graph.ef_construction,
|
|
7678
|
-
metric: validateHnswMetric2(data.hnsw_graph.metric),
|
|
7679
|
-
nodes: data.hnsw_graph.nodes
|
|
7680
|
-
} : null,
|
|
7681
|
-
sq8: data.sq8 ? {
|
|
7682
|
-
alpha: data.sq8.alpha,
|
|
7683
|
-
offset: data.sq8.offset,
|
|
7684
|
-
quantizedVectors: data.sq8.quantized_vectors,
|
|
7685
|
-
vectorSums: data.sq8.vector_sums,
|
|
7686
|
-
vectorSumSqs: data.sq8.vector_sum_sqs
|
|
7687
|
-
} : null
|
|
7688
|
-
};
|
|
7689
|
-
}
|
|
7690
|
-
return {
|
|
7691
|
-
indexName: raw.index_name,
|
|
7692
|
-
partitionId: raw.partition_id,
|
|
7693
|
-
totalPartitions: raw.total_partitions,
|
|
7694
|
-
language: raw.language ?? "english",
|
|
7695
|
-
schema: raw.schema ?? {},
|
|
7696
|
-
docCount: raw.doc_count ?? 0,
|
|
7697
|
-
avgDocLength: raw.avg_doc_length ?? 0,
|
|
7698
|
-
documents,
|
|
7699
|
-
invertedIndex,
|
|
7700
|
-
fieldIndexes: {
|
|
7701
|
-
numeric,
|
|
7702
|
-
boolean,
|
|
7703
|
-
enum: raw.field_indexes?.enum ?? {},
|
|
7704
|
-
geopoint
|
|
7705
|
-
},
|
|
7706
|
-
vectorData,
|
|
7707
|
-
statistics: {
|
|
7708
|
-
totalDocuments: raw.statistics?.total_documents ?? 0,
|
|
7709
|
-
totalFieldLengths: raw.statistics?.total_field_lengths ?? {},
|
|
7710
|
-
averageFieldLengths: raw.statistics?.average_field_lengths ?? {},
|
|
7711
|
-
docFrequencies: raw.statistics?.doc_frequencies ?? {}
|
|
7712
|
-
}
|
|
7713
|
-
};
|
|
7714
|
-
}
|
|
7715
|
-
function deserializePayloadV1(data) {
|
|
7716
|
-
const raw = decode(data);
|
|
7717
|
-
return wireToPartition(raw);
|
|
7718
|
-
}
|
|
7719
|
-
function metadataToWire(meta) {
|
|
7720
|
-
const wire = {
|
|
7721
|
-
index_name: meta.indexName,
|
|
7722
|
-
schema: meta.schema,
|
|
7723
|
-
language: meta.language,
|
|
7724
|
-
partition_count: meta.partitionCount,
|
|
7725
|
-
bm25_params: meta.bm25Params,
|
|
7726
|
-
created_at: meta.createdAt,
|
|
7727
|
-
engine_version: meta.engineVersion
|
|
7728
|
-
};
|
|
7729
|
-
if (meta.vectorFields) {
|
|
7730
|
-
wire.vector_fields = meta.vectorFields;
|
|
7731
|
-
}
|
|
7732
|
-
if (meta.embedding) {
|
|
7733
|
-
wire.embedding = meta.embedding.adapter !== void 0 ? { adapter: meta.embedding.adapter, fields: meta.embedding.fields } : { fields: meta.embedding.fields };
|
|
7734
|
-
}
|
|
7735
|
-
return wire;
|
|
7736
|
-
}
|
|
7737
|
-
function wireToMetadata(raw) {
|
|
7738
|
-
const meta = {
|
|
7739
|
-
indexName: raw.index_name,
|
|
7740
|
-
schema: raw.schema ?? {},
|
|
7741
|
-
language: raw.language ?? "english",
|
|
7742
|
-
partitionCount: raw.partition_count ?? 1,
|
|
7743
|
-
bm25Params: raw.bm25_params ?? { k1: 1.2, b: 0.75 },
|
|
7744
|
-
createdAt: raw.created_at ?? 0,
|
|
7745
|
-
engineVersion: raw.engine_version ?? "0.0.0"
|
|
7746
|
-
};
|
|
7747
|
-
if (raw.vector_fields) {
|
|
7748
|
-
meta.vectorFields = raw.vector_fields;
|
|
7749
|
-
}
|
|
7750
|
-
if (raw.embedding && typeof raw.embedding === "object" && typeof raw.embedding.fields === "object") {
|
|
7751
|
-
meta.embedding = typeof raw.embedding.adapter === "string" ? { adapter: raw.embedding.adapter, fields: raw.embedding.fields } : { fields: raw.embedding.fields };
|
|
7752
|
-
}
|
|
7753
|
-
return meta;
|
|
7754
|
-
}
|
|
7755
|
-
function serializeMetadata(meta) {
|
|
7756
|
-
const wire = metadataToWire(meta);
|
|
7757
|
-
return encode(wire);
|
|
7758
|
-
}
|
|
7759
|
-
function deserializeMetadata(data) {
|
|
7760
|
-
const raw = decode(data);
|
|
7761
|
-
return wireToMetadata(raw);
|
|
7762
|
-
}
|
|
7763
8269
|
|
|
7764
8270
|
// src/serialization/envelope.ts
|
|
7765
8271
|
var CURRENT_ENVELOPE_VERSION = 1;
|
|
@@ -8775,6 +9281,7 @@ function mergeTimeOrderedSegments(ordered, fallback) {
|
|
|
8775
9281
|
collectDocuments(merged, part, winner, segIndex);
|
|
8776
9282
|
collectInvertedIndex(merged, part, winner, segIndex);
|
|
8777
9283
|
collectFieldIndexes(merged, part, winner, segIndex);
|
|
9284
|
+
collectSurfaceForms(merged, part);
|
|
8778
9285
|
}
|
|
8779
9286
|
recomputeStatistics(merged);
|
|
8780
9287
|
validateMergedPartition(merged);
|
|
@@ -8902,6 +9409,26 @@ function collectFieldIndexes(merged, part, winner, segIndex) {
|
|
|
8902
9409
|
}
|
|
8903
9410
|
}
|
|
8904
9411
|
}
|
|
9412
|
+
function collectSurfaceForms(merged, part) {
|
|
9413
|
+
if (!part.surfaceForms) return;
|
|
9414
|
+
let target = merged.surfaceForms;
|
|
9415
|
+
if (!target) {
|
|
9416
|
+
target = /* @__PURE__ */ Object.create(null);
|
|
9417
|
+
merged.surfaceForms = target;
|
|
9418
|
+
}
|
|
9419
|
+
for (const [surface, value] of Object.entries(part.surfaceForms)) {
|
|
9420
|
+
const count = typeof value === "number" ? value : value[0];
|
|
9421
|
+
const token = typeof value === "number" ? surface : value[1];
|
|
9422
|
+
const existing = target[surface];
|
|
9423
|
+
if (existing === void 0) {
|
|
9424
|
+
target[surface] = value;
|
|
9425
|
+
continue;
|
|
9426
|
+
}
|
|
9427
|
+
const existingCount = typeof existing === "number" ? existing : existing[0];
|
|
9428
|
+
const total = existingCount + count;
|
|
9429
|
+
target[surface] = token === surface ? total : [total, token];
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
8905
9432
|
function recomputeStatistics(merged) {
|
|
8906
9433
|
const totalFieldLengths = /* @__PURE__ */ Object.create(null);
|
|
8907
9434
|
let totalDocuments = 0;
|
|
@@ -9112,6 +9639,9 @@ function reconstructSchemaFromMetadata(metadata) {
|
|
|
9112
9639
|
if (metadata.embedding) {
|
|
9113
9640
|
config.embedding = metadata.embedding.adapter !== void 0 ? { adapter: metadata.embedding.adapter, fields: metadata.embedding.fields } : { fields: metadata.embedding.fields };
|
|
9114
9641
|
}
|
|
9642
|
+
if (metadata.surfaceForms === true) {
|
|
9643
|
+
config.surfaceForms = true;
|
|
9644
|
+
}
|
|
9115
9645
|
return config;
|
|
9116
9646
|
}
|
|
9117
9647
|
|
|
@@ -10572,6 +11102,9 @@ function buildMetadata(indexName, hooks) {
|
|
|
10572
11102
|
if (config.embedding) {
|
|
10573
11103
|
metadata.embedding = config.embedding;
|
|
10574
11104
|
}
|
|
11105
|
+
if (config.surfaceForms) {
|
|
11106
|
+
metadata.surfaceForms = true;
|
|
11107
|
+
}
|
|
10575
11108
|
return metadata;
|
|
10576
11109
|
}
|
|
10577
11110
|
function createDurabilityIntegration(tier, hooks) {
|
|
@@ -11102,7 +11635,8 @@ function createDurabilityFromConfig(config, wiring) {
|
|
|
11102
11635
|
language: entry.language.name,
|
|
11103
11636
|
k1: entry.config.bm25?.k1 ?? 1.2,
|
|
11104
11637
|
b: entry.config.bm25?.b ?? 0.75,
|
|
11105
|
-
...embedding !== void 0 ? { embedding } : {}
|
|
11638
|
+
...embedding !== void 0 ? { embedding } : {},
|
|
11639
|
+
...entry.config.surfaceForms === true ? { surfaceForms: true } : {}
|
|
11106
11640
|
};
|
|
11107
11641
|
},
|
|
11108
11642
|
createIndexFromMetadata: wiring.createIndexFromMetadata,
|
|
@@ -12586,10 +13120,15 @@ function highlightField(text, queryTokens, language, options) {
|
|
|
12586
13120
|
return qt.token.toLowerCase();
|
|
12587
13121
|
});
|
|
12588
13122
|
const matchedRanges = [];
|
|
13123
|
+
const prefixToken = options?.prefixToken;
|
|
12589
13124
|
for (let i = 0; i < fieldResult.tokens.length; i++) {
|
|
12590
13125
|
if (i >= charOffsets.length) break;
|
|
12591
13126
|
const fieldToken = fieldResult.tokens[i].token;
|
|
12592
13127
|
const stemmedField = language.stemmer ? language.stemmer(fieldToken) : fieldToken;
|
|
13128
|
+
if (prefixToken !== void 0 && fieldToken.startsWith(prefixToken)) {
|
|
13129
|
+
matchedRanges.push({ start: charOffsets[i].start, end: charOffsets[i].end });
|
|
13130
|
+
continue;
|
|
13131
|
+
}
|
|
12593
13132
|
for (const stemmedQuery of stemmedQueryTokens) {
|
|
12594
13133
|
if (stemmedField === stemmedQuery) {
|
|
12595
13134
|
matchedRanges.push({ start: charOffsets[i].start, end: charOffsets[i].end });
|
|
@@ -12633,6 +13172,22 @@ function applyHighlights(hits, params, language, config) {
|
|
|
12633
13172
|
customTokenizer: config.tokenizer,
|
|
12634
13173
|
stopWordOverride: config.stopWords
|
|
12635
13174
|
});
|
|
13175
|
+
let prefixToken;
|
|
13176
|
+
if (params.prefix === true && params.exact !== true && queryTokenResult.tokens.length > 0) {
|
|
13177
|
+
const lastPosition = queryTokenResult.tokens[queryTokenResult.tokens.length - 1].position;
|
|
13178
|
+
const unstemmed = tokenize(params.term ?? "", language, {
|
|
13179
|
+
stem: false,
|
|
13180
|
+
removeStopWords: true,
|
|
13181
|
+
customTokenizer: config.tokenizer,
|
|
13182
|
+
stopWordOverride: config.stopWords
|
|
13183
|
+
});
|
|
13184
|
+
for (const t of unstemmed.tokens) {
|
|
13185
|
+
if (t.position === lastPosition) {
|
|
13186
|
+
prefixToken = t.token;
|
|
13187
|
+
break;
|
|
13188
|
+
}
|
|
13189
|
+
}
|
|
13190
|
+
}
|
|
12636
13191
|
for (const hit of hits) {
|
|
12637
13192
|
const highlights = {};
|
|
12638
13193
|
for (const field of params.highlight.fields) {
|
|
@@ -12642,7 +13197,8 @@ function applyHighlights(hits, params, language, config) {
|
|
|
12642
13197
|
highlights[field] = highlightField(fieldValue, queryTokenResult.tokens, language, {
|
|
12643
13198
|
preTag: params.highlight.preTag,
|
|
12644
13199
|
postTag: params.highlight.postTag,
|
|
12645
|
-
maxSnippetLength: params.highlight.maxSnippetLength
|
|
13200
|
+
maxSnippetLength: params.highlight.maxSnippetLength,
|
|
13201
|
+
prefixToken
|
|
12646
13202
|
});
|
|
12647
13203
|
}
|
|
12648
13204
|
}
|
|
@@ -13291,25 +13847,36 @@ function executeSuggest(manager, language, params) {
|
|
|
13291
13847
|
return { terms: [], elapsed: performance.now() - t0 };
|
|
13292
13848
|
}
|
|
13293
13849
|
const stemmed = language.stemmer ? language.stemmer(lastToken) : lastToken;
|
|
13294
|
-
const prefixes = stemmed !== lastToken ? [lastToken, stemmed] : [lastToken];
|
|
13295
|
-
const partitions = manager.getAllPartitions();
|
|
13296
13850
|
const merged = /* @__PURE__ */ new Map();
|
|
13297
13851
|
const perPartitionLimit = limit * 2;
|
|
13298
|
-
for (const partition of
|
|
13299
|
-
const
|
|
13300
|
-
|
|
13301
|
-
|
|
13302
|
-
|
|
13303
|
-
|
|
13304
|
-
|
|
13305
|
-
|
|
13852
|
+
for (const partition of manager.getAllPartitions()) {
|
|
13853
|
+
for (const suggestion of partition.suggestTerms(lastToken, stemmed, perPartitionLimit)) {
|
|
13854
|
+
let entry = merged.get(suggestion.token);
|
|
13855
|
+
if (!entry) {
|
|
13856
|
+
entry = { documentFrequency: 0, surfaceOccurrences: /* @__PURE__ */ new Map() };
|
|
13857
|
+
merged.set(suggestion.token, entry);
|
|
13858
|
+
}
|
|
13859
|
+
entry.documentFrequency += suggestion.documentFrequency;
|
|
13860
|
+
for (const s of suggestion.surfaces) {
|
|
13861
|
+
entry.surfaceOccurrences.set(s.surface, (entry.surfaceOccurrences.get(s.surface) ?? 0) + s.occurrences);
|
|
13306
13862
|
}
|
|
13307
13863
|
}
|
|
13308
13864
|
}
|
|
13309
|
-
const terms = Array.from(merged.
|
|
13865
|
+
const terms = Array.from(merged.values()).map((entry) => ({ term: pickDisplaySurface(entry.surfaceOccurrences), documentFrequency: entry.documentFrequency })).sort((a, b) => b.documentFrequency - a.documentFrequency || (a.term < b.term ? -1 : 1));
|
|
13310
13866
|
if (terms.length > limit) terms.length = limit;
|
|
13311
13867
|
return { terms, elapsed: performance.now() - t0 };
|
|
13312
13868
|
}
|
|
13869
|
+
function pickDisplaySurface(surfaceOccurrences) {
|
|
13870
|
+
let best = "";
|
|
13871
|
+
let bestCount = -1;
|
|
13872
|
+
for (const [surface, occurrences] of surfaceOccurrences) {
|
|
13873
|
+
if (occurrences > bestCount || occurrences === bestCount && surface < best) {
|
|
13874
|
+
best = surface;
|
|
13875
|
+
bestCount = occurrences;
|
|
13876
|
+
}
|
|
13877
|
+
}
|
|
13878
|
+
return best;
|
|
13879
|
+
}
|
|
13313
13880
|
|
|
13314
13881
|
// src/engine/vector-maintenance.ts
|
|
13315
13882
|
function resolveVectorIndexes(manager, indexName, fieldName) {
|