@delali/narsil 0.3.1-next.71 → 0.3.1-next.73
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 +2 -2
- package/dist/adapters/filesystem.mjs +1 -1
- package/dist/adapters/indexeddb.mjs +1 -1
- package/dist/{capabilities-Dt-7B4Wz.d.ts → capabilities-CYeAWYxQ.d.ts} +19 -4
- package/dist/{chunk-3JGWULDF.mjs → chunk-2WLD2NFW.mjs} +5 -5
- package/dist/{chunk-LMO5TJUS.mjs → chunk-3YKWSO54.mjs} +642 -196
- package/dist/{chunk-HWQLTLNG.mjs → chunk-4MQ5F4IL.mjs} +1 -1
- package/dist/{chunk-FVLQ63B7.mjs → chunk-AFKH4ZK3.mjs} +30 -6
- package/dist/{chunk-653BNMAF.mjs → chunk-AK3BLGCI.mjs} +195 -25
- package/dist/{chunk-IOR2K26O.mjs → chunk-CVFAFZSN.mjs} +8000 -4391
- package/dist/{chunk-USKFU7J5.mjs → chunk-I64LXKO3.mjs} +1 -1
- package/dist/{chunk-OI42GWLR.mjs → chunk-LJYTW7GT.mjs} +57 -11
- package/dist/{chunk-2STVUTNA.mjs → chunk-LPBWCHBE.mjs} +194 -13
- package/dist/{chunk-YNVIUGF5.mjs → chunk-PU43WDY4.mjs} +4 -2
- package/dist/{chunk-M76V3OGJ.mjs → chunk-THMSFOJO.mjs} +1289 -1405
- package/dist/{chunk-YJDQ7NLA.mjs → chunk-WVDFG45M.mjs} +93 -95
- package/dist/chunk-XW25X6AU.mjs +614 -0
- package/dist/chunk-ZKRV5M2P.mjs +75 -0
- package/dist/{chunk-P4DSQ6M4.mjs → chunk-ZULSEZCE.mjs} +68 -28
- package/dist/{client-COek5GXK.d.ts → client-D698E5ge.d.ts} +77 -26
- package/dist/client.d.ts +9 -9
- package/dist/client.mjs +45 -22
- package/dist/{config-rBpbgTw8.d.ts → config-Bv8etwUZ.d.ts} +11 -4
- package/dist/{config-Dfanyqqt.d.ts → config-QXU3HMpq.d.ts} +26 -2
- package/dist/direct-executor-5SRDFZAS.mjs +13 -0
- package/dist/distribution/coordinator/etcd.d.ts +29 -16
- package/dist/distribution/coordinator/etcd.mjs +163 -76
- package/dist/distribution/coordinator/in-memory.d.ts +2 -2
- package/dist/distribution/coordinator/in-memory.mjs +1 -1
- package/dist/distribution/transport/grpc.d.ts +2 -2
- package/dist/distribution/transport/grpc.mjs +22 -2
- package/dist/distribution/transport/in-memory.d.ts +2 -2
- package/dist/distribution/transport/in-memory.mjs +6 -3
- package/dist/distribution/transport/tcp.d.ts +2 -2
- package/dist/distribution/transport/tcp.mjs +40 -16
- package/dist/distribution.d.ts +9 -9
- package/dist/distribution.mjs +682 -388
- package/dist/embeddings/openai.mjs +1 -1
- package/dist/{engine-igk0aMpQ.d.ts → engine-D_OzKZ-6.d.ts} +97 -188
- package/dist/index.browser.mjs +11671 -7427
- package/dist/index.d.ts +27 -8
- package/dist/index.mjs +13 -14
- package/dist/invalidation/filesystem.mjs +1 -1
- package/dist/{memory-C7UMCMH3.d.ts → memory-B4jMmygw.d.ts} +279 -27
- package/dist/pattern-Cledccbl.d.ts +18 -0
- package/dist/pattern.d.ts +22 -0
- package/dist/pattern.mjs +7 -0
- package/dist/persistence/durability/checkpoint-worker.d.ts +41 -17
- package/dist/persistence/durability/checkpoint-worker.mjs +24 -16
- package/dist/react.d.ts +7 -7
- package/dist/react.mjs +1 -1
- package/dist/{schema-Bgzr4g7b.d.ts → schema-BBOCdhND.d.ts} +9 -7
- package/dist/server.d.ts +11 -11
- package/dist/server.mjs +213 -34
- package/dist/{tasks-BZt_zes6.d.ts → tasks-DpgCpW1x.d.ts} +3 -3
- package/dist/{thread-JEVAYETR.mjs → thread-3LNVA43V.mjs} +94 -27
- package/dist/{types-BQDlIvfS.d.ts → types-B3b68nfF.d.ts} +1 -1
- package/dist/{types-gOUqhs2F.d.ts → types-BiUmaiWT.d.ts} +1 -1
- package/dist/{types-D5OwhtsQ.d.ts → types-Cb9kiY5P.d.ts} +1 -1
- package/dist/vector/search-worker.d.ts +1 -1
- package/dist/vector/search-worker.mjs +4 -4
- package/dist/workers/entry.mjs +4 -4
- package/package.json +15 -7
- package/dist/chunk-3L4U6EJQ.mjs +0 -49
- package/dist/chunk-GIVAGD3Q.mjs +0 -25
- package/dist/chunk-IFBKLBGB.mjs +0 -7
- package/dist/direct-executor-GYJEKNV5.mjs +0 -12
- package/dist/serialization/crc32-worker.d.ts +0 -20
- package/dist/serialization/crc32-worker.mjs +0 -62
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { getLanguage, detectRuntime } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import { schemaFieldsOf, isWordIndexedBase, validateDocument, validateDocumentStrict,
|
|
4
|
-
import { SCRATCH_SLOTS_PER_THREAD_POOL, osqRecordBytes, openSharedVectorField, osqBitsOf, createVectorStore, DISK_STORAGE_BLOCK_BYTES, createOsqQuantizer, magnitude, createHNSWIndex, nativeStoreScratchBytes, createOrdinalFilter, isTombstoned, addToOrdinalFilter, ordinalFilterValues, nativeScoresOf, toScore, removeFromOrdinalFilter, euclideanDistance, dotProduct, cosineSimilarityWithMagnitudes, ordinalFilterHas,
|
|
5
|
-
import { noteFallback } from './chunk-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import { getLanguage, detectRuntime } from './chunk-4MQ5F4IL.mjs';
|
|
2
|
+
import { tokenizeOptions, getNestedValue, visitTokens, joinPatternWorkMeter, countsWithoutScores, fanOutMatchCount, fanOutQuery, sanitizeGlobalStats, computeFacets, cloneProjected, textFieldsChanged, projectionKeepsField, setNestedValue, fnv1a, fuzzyPrefixOf, boundedLevenshtein, getFlatSchema, tokenize, getOrCreateFieldNameIndex, tokenizeIterator, everyValueFacetConfig, mergeFacets, getFieldValueByInternalId, undeclaredFilterField, requireValidOperands, kWayMerge, polygonContainment, polygonBoxes, radiusBoxes, locationCode, coveringRanges, GEO_UNIT_METRES } from './chunk-3YKWSO54.mjs';
|
|
3
|
+
import { schemaFieldsOf, isWordIndexedBase, validateDocument, validateDocumentStrict, encodeDocument, extractVectorFieldsFromSchema, MAX_DOC_ID_LENGTH, parsedTypeOf, isListBase, VINCENTY_CONVERGENCE_THRESHOLD, EARTH_RADIUS_METERS, VINCENTY_MAX_ITERATIONS, DEG_TO_RAD } from './chunk-AK3BLGCI.mjs';
|
|
4
|
+
import { SCRATCH_SLOTS_PER_THREAD_POOL, osqRecordBytes, openSharedVectorField, osqBitsOf, createVectorStore, DISK_STORAGE_BLOCK_BYTES, createOsqQuantizer, magnitude, createHNSWIndex, nativeStoreScratchBytes, createOrdinalFilter, isTombstoned, addToOrdinalFilter, ordinalFilterValues, nativeScoresOf, toScore, removeFromOrdinalFilter, euclideanDistance, dotProduct, cosineSimilarityWithMagnitudes, ordinalFilterHas, STORE_CALIBRATION_GENERATION, entryForOrd, toDistance, sharedMemoryAvailable, requireWholeNeighbourLists, topLayerInParts, GRAPH_BUILD_CHUNKS_IN_FLIGHT_PER_WORKER, GRAPH_BUILD_DISPATCH_CHUNK, releaseLocksHeldBy, VECTOR_SEARCH_TIMEOUT_MS, VECTOR_SEARCH_LOAD_TIMEOUT_MS, VECTOR_INSERT_TIMEOUT_MS, VECTOR_SCRATCH_SLOTS } from './chunk-WVDFG45M.mjs';
|
|
5
|
+
import { MAX_M, noteFallback, EARLIER_RELEASE_MIN_M, MIN_M } from './chunk-PU43WDY4.mjs';
|
|
6
|
+
import { GrowableUint32, patternSearchModule } from './chunk-XW25X6AU.mjs';
|
|
7
|
+
import { bitsetHas, createBitSet, bitsetSet, bitsetOrInto, bitsetAndNotInto, bitsetAndInto, PATTERN_RUN_CODE_POINTS, bitsetFromSet, bitsetIsEmpty, bitsetAnd, bitsetNot } from './chunk-ZKRV5M2P.mjs';
|
|
8
|
+
import { compareCodePoints, appendFoldedCodePoint, toReducedSortValue, readSortField, defaultSortMode, compareComparableValues, truncateSortString, SORT_VALUE_MAX_CODE_POINTS } from './chunk-LJYTW7GT.mjs';
|
|
9
|
+
import { NarsilError, ErrorCodes } from './chunk-I64LXKO3.mjs';
|
|
9
10
|
import { totalmem } from 'os';
|
|
10
11
|
import { getHeapStatistics } from 'v8';
|
|
11
|
-
import {
|
|
12
|
+
import { decode } from '@msgpack/msgpack';
|
|
12
13
|
|
|
13
14
|
var MEGABYTE_BYTES = 1048576;
|
|
14
15
|
var IN_MEGABYTES = /^--max-old-space-size=(\d+)$/;
|
|
@@ -116,478 +117,6 @@ function resolveIndexAnalysis(config) {
|
|
|
116
117
|
customTokenizer: typeof config.tokenizer === "string" ? getTokenizer(config.tokenizer) : config.tokenizer
|
|
117
118
|
};
|
|
118
119
|
}
|
|
119
|
-
function metadataToWire(meta) {
|
|
120
|
-
const wire = {
|
|
121
|
-
index_name: meta.indexName,
|
|
122
|
-
schema: meta.schema,
|
|
123
|
-
language: meta.language,
|
|
124
|
-
partition_count: meta.partitionCount,
|
|
125
|
-
bm25_params: meta.bm25Params,
|
|
126
|
-
created_at: meta.createdAt,
|
|
127
|
-
engine_version: meta.engineVersion
|
|
128
|
-
};
|
|
129
|
-
if (meta.vectorFields) {
|
|
130
|
-
wire.vector_fields = meta.vectorFields;
|
|
131
|
-
}
|
|
132
|
-
if (isNonNegativeInteger(meta.documentCount) && Number.isSafeInteger(meta.documentCount)) {
|
|
133
|
-
wire.document_count = meta.documentCount;
|
|
134
|
-
}
|
|
135
|
-
if (meta.embedding) {
|
|
136
|
-
wire.embedding = meta.embedding.adapter !== void 0 ? { adapter: meta.embedding.adapter, fields: meta.embedding.fields } : { fields: meta.embedding.fields };
|
|
137
|
-
}
|
|
138
|
-
if (meta.surfaceForms !== void 0) {
|
|
139
|
-
wire.surface_forms_enabled = meta.surfaceForms;
|
|
140
|
-
}
|
|
141
|
-
if (meta.analysisRevision !== void 0) {
|
|
142
|
-
wire.analysis_revision = meta.analysisRevision;
|
|
143
|
-
}
|
|
144
|
-
if (meta.tokenizer !== void 0) {
|
|
145
|
-
wire.tokenizer = meta.tokenizer;
|
|
146
|
-
}
|
|
147
|
-
if (meta.stopWords !== void 0) {
|
|
148
|
-
wire.stop_words = meta.stopWords;
|
|
149
|
-
}
|
|
150
|
-
if (meta.stopWordList !== void 0) {
|
|
151
|
-
wire.stop_word_list = meta.stopWordList;
|
|
152
|
-
}
|
|
153
|
-
if (meta.partitionLimits !== void 0) {
|
|
154
|
-
wire.partition_limits = {
|
|
155
|
-
...meta.partitionLimits.maxDocsPerPartition !== void 0 ? { max_docs_per_partition: meta.partitionLimits.maxDocsPerPartition } : {},
|
|
156
|
-
...meta.partitionLimits.maxPartitions !== void 0 ? { max_partitions: meta.partitionLimits.maxPartitions } : {},
|
|
157
|
-
...meta.partitionLimits.watermark !== void 0 ? { watermark: meta.partitionLimits.watermark } : {}
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
if (meta.defaultScoring !== void 0) {
|
|
161
|
-
wire.default_scoring = meta.defaultScoring;
|
|
162
|
-
}
|
|
163
|
-
if (meta.trackPositions !== void 0) {
|
|
164
|
-
wire.track_positions = meta.trackPositions;
|
|
165
|
-
}
|
|
166
|
-
if (meta.strict !== void 0) {
|
|
167
|
-
wire.strict = meta.strict;
|
|
168
|
-
}
|
|
169
|
-
if (meta.required !== void 0) {
|
|
170
|
-
wire.required = meta.required;
|
|
171
|
-
}
|
|
172
|
-
if (meta.vectorPromotion !== void 0) {
|
|
173
|
-
const promotion = meta.vectorPromotion;
|
|
174
|
-
wire.vector_promotion = {
|
|
175
|
-
...promotion.threshold !== void 0 ? { threshold: promotion.threshold } : {},
|
|
176
|
-
...promotion.filterThreshold !== void 0 ? { filter_threshold: promotion.filterThreshold } : {},
|
|
177
|
-
...promotion.hnswConfig !== void 0 ? {
|
|
178
|
-
hnsw_config: {
|
|
179
|
-
...promotion.hnswConfig.m !== void 0 ? { m: promotion.hnswConfig.m } : {},
|
|
180
|
-
...promotion.hnswConfig.efConstruction !== void 0 ? { ef_construction: promotion.hnswConfig.efConstruction } : {},
|
|
181
|
-
...promotion.hnswConfig.metric !== void 0 ? { metric: promotion.hnswConfig.metric } : {}
|
|
182
|
-
}
|
|
183
|
-
} : {},
|
|
184
|
-
...promotion.quantization !== void 0 ? { quantization: promotion.quantization } : {},
|
|
185
|
-
...promotion.storage !== void 0 ? { storage: promotion.storage } : {}
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
if (meta.patternValueLimit !== void 0) {
|
|
189
|
-
wire.pattern_value_limit = meta.patternValueLimit;
|
|
190
|
-
}
|
|
191
|
-
if (meta.indexUuid !== void 0) {
|
|
192
|
-
wire.index_uuid = meta.indexUuid;
|
|
193
|
-
}
|
|
194
|
-
if (meta.heldPartitions !== void 0) {
|
|
195
|
-
wire.held_partitions = meta.heldPartitions;
|
|
196
|
-
}
|
|
197
|
-
return wire;
|
|
198
|
-
}
|
|
199
|
-
function wireToMetadata(raw) {
|
|
200
|
-
const meta = {
|
|
201
|
-
indexName: raw.index_name,
|
|
202
|
-
schema: raw.schema ?? {},
|
|
203
|
-
language: raw.language ?? "english",
|
|
204
|
-
partitionCount: raw.partition_count ?? 1,
|
|
205
|
-
bm25Params: raw.bm25_params ?? { k1: 1.2, b: 0.75 },
|
|
206
|
-
createdAt: raw.created_at ?? 0,
|
|
207
|
-
engineVersion: raw.engine_version ?? "0.0.0"
|
|
208
|
-
};
|
|
209
|
-
if (raw.vector_fields) {
|
|
210
|
-
for (const [fieldPath, field] of Object.entries(raw.vector_fields)) {
|
|
211
|
-
if (!isQuantizationMode(field.quantization)) {
|
|
212
|
-
throw new NarsilError(
|
|
213
|
-
ErrorCodes.CONFIG_INVALID,
|
|
214
|
-
`Index metadata names a vector quantization "${String(field.quantization)}" for "${fieldPath}" this engine does not read`,
|
|
215
|
-
{ fieldPath, quantization: field.quantization }
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
meta.vectorFields = raw.vector_fields;
|
|
220
|
-
}
|
|
221
|
-
if (isNonNegativeInteger(raw.document_count) && Number.isSafeInteger(raw.document_count)) {
|
|
222
|
-
meta.documentCount = raw.document_count;
|
|
223
|
-
}
|
|
224
|
-
if (raw.embedding && typeof raw.embedding === "object" && typeof raw.embedding.fields === "object") {
|
|
225
|
-
meta.embedding = typeof raw.embedding.adapter === "string" ? { adapter: raw.embedding.adapter, fields: raw.embedding.fields } : { fields: raw.embedding.fields };
|
|
226
|
-
}
|
|
227
|
-
if (typeof raw.surface_forms_enabled === "boolean") {
|
|
228
|
-
meta.surfaceForms = raw.surface_forms_enabled;
|
|
229
|
-
}
|
|
230
|
-
if (typeof raw.analysis_revision === "string") {
|
|
231
|
-
meta.analysisRevision = raw.analysis_revision;
|
|
232
|
-
}
|
|
233
|
-
if (typeof raw.tokenizer === "string") {
|
|
234
|
-
meta.tokenizer = raw.tokenizer;
|
|
235
|
-
}
|
|
236
|
-
if (typeof raw.stop_words === "string") {
|
|
237
|
-
meta.stopWords = raw.stop_words;
|
|
238
|
-
}
|
|
239
|
-
if (Array.isArray(raw.stop_word_list) && raw.stop_word_list.every((word) => typeof word === "string")) {
|
|
240
|
-
meta.stopWordList = raw.stop_word_list;
|
|
241
|
-
}
|
|
242
|
-
if (raw.partition_limits && typeof raw.partition_limits === "object") {
|
|
243
|
-
const limits = {
|
|
244
|
-
...isPositiveInteger(raw.partition_limits.max_docs_per_partition) ? { maxDocsPerPartition: raw.partition_limits.max_docs_per_partition } : {},
|
|
245
|
-
...isPositiveInteger(raw.partition_limits.max_partitions) ? { maxPartitions: raw.partition_limits.max_partitions } : {},
|
|
246
|
-
...isWatermarkFraction(raw.partition_limits.watermark) ? { watermark: raw.partition_limits.watermark } : {}
|
|
247
|
-
};
|
|
248
|
-
if (Object.keys(limits).length > 0) {
|
|
249
|
-
meta.partitionLimits = limits;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (raw.default_scoring === "local" || raw.default_scoring === "dfs" || raw.default_scoring === "broadcast") {
|
|
253
|
-
meta.defaultScoring = raw.default_scoring;
|
|
254
|
-
}
|
|
255
|
-
if (typeof raw.track_positions === "boolean") {
|
|
256
|
-
meta.trackPositions = raw.track_positions;
|
|
257
|
-
}
|
|
258
|
-
if (typeof raw.strict === "boolean") {
|
|
259
|
-
meta.strict = raw.strict;
|
|
260
|
-
}
|
|
261
|
-
if (Array.isArray(raw.required) && raw.required.every((field) => typeof field === "string")) {
|
|
262
|
-
meta.required = raw.required;
|
|
263
|
-
}
|
|
264
|
-
if (raw.vector_promotion && typeof raw.vector_promotion === "object") {
|
|
265
|
-
const promotion = raw.vector_promotion;
|
|
266
|
-
const hnsw = promotion.hnsw_config && typeof promotion.hnsw_config === "object" ? promotion.hnsw_config : void 0;
|
|
267
|
-
const metric = hnsw?.metric;
|
|
268
|
-
const restored = {};
|
|
269
|
-
const hnswConfig = {};
|
|
270
|
-
if (isPositiveInteger(hnsw?.m)) {
|
|
271
|
-
hnswConfig.m = hnsw?.m;
|
|
272
|
-
}
|
|
273
|
-
if (isPositiveInteger(hnsw?.ef_construction)) {
|
|
274
|
-
hnswConfig.efConstruction = hnsw?.ef_construction;
|
|
275
|
-
}
|
|
276
|
-
if (metric === "cosine" || metric === "dotProduct" || metric === "euclidean") {
|
|
277
|
-
hnswConfig.metric = metric;
|
|
278
|
-
}
|
|
279
|
-
if (isPositiveInteger(promotion.threshold)) {
|
|
280
|
-
restored.threshold = promotion.threshold;
|
|
281
|
-
}
|
|
282
|
-
if (isSelectivityRatio(promotion.filter_threshold)) {
|
|
283
|
-
restored.filterThreshold = promotion.filter_threshold;
|
|
284
|
-
}
|
|
285
|
-
if (Object.keys(hnswConfig).length > 0) {
|
|
286
|
-
restored.hnswConfig = hnswConfig;
|
|
287
|
-
}
|
|
288
|
-
if (promotion.quantization !== void 0) {
|
|
289
|
-
if (!isQuantizationMode(promotion.quantization)) {
|
|
290
|
-
throw new NarsilError(
|
|
291
|
-
ErrorCodes.CONFIG_INVALID,
|
|
292
|
-
`Index metadata names a vector quantization "${String(promotion.quantization)}" this engine does not read`,
|
|
293
|
-
{ quantization: promotion.quantization }
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
restored.quantization = promotion.quantization;
|
|
297
|
-
}
|
|
298
|
-
if (promotion.storage !== void 0) {
|
|
299
|
-
if (!isStorageMode(promotion.storage)) {
|
|
300
|
-
throw new NarsilError(
|
|
301
|
-
ErrorCodes.CONFIG_INVALID,
|
|
302
|
-
`Index metadata names a vector storage "${String(promotion.storage)}" this engine does not read`,
|
|
303
|
-
{ storage: promotion.storage }
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
restored.storage = promotion.storage;
|
|
307
|
-
}
|
|
308
|
-
if (Object.keys(restored).length > 0) {
|
|
309
|
-
meta.vectorPromotion = restored;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
if (raw.pattern_value_limit !== void 0) {
|
|
313
|
-
if (!isPositiveInteger(raw.pattern_value_limit)) {
|
|
314
|
-
throw new NarsilError(
|
|
315
|
-
ErrorCodes.CONFIG_INVALID,
|
|
316
|
-
`The pattern value limit in the index metadata is ${String(raw.pattern_value_limit)}, but the limit must be a whole number of code points`,
|
|
317
|
-
{ patternValueLimit: String(raw.pattern_value_limit) }
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
meta.patternValueLimit = raw.pattern_value_limit;
|
|
321
|
-
}
|
|
322
|
-
if (typeof raw.index_uuid === "string" && raw.index_uuid.length > 0) {
|
|
323
|
-
meta.indexUuid = raw.index_uuid;
|
|
324
|
-
}
|
|
325
|
-
if (Array.isArray(raw.held_partitions) && raw.held_partitions.every(isNonNegativeInteger)) {
|
|
326
|
-
meta.heldPartitions = raw.held_partitions;
|
|
327
|
-
}
|
|
328
|
-
return meta;
|
|
329
|
-
}
|
|
330
|
-
function isNonNegativeInteger(value) {
|
|
331
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
332
|
-
}
|
|
333
|
-
function isPositiveInteger(value) {
|
|
334
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
335
|
-
}
|
|
336
|
-
function isSelectivityRatio(value) {
|
|
337
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
|
|
338
|
-
}
|
|
339
|
-
function isWatermarkFraction(value) {
|
|
340
|
-
return typeof value === "number" && value > 0 && value <= 1;
|
|
341
|
-
}
|
|
342
|
-
function serializeMetadata(meta) {
|
|
343
|
-
const wire = metadataToWire(meta);
|
|
344
|
-
return encode(wire);
|
|
345
|
-
}
|
|
346
|
-
function deserializeMetadata(data) {
|
|
347
|
-
const raw = decode(data);
|
|
348
|
-
return wireToMetadata(raw);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// src/serialization/stored-vector-values.ts
|
|
352
|
-
function dropStoredVectorValues(documents, schema) {
|
|
353
|
-
const vectorFieldPaths = [];
|
|
354
|
-
for (const [fieldPath, fieldType] of Object.entries(schema)) {
|
|
355
|
-
if (typeof fieldType === "string" && VECTOR_PATTERN.test(fieldType)) {
|
|
356
|
-
vectorFieldPaths.push(fieldPath);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (vectorFieldPaths.length === 0) {
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
for (const document of Object.values(documents)) {
|
|
363
|
-
for (const fieldPath of vectorFieldPaths) {
|
|
364
|
-
removeFieldAtPath(document.fields, fieldPath);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
function removeFieldAtPath(fields, fieldPath) {
|
|
369
|
-
if (!fieldPath.includes(".")) {
|
|
370
|
-
delete fields[fieldPath];
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
const segments = fieldPath.split(".");
|
|
374
|
-
let current = fields;
|
|
375
|
-
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
376
|
-
const next = current[segments[i]];
|
|
377
|
-
if (next === null || typeof next !== "object") {
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
current = next;
|
|
381
|
-
}
|
|
382
|
-
delete current[segments[segments.length - 1]];
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// src/serialization/payload-v1/partition.ts
|
|
386
|
-
function sanitizeSurfaceForms(raw) {
|
|
387
|
-
if (raw === void 0 || raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
388
|
-
const result = /* @__PURE__ */ Object.create(null);
|
|
389
|
-
let count = 0;
|
|
390
|
-
for (const [surface, value] of Object.entries(raw)) {
|
|
391
|
-
if (typeof value === "number") {
|
|
392
|
-
result[surface] = value;
|
|
393
|
-
count++;
|
|
394
|
-
} else if (Array.isArray(value) && typeof value[0] === "number" && typeof value[1] === "string") {
|
|
395
|
-
result[surface] = [value[0], value[1]];
|
|
396
|
-
count++;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
return count > 0 ? result : void 0;
|
|
400
|
-
}
|
|
401
|
-
function validateHnswMetric(value) {
|
|
402
|
-
if (value === void 0 || value === null) return void 0;
|
|
403
|
-
if (typeof value === "string" && VALID_HNSW_METRICS.has(value)) {
|
|
404
|
-
return value;
|
|
405
|
-
}
|
|
406
|
-
return void 0;
|
|
407
|
-
}
|
|
408
|
-
function wireToPartition(raw) {
|
|
409
|
-
const documents = {};
|
|
410
|
-
for (const [docId, doc] of Object.entries(raw.documents ?? {})) {
|
|
411
|
-
documents[docId] = {
|
|
412
|
-
fields: doc.fields,
|
|
413
|
-
fieldLengths: doc.field_lengths ?? {}
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
requireValidFieldTypes(raw.schema ?? {});
|
|
417
|
-
dropStoredVectorValues(documents, raw.schema ?? {});
|
|
418
|
-
const invertedIndex = {};
|
|
419
|
-
for (const [token, list] of Object.entries(raw.inverted_index ?? {})) {
|
|
420
|
-
invertedIndex[token] = {
|
|
421
|
-
docFrequency: list.doc_freq,
|
|
422
|
-
postings: (list.postings ?? []).map((p) => ({
|
|
423
|
-
docId: p.doc_id,
|
|
424
|
-
termFrequency: p.term_freq,
|
|
425
|
-
field: p.field,
|
|
426
|
-
positions: p.positions ?? []
|
|
427
|
-
}))
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
const numeric = {};
|
|
431
|
-
for (const [field, entries] of Object.entries(raw.field_indexes?.numeric ?? {})) {
|
|
432
|
-
numeric[field] = entries.map((e) => ({ value: e.value, docId: e.doc_id }));
|
|
433
|
-
}
|
|
434
|
-
const boolean = {};
|
|
435
|
-
for (const [field, idx] of Object.entries(raw.field_indexes?.boolean ?? {})) {
|
|
436
|
-
boolean[field] = { trueDocs: idx.true_docs ?? [], falseDocs: idx.false_docs ?? [] };
|
|
437
|
-
}
|
|
438
|
-
const geopoint = {};
|
|
439
|
-
for (const [field, entries] of Object.entries(raw.field_indexes?.geopoint ?? {})) {
|
|
440
|
-
geopoint[field] = entries.map((e) => ({ lat: e.lat, lon: e.lon, docId: e.doc_id }));
|
|
441
|
-
}
|
|
442
|
-
const vectorData = {};
|
|
443
|
-
for (const [field, data] of Object.entries(raw.vector_data ?? {})) {
|
|
444
|
-
vectorData[field] = {
|
|
445
|
-
dimension: data.dimension,
|
|
446
|
-
vectors: (data.vectors ?? []).map((v) => ({ docId: v.doc_id, vector: v.vector })),
|
|
447
|
-
hnswGraph: data.hnsw_graph ? {
|
|
448
|
-
entryPoint: data.hnsw_graph.entry_point,
|
|
449
|
-
maxLayer: data.hnsw_graph.max_layer,
|
|
450
|
-
m: data.hnsw_graph.m,
|
|
451
|
-
efConstruction: data.hnsw_graph.ef_construction,
|
|
452
|
-
metric: validateHnswMetric(data.hnsw_graph.metric),
|
|
453
|
-
nodes: data.hnsw_graph.nodes
|
|
454
|
-
} : null,
|
|
455
|
-
codes: data.codes ? { bits: data.codes.bits, centroid: data.codes.centroid, records: data.codes.records } : null
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
return {
|
|
459
|
-
indexName: raw.index_name,
|
|
460
|
-
partitionId: raw.partition_id,
|
|
461
|
-
totalPartitions: raw.total_partitions,
|
|
462
|
-
language: raw.language ?? "english",
|
|
463
|
-
schema: raw.schema ?? {},
|
|
464
|
-
docCount: raw.doc_count ?? 0,
|
|
465
|
-
avgDocLength: raw.avg_doc_length ?? 0,
|
|
466
|
-
documents,
|
|
467
|
-
invertedIndex,
|
|
468
|
-
fieldIndexes: {
|
|
469
|
-
numeric,
|
|
470
|
-
boolean,
|
|
471
|
-
enum: raw.field_indexes?.enum ?? {},
|
|
472
|
-
geopoint
|
|
473
|
-
},
|
|
474
|
-
surfaceForms: sanitizeSurfaceForms(raw.surface_forms),
|
|
475
|
-
vectorData,
|
|
476
|
-
statistics: {
|
|
477
|
-
totalDocuments: raw.statistics?.total_documents ?? 0,
|
|
478
|
-
totalFieldLengths: raw.statistics?.total_field_lengths ?? {},
|
|
479
|
-
averageFieldLengths: raw.statistics?.average_field_lengths ?? {},
|
|
480
|
-
docFrequencies: raw.statistics?.doc_frequencies ?? {}
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
function deserializePayloadV1(data) {
|
|
485
|
-
const raw = decode(data);
|
|
486
|
-
return wireToPartition(raw);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// src/serialization/payload-v2.ts
|
|
490
|
-
function encodeRawPayloadV2(wire) {
|
|
491
|
-
return encode(wire);
|
|
492
|
-
}
|
|
493
|
-
function validateHnswMetric2(value) {
|
|
494
|
-
if (value === void 0 || value === null) return void 0;
|
|
495
|
-
if (typeof value === "string" && VALID_HNSW_METRICS.has(value)) {
|
|
496
|
-
return value;
|
|
497
|
-
}
|
|
498
|
-
return void 0;
|
|
499
|
-
}
|
|
500
|
-
function storedDocumentsOf(raw) {
|
|
501
|
-
requireValidFieldTypes(raw.schema ?? {});
|
|
502
|
-
const documents = {};
|
|
503
|
-
for (const [docId, doc] of Object.entries(raw.documents ?? {})) {
|
|
504
|
-
documents[docId] = {
|
|
505
|
-
fields: doc.fields,
|
|
506
|
-
fieldLengths: doc.field_lengths ?? {}
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
dropStoredVectorValues(documents, raw.schema ?? {});
|
|
510
|
-
return documents;
|
|
511
|
-
}
|
|
512
|
-
function documentsOfPayloadV2(data) {
|
|
513
|
-
const raw = decode(data);
|
|
514
|
-
return { documents: storedDocumentsOf(raw), schema: raw.schema ?? {} };
|
|
515
|
-
}
|
|
516
|
-
function deserializePayloadV2(data) {
|
|
517
|
-
const raw = decode(data);
|
|
518
|
-
const documents = storedDocumentsOf(raw);
|
|
519
|
-
const fieldNames = raw.inverted_index?.field_names ?? [];
|
|
520
|
-
const invertedIndex = {};
|
|
521
|
-
for (const [token, list] of Object.entries(raw.inverted_index?.entries ?? {})) {
|
|
522
|
-
const count = list.ids.length;
|
|
523
|
-
const postings = new Array(count);
|
|
524
|
-
for (let i = 0; i < count; i++) {
|
|
525
|
-
postings[i] = {
|
|
526
|
-
docId: list.ids[i],
|
|
527
|
-
termFrequency: list.tf[i],
|
|
528
|
-
field: fieldNames[list.fi[i]] ?? "",
|
|
529
|
-
positions: list.pos ? list.pos[i] : []
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
invertedIndex[token] = {
|
|
533
|
-
docFrequency: list.df,
|
|
534
|
-
postings
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
const numeric = {};
|
|
538
|
-
for (const [field, entries] of Object.entries(raw.field_indexes?.numeric ?? {})) {
|
|
539
|
-
numeric[field] = entries.map((e) => ({ value: e.value, docId: e.doc_id }));
|
|
540
|
-
}
|
|
541
|
-
const boolean = {};
|
|
542
|
-
for (const [field, idx] of Object.entries(raw.field_indexes?.boolean ?? {})) {
|
|
543
|
-
boolean[field] = { trueDocs: idx.true_docs ?? [], falseDocs: idx.false_docs ?? [] };
|
|
544
|
-
}
|
|
545
|
-
const geopoint = {};
|
|
546
|
-
for (const [field, entries] of Object.entries(raw.field_indexes?.geopoint ?? {})) {
|
|
547
|
-
geopoint[field] = entries.map((e) => ({ lat: e.lat, lon: e.lon, docId: e.doc_id }));
|
|
548
|
-
}
|
|
549
|
-
const vectorData = {};
|
|
550
|
-
for (const [field, data2] of Object.entries(raw.vector_data ?? {})) {
|
|
551
|
-
vectorData[field] = {
|
|
552
|
-
dimension: data2.dimension,
|
|
553
|
-
vectors: (data2.vectors ?? []).map((v) => ({ docId: v.doc_id, vector: v.vector })),
|
|
554
|
-
hnswGraph: data2.hnsw_graph ? {
|
|
555
|
-
entryPoint: data2.hnsw_graph.entry_point,
|
|
556
|
-
maxLayer: data2.hnsw_graph.max_layer,
|
|
557
|
-
m: data2.hnsw_graph.m,
|
|
558
|
-
efConstruction: data2.hnsw_graph.ef_construction,
|
|
559
|
-
metric: validateHnswMetric2(data2.hnsw_graph.metric),
|
|
560
|
-
nodes: data2.hnsw_graph.nodes
|
|
561
|
-
} : null,
|
|
562
|
-
codes: data2.codes ? { bits: data2.codes.bits, centroid: data2.codes.centroid, records: data2.codes.records } : null
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
return {
|
|
566
|
-
indexName: raw.index_name,
|
|
567
|
-
partitionId: raw.partition_id,
|
|
568
|
-
totalPartitions: raw.total_partitions,
|
|
569
|
-
language: raw.language ?? "english",
|
|
570
|
-
schema: raw.schema ?? {},
|
|
571
|
-
docCount: raw.doc_count ?? 0,
|
|
572
|
-
avgDocLength: raw.avg_doc_length ?? 0,
|
|
573
|
-
documents,
|
|
574
|
-
invertedIndex,
|
|
575
|
-
fieldIndexes: {
|
|
576
|
-
numeric,
|
|
577
|
-
boolean,
|
|
578
|
-
enum: raw.field_indexes?.enum ?? {},
|
|
579
|
-
geopoint
|
|
580
|
-
},
|
|
581
|
-
surfaceForms: sanitizeSurfaceForms(raw.surface_forms),
|
|
582
|
-
vectorData,
|
|
583
|
-
statistics: {
|
|
584
|
-
totalDocuments: raw.statistics?.total_documents ?? 0,
|
|
585
|
-
totalFieldLengths: raw.statistics?.total_field_lengths ?? {},
|
|
586
|
-
averageFieldLengths: raw.statistics?.average_field_lengths ?? {},
|
|
587
|
-
docFrequencies: raw.statistics?.doc_frequencies ?? {}
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
120
|
|
|
592
121
|
// src/core/document-store.ts
|
|
593
122
|
function detachedView(view) {
|
|
@@ -847,7 +376,7 @@ function createPostingList() {
|
|
|
847
376
|
length: 0,
|
|
848
377
|
docIds: [],
|
|
849
378
|
termFrequencies: new Uint16Array(POSTING_LIST_INITIAL_CAPACITY),
|
|
850
|
-
fieldNameIndices: new
|
|
379
|
+
fieldNameIndices: new Uint16Array(POSTING_LIST_INITIAL_CAPACITY),
|
|
851
380
|
positions: null,
|
|
852
381
|
docIdSet: /* @__PURE__ */ new Set(),
|
|
853
382
|
deletedDocs: /* @__PURE__ */ new Set(),
|
|
@@ -861,7 +390,7 @@ function growTypedArrays(list) {
|
|
|
861
390
|
const newTF = new Uint16Array(newCap);
|
|
862
391
|
newTF.set(list.termFrequencies);
|
|
863
392
|
list.termFrequencies = newTF;
|
|
864
|
-
const newFNI = new
|
|
393
|
+
const newFNI = new Uint16Array(newCap);
|
|
865
394
|
newFNI.set(list.fieldNameIndices);
|
|
866
395
|
list.fieldNameIndices = newFNI;
|
|
867
396
|
}
|
|
@@ -1106,7 +635,7 @@ function createInvertedIndex(fieldNameTable) {
|
|
|
1106
635
|
const docIdSet = /* @__PURE__ */ new Set();
|
|
1107
636
|
const docIds = new Array(count);
|
|
1108
637
|
const termFrequencies = new Uint16Array(count);
|
|
1109
|
-
const fieldNameIndices = new
|
|
638
|
+
const fieldNameIndices = new Uint16Array(count);
|
|
1110
639
|
let hasPositions = false;
|
|
1111
640
|
let validCount = 0;
|
|
1112
641
|
let totalTermFrequency = 0;
|
|
@@ -1411,8 +940,55 @@ function createSurfaceRegistry() {
|
|
|
1411
940
|
};
|
|
1412
941
|
}
|
|
1413
942
|
|
|
943
|
+
// src/core/pattern-index/platform.ts
|
|
944
|
+
function platformPatternSearch() {
|
|
945
|
+
return patternSearchModule;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/core/pattern-index/registry.ts
|
|
949
|
+
var registered = null;
|
|
950
|
+
function isPatternSearchModule(value) {
|
|
951
|
+
return value !== null && typeof value === "object" && value.name === "pattern" && "createIndex" in value && typeof value.createIndex === "function" && "readArrays" in value && typeof value.readArrays === "function" && "mergeArrays" in value && typeof value.mergeArrays === "function" && "matchInto" in value && typeof value.matchInto === "function";
|
|
952
|
+
}
|
|
953
|
+
function registerPatternSearch(module) {
|
|
954
|
+
if (!isPatternSearchModule(module)) {
|
|
955
|
+
throw new NarsilError(
|
|
956
|
+
ErrorCodes.CONFIG_INVALID,
|
|
957
|
+
'registerPatternSearch takes the patternSearch object that "@delali/narsil/pattern" exports'
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
registered = module;
|
|
961
|
+
}
|
|
962
|
+
function availablePatternSearch() {
|
|
963
|
+
return registered ?? platformPatternSearch();
|
|
964
|
+
}
|
|
965
|
+
function requirePatternSearchFor(schema) {
|
|
966
|
+
for (const { path, pattern } of schemaFieldsOf(schema)) {
|
|
967
|
+
if (pattern) requirePatternSearch(path);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function requirePatternSearch(fieldPath) {
|
|
971
|
+
const module = availablePatternSearch();
|
|
972
|
+
if (module !== null) return module;
|
|
973
|
+
throw new NarsilError(
|
|
974
|
+
ErrorCodes.CONFIG_INVALID,
|
|
975
|
+
`Field "${fieldPath}" is a pattern field, but this bundle contains no pattern search code. Add import { patternSearch } from '@delali/narsil/pattern', then call registerPatternSearch(patternSearch) before you create or load the index`,
|
|
976
|
+
{ field: fieldPath }
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// src/core/pattern-index/host.ts
|
|
981
|
+
var PATTERN_HOST = { appendFoldedCodePoint };
|
|
982
|
+
function patternIndexOf(indexes, fieldPath) {
|
|
983
|
+
let index = indexes.get(fieldPath);
|
|
984
|
+
if (index === void 0) {
|
|
985
|
+
index = requirePatternSearch(fieldPath).createIndex(PATTERN_HOST);
|
|
986
|
+
indexes.set(fieldPath, index);
|
|
987
|
+
}
|
|
988
|
+
return index;
|
|
989
|
+
}
|
|
990
|
+
|
|
1414
991
|
// src/geo/haversine.ts
|
|
1415
|
-
var EARTH_RADIUS_METERS = 63710088e-1;
|
|
1416
992
|
function haversineDistance(lat1, lon1, lat2, lon2) {
|
|
1417
993
|
const phi1 = lat1 * DEG_TO_RAD;
|
|
1418
994
|
const phi2 = lat2 * DEG_TO_RAD;
|
|
@@ -1475,61 +1051,94 @@ function vincentyDistance(lat1, lon1, lat2, lon2) {
|
|
|
1475
1051
|
}
|
|
1476
1052
|
|
|
1477
1053
|
// src/geo/geo-index.ts
|
|
1054
|
+
function firstAtOrAfter(entries, code) {
|
|
1055
|
+
let lo = 0;
|
|
1056
|
+
let hi = entries.length;
|
|
1057
|
+
while (lo < hi) {
|
|
1058
|
+
const mid = lo + hi >>> 1;
|
|
1059
|
+
if (entries[mid].code < code) lo = mid + 1;
|
|
1060
|
+
else hi = mid;
|
|
1061
|
+
}
|
|
1062
|
+
return lo;
|
|
1063
|
+
}
|
|
1478
1064
|
function createGeoIndex() {
|
|
1479
1065
|
const entries = [];
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1066
|
+
const byDocument = /* @__PURE__ */ new Map();
|
|
1067
|
+
let sorted = true;
|
|
1068
|
+
function ensureSorted() {
|
|
1069
|
+
if (sorted) return;
|
|
1070
|
+
entries.sort((left, right) => left.code - right.code || left.docId - right.docId);
|
|
1071
|
+
sorted = true;
|
|
1072
|
+
}
|
|
1073
|
+
function removeEntry(internalId) {
|
|
1074
|
+
const entry = byDocument.get(internalId);
|
|
1075
|
+
if (entry === void 0) return;
|
|
1076
|
+
byDocument.delete(internalId);
|
|
1077
|
+
ensureSorted();
|
|
1078
|
+
for (let at = firstAtOrAfter(entries, entry.code); at < entries.length; at++) {
|
|
1079
|
+
if (entries[at] !== entry) continue;
|
|
1080
|
+
entries.splice(at, 1);
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
function matching(boxes, passes, inside) {
|
|
1085
|
+
ensureSorted();
|
|
1086
|
+
const within = /* @__PURE__ */ new Set();
|
|
1087
|
+
for (const range of coveringRanges(boxes)) {
|
|
1088
|
+
for (let at = firstAtOrAfter(entries, range.from); at < entries.length && entries[at].code < range.to; at++) {
|
|
1089
|
+
if (passes(entries[at])) within.add(entries[at].docId);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (inside) return within;
|
|
1093
|
+
const outside = /* @__PURE__ */ new Set();
|
|
1094
|
+
for (const entry of entries) {
|
|
1095
|
+
if (!within.has(entry.docId)) outside.add(entry.docId);
|
|
1096
|
+
}
|
|
1097
|
+
return outside;
|
|
1098
|
+
}
|
|
1099
|
+
const index = {
|
|
1484
1100
|
insert(internalId, lat, lon) {
|
|
1485
|
-
|
|
1101
|
+
removeEntry(internalId);
|
|
1102
|
+
const entry = { lat, lon, docId: internalId, code: locationCode(lat, lon) };
|
|
1103
|
+
entries.push(entry);
|
|
1104
|
+
byDocument.set(internalId, entry);
|
|
1105
|
+
sorted = false;
|
|
1486
1106
|
},
|
|
1487
1107
|
remove(internalId) {
|
|
1488
|
-
|
|
1489
|
-
if (entries[i].docId === internalId) {
|
|
1490
|
-
entries.splice(i, 1);
|
|
1491
|
-
return;
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1108
|
+
removeEntry(internalId);
|
|
1494
1109
|
},
|
|
1495
1110
|
radiusQuery(lat, lon, distanceMeters, inside, highPrecision) {
|
|
1496
|
-
const result = /* @__PURE__ */ new Set();
|
|
1497
1111
|
const distanceFn = highPrecision ? vincentyDistance : haversineDistance;
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
result.add(entry.docId);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
return result;
|
|
1112
|
+
return matching(
|
|
1113
|
+
radiusBoxes(lat, lon, distanceMeters),
|
|
1114
|
+
(entry) => distanceFn(lat, lon, entry.lat, entry.lon) <= distanceMeters,
|
|
1115
|
+
inside
|
|
1116
|
+
);
|
|
1507
1117
|
},
|
|
1508
1118
|
polygonQuery(points, inside) {
|
|
1509
|
-
const result = /* @__PURE__ */ new Set();
|
|
1510
1119
|
const contains = polygonContainment(points);
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1518
|
-
return result;
|
|
1120
|
+
return matching(polygonBoxes(points), (entry) => contains(entry.lat, entry.lon), inside);
|
|
1121
|
+
},
|
|
1122
|
+
documentsBitset(capacity) {
|
|
1123
|
+
const bits = createBitSet(capacity);
|
|
1124
|
+
for (const entry of entries) bitsetSet(bits, entry.docId);
|
|
1125
|
+
return bits;
|
|
1519
1126
|
},
|
|
1520
1127
|
clear() {
|
|
1521
1128
|
entries.length = 0;
|
|
1129
|
+
byDocument.clear();
|
|
1130
|
+
sorted = true;
|
|
1522
1131
|
},
|
|
1523
1132
|
serialize() {
|
|
1524
|
-
|
|
1133
|
+
ensureSorted();
|
|
1134
|
+
return entries.map((entry) => ({ lat: entry.lat, lon: entry.lon, docId: entry.docId }));
|
|
1525
1135
|
},
|
|
1526
1136
|
deserialize(data) {
|
|
1527
|
-
|
|
1528
|
-
for (const entry of data)
|
|
1529
|
-
entries.push({ lat: entry.lat, lon: entry.lon, docId: entry.docId });
|
|
1530
|
-
}
|
|
1137
|
+
index.clear();
|
|
1138
|
+
for (const entry of data) index.insert(entry.docId, entry.lat, entry.lon);
|
|
1531
1139
|
}
|
|
1532
1140
|
};
|
|
1141
|
+
return index;
|
|
1533
1142
|
}
|
|
1534
1143
|
|
|
1535
1144
|
// src/core/field-index/boolean.ts
|
|
@@ -1603,9 +1212,90 @@ function createBooleanIndex() {
|
|
|
1603
1212
|
};
|
|
1604
1213
|
}
|
|
1605
1214
|
|
|
1215
|
+
// src/core/field-index/affixes.ts
|
|
1216
|
+
function compareUnits(left, right) {
|
|
1217
|
+
if (left < right) return -1;
|
|
1218
|
+
return left > right ? 1 : 0;
|
|
1219
|
+
}
|
|
1220
|
+
function reversedUnits(value) {
|
|
1221
|
+
let reversed = "";
|
|
1222
|
+
for (let at = value.length - 1; at >= 0; at--) reversed += value[at];
|
|
1223
|
+
return reversed;
|
|
1224
|
+
}
|
|
1225
|
+
function firstAtOrAfter2(sorted, key) {
|
|
1226
|
+
let lo = 0;
|
|
1227
|
+
let hi = sorted.length;
|
|
1228
|
+
while (lo < hi) {
|
|
1229
|
+
const mid = lo + hi >>> 1;
|
|
1230
|
+
if (sorted[mid] < key) lo = mid + 1;
|
|
1231
|
+
else hi = mid;
|
|
1232
|
+
}
|
|
1233
|
+
return lo;
|
|
1234
|
+
}
|
|
1235
|
+
function settledColumn(column, holds) {
|
|
1236
|
+
const { keys, values } = column;
|
|
1237
|
+
const order = [];
|
|
1238
|
+
for (let at = 0; at < keys.length; at++) if (holds(values[at])) order.push(at);
|
|
1239
|
+
order.sort((left, right) => compareUnits(keys[left], keys[right]) || compareUnits(values[left], values[right]));
|
|
1240
|
+
const settled = { keys: [], values: [] };
|
|
1241
|
+
for (const at of order) {
|
|
1242
|
+
const last = settled.values.length - 1;
|
|
1243
|
+
if (last >= 0 && settled.keys[last] === keys[at] && settled.values[last] === values[at]) continue;
|
|
1244
|
+
settled.keys.push(keys[at]);
|
|
1245
|
+
settled.values.push(values[at]);
|
|
1246
|
+
}
|
|
1247
|
+
return settled;
|
|
1248
|
+
}
|
|
1249
|
+
var ValueAffixes = class {
|
|
1250
|
+
constructor(holds, values) {
|
|
1251
|
+
this.holds = holds;
|
|
1252
|
+
for (const value of values) this.add(value);
|
|
1253
|
+
}
|
|
1254
|
+
forward = { keys: [], values: [] };
|
|
1255
|
+
backward = { keys: [], values: [] };
|
|
1256
|
+
settled = true;
|
|
1257
|
+
add(value) {
|
|
1258
|
+
this.forward.keys.push(value);
|
|
1259
|
+
this.forward.values.push(value);
|
|
1260
|
+
this.backward.keys.push(reversedUnits(value));
|
|
1261
|
+
this.backward.values.push(value);
|
|
1262
|
+
this.settled = false;
|
|
1263
|
+
}
|
|
1264
|
+
forget() {
|
|
1265
|
+
this.settled = false;
|
|
1266
|
+
}
|
|
1267
|
+
withPrefix(prefix) {
|
|
1268
|
+
this.settle();
|
|
1269
|
+
return this.matching(this.forward, prefix);
|
|
1270
|
+
}
|
|
1271
|
+
withSuffix(suffix) {
|
|
1272
|
+
this.settle();
|
|
1273
|
+
return this.matching(this.backward, reversedUnits(suffix));
|
|
1274
|
+
}
|
|
1275
|
+
settle() {
|
|
1276
|
+
if (this.settled) return;
|
|
1277
|
+
this.forward = settledColumn(this.forward, this.holds);
|
|
1278
|
+
this.backward = settledColumn(this.backward, this.holds);
|
|
1279
|
+
this.settled = true;
|
|
1280
|
+
}
|
|
1281
|
+
matching(column, start) {
|
|
1282
|
+
const found = [];
|
|
1283
|
+
for (let at = firstAtOrAfter2(column.keys, start); at < column.keys.length; at++) {
|
|
1284
|
+
if (!column.keys[at].startsWith(start)) break;
|
|
1285
|
+
found.push(column.values[at]);
|
|
1286
|
+
}
|
|
1287
|
+
return found;
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
|
|
1606
1291
|
// src/core/field-index/enum.ts
|
|
1607
1292
|
function createEnumIndex() {
|
|
1608
1293
|
const valueMap = /* @__PURE__ */ new Map();
|
|
1294
|
+
let affixes = null;
|
|
1295
|
+
function affixesOf() {
|
|
1296
|
+
affixes ??= new ValueAffixes((value) => valueMap.has(value), valueMap.keys());
|
|
1297
|
+
return affixes;
|
|
1298
|
+
}
|
|
1609
1299
|
function allDocs() {
|
|
1610
1300
|
const result = /* @__PURE__ */ new Set();
|
|
1611
1301
|
for (const docSet of valueMap.values()) {
|
|
@@ -1619,6 +1309,7 @@ function createEnumIndex() {
|
|
|
1619
1309
|
if (!docSet) {
|
|
1620
1310
|
docSet = /* @__PURE__ */ new Set();
|
|
1621
1311
|
valueMap.set(value, docSet);
|
|
1312
|
+
affixes?.add(value);
|
|
1622
1313
|
}
|
|
1623
1314
|
docSet.add(internalId);
|
|
1624
1315
|
},
|
|
@@ -1626,7 +1317,15 @@ function createEnumIndex() {
|
|
|
1626
1317
|
const docSet = valueMap.get(value);
|
|
1627
1318
|
if (!docSet) return;
|
|
1628
1319
|
docSet.delete(internalId);
|
|
1629
|
-
if (docSet.size
|
|
1320
|
+
if (docSet.size > 0) return;
|
|
1321
|
+
valueMap.delete(value);
|
|
1322
|
+
affixes?.forget();
|
|
1323
|
+
},
|
|
1324
|
+
queryPrefixBitset(prefix, capacity) {
|
|
1325
|
+
return this.queryInBitset(affixesOf().withPrefix(prefix), capacity);
|
|
1326
|
+
},
|
|
1327
|
+
querySuffixBitset(suffix, capacity) {
|
|
1328
|
+
return this.queryInBitset(affixesOf().withSuffix(suffix), capacity);
|
|
1630
1329
|
},
|
|
1631
1330
|
queryEq(value) {
|
|
1632
1331
|
const docSet = valueMap.get(value);
|
|
@@ -1702,6 +1401,7 @@ function createEnumIndex() {
|
|
|
1702
1401
|
},
|
|
1703
1402
|
clear() {
|
|
1704
1403
|
valueMap.clear();
|
|
1404
|
+
affixes = null;
|
|
1705
1405
|
},
|
|
1706
1406
|
serialize() {
|
|
1707
1407
|
const result = /* @__PURE__ */ Object.create(null);
|
|
@@ -1712,6 +1412,7 @@ function createEnumIndex() {
|
|
|
1712
1412
|
},
|
|
1713
1413
|
deserialize(data) {
|
|
1714
1414
|
valueMap.clear();
|
|
1415
|
+
affixes = null;
|
|
1715
1416
|
for (const value of Object.keys(data)) {
|
|
1716
1417
|
valueMap.set(value, new Set(data[value]));
|
|
1717
1418
|
}
|
|
@@ -1893,6 +1594,44 @@ function createNumericIndex() {
|
|
|
1893
1594
|
};
|
|
1894
1595
|
}
|
|
1895
1596
|
|
|
1597
|
+
// src/core/partition/value-sizes.ts
|
|
1598
|
+
function keepsValueSize(base) {
|
|
1599
|
+
return base === "string" || base === "verbatim" || base.endsWith("[]");
|
|
1600
|
+
}
|
|
1601
|
+
function valueSizeOf(value) {
|
|
1602
|
+
if (Array.isArray(value)) return value.length;
|
|
1603
|
+
return value === "" ? 0 : 1;
|
|
1604
|
+
}
|
|
1605
|
+
function sizeIndexOf(indexes, fieldPath) {
|
|
1606
|
+
let index = indexes.get(fieldPath);
|
|
1607
|
+
if (index === void 0) {
|
|
1608
|
+
index = createNumericIndex();
|
|
1609
|
+
indexes.set(fieldPath, index);
|
|
1610
|
+
}
|
|
1611
|
+
return index;
|
|
1612
|
+
}
|
|
1613
|
+
function recordValueSize(indexes, internalId, fieldPath, base, value) {
|
|
1614
|
+
if (value === void 0 || value === null || !keepsValueSize(base)) return;
|
|
1615
|
+
sizeIndexOf(indexes, fieldPath).insert(internalId, valueSizeOf(value));
|
|
1616
|
+
}
|
|
1617
|
+
function forgetValueSize(indexes, internalId, fieldPath, base, value) {
|
|
1618
|
+
if (value === void 0 || value === null || !keepsValueSize(base)) return;
|
|
1619
|
+
indexes.get(fieldPath)?.remove(internalId, valueSizeOf(value));
|
|
1620
|
+
}
|
|
1621
|
+
function rebuildSizeIndexes(state, schema) {
|
|
1622
|
+
state.sizeIndexes.clear();
|
|
1623
|
+
const fields = schemaFieldsOf(schema).filter((field) => keepsValueSize(field.base));
|
|
1624
|
+
if (fields.length === 0) return;
|
|
1625
|
+
for (const [docId, stored] of state.docStore.all()) {
|
|
1626
|
+
const internalId = state.docStore.getInternalId(docId);
|
|
1627
|
+
if (internalId === void 0) continue;
|
|
1628
|
+
for (const { path, base } of fields) {
|
|
1629
|
+
const value = getNestedValue(stored.fields, path);
|
|
1630
|
+
recordValueSize(state.sizeIndexes, internalId, path, base, value);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1896
1635
|
// src/core/partition/indexing.ts
|
|
1897
1636
|
function ensureFieldIndex(state, fieldPath, base) {
|
|
1898
1637
|
if ((base === "number" || base === "number[]") && !state.numericIndexes.has(fieldPath)) {
|
|
@@ -2031,10 +1770,12 @@ function indexDocument(state, docId, document, fields, language, options) {
|
|
|
2031
1770
|
const fieldLengths = {};
|
|
2032
1771
|
const tokensByField = {};
|
|
2033
1772
|
const internalId = resolveInternalId(state, docId);
|
|
2034
|
-
for (const { path: fieldPath, base } of fields) {
|
|
1773
|
+
for (const { path: fieldPath, base, pattern } of fields) {
|
|
2035
1774
|
const value = getNestedValue(document, fieldPath);
|
|
2036
1775
|
if (value === void 0 || value === null) continue;
|
|
1776
|
+
recordValueSize(state.sizeIndexes, internalId, fieldPath, base, value);
|
|
2037
1777
|
ensureFieldIndex(state, fieldPath, base);
|
|
1778
|
+
if (pattern) patternIndexOf(state.patternIndexes, fieldPath).add(internalId, value);
|
|
2038
1779
|
if (base === "string") {
|
|
2039
1780
|
indexStringField(state, internalId, fieldPath, value, language, options, fieldLengths, tokensByField);
|
|
2040
1781
|
} else if (base === "number") {
|
|
@@ -2078,9 +1819,11 @@ function removeFromIndexes(state, docId, storedDoc, schemaFields, language, opti
|
|
|
2078
1819
|
const fields = storedDoc.fields;
|
|
2079
1820
|
const internalId = resolveInternalId(state, docId);
|
|
2080
1821
|
const opts = tokenizeOptions(options);
|
|
2081
|
-
for (const { path: fieldPath, base } of schemaFields) {
|
|
1822
|
+
for (const { path: fieldPath, base, pattern } of schemaFields) {
|
|
2082
1823
|
const value = getNestedValue(fields, fieldPath);
|
|
2083
1824
|
if (value === void 0 || value === null) continue;
|
|
1825
|
+
forgetValueSize(state.sizeIndexes, internalId, fieldPath, base, value);
|
|
1826
|
+
if (pattern) state.patternIndexes.get(fieldPath)?.remove(internalId, value);
|
|
2084
1827
|
if (base === "string") {
|
|
2085
1828
|
const result = tokenize(value, language, opts);
|
|
2086
1829
|
fieldLengths[fieldPath] = result.tokens.length;
|
|
@@ -2152,16 +1895,24 @@ function resolveInternalId2(state, docId) {
|
|
|
2152
1895
|
}
|
|
2153
1896
|
function updateFieldIndexOnly(state, docId, oldFields, newDoc, fields) {
|
|
2154
1897
|
const internalId = resolveInternalId2(state, docId);
|
|
2155
|
-
for (const { path: fieldPath, base: fieldType } of fields) {
|
|
1898
|
+
for (const { path: fieldPath, base: fieldType, pattern } of fields) {
|
|
2156
1899
|
if (isWordIndexedBase(fieldType)) continue;
|
|
2157
1900
|
const oldVal = getNestedValue(oldFields, fieldPath);
|
|
2158
1901
|
const newVal = getNestedValue(newDoc, fieldPath);
|
|
2159
1902
|
if (oldVal === newVal) continue;
|
|
2160
1903
|
if (oldVal !== void 0 && oldVal !== null && newVal !== void 0 && newVal !== null && typeof oldVal === typeof newVal && JSON.stringify(oldVal) === JSON.stringify(newVal))
|
|
2161
1904
|
continue;
|
|
1905
|
+
forgetValueSize(state.sizeIndexes, internalId, fieldPath, fieldType, oldVal);
|
|
1906
|
+
recordValueSize(state.sizeIndexes, internalId, fieldPath, fieldType, newVal);
|
|
2162
1907
|
if (newVal !== void 0 && newVal !== null) {
|
|
2163
1908
|
ensureFieldIndex(state, fieldPath, fieldType);
|
|
2164
1909
|
}
|
|
1910
|
+
if (pattern) {
|
|
1911
|
+
if (oldVal !== void 0 && oldVal !== null) state.patternIndexes.get(fieldPath)?.remove(internalId, oldVal);
|
|
1912
|
+
if (newVal !== void 0 && newVal !== null) {
|
|
1913
|
+
patternIndexOf(state.patternIndexes, fieldPath).add(internalId, newVal);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
2165
1916
|
if (fieldType === "number") {
|
|
2166
1917
|
const numIdx = state.numericIndexes.get(fieldPath);
|
|
2167
1918
|
if (numIdx) {
|
|
@@ -2223,277 +1974,248 @@ function updateFieldIndexOnly(state, docId, oldFields, newDoc, fields) {
|
|
|
2223
1974
|
}
|
|
2224
1975
|
}
|
|
2225
1976
|
|
|
2226
|
-
// src/filters/
|
|
2227
|
-
function
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
var FIELD_FILTER_OPERATORS = [
|
|
2252
|
-
"eq",
|
|
2253
|
-
"ne",
|
|
2254
|
-
"gt",
|
|
2255
|
-
"lt",
|
|
2256
|
-
"gte",
|
|
2257
|
-
"lte",
|
|
2258
|
-
"between",
|
|
2259
|
-
"in",
|
|
2260
|
-
"nin",
|
|
2261
|
-
"startsWith",
|
|
2262
|
-
"endsWith",
|
|
2263
|
-
"containsAll",
|
|
2264
|
-
"matchesAny",
|
|
2265
|
-
"size",
|
|
2266
|
-
"exists",
|
|
2267
|
-
"notExists",
|
|
2268
|
-
"isEmpty",
|
|
2269
|
-
"isNotEmpty",
|
|
2270
|
-
"radius",
|
|
2271
|
-
"polygon"
|
|
2272
|
-
];
|
|
2273
|
-
|
|
2274
|
-
// src/filters/operators/shared.ts
|
|
2275
|
-
var METERS_PER_KM = 1e3;
|
|
2276
|
-
var METERS_PER_MI = 1609.344;
|
|
2277
|
-
function convertToMeters(distance, unit) {
|
|
2278
|
-
if (unit === "km") return distance * METERS_PER_KM;
|
|
2279
|
-
if (unit === "mi") return distance * METERS_PER_MI;
|
|
2280
|
-
if (unit === "m") return distance;
|
|
2281
|
-
throw new NarsilError(
|
|
2282
|
-
ErrorCodes.SEARCH_INVALID_FILTER,
|
|
2283
|
-
`The engine measures a radius in "km", "mi", or "m", and "${String(unit)}" is none of them`,
|
|
2284
|
-
{ unit: String(unit) }
|
|
2285
|
-
);
|
|
2286
|
-
}
|
|
2287
|
-
function matchesNumericComparison(value, filter) {
|
|
2288
|
-
if (filter.eq !== void 0 && value !== filter.eq) return false;
|
|
2289
|
-
if (filter.ne !== void 0 && value === filter.ne) return false;
|
|
2290
|
-
if (filter.gt !== void 0 && !(value > filter.gt)) return false;
|
|
2291
|
-
if (filter.lt !== void 0 && !(value < filter.lt)) return false;
|
|
2292
|
-
if (filter.gte !== void 0 && !(value >= filter.gte)) return false;
|
|
2293
|
-
if (filter.lte !== void 0 && !(value <= filter.lte)) return false;
|
|
2294
|
-
if (filter.between !== void 0 && !(value >= filter.between[0] && value <= filter.between[1])) return false;
|
|
2295
|
-
return true;
|
|
2296
|
-
}
|
|
2297
|
-
function holdsValue(stored, value) {
|
|
2298
|
-
return Array.isArray(stored) ? stored.includes(value) : stored === value;
|
|
2299
|
-
}
|
|
2300
|
-
function lacksValue(stored, value) {
|
|
2301
|
-
if (stored === void 0 || stored === null) return false;
|
|
2302
|
-
return Array.isArray(stored) ? !stored.includes(value) : stored !== value;
|
|
2303
|
-
}
|
|
2304
|
-
function isStringIn(values) {
|
|
2305
|
-
return (element) => typeof element === "string" && values.has(element);
|
|
2306
|
-
}
|
|
2307
|
-
function holdsStringIn(stored, values) {
|
|
2308
|
-
return Array.isArray(stored) ? stored.some(isStringIn(values)) : isStringIn(values)(stored);
|
|
2309
|
-
}
|
|
2310
|
-
function holdsNoStringIn(stored, values) {
|
|
2311
|
-
if (Array.isArray(stored)) return !stored.some(isStringIn(values));
|
|
2312
|
-
return typeof stored === "string" && !values.has(stored);
|
|
2313
|
-
}
|
|
2314
|
-
function holdsStringWhere(stored, test) {
|
|
2315
|
-
const matches = (element) => typeof element === "string" && test(element);
|
|
2316
|
-
return Array.isArray(stored) ? stored.some(matches) : matches(stored);
|
|
1977
|
+
// src/filters/pattern-tests.ts
|
|
1978
|
+
function excluding(context, fieldPath, excluded) {
|
|
1979
|
+
const universe = bitsetAnd(context.presentBitset(fieldPath), context.allDocIdsBitset);
|
|
1980
|
+
return bitsetAnd(universe, bitsetNot(excluded, context.capacity));
|
|
1981
|
+
}
|
|
1982
|
+
function textsOf(operand) {
|
|
1983
|
+
const texts = /* @__PURE__ */ new Set();
|
|
1984
|
+
if (!Array.isArray(operand)) return texts;
|
|
1985
|
+
for (const text of operand) if (typeof text === "string") texts.add(text);
|
|
1986
|
+
return texts;
|
|
1987
|
+
}
|
|
1988
|
+
function evaluatePatternTests(fieldPath, filter, context, bitsets) {
|
|
1989
|
+
const caseFold = filter.caseFold === true;
|
|
1990
|
+
const matching = (operator, texts) => {
|
|
1991
|
+
const into = createBitSet(context.capacity);
|
|
1992
|
+
for (const text of texts) context.matchPattern(fieldPath, operator, text, caseFold, into);
|
|
1993
|
+
return into;
|
|
1994
|
+
};
|
|
1995
|
+
if (typeof filter.eq === "string") bitsets.push(matching("eq", [filter.eq]));
|
|
1996
|
+
if (typeof filter.ne === "string") bitsets.push(excluding(context, fieldPath, matching("eq", [filter.ne])));
|
|
1997
|
+
if (filter.in !== void 0) bitsets.push(matching("eq", textsOf(filter.in)));
|
|
1998
|
+
if (filter.nin !== void 0) bitsets.push(excluding(context, fieldPath, matching("eq", textsOf(filter.nin))));
|
|
1999
|
+
if (typeof filter.startsWith === "string") bitsets.push(matching("startsWith", [filter.startsWith]));
|
|
2000
|
+
if (typeof filter.endsWith === "string") bitsets.push(matching("endsWith", [filter.endsWith]));
|
|
2001
|
+
if (typeof filter.contains === "string") bitsets.push(matching("contains", [filter.contains]));
|
|
2317
2002
|
}
|
|
2318
2003
|
|
|
2319
|
-
// src/filters/
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
for (let wi = 0; wi < resolved.length; wi++) {
|
|
2327
|
-
let word = resolved[wi];
|
|
2328
|
-
if (word === 0) continue;
|
|
2329
|
-
const base = wi << 5;
|
|
2330
|
-
while (word !== 0) {
|
|
2331
|
-
const tz = Math.clz32(word & -word) ^ 31;
|
|
2332
|
-
const id = base + tz;
|
|
2333
|
-
if (predicate(getValue(id))) {
|
|
2334
|
-
bitsetSet(result, id);
|
|
2335
|
-
}
|
|
2336
|
-
word &= word - 1;
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
return result;
|
|
2340
|
-
}
|
|
2341
|
-
function applyEqBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2342
|
-
if (fieldIndex?.type === "numeric" && typeof value === "number") {
|
|
2343
|
-
return fieldIndex.index.eqBitset(value, capacity);
|
|
2344
|
-
}
|
|
2345
|
-
if (fieldIndex?.type === "boolean" && typeof value === "boolean") {
|
|
2346
|
-
return value ? fieldIndex.index.getTrueBitset(capacity) : fieldIndex.index.getFalseBitset(capacity);
|
|
2347
|
-
}
|
|
2348
|
-
if (fieldIndex?.type === "enum" && typeof value === "string") {
|
|
2349
|
-
return fieldIndex.index.getDocIdsBitset(value, capacity);
|
|
2350
|
-
}
|
|
2351
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => holdsValue(v, value));
|
|
2352
|
-
}
|
|
2353
|
-
function applyNeBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2354
|
-
if (fieldIndex?.type === "numeric" && typeof value === "number") {
|
|
2355
|
-
const all = fieldIndex.index.allDocIdsBitset(capacity);
|
|
2356
|
-
const eq = fieldIndex.index.eqBitset(value, capacity);
|
|
2357
|
-
return bitsetAnd(all, bitsetNot(eq, capacity));
|
|
2358
|
-
}
|
|
2359
|
-
if (fieldIndex?.type === "boolean" && typeof value === "boolean") {
|
|
2360
|
-
return value ? fieldIndex.index.getFalseBitset(capacity) : fieldIndex.index.getTrueBitset(capacity);
|
|
2004
|
+
// src/filters/field-tests.ts
|
|
2005
|
+
var COMPARISON_OPERATORS = ["eq", "ne", "gt", "gte", "lt", "lte", "between"];
|
|
2006
|
+
var TEXT_RANGE_OPERATORS = ["gt", "gte", "lt", "lte"];
|
|
2007
|
+
var FieldBits = class {
|
|
2008
|
+
constructor(context, fieldPath) {
|
|
2009
|
+
this.context = context;
|
|
2010
|
+
this.fieldPath = fieldPath;
|
|
2361
2011
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
const eq = fieldIndex.index.getDocIdsBitset(value, capacity);
|
|
2365
|
-
return bitsetAnd(all, bitsetNot(eq, capacity));
|
|
2012
|
+
none() {
|
|
2013
|
+
return createBitSet(this.context.capacity);
|
|
2366
2014
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
function applyGtBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2370
|
-
if (fieldIndex?.type === "numeric") {
|
|
2371
|
-
return fieldIndex.index.gtBitset(value, capacity);
|
|
2015
|
+
present() {
|
|
2016
|
+
return this.context.presentBitset(this.fieldPath);
|
|
2372
2017
|
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
if (fieldIndex?.type === "numeric") {
|
|
2377
|
-
return fieldIndex.index.ltBitset(value, capacity);
|
|
2018
|
+
holdingSomething() {
|
|
2019
|
+
const empty = this.context.sizeIndex(this.fieldPath)?.queryEqBitset(0, this.context.capacity);
|
|
2020
|
+
return empty === void 0 ? this.present() : this.without(this.present(), empty);
|
|
2378
2021
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
return fieldIndex.index.gteBitset(value, capacity);
|
|
2022
|
+
without(from, removed) {
|
|
2023
|
+
const kept = from.slice();
|
|
2024
|
+
bitsetAndNotInto(kept, removed);
|
|
2025
|
+
return kept;
|
|
2384
2026
|
}
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
function applyLteBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2388
|
-
if (fieldIndex?.type === "numeric") {
|
|
2389
|
-
return fieldIndex.index.lteBitset(value, capacity);
|
|
2027
|
+
complement(bits) {
|
|
2028
|
+
return this.without(this.context.allDocIdsBitset, bits);
|
|
2390
2029
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
return
|
|
2030
|
+
};
|
|
2031
|
+
function comparisonBits(index, operator, operand, bits, capacity) {
|
|
2032
|
+
if (operator === "between") {
|
|
2033
|
+
const [low, high] = operand;
|
|
2034
|
+
return index?.queryBetweenBitset(low, high, capacity) ?? bits.none();
|
|
2035
|
+
}
|
|
2036
|
+
const value = operand;
|
|
2037
|
+
if (operator === "ne") return bits.without(bits.present(), index?.queryEqBitset(value, capacity) ?? bits.none());
|
|
2038
|
+
if (index === void 0) return bits.none();
|
|
2039
|
+
if (operator === "eq") return index.queryEqBitset(value, capacity);
|
|
2040
|
+
if (operator === "gt") return index.queryGtBitset(value, capacity);
|
|
2041
|
+
if (operator === "gte") return index.queryGteBitset(value, capacity);
|
|
2042
|
+
if (operator === "lt") return index.queryLtBitset(value, capacity);
|
|
2043
|
+
return index.queryLteBitset(value, capacity);
|
|
2044
|
+
}
|
|
2045
|
+
function elementBits(context, fieldPath, parsed, value) {
|
|
2046
|
+
const { capacity } = context;
|
|
2047
|
+
if (parsed.pattern) {
|
|
2048
|
+
const matches = createBitSet(capacity);
|
|
2049
|
+
context.matchPattern(fieldPath, "eq", value, false, matches);
|
|
2050
|
+
return matches;
|
|
2051
|
+
}
|
|
2052
|
+
if (typeof value === "number")
|
|
2053
|
+
return context.numericIndex(fieldPath)?.queryEqBitset(value, capacity) ?? createBitSet(capacity);
|
|
2054
|
+
if (typeof value === "boolean")
|
|
2055
|
+
return context.booleanIndex(fieldPath)?.queryEqBitset(value, capacity) ?? createBitSet(capacity);
|
|
2056
|
+
return context.enumIndex(fieldPath)?.queryEqBitset(value, capacity) ?? createBitSet(capacity);
|
|
2057
|
+
}
|
|
2058
|
+
function anyElementBits(context, fieldPath, parsed, values) {
|
|
2059
|
+
const { capacity } = context;
|
|
2060
|
+
const union = createBitSet(capacity);
|
|
2061
|
+
if (parsed.pattern) {
|
|
2062
|
+
for (const value of values) context.matchPattern(fieldPath, "eq", value, false, union);
|
|
2063
|
+
return union;
|
|
2396
2064
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
function applyInBitset(values, allDocsBitset, capacity, getValue, fieldIndex) {
|
|
2400
|
-
if (fieldIndex?.type === "enum") {
|
|
2401
|
-
return fieldIndex.index.getDocIdsInBitset(values, capacity);
|
|
2065
|
+
if (parsed.base === "enum[]") {
|
|
2066
|
+
return context.enumIndex(fieldPath)?.queryInBitset(values, capacity) ?? union;
|
|
2402
2067
|
}
|
|
2403
|
-
const
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
const all = fieldIndex.index.allDocIdsBitset(capacity);
|
|
2409
|
-
const matched = fieldIndex.index.getDocIdsInBitset(values, capacity);
|
|
2410
|
-
return bitsetAnd(all, bitsetNot(matched, capacity));
|
|
2068
|
+
const numbers = context.numericIndex(fieldPath);
|
|
2069
|
+
const booleans = context.booleanIndex(fieldPath);
|
|
2070
|
+
for (const value of values) {
|
|
2071
|
+
if (typeof value === "number") for (const docId of numbers?.queryEq(value) ?? []) bitsetSet(union, docId);
|
|
2072
|
+
if (typeof value === "boolean") for (const docId of booleans?.queryEq(value) ?? []) bitsetSet(union, docId);
|
|
2411
2073
|
}
|
|
2412
|
-
|
|
2413
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => holdsNoStringIn(v, valSet));
|
|
2414
|
-
}
|
|
2415
|
-
function applyStartsWithBitset(prefix, allDocsBitset, capacity, getValue) {
|
|
2416
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => holdsStringWhere(v, (text) => text.startsWith(prefix)));
|
|
2417
|
-
}
|
|
2418
|
-
function applyEndsWithBitset(suffix, allDocsBitset, capacity, getValue) {
|
|
2419
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => holdsStringWhere(v, (text) => text.endsWith(suffix)));
|
|
2420
|
-
}
|
|
2421
|
-
function applyContainsAllBitset(values, allDocsBitset, capacity, getValue) {
|
|
2422
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
|
|
2423
|
-
if (!Array.isArray(v)) return false;
|
|
2424
|
-
const arr = v;
|
|
2425
|
-
for (const val of values) {
|
|
2426
|
-
if (!arr.includes(val)) return false;
|
|
2427
|
-
}
|
|
2428
|
-
return true;
|
|
2429
|
-
});
|
|
2430
|
-
}
|
|
2431
|
-
function applyMatchesAnyBitset(values, allDocsBitset, capacity, getValue) {
|
|
2432
|
-
const valSet = new Set(values);
|
|
2433
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
|
|
2434
|
-
if (!Array.isArray(v)) return false;
|
|
2435
|
-
for (const item of v) {
|
|
2436
|
-
if (valSet.has(item)) return true;
|
|
2437
|
-
}
|
|
2438
|
-
return false;
|
|
2439
|
-
});
|
|
2440
|
-
}
|
|
2441
|
-
function applySizeBitset(sizeFilter, allDocsBitset, capacity, getValue) {
|
|
2442
|
-
return scanToBitset(
|
|
2443
|
-
allDocsBitset,
|
|
2444
|
-
capacity,
|
|
2445
|
-
getValue,
|
|
2446
|
-
(v) => Array.isArray(v) && matchesNumericComparison(v.length, sizeFilter)
|
|
2447
|
-
);
|
|
2448
|
-
}
|
|
2449
|
-
function applyExistsBitset(allDocsBitset, capacity, getValue) {
|
|
2450
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => v !== void 0 && v !== null);
|
|
2451
|
-
}
|
|
2452
|
-
function applyNotExistsBitset(allDocsBitset, capacity, getValue) {
|
|
2453
|
-
return scanToBitset(allDocsBitset, capacity, getValue, (v) => v === void 0 || v === null);
|
|
2074
|
+
return union;
|
|
2454
2075
|
}
|
|
2455
|
-
function
|
|
2456
|
-
return
|
|
2457
|
-
if (v === void 0 || v === null) return true;
|
|
2458
|
-
if (typeof v === "string" && v === "") return true;
|
|
2459
|
-
if (Array.isArray(v) && v.length === 0) return true;
|
|
2460
|
-
return false;
|
|
2461
|
-
});
|
|
2076
|
+
function distinctScalars(values) {
|
|
2077
|
+
return [...new Set(values)];
|
|
2462
2078
|
}
|
|
2463
|
-
function
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2079
|
+
function narrowByElements(fieldPath, f, parsed, context, bits, narrow) {
|
|
2080
|
+
if (f.matchesAny !== void 0) narrow(anyElementBits(context, fieldPath, parsed, distinctScalars(f.matchesAny)));
|
|
2081
|
+
if (f.containsAll !== void 0) {
|
|
2082
|
+
const all = bits.present().slice();
|
|
2083
|
+
for (const value of distinctScalars(f.containsAll)) {
|
|
2084
|
+
if (bitsetIsEmpty(all)) break;
|
|
2085
|
+
bitsetAndInto(all, elementBits(context, fieldPath, parsed, value));
|
|
2086
|
+
}
|
|
2087
|
+
narrow(all);
|
|
2088
|
+
}
|
|
2089
|
+
if (f.size !== void 0) {
|
|
2090
|
+
const sizes = context.sizeIndex(fieldPath);
|
|
2091
|
+
const comparison = f.size;
|
|
2092
|
+
for (const operator of COMPARISON_OPERATORS) {
|
|
2093
|
+
if (comparison[operator] === void 0) continue;
|
|
2094
|
+
narrow(comparisonBits(sizes, operator, comparison[operator], bits, context.capacity));
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2470
2097
|
}
|
|
2471
|
-
function
|
|
2472
|
-
const
|
|
2473
|
-
const
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2098
|
+
function narrowByValue(fieldPath, f, parsed, context, bits, narrow) {
|
|
2099
|
+
const { capacity } = context;
|
|
2100
|
+
const base = parsed.base;
|
|
2101
|
+
if (base === "number" || base === "number[]") {
|
|
2102
|
+
const index = context.numericIndex(fieldPath);
|
|
2103
|
+
for (const operator of COMPARISON_OPERATORS) {
|
|
2104
|
+
const operand = f[operator];
|
|
2105
|
+
if (operand === void 0 || typeof operand === "string") continue;
|
|
2106
|
+
if (operator === "between" && typeof operand[0] === "string") continue;
|
|
2107
|
+
narrow(comparisonBits(index, operator, operand, bits, capacity));
|
|
2108
|
+
}
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
if (base === "boolean" || base === "boolean[]") {
|
|
2112
|
+
const index = context.booleanIndex(fieldPath);
|
|
2113
|
+
const equal = (value) => index?.queryEqBitset(value, capacity) ?? bits.none();
|
|
2114
|
+
if (typeof f.eq === "boolean") narrow(equal(f.eq));
|
|
2115
|
+
if (typeof f.ne === "boolean") narrow(bits.without(bits.present(), equal(f.ne)));
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
if (base === "enum" || base === "enum[]") {
|
|
2119
|
+
const index = context.enumIndex(fieldPath);
|
|
2120
|
+
const equal = (value) => index?.queryEqBitset(value, capacity) ?? bits.none();
|
|
2121
|
+
const anyOf = (values) => index?.queryInBitset(values, capacity) ?? bits.none();
|
|
2122
|
+
if (typeof f.eq === "string") narrow(equal(f.eq));
|
|
2123
|
+
if (typeof f.ne === "string") narrow(bits.without(bits.present(), equal(f.ne)));
|
|
2124
|
+
if (Array.isArray(f.in)) narrow(anyOf(f.in));
|
|
2125
|
+
if (Array.isArray(f.nin)) narrow(bits.without(bits.present(), anyOf(f.nin)));
|
|
2126
|
+
if (typeof f.startsWith === "string") narrow(index?.queryPrefixBitset(f.startsWith, capacity) ?? bits.none());
|
|
2127
|
+
if (typeof f.endsWith === "string") narrow(index?.querySuffixBitset(f.endsWith, capacity) ?? bits.none());
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
function narrowByLocation(fieldPath, f, context, narrow) {
|
|
2131
|
+
const index = context.geoIndex(fieldPath);
|
|
2132
|
+
if (f.radius !== void 0) {
|
|
2133
|
+
const circle = f.radius;
|
|
2134
|
+
const found = index?.radiusQuery(
|
|
2135
|
+
circle.lat,
|
|
2136
|
+
circle.lon,
|
|
2137
|
+
circle.distance * GEO_UNIT_METRES[circle.unit],
|
|
2138
|
+
circle.inside ?? true,
|
|
2139
|
+
circle.highPrecision ?? false
|
|
2140
|
+
);
|
|
2141
|
+
narrow(bitsetFromSet(found ?? /* @__PURE__ */ new Set(), context.capacity));
|
|
2142
|
+
}
|
|
2143
|
+
if (f.polygon !== void 0) {
|
|
2144
|
+
const polygon = f.polygon;
|
|
2145
|
+
narrow(bitsetFromSet(index?.polygonQuery(polygon.points, polygon.inside ?? true) ?? /* @__PURE__ */ new Set(), context.capacity));
|
|
2146
|
+
}
|
|
2481
2147
|
}
|
|
2482
|
-
function
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2148
|
+
function narrowByPresence(f, bits, narrow) {
|
|
2149
|
+
if (typeof f.exists === "boolean") narrow(f.exists ? bits.present() : bits.complement(bits.present()));
|
|
2150
|
+
if (typeof f.notExists === "boolean") narrow(f.notExists ? bits.complement(bits.present()) : bits.present());
|
|
2151
|
+
if (typeof f.isNotEmpty === "boolean") {
|
|
2152
|
+
const holding = bits.holdingSomething();
|
|
2153
|
+
narrow(f.isNotEmpty ? holding : bits.complement(holding));
|
|
2154
|
+
}
|
|
2155
|
+
if (typeof f.isEmpty === "boolean") {
|
|
2156
|
+
const holding = bits.holdingSomething();
|
|
2157
|
+
narrow(f.isEmpty ? bits.complement(holding) : holding);
|
|
2158
|
+
}
|
|
2486
2159
|
}
|
|
2487
|
-
|
|
2488
|
-
// src/filters/evaluator.ts
|
|
2489
|
-
var UNBOUNDED = { lower: null, upper: null };
|
|
2490
2160
|
function textBoundsOf(operator, bound) {
|
|
2491
2161
|
if (operator === "gt" || operator === "gte") {
|
|
2492
|
-
return {
|
|
2162
|
+
return { lower: bound, upper: null, lowerInclusive: operator === "gte", upperInclusive: false };
|
|
2493
2163
|
}
|
|
2494
|
-
return {
|
|
2164
|
+
return { lower: null, upper: bound, upperInclusive: operator === "lte", lowerInclusive: false };
|
|
2495
2165
|
}
|
|
2496
|
-
|
|
2166
|
+
function narrowByTextRange(fieldPath, f, context, narrow) {
|
|
2167
|
+
for (const operator of TEXT_RANGE_OPERATORS) {
|
|
2168
|
+
const bound = f[operator];
|
|
2169
|
+
if (typeof bound === "string") narrow(context.textRangeBitset(fieldPath, textBoundsOf(operator, bound)));
|
|
2170
|
+
}
|
|
2171
|
+
if (!Array.isArray(f.between)) return;
|
|
2172
|
+
const [low, high] = f.between;
|
|
2173
|
+
if (typeof low !== "string" || typeof high !== "string") return;
|
|
2174
|
+
narrow(context.textRangeBitset(fieldPath, { lower: low, lowerInclusive: true, upper: high, upperInclusive: true }));
|
|
2175
|
+
}
|
|
2176
|
+
function narrowByFieldTests(fieldPath, f, parsed, context, narrow) {
|
|
2177
|
+
const bits = new FieldBits(context, fieldPath);
|
|
2178
|
+
narrowByPresence(f, bits, narrow);
|
|
2179
|
+
narrowByLocation(fieldPath, f, context, narrow);
|
|
2180
|
+
narrowByTextRange(fieldPath, f, context, narrow);
|
|
2181
|
+
if (parsed.pattern) {
|
|
2182
|
+
const patternBits = [];
|
|
2183
|
+
evaluatePatternTests(fieldPath, f, context, patternBits);
|
|
2184
|
+
for (const matched of patternBits) narrow(matched);
|
|
2185
|
+
} else {
|
|
2186
|
+
narrowByValue(fieldPath, f, parsed, context, bits, narrow);
|
|
2187
|
+
}
|
|
2188
|
+
narrowByElements(fieldPath, f, parsed, context, bits, narrow);
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
// src/filters/keys.ts
|
|
2192
|
+
var FILTER_EXPRESSION_KEYS = ["fields", "and", "or", "not"];
|
|
2193
|
+
var FIELD_FILTER_OPERATORS = [
|
|
2194
|
+
"eq",
|
|
2195
|
+
"ne",
|
|
2196
|
+
"gt",
|
|
2197
|
+
"lt",
|
|
2198
|
+
"gte",
|
|
2199
|
+
"lte",
|
|
2200
|
+
"between",
|
|
2201
|
+
"in",
|
|
2202
|
+
"nin",
|
|
2203
|
+
"startsWith",
|
|
2204
|
+
"endsWith",
|
|
2205
|
+
"contains",
|
|
2206
|
+
"caseFold",
|
|
2207
|
+
"containsAll",
|
|
2208
|
+
"matchesAny",
|
|
2209
|
+
"size",
|
|
2210
|
+
"exists",
|
|
2211
|
+
"notExists",
|
|
2212
|
+
"isEmpty",
|
|
2213
|
+
"isNotEmpty",
|
|
2214
|
+
"radius",
|
|
2215
|
+
"polygon"
|
|
2216
|
+
];
|
|
2217
|
+
|
|
2218
|
+
// src/filters/evaluator.ts
|
|
2497
2219
|
var EXPRESSION_KEYS = new Set(FILTER_EXPRESSION_KEYS);
|
|
2498
2220
|
var OPERATOR_KEYS = new Set(FIELD_FILTER_OPERATORS);
|
|
2499
2221
|
function requireKnownKeys(value, known, describe) {
|
|
@@ -2503,160 +2225,66 @@ function requireKnownKeys(value, known, describe) {
|
|
|
2503
2225
|
}
|
|
2504
2226
|
}
|
|
2505
2227
|
}
|
|
2228
|
+
var Narrowing = class {
|
|
2229
|
+
bits = null;
|
|
2230
|
+
narrow = (matched) => {
|
|
2231
|
+
if (this.bits === null) this.bits = matched.slice();
|
|
2232
|
+
else bitsetAndInto(this.bits, matched);
|
|
2233
|
+
};
|
|
2234
|
+
value(universe) {
|
|
2235
|
+
return this.bits ?? universe().slice();
|
|
2236
|
+
}
|
|
2237
|
+
};
|
|
2238
|
+
function narrowByField(fieldPath, filter, context, narrow) {
|
|
2239
|
+
requireKnownKeys(
|
|
2240
|
+
filter,
|
|
2241
|
+
OPERATOR_KEYS,
|
|
2242
|
+
(key) => `Field "${fieldPath}" is tested with "${key}", which is no filter operator`
|
|
2243
|
+
);
|
|
2244
|
+
const type = context.fieldTypes[fieldPath];
|
|
2245
|
+
const parsed = parsedTypeOf(type);
|
|
2246
|
+
if (type === void 0 || parsed === void 0) throw undeclaredFilterField(fieldPath);
|
|
2247
|
+
const f = filter;
|
|
2248
|
+
requireValidOperands(fieldPath, f, type);
|
|
2249
|
+
narrowByFieldTests(fieldPath, f, parsed, context, narrow.narrow);
|
|
2250
|
+
}
|
|
2506
2251
|
function evaluateFilters(expression, context) {
|
|
2507
2252
|
requireKnownKeys(
|
|
2508
2253
|
expression,
|
|
2509
2254
|
EXPRESSION_KEYS,
|
|
2510
2255
|
(key) => `A filter expression takes "fields", "and", "or", and "not", and "${key}" is none of them. A per-field test such as { ${key}: { eq: ... } } belongs under "fields"`
|
|
2511
2256
|
);
|
|
2512
|
-
const
|
|
2257
|
+
const narrowing = new Narrowing();
|
|
2513
2258
|
if (expression.fields) {
|
|
2514
2259
|
for (const [fieldPath, filter] of Object.entries(expression.fields)) {
|
|
2515
|
-
|
|
2260
|
+
narrowByField(fieldPath, filter, context, narrowing);
|
|
2516
2261
|
}
|
|
2517
2262
|
}
|
|
2518
2263
|
if (expression.and?.length) {
|
|
2519
|
-
const
|
|
2520
|
-
bitsets.push(applyAndBitset(andBitsets));
|
|
2264
|
+
for (const nested of expression.and) narrowing.narrow(evaluateFilters(nested, context));
|
|
2521
2265
|
}
|
|
2522
2266
|
if (expression.or?.length) {
|
|
2523
|
-
const
|
|
2524
|
-
|
|
2267
|
+
const union = createBitSet(context.capacity);
|
|
2268
|
+
for (const nested of expression.or) bitsetOrInto(union, evaluateFilters(nested, context));
|
|
2269
|
+
narrowing.narrow(union);
|
|
2525
2270
|
}
|
|
2526
2271
|
if (expression.not) {
|
|
2527
2272
|
const excluded = evaluateFilters(expression.not, context);
|
|
2528
|
-
const
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
if (bitsets.length === 0) return context.allDocIdsBitset;
|
|
2532
|
-
if (bitsets.length === 1) return bitsets[0];
|
|
2533
|
-
return applyAndBitset(bitsets);
|
|
2534
|
-
}
|
|
2535
|
-
function evaluateFieldFilter(fieldPath, filter, context) {
|
|
2536
|
-
requireKnownKeys(
|
|
2537
|
-
filter,
|
|
2538
|
-
OPERATOR_KEYS,
|
|
2539
|
-
(key) => `Field "${fieldPath}" is tested with "${key}", which is no filter operator`
|
|
2540
|
-
);
|
|
2541
|
-
const f = filter;
|
|
2542
|
-
requireValidOperands(fieldPath, f, context.fieldTypes[fieldPath]);
|
|
2543
|
-
const fieldIndex = context.fieldIndexes[fieldPath];
|
|
2544
|
-
const getValue = (internalId) => context.getFieldValue(internalId, fieldPath);
|
|
2545
|
-
const bitsets = [];
|
|
2546
|
-
const { capacity } = context;
|
|
2547
|
-
let allDocsBitset = null;
|
|
2548
|
-
const getAllDocsBitset = () => {
|
|
2549
|
-
if (allDocsBitset === null) {
|
|
2550
|
-
allDocsBitset = context.allDocIdsBitset;
|
|
2551
|
-
}
|
|
2552
|
-
return allDocsBitset;
|
|
2553
|
-
};
|
|
2554
|
-
if ("radius" in f && f.radius) {
|
|
2555
|
-
const geoIndex = resolveGeoIndex(fieldPath, fieldIndex);
|
|
2556
|
-
bitsets.push(applyGeoRadiusBitset(filter.radius, geoIndex, capacity));
|
|
2557
|
-
}
|
|
2558
|
-
if ("polygon" in f && f.polygon) {
|
|
2559
|
-
const geoIndex = resolveGeoIndex(fieldPath, fieldIndex);
|
|
2560
|
-
bitsets.push(applyGeoPolygonBitset(filter.polygon, geoIndex, capacity));
|
|
2561
|
-
}
|
|
2562
|
-
if ("eq" in f && f.eq !== void 0) {
|
|
2563
|
-
bitsets.push(applyEqBitset(f.eq, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2564
|
-
}
|
|
2565
|
-
if ("ne" in f && f.ne !== void 0) {
|
|
2566
|
-
bitsets.push(applyNeBitset(f.ne, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2567
|
-
}
|
|
2568
|
-
for (const operator of TEXT_RANGE_OPERATORS) {
|
|
2569
|
-
const bound = f[operator];
|
|
2570
|
-
if (typeof bound === "string") bitsets.push(context.textRangeBitset(fieldPath, textBoundsOf(operator, bound)));
|
|
2571
|
-
}
|
|
2572
|
-
if (typeof f.gt === "number") {
|
|
2573
|
-
bitsets.push(applyGtBitset(f.gt, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2574
|
-
}
|
|
2575
|
-
if (typeof f.lt === "number") {
|
|
2576
|
-
bitsets.push(applyLtBitset(f.lt, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2577
|
-
}
|
|
2578
|
-
if (typeof f.gte === "number") {
|
|
2579
|
-
bitsets.push(applyGteBitset(f.gte, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2580
|
-
}
|
|
2581
|
-
if (typeof f.lte === "number") {
|
|
2582
|
-
bitsets.push(applyLteBitset(f.lte, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2583
|
-
}
|
|
2584
|
-
if (Array.isArray(f.between)) {
|
|
2585
|
-
const [low, high] = f.between;
|
|
2586
|
-
if (typeof low === "string" && typeof high === "string") {
|
|
2587
|
-
bitsets.push(
|
|
2588
|
-
context.textRangeBitset(fieldPath, { lower: low, lowerInclusive: true, upper: high, upperInclusive: true })
|
|
2589
|
-
);
|
|
2590
|
-
} else {
|
|
2591
|
-
bitsets.push(
|
|
2592
|
-
applyBetweenBitset([low, high], getAllDocsBitset, capacity, getValue, fieldIndex)
|
|
2593
|
-
);
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
if ("in" in f && f.in !== void 0) {
|
|
2597
|
-
bitsets.push(applyInBitset(f.in, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2598
|
-
}
|
|
2599
|
-
if ("nin" in f && f.nin !== void 0) {
|
|
2600
|
-
bitsets.push(applyNinBitset(f.nin, getAllDocsBitset, capacity, getValue, fieldIndex));
|
|
2601
|
-
}
|
|
2602
|
-
if ("startsWith" in f && f.startsWith !== void 0) {
|
|
2603
|
-
bitsets.push(applyStartsWithBitset(f.startsWith, getAllDocsBitset, capacity, getValue));
|
|
2604
|
-
}
|
|
2605
|
-
if ("endsWith" in f && f.endsWith !== void 0) {
|
|
2606
|
-
bitsets.push(applyEndsWithBitset(f.endsWith, getAllDocsBitset, capacity, getValue));
|
|
2607
|
-
}
|
|
2608
|
-
if ("containsAll" in f && f.containsAll !== void 0) {
|
|
2609
|
-
bitsets.push(
|
|
2610
|
-
applyContainsAllBitset(f.containsAll, getAllDocsBitset, capacity, getValue)
|
|
2611
|
-
);
|
|
2612
|
-
}
|
|
2613
|
-
if ("matchesAny" in f && f.matchesAny !== void 0) {
|
|
2614
|
-
bitsets.push(
|
|
2615
|
-
applyMatchesAnyBitset(f.matchesAny, getAllDocsBitset, capacity, getValue)
|
|
2616
|
-
);
|
|
2617
|
-
}
|
|
2618
|
-
if ("size" in f && f.size !== void 0) {
|
|
2619
|
-
bitsets.push(applySizeBitset(f.size, getAllDocsBitset, capacity, getValue));
|
|
2620
|
-
}
|
|
2621
|
-
if ("exists" in f && f.exists !== void 0) {
|
|
2622
|
-
bitsets.push(
|
|
2623
|
-
f.exists ? applyExistsBitset(getAllDocsBitset, capacity, getValue) : applyNotExistsBitset(getAllDocsBitset, capacity, getValue)
|
|
2624
|
-
);
|
|
2625
|
-
}
|
|
2626
|
-
if ("notExists" in f && f.notExists !== void 0) {
|
|
2627
|
-
bitsets.push(
|
|
2628
|
-
f.notExists ? applyNotExistsBitset(getAllDocsBitset, capacity, getValue) : applyExistsBitset(getAllDocsBitset, capacity, getValue)
|
|
2629
|
-
);
|
|
2630
|
-
}
|
|
2631
|
-
if ("isEmpty" in f && f.isEmpty !== void 0) {
|
|
2632
|
-
bitsets.push(
|
|
2633
|
-
f.isEmpty ? applyIsEmptyBitset(getAllDocsBitset, capacity, getValue) : applyIsNotEmptyBitset(getAllDocsBitset, capacity, getValue)
|
|
2634
|
-
);
|
|
2635
|
-
}
|
|
2636
|
-
if ("isNotEmpty" in f && f.isNotEmpty !== void 0) {
|
|
2637
|
-
bitsets.push(
|
|
2638
|
-
f.isNotEmpty ? applyIsNotEmptyBitset(getAllDocsBitset, capacity, getValue) : applyIsEmptyBitset(getAllDocsBitset, capacity, getValue)
|
|
2639
|
-
);
|
|
2640
|
-
}
|
|
2641
|
-
if (bitsets.length === 0) return context.allDocIdsBitset;
|
|
2642
|
-
if (bitsets.length === 1) return bitsets[0];
|
|
2643
|
-
return applyAndBitset(bitsets);
|
|
2644
|
-
}
|
|
2645
|
-
function resolveGeoIndex(fieldPath, fieldIndex) {
|
|
2646
|
-
if (fieldIndex?.type !== "geopoint") {
|
|
2647
|
-
throw new NarsilError(
|
|
2648
|
-
ErrorCodes.SEARCH_INVALID_FILTER,
|
|
2649
|
-
`Field "${fieldPath}" requires a geopoint index for geo filters`,
|
|
2650
|
-
{ fieldPath }
|
|
2651
|
-
);
|
|
2273
|
+
const kept = context.allDocIdsBitset.slice();
|
|
2274
|
+
bitsetAndNotInto(kept, excluded);
|
|
2275
|
+
narrowing.narrow(kept);
|
|
2652
2276
|
}
|
|
2653
|
-
return
|
|
2277
|
+
return narrowing.value(() => context.allDocIdsBitset);
|
|
2654
2278
|
}
|
|
2655
2279
|
|
|
2656
2280
|
// src/core/partition/constants.ts
|
|
2657
2281
|
var TARGET_BLOCK_ENTRIES = 128;
|
|
2658
2282
|
var PRUNING_REJECTION_SLACK = 1e-12;
|
|
2659
2283
|
var SCORE_BUFFER_INITIAL_TOUCHED_CAPACITY = 1024;
|
|
2284
|
+
var SEGMENT_TOKEN_TABLE_START = 1 << 12;
|
|
2285
|
+
var SEGMENT_POSTING_LOG_START = 1 << 14;
|
|
2286
|
+
var SEGMENT_POSITION_POOL_START = 1 << 16;
|
|
2287
|
+
var SEGMENT_FIELD_SCRATCH_START = 1 << 10;
|
|
2660
2288
|
var SORT_COLUMN_MINIMUM_REBUILD_THRESHOLD = 1024;
|
|
2661
2289
|
var SORT_COLUMN_REBUILD_FRACTION_SHIFT = 2;
|
|
2662
2290
|
|
|
@@ -3273,99 +2901,72 @@ function sortColumnBytes(state) {
|
|
|
3273
2901
|
}
|
|
3274
2902
|
|
|
3275
2903
|
// src/core/partition/filters.ts
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
2904
|
+
function presentBitsetOf(state, fieldPath, type, capacity) {
|
|
2905
|
+
const base = parsedTypeOf(type)?.base;
|
|
2906
|
+
let present;
|
|
2907
|
+
if (base === "number") present = state.numericIndexes.get(fieldPath)?.getAllDocIdsBitset(capacity);
|
|
2908
|
+
else if (base === "boolean") present = state.booleanIndexes.get(fieldPath)?.getAllDocIdsBitset(capacity);
|
|
2909
|
+
else if (base === "enum") present = state.enumIndexes.get(fieldPath)?.getAllDocIdsBitset(capacity);
|
|
2910
|
+
else if (base === "geopoint") present = state.geoIndexes.get(fieldPath)?.documentsBitset(capacity);
|
|
2911
|
+
else if (base !== void 0 && keepsValueSize(base)) {
|
|
2912
|
+
present = state.sizeIndexes.get(fieldPath)?.getAllDocIdsBitset(capacity);
|
|
2913
|
+
}
|
|
2914
|
+
return present ?? createBitSet(capacity);
|
|
2915
|
+
}
|
|
2916
|
+
function liveDocumentsBitset(state, capacity) {
|
|
2917
|
+
const bits = createBitSet(capacity);
|
|
2918
|
+
for (const internalId of state.docStore.allInternalIds()) bitsetSet(bits, internalId);
|
|
2919
|
+
return bits;
|
|
2920
|
+
}
|
|
2921
|
+
function buildFilterContext(state, schema, meter) {
|
|
3281
2922
|
const flat = getFlatSchema(state, schema);
|
|
3282
|
-
const fieldIndexes = {};
|
|
3283
2923
|
const capacity = state.docStore.internalIdCapacity();
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
if (numIdx) {
|
|
3288
|
-
fieldIndexes[fieldPath] = {
|
|
3289
|
-
type: "numeric",
|
|
3290
|
-
index: {
|
|
3291
|
-
eq: (v) => numIdx.queryEq(v),
|
|
3292
|
-
gt: (v) => numIdx.queryGt(v),
|
|
3293
|
-
gte: (v) => numIdx.queryGte(v),
|
|
3294
|
-
lt: (v) => numIdx.queryLt(v),
|
|
3295
|
-
lte: (v) => numIdx.queryLte(v),
|
|
3296
|
-
between: (min, max) => numIdx.queryBetween(min, max),
|
|
3297
|
-
allDocIds: () => numIdx.getAllDocIds(),
|
|
3298
|
-
eqBitset: (v, cap) => numIdx.queryEqBitset(v, cap),
|
|
3299
|
-
gtBitset: (v, cap) => numIdx.queryGtBitset(v, cap),
|
|
3300
|
-
gteBitset: (v, cap) => numIdx.queryGteBitset(v, cap),
|
|
3301
|
-
ltBitset: (v, cap) => numIdx.queryLtBitset(v, cap),
|
|
3302
|
-
lteBitset: (v, cap) => numIdx.queryLteBitset(v, cap),
|
|
3303
|
-
betweenBitset: (min, max, cap) => numIdx.queryBetweenBitset(min, max, cap),
|
|
3304
|
-
allDocIdsBitset: (cap) => numIdx.getAllDocIdsBitset(cap)
|
|
3305
|
-
}
|
|
3306
|
-
};
|
|
3307
|
-
}
|
|
3308
|
-
} else if (fieldType === "boolean" || fieldType === "boolean[]") {
|
|
3309
|
-
const boolIdx = state.booleanIndexes.get(fieldPath);
|
|
3310
|
-
if (boolIdx) {
|
|
3311
|
-
fieldIndexes[fieldPath] = {
|
|
3312
|
-
type: "boolean",
|
|
3313
|
-
index: {
|
|
3314
|
-
getTrue: () => boolIdx.queryEq(true),
|
|
3315
|
-
getFalse: () => boolIdx.queryEq(false),
|
|
3316
|
-
allDocIds: () => boolIdx.getAllDocIds(),
|
|
3317
|
-
getTrueBitset: (cap) => boolIdx.queryEqBitset(true, cap),
|
|
3318
|
-
getFalseBitset: (cap) => boolIdx.queryEqBitset(false, cap),
|
|
3319
|
-
allDocIdsBitset: (cap) => boolIdx.getAllDocIdsBitset(cap)
|
|
3320
|
-
}
|
|
3321
|
-
};
|
|
3322
|
-
}
|
|
3323
|
-
} else if (fieldType === "enum" || fieldType === "enum[]") {
|
|
3324
|
-
const enumIdx = state.enumIndexes.get(fieldPath);
|
|
3325
|
-
if (enumIdx) {
|
|
3326
|
-
fieldIndexes[fieldPath] = {
|
|
3327
|
-
type: "enum",
|
|
3328
|
-
index: {
|
|
3329
|
-
getDocIds: (v) => enumIdx.queryEq(v),
|
|
3330
|
-
allDocIds: () => enumIdx.getAllDocIds(),
|
|
3331
|
-
getDocIdsBitset: (v, cap) => enumIdx.queryEqBitset(v, cap),
|
|
3332
|
-
getDocIdsInBitset: (values, cap) => enumIdx.queryInBitset(values, cap),
|
|
3333
|
-
allDocIdsBitset: (cap) => enumIdx.getAllDocIdsBitset(cap)
|
|
3334
|
-
}
|
|
3335
|
-
};
|
|
3336
|
-
}
|
|
3337
|
-
} else if (fieldType === "geopoint") {
|
|
3338
|
-
const geoIdx = state.geoIndexes.get(fieldPath);
|
|
3339
|
-
fieldIndexes[fieldPath] = {
|
|
3340
|
-
type: "geopoint",
|
|
3341
|
-
index: geoIdx === void 0 ? EMPTY_GEO_INDEX : geoIdx
|
|
3342
|
-
};
|
|
3343
|
-
}
|
|
2924
|
+
const patternFields = /* @__PURE__ */ new Set();
|
|
2925
|
+
for (const { path: fieldPath, pattern } of schemaFieldsOf(schema)) {
|
|
2926
|
+
if (pattern) patternFields.add(fieldPath);
|
|
3344
2927
|
}
|
|
3345
|
-
|
|
2928
|
+
const getFieldValue = (internalId, fieldPath) => getFieldValueByInternalId(state.docStore, internalId, fieldPath);
|
|
3346
2929
|
let cachedAllDocIdsBitset = null;
|
|
2930
|
+
const cachedPresentBitsets = /* @__PURE__ */ new Map();
|
|
3347
2931
|
return {
|
|
3348
2932
|
fieldTypes: flat,
|
|
3349
|
-
|
|
3350
|
-
|
|
2933
|
+
numericIndex: (fieldPath) => state.numericIndexes.get(fieldPath),
|
|
2934
|
+
booleanIndex: (fieldPath) => state.booleanIndexes.get(fieldPath),
|
|
2935
|
+
enumIndex: (fieldPath) => state.enumIndexes.get(fieldPath),
|
|
2936
|
+
geoIndex: (fieldPath) => state.geoIndexes.get(fieldPath),
|
|
2937
|
+
sizeIndex: (fieldPath) => state.sizeIndexes.get(fieldPath),
|
|
3351
2938
|
textRangeBitset: (fieldPath, bounds) => textRangeBitsetOf(state, fieldPath, flat[fieldPath], bounds, capacity),
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
2939
|
+
isPatternField: (fieldPath) => patternFields.has(fieldPath),
|
|
2940
|
+
matchPattern: (fieldPath, operator, text, caseFold, into) => {
|
|
2941
|
+
const index = state.patternIndexes.get(fieldPath);
|
|
2942
|
+
if (index === void 0) return;
|
|
2943
|
+
requirePatternSearch(fieldPath).matchInto({
|
|
2944
|
+
operator,
|
|
2945
|
+
text,
|
|
2946
|
+
caseFold,
|
|
2947
|
+
index,
|
|
2948
|
+
matches: into,
|
|
2949
|
+
meter,
|
|
2950
|
+
host: PATTERN_HOST,
|
|
2951
|
+
valueOf: (internalId) => getFieldValue(internalId, fieldPath)
|
|
2952
|
+
});
|
|
2953
|
+
},
|
|
2954
|
+
presentBitset: (fieldPath) => {
|
|
2955
|
+
const cached = cachedPresentBitsets.get(fieldPath);
|
|
2956
|
+
if (cached !== void 0) return cached;
|
|
2957
|
+
const present = presentBitsetOf(state, fieldPath, flat[fieldPath], capacity);
|
|
2958
|
+
cachedPresentBitsets.set(fieldPath, present);
|
|
2959
|
+
return present;
|
|
3357
2960
|
},
|
|
3358
2961
|
capacity,
|
|
3359
2962
|
get allDocIdsBitset() {
|
|
3360
|
-
|
|
3361
|
-
cachedAllDocIdsBitset = bitsetFromSet(this.allDocIds, capacity);
|
|
3362
|
-
}
|
|
2963
|
+
cachedAllDocIdsBitset ??= liveDocumentsBitset(state, capacity);
|
|
3363
2964
|
return cachedAllDocIdsBitset;
|
|
3364
2965
|
}
|
|
3365
2966
|
};
|
|
3366
2967
|
}
|
|
3367
|
-
function applyPartitionFilters(state, filters, schema) {
|
|
3368
|
-
const context = buildFilterContext(state, schema);
|
|
2968
|
+
function applyPartitionFilters(state, filters, schema, meter) {
|
|
2969
|
+
const context = buildFilterContext(state, schema, meter);
|
|
3369
2970
|
const resultBitset = evaluateFilters(filters, context);
|
|
3370
2971
|
const resolver = state.docStore.resolver();
|
|
3371
2972
|
const externalResult = /* @__PURE__ */ new Set();
|
|
@@ -3385,12 +2986,12 @@ function applyPartitionFilters(state, filters, schema) {
|
|
|
3385
2986
|
}
|
|
3386
2987
|
return externalResult;
|
|
3387
2988
|
}
|
|
3388
|
-
function applyPartitionFiltersBitset(state, filters, schema) {
|
|
3389
|
-
const context = buildFilterContext(state, schema);
|
|
2989
|
+
function applyPartitionFiltersBitset(state, filters, schema, meter) {
|
|
2990
|
+
const context = buildFilterContext(state, schema, meter);
|
|
3390
2991
|
return evaluateFilters(filters, context);
|
|
3391
2992
|
}
|
|
3392
|
-
function partitionFilterMatches(state, filters, schema) {
|
|
3393
|
-
const accepted = applyPartitionFiltersBitset(state, filters, schema);
|
|
2993
|
+
function partitionFilterMatches(state, filters, schema, meter) {
|
|
2994
|
+
const accepted = applyPartitionFiltersBitset(state, filters, schema, meter);
|
|
3394
2995
|
const docStore = state.docStore;
|
|
3395
2996
|
const resolver = docStore.resolver();
|
|
3396
2997
|
let count = 0;
|
|
@@ -3547,6 +3148,8 @@ function estimatePartitionBytes(state) {
|
|
|
3547
3148
|
bytes += docCount * state.booleanIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
|
|
3548
3149
|
bytes += docCount * state.enumIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
|
|
3549
3150
|
bytes += docCount * state.geoIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
|
|
3151
|
+
bytes += docCount * state.sizeIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
|
|
3152
|
+
for (const index of state.patternIndexes.values()) bytes += index.bytes();
|
|
3550
3153
|
return bytes + sortColumnBytes(state);
|
|
3551
3154
|
}
|
|
3552
3155
|
|
|
@@ -3606,12 +3209,12 @@ function mergeSurfaceForms(target, source) {
|
|
|
3606
3209
|
target.surfaceRegistry.add(surface, value[1], value[0]);
|
|
3607
3210
|
}
|
|
3608
3211
|
}
|
|
3609
|
-
function
|
|
3610
|
-
for (const [fieldPath, index] of source
|
|
3611
|
-
let targetIndex = target.
|
|
3212
|
+
function mergeNumericIndexes(target, source, idMapping) {
|
|
3213
|
+
for (const [fieldPath, index] of source) {
|
|
3214
|
+
let targetIndex = target.get(fieldPath);
|
|
3612
3215
|
if (targetIndex === void 0) {
|
|
3613
3216
|
targetIndex = createNumericIndex();
|
|
3614
|
-
target.
|
|
3217
|
+
target.set(fieldPath, targetIndex);
|
|
3615
3218
|
}
|
|
3616
3219
|
for (const entry of index.serialize()) {
|
|
3617
3220
|
const targetInternalId = idMapping.get(entry.docId);
|
|
@@ -3619,6 +3222,10 @@ function mergeFieldIndexes(target, source, idMapping) {
|
|
|
3619
3222
|
targetIndex.insert(targetInternalId, entry.value);
|
|
3620
3223
|
}
|
|
3621
3224
|
}
|
|
3225
|
+
}
|
|
3226
|
+
function mergeFieldIndexes(target, source, idMapping) {
|
|
3227
|
+
mergeNumericIndexes(target.numericIndexes, source.numericIndexes, idMapping);
|
|
3228
|
+
mergeNumericIndexes(target.sizeIndexes, source.sizeIndexes, idMapping);
|
|
3622
3229
|
for (const [fieldPath, index] of source.booleanIndexes) {
|
|
3623
3230
|
let targetIndex = target.booleanIndexes.get(fieldPath);
|
|
3624
3231
|
if (targetIndex === void 0) {
|
|
@@ -3660,6 +3267,12 @@ function mergeFieldIndexes(target, source, idMapping) {
|
|
|
3660
3267
|
targetIndex.insert(targetInternalId, entry.lat, entry.lon);
|
|
3661
3268
|
}
|
|
3662
3269
|
}
|
|
3270
|
+
for (const [fieldPath, index] of source.patternIndexes) {
|
|
3271
|
+
patternIndexOf(target.patternIndexes, fieldPath).addArrays(
|
|
3272
|
+
index.toArrays(fieldPath, null),
|
|
3273
|
+
(sourceInternalId) => idMapping.get(sourceInternalId) ?? -1
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3663
3276
|
}
|
|
3664
3277
|
function mergeSegmentState(target, source) {
|
|
3665
3278
|
if (source.stats.totalDocuments === 0 && source.docStore.count() === 0) return;
|
|
@@ -4680,19 +4293,23 @@ function searchFulltext(state, params) {
|
|
|
4680
4293
|
}
|
|
4681
4294
|
|
|
4682
4295
|
// src/core/partition/segment-field-indexes.ts
|
|
4683
|
-
function
|
|
4684
|
-
const
|
|
4685
|
-
const
|
|
4686
|
-
|
|
4687
|
-
const entries = index.serialize().filter((entry) => survives(entry.docId));
|
|
4296
|
+
function encodeNumericIndexes(indexes, remap) {
|
|
4297
|
+
const encoded = [];
|
|
4298
|
+
for (const [fieldPath, index] of indexes) {
|
|
4299
|
+
const entries = index.serialize().filter((entry) => remap[entry.docId] >= 0);
|
|
4688
4300
|
const docIds = new Uint32Array(entries.length);
|
|
4689
4301
|
const values = new Float64Array(entries.length);
|
|
4690
4302
|
for (let i = 0; i < entries.length; i++) {
|
|
4691
4303
|
docIds[i] = remap[entries[i].docId];
|
|
4692
4304
|
values[i] = entries[i].value;
|
|
4693
4305
|
}
|
|
4694
|
-
|
|
4306
|
+
encoded.push({ fieldPath, docIds, values });
|
|
4695
4307
|
}
|
|
4308
|
+
return encoded;
|
|
4309
|
+
}
|
|
4310
|
+
function encodeFieldIndexes(state, remap) {
|
|
4311
|
+
const survives = (internalId) => remap[internalId] >= 0;
|
|
4312
|
+
const numeric = encodeNumericIndexes(state.numericIndexes, remap);
|
|
4696
4313
|
const booleans = [];
|
|
4697
4314
|
for (const [fieldPath, index] of state.booleanIndexes) {
|
|
4698
4315
|
const { trueDocs, falseDocs } = index.serialize();
|
|
@@ -4733,7 +4350,9 @@ function encodeFieldIndexes(state, remap) {
|
|
|
4733
4350
|
}
|
|
4734
4351
|
geo.push({ fieldPath, docIds, latitudes, longitudes });
|
|
4735
4352
|
}
|
|
4736
|
-
|
|
4353
|
+
const pattern = [];
|
|
4354
|
+
for (const [fieldPath, index] of state.patternIndexes) pattern.push(index.toArrays(fieldPath, remap));
|
|
4355
|
+
return { numeric, boolean: booleans, enums, geo, pattern, sizes: encodeNumericIndexes(state.sizeIndexes, remap) };
|
|
4737
4356
|
}
|
|
4738
4357
|
|
|
4739
4358
|
// src/core/partition/segment-payload.ts
|
|
@@ -4799,7 +4418,7 @@ function encodeSegmentState(state) {
|
|
|
4799
4418
|
const postingOffsets = new Uint32Array(state.invertedIdx.size() + 1);
|
|
4800
4419
|
const postingDocIds = new Uint32Array(postings);
|
|
4801
4420
|
const postingFrequencies = new Uint16Array(postings);
|
|
4802
|
-
const postingFieldIndices = new
|
|
4421
|
+
const postingFieldIndices = new Uint16Array(postings);
|
|
4803
4422
|
const positionOffsets = hasPositions ? new Uint32Array(postings + 1) : null;
|
|
4804
4423
|
const positionValues = hasPositions ? new Uint32Array(positions) : null;
|
|
4805
4424
|
let postingCursor = 0;
|
|
@@ -4890,15 +4509,19 @@ function mergePayloadPostings(target, payload, ordinalBase) {
|
|
|
4890
4509
|
}
|
|
4891
4510
|
}
|
|
4892
4511
|
}
|
|
4893
|
-
function
|
|
4894
|
-
for (const entry of
|
|
4895
|
-
let index = target.
|
|
4512
|
+
function mergePayloadNumericIndexes(target, entries, ordinalBase) {
|
|
4513
|
+
for (const entry of entries) {
|
|
4514
|
+
let index = target.get(entry.fieldPath);
|
|
4896
4515
|
if (index === void 0) {
|
|
4897
4516
|
index = createNumericIndex();
|
|
4898
|
-
target.
|
|
4517
|
+
target.set(entry.fieldPath, index);
|
|
4899
4518
|
}
|
|
4900
4519
|
for (let i = 0; i < entry.docIds.length; i++) index.insert(ordinalBase + entry.docIds[i], entry.values[i]);
|
|
4901
4520
|
}
|
|
4521
|
+
}
|
|
4522
|
+
function mergePayloadFieldIndexes(target, payload, ordinalBase) {
|
|
4523
|
+
mergePayloadNumericIndexes(target.numericIndexes, payload.numeric, ordinalBase);
|
|
4524
|
+
mergePayloadNumericIndexes(target.sizeIndexes, payload.sizes, ordinalBase);
|
|
4902
4525
|
for (const entry of payload.boolean) {
|
|
4903
4526
|
let index = target.booleanIndexes.get(entry.fieldPath);
|
|
4904
4527
|
if (index === void 0) {
|
|
@@ -4929,6 +4552,9 @@ function mergePayloadFieldIndexes(target, payload, ordinalBase) {
|
|
|
4929
4552
|
index.insert(ordinalBase + entry.docIds[i], entry.latitudes[i], entry.longitudes[i]);
|
|
4930
4553
|
}
|
|
4931
4554
|
}
|
|
4555
|
+
for (const entry of payload.pattern) {
|
|
4556
|
+
patternIndexOf(target.patternIndexes, entry.fieldPath).addArrays(entry, (ordinal) => ordinalBase + ordinal);
|
|
4557
|
+
}
|
|
4932
4558
|
}
|
|
4933
4559
|
function mergeSegmentPayload(target, payload, documents) {
|
|
4934
4560
|
if (payload.documentCount === 0) return;
|
|
@@ -4973,6 +4599,123 @@ function mergeSegmentPayload(target, payload, documents) {
|
|
|
4973
4599
|
target.scoreBuffer = null;
|
|
4974
4600
|
}
|
|
4975
4601
|
|
|
4602
|
+
// src/core/partition/pattern-snapshot.ts
|
|
4603
|
+
function loadFailed(fieldPath, problem) {
|
|
4604
|
+
return new NarsilError(
|
|
4605
|
+
ErrorCodes.PERSISTENCE_LOAD_FAILED,
|
|
4606
|
+
`The pattern index of field "${fieldPath}" in this partition is corrupt: ${problem}`,
|
|
4607
|
+
{ field: fieldPath }
|
|
4608
|
+
);
|
|
4609
|
+
}
|
|
4610
|
+
function serializePatternIndex(state, fieldPath, index) {
|
|
4611
|
+
const resolver = state.docStore.resolver();
|
|
4612
|
+
const arrays = index.toArrays(fieldPath, null);
|
|
4613
|
+
const positionOf = /* @__PURE__ */ new Map();
|
|
4614
|
+
const docIds = [];
|
|
4615
|
+
for (const ordinal of arrays.docIds) {
|
|
4616
|
+
const externalId = resolver.toExternal(ordinal);
|
|
4617
|
+
if (externalId === void 0) continue;
|
|
4618
|
+
positionOf.set(ordinal, docIds.length);
|
|
4619
|
+
docIds.push(externalId);
|
|
4620
|
+
}
|
|
4621
|
+
const runs = /* @__PURE__ */ Object.create(null);
|
|
4622
|
+
for (let run = 0; run + 1 < arrays.offsets.length; run++) {
|
|
4623
|
+
const positions = [];
|
|
4624
|
+
for (let at = arrays.offsets[run]; at < arrays.offsets[run + 1]; at++) {
|
|
4625
|
+
const position = positionOf.get(arrays.postings[at]);
|
|
4626
|
+
if (position !== void 0) positions.push(position);
|
|
4627
|
+
}
|
|
4628
|
+
if (positions.length === 0) continue;
|
|
4629
|
+
const base = run * PATTERN_RUN_CODE_POINTS;
|
|
4630
|
+
runs[String.fromCodePoint(arrays.runs[base], arrays.runs[base + 1], arrays.runs[base + 2])] = positions;
|
|
4631
|
+
}
|
|
4632
|
+
return { docIds, runs };
|
|
4633
|
+
}
|
|
4634
|
+
function serializePatternIndexes(state, schema) {
|
|
4635
|
+
const patternFields = schemaFieldsOf(schema).filter((field) => field.pattern);
|
|
4636
|
+
if (patternFields.length === 0) return void 0;
|
|
4637
|
+
const serialized = /* @__PURE__ */ Object.create(null);
|
|
4638
|
+
for (const { path: fieldPath } of patternFields) {
|
|
4639
|
+
const index = state.patternIndexes.get(fieldPath);
|
|
4640
|
+
serialized[fieldPath] = index === void 0 ? { docIds: [], runs: {} } : serializePatternIndex(state, fieldPath, index);
|
|
4641
|
+
}
|
|
4642
|
+
return serialized;
|
|
4643
|
+
}
|
|
4644
|
+
function appendRunCodePoints(fieldPath, key, out) {
|
|
4645
|
+
const start = out.length;
|
|
4646
|
+
for (const character of key) out.push(character.codePointAt(0) ?? 0);
|
|
4647
|
+
const count = out.length - start;
|
|
4648
|
+
if (count !== PATTERN_RUN_CODE_POINTS) {
|
|
4649
|
+
throw loadFailed(
|
|
4650
|
+
fieldPath,
|
|
4651
|
+
`the key "${key}" holds ${count} code points, while each key must hold ${PATTERN_RUN_CODE_POINTS}`
|
|
4652
|
+
);
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
function requirePositions(fieldPath, key, list, documentCount) {
|
|
4656
|
+
if (!Array.isArray(list)) throw loadFailed(fieldPath, `the run "${key}" must map to a list of positions`);
|
|
4657
|
+
let previous = -1;
|
|
4658
|
+
for (const position of list) {
|
|
4659
|
+
if (!Number.isInteger(position) || position < 0 || position >= documentCount) {
|
|
4660
|
+
throw loadFailed(fieldPath, `the run "${key}" names position ${String(position)} outside its document list`);
|
|
4661
|
+
}
|
|
4662
|
+
if (position <= previous) {
|
|
4663
|
+
throw loadFailed(fieldPath, `the positions of the run "${key}" are out of ascending order`);
|
|
4664
|
+
}
|
|
4665
|
+
previous = position;
|
|
4666
|
+
}
|
|
4667
|
+
return list;
|
|
4668
|
+
}
|
|
4669
|
+
function requireDistinctDocuments(fieldPath, docIds) {
|
|
4670
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4671
|
+
for (const docId of docIds) {
|
|
4672
|
+
if (seen.has(docId)) throw loadFailed(fieldPath, `the document list names "${docId}" twice`);
|
|
4673
|
+
seen.add(docId);
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
function decodePatternEntry(fieldPath, entry) {
|
|
4677
|
+
requireDistinctDocuments(fieldPath, entry.docIds);
|
|
4678
|
+
const runs = [];
|
|
4679
|
+
const offsets = [0];
|
|
4680
|
+
const postings = [];
|
|
4681
|
+
for (const [key, list] of Object.entries(entry.runs)) {
|
|
4682
|
+
appendRunCodePoints(fieldPath, key, runs);
|
|
4683
|
+
for (const position of requirePositions(fieldPath, key, list, entry.docIds.length)) postings.push(position);
|
|
4684
|
+
offsets.push(postings.length);
|
|
4685
|
+
}
|
|
4686
|
+
const positions = new Uint32Array(entry.docIds.length);
|
|
4687
|
+
for (let position = 0; position < positions.length; position++) positions[position] = position;
|
|
4688
|
+
return {
|
|
4689
|
+
fieldPath,
|
|
4690
|
+
docIds: positions,
|
|
4691
|
+
runs: Uint32Array.from(runs),
|
|
4692
|
+
offsets: Uint32Array.from(offsets),
|
|
4693
|
+
postings: Uint32Array.from(postings)
|
|
4694
|
+
};
|
|
4695
|
+
}
|
|
4696
|
+
function rebuildPatternIndex(state, fieldPath) {
|
|
4697
|
+
for (const [docId, stored] of state.docStore.all()) {
|
|
4698
|
+
const value = getNestedValue(stored.fields, fieldPath);
|
|
4699
|
+
if (value === void 0 || value === null) continue;
|
|
4700
|
+
const internalId = state.docStore.getInternalId(docId);
|
|
4701
|
+
if (internalId !== void 0) patternIndexOf(state.patternIndexes, fieldPath).add(internalId, value);
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
function loadPatternIndexes(state, serialized, schema) {
|
|
4705
|
+
const resolver = state.docStore.resolver();
|
|
4706
|
+
for (const { path: fieldPath, pattern } of schemaFieldsOf(schema)) {
|
|
4707
|
+
if (!pattern) continue;
|
|
4708
|
+
const entry = serialized !== void 0 && Object.hasOwn(serialized, fieldPath) ? serialized[fieldPath] : void 0;
|
|
4709
|
+
if (entry === void 0) {
|
|
4710
|
+
rebuildPatternIndex(state, fieldPath);
|
|
4711
|
+
continue;
|
|
4712
|
+
}
|
|
4713
|
+
const decoded = decodePatternEntry(fieldPath, entry);
|
|
4714
|
+
const ordinals = entry.docIds.map((docId) => resolver.toInternal(docId) ?? -1);
|
|
4715
|
+
patternIndexOf(state.patternIndexes, fieldPath).addArrays(decoded, (position) => ordinals[position]);
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4976
4719
|
// src/core/partition/serialization.ts
|
|
4977
4720
|
function serializePartition(state, partitionId, indexName, totalPartitions, language, schema) {
|
|
4978
4721
|
const flatSchema = getFlatSchema(state, schema);
|
|
@@ -5045,6 +4788,7 @@ function serializePartition(state, partitionId, indexName, totalPartitions, lang
|
|
|
5045
4788
|
}));
|
|
5046
4789
|
}
|
|
5047
4790
|
const serializedStats = state.stats.serialize();
|
|
4791
|
+
const serializedPattern = serializePatternIndexes(state, schema);
|
|
5048
4792
|
return {
|
|
5049
4793
|
indexName,
|
|
5050
4794
|
partitionId,
|
|
@@ -5059,7 +4803,8 @@ function serializePartition(state, partitionId, indexName, totalPartitions, lang
|
|
|
5059
4803
|
numeric: serializedNumeric,
|
|
5060
4804
|
boolean: serializedBoolean,
|
|
5061
4805
|
enum: serializedEnum,
|
|
5062
|
-
geopoint: serializedGeo
|
|
4806
|
+
geopoint: serializedGeo,
|
|
4807
|
+
...serializedPattern === void 0 ? {} : { pattern: serializedPattern }
|
|
5063
4808
|
},
|
|
5064
4809
|
surfaceForms: state.surfaceRegistry.serialize(),
|
|
5065
4810
|
statistics: serializedStats
|
|
@@ -5137,6 +4882,8 @@ function deserializePartition(state, data, clearFn, schema) {
|
|
|
5137
4882
|
throw new Error(`Schema mismatch: serialized data contains unknown field "${field}" not present in schema`);
|
|
5138
4883
|
}
|
|
5139
4884
|
}
|
|
4885
|
+
loadPatternIndexes(state, data.fieldIndexes.pattern, schema);
|
|
4886
|
+
rebuildSizeIndexes(state, schema);
|
|
5140
4887
|
}
|
|
5141
4888
|
|
|
5142
4889
|
// src/core/partition/suggestions.ts
|
|
@@ -5175,140 +4922,34 @@ function suggestDisplayTerms(state, surfacePrefix, stemmedPrefix, limit) {
|
|
|
5175
4922
|
}
|
|
5176
4923
|
}
|
|
5177
4924
|
const results = [];
|
|
5178
|
-
for (const [token, group] of groups) {
|
|
5179
|
-
results.push({ token, documentFrequency: group.documentFrequency, surfaces: group.surfaces });
|
|
5180
|
-
}
|
|
5181
|
-
results.sort((a, b) => b.documentFrequency - a.documentFrequency || compareCodePoints(a.token, b.token));
|
|
5182
|
-
if (results.length > limit) results.length = limit;
|
|
5183
|
-
return results;
|
|
5184
|
-
}
|
|
5185
|
-
function expandTermPrefix(state, surfacePrefix, stemmedToken, maxExpansions) {
|
|
5186
|
-
if (maxExpansions <= 0 || surfacePrefix.length === 0) return [];
|
|
5187
|
-
const dfByToken = /* @__PURE__ */ new Map();
|
|
5188
|
-
for (const candidate of state.surfaceRegistry.candidatesForPrefix(surfacePrefix)) {
|
|
5189
|
-
if (dfByToken.has(candidate.token)) continue;
|
|
5190
|
-
const postingList = state.invertedIdx.lookup(candidate.token);
|
|
5191
|
-
if (!postingList || postingList.docIdSet.size === 0) continue;
|
|
5192
|
-
dfByToken.set(candidate.token, postingList.docIdSet.size);
|
|
5193
|
-
}
|
|
5194
|
-
const termPrefixes = state.surfaceRegistry.size() === 0 && stemmedToken !== surfacePrefix && stemmedToken.length > 0 ? [surfacePrefix, stemmedToken] : [surfacePrefix];
|
|
5195
|
-
for (const prefix of termPrefixes) {
|
|
5196
|
-
for (const suggestion of state.invertedIdx.prefixSearch(prefix, Number.MAX_SAFE_INTEGER)) {
|
|
5197
|
-
if (dfByToken.has(suggestion.term)) continue;
|
|
5198
|
-
if (verbatimOccurrences(state, suggestion.term) === 0) continue;
|
|
5199
|
-
dfByToken.set(suggestion.term, suggestion.documentFrequency);
|
|
5200
|
-
}
|
|
5201
|
-
}
|
|
5202
|
-
const entries = Array.from(dfByToken.entries());
|
|
5203
|
-
entries.sort((a, b) => b[1] - a[1] || compareCodePoints(a[0], b[0]));
|
|
5204
|
-
if (entries.length > maxExpansions) entries.length = maxExpansions;
|
|
5205
|
-
return entries.map((e) => e[0]);
|
|
5206
|
-
}
|
|
5207
|
-
|
|
5208
|
-
// src/core/partition/wire-payload.ts
|
|
5209
|
-
function serializePartitionToWirePayloadV2(state, partitionId, indexName, totalPartitions, language, schema) {
|
|
5210
|
-
const flatSchema = getFlatSchema(state, schema);
|
|
5211
|
-
const flatSchemaStrings = {};
|
|
5212
|
-
for (const [k, v] of Object.entries(flatSchema)) {
|
|
5213
|
-
flatSchemaStrings[k] = v;
|
|
5214
|
-
}
|
|
5215
|
-
const wireDocs = /* @__PURE__ */ Object.create(null);
|
|
5216
|
-
for (const [docId, stored] of state.docStore.all()) {
|
|
5217
|
-
wireDocs[docId] = {
|
|
5218
|
-
fields: stored.fields,
|
|
5219
|
-
field_lengths: { ...stored.fieldLengths }
|
|
5220
|
-
};
|
|
5221
|
-
}
|
|
5222
|
-
const resolver = state.docStore.resolver();
|
|
5223
|
-
const fieldNames = [...state.fieldNameTable.names];
|
|
5224
|
-
const wireEntries = /* @__PURE__ */ Object.create(null);
|
|
5225
|
-
for (const token of state.invertedIdx.tokens()) {
|
|
5226
|
-
const list = state.invertedIdx.lookup(token);
|
|
5227
|
-
if (!list) continue;
|
|
5228
|
-
const hasDeleted = list.deletedDocs.size > 0;
|
|
5229
|
-
const ids = [];
|
|
5230
|
-
const tf = [];
|
|
5231
|
-
const fi = new Uint8Array(list.length);
|
|
5232
|
-
const pos = list.positions ? [] : null;
|
|
5233
|
-
let writeIdx = 0;
|
|
5234
|
-
for (let i = 0; i < list.length; i++) {
|
|
5235
|
-
if (hasDeleted && list.deletedDocs.has(list.docIds[i])) continue;
|
|
5236
|
-
const externalId = resolver.toExternal(list.docIds[i]);
|
|
5237
|
-
if (externalId === void 0) continue;
|
|
5238
|
-
ids.push(externalId);
|
|
5239
|
-
tf.push(list.termFrequencies[i]);
|
|
5240
|
-
fi[writeIdx] = list.fieldNameIndices[i];
|
|
5241
|
-
writeIdx++;
|
|
5242
|
-
if (pos && list.positions) {
|
|
5243
|
-
pos.push(list.positions[i]);
|
|
5244
|
-
}
|
|
5245
|
-
}
|
|
5246
|
-
if (ids.length > 0) {
|
|
5247
|
-
const finalFi = writeIdx < list.length ? fi.subarray(0, writeIdx) : fi;
|
|
5248
|
-
wireEntries[token] = { df: list.docIdSet.size, ids, tf, fi: finalFi, pos };
|
|
5249
|
-
}
|
|
5250
|
-
}
|
|
5251
|
-
const wireNumeric = {};
|
|
5252
|
-
for (const [path, idx] of state.numericIndexes) {
|
|
5253
|
-
wireNumeric[path] = idx.serialize().map((e) => ({
|
|
5254
|
-
value: e.value,
|
|
5255
|
-
doc_id: resolver.toExternal(e.docId) ?? ""
|
|
5256
|
-
}));
|
|
5257
|
-
}
|
|
5258
|
-
const wireBoolean = {};
|
|
5259
|
-
for (const [path, idx] of state.booleanIndexes) {
|
|
5260
|
-
const s = idx.serialize();
|
|
5261
|
-
wireBoolean[path] = {
|
|
5262
|
-
true_docs: s.trueDocs.map((id) => resolver.toExternal(id) ?? "").filter((id) => id !== ""),
|
|
5263
|
-
false_docs: s.falseDocs.map((id) => resolver.toExternal(id) ?? "").filter((id) => id !== "")
|
|
5264
|
-
};
|
|
5265
|
-
}
|
|
5266
|
-
const wireEnum = {};
|
|
5267
|
-
for (const [path, idx] of state.enumIndexes) {
|
|
5268
|
-
const raw = idx.serialize();
|
|
5269
|
-
const converted = /* @__PURE__ */ Object.create(null);
|
|
5270
|
-
for (const [value, internalIds] of Object.entries(raw)) {
|
|
5271
|
-
converted[value] = internalIds.map((id) => resolver.toExternal(id) ?? "").filter((id) => id !== "");
|
|
5272
|
-
}
|
|
5273
|
-
wireEnum[path] = converted;
|
|
4925
|
+
for (const [token, group] of groups) {
|
|
4926
|
+
results.push({ token, documentFrequency: group.documentFrequency, surfaces: group.surfaces });
|
|
5274
4927
|
}
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
4928
|
+
results.sort((a, b) => b.documentFrequency - a.documentFrequency || compareCodePoints(a.token, b.token));
|
|
4929
|
+
if (results.length > limit) results.length = limit;
|
|
4930
|
+
return results;
|
|
4931
|
+
}
|
|
4932
|
+
function expandTermPrefix(state, surfacePrefix, stemmedToken, maxExpansions) {
|
|
4933
|
+
if (maxExpansions <= 0 || surfacePrefix.length === 0) return [];
|
|
4934
|
+
const dfByToken = /* @__PURE__ */ new Map();
|
|
4935
|
+
for (const candidate of state.surfaceRegistry.candidatesForPrefix(surfacePrefix)) {
|
|
4936
|
+
if (dfByToken.has(candidate.token)) continue;
|
|
4937
|
+
const postingList = state.invertedIdx.lookup(candidate.token);
|
|
4938
|
+
if (!postingList || postingList.docIdSet.size === 0) continue;
|
|
4939
|
+
dfByToken.set(candidate.token, postingList.docIdSet.size);
|
|
5282
4940
|
}
|
|
5283
|
-
const
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
language,
|
|
5290
|
-
schema: flatSchemaStrings,
|
|
5291
|
-
doc_count: state.docStore.count(),
|
|
5292
|
-
avg_doc_length: Object.values(serializedStats.averageFieldLengths).reduce((sum, v) => sum + v, 0),
|
|
5293
|
-
documents: wireDocs,
|
|
5294
|
-
inverted_index: {
|
|
5295
|
-
field_names: fieldNames,
|
|
5296
|
-
entries: wireEntries
|
|
5297
|
-
},
|
|
5298
|
-
field_indexes: {
|
|
5299
|
-
numeric: wireNumeric,
|
|
5300
|
-
boolean: wireBoolean,
|
|
5301
|
-
enum: wireEnum,
|
|
5302
|
-
geopoint: wireGeo
|
|
5303
|
-
},
|
|
5304
|
-
surface_forms: state.surfaceRegistry.serialize(),
|
|
5305
|
-
statistics: {
|
|
5306
|
-
total_documents: serializedStats.totalDocuments,
|
|
5307
|
-
total_field_lengths: serializedStats.totalFieldLengths,
|
|
5308
|
-
average_field_lengths: serializedStats.averageFieldLengths,
|
|
5309
|
-
doc_frequencies: serializedStats.docFrequencies
|
|
4941
|
+
const termPrefixes = state.surfaceRegistry.size() === 0 && stemmedToken !== surfacePrefix && stemmedToken.length > 0 ? [surfacePrefix, stemmedToken] : [surfacePrefix];
|
|
4942
|
+
for (const prefix of termPrefixes) {
|
|
4943
|
+
for (const suggestion of state.invertedIdx.prefixSearch(prefix, Number.MAX_SAFE_INTEGER)) {
|
|
4944
|
+
if (dfByToken.has(suggestion.term)) continue;
|
|
4945
|
+
if (verbatimOccurrences(state, suggestion.term) === 0) continue;
|
|
4946
|
+
dfByToken.set(suggestion.term, suggestion.documentFrequency);
|
|
5310
4947
|
}
|
|
5311
|
-
}
|
|
4948
|
+
}
|
|
4949
|
+
const entries = Array.from(dfByToken.entries());
|
|
4950
|
+
entries.sort((a, b) => b[1] - a[1] || compareCodePoints(a[0], b[0]));
|
|
4951
|
+
if (entries.length > maxExpansions) entries.length = maxExpansions;
|
|
4952
|
+
return entries.map((e) => e[0]);
|
|
5312
4953
|
}
|
|
5313
4954
|
|
|
5314
4955
|
// src/core/partition/index.ts
|
|
@@ -5334,6 +4975,8 @@ function createPartitionIndex(partitionId, trackPositions = true) {
|
|
|
5334
4975
|
booleanIndexes: /* @__PURE__ */ new Map(),
|
|
5335
4976
|
enumIndexes: /* @__PURE__ */ new Map(),
|
|
5336
4977
|
geoIndexes: /* @__PURE__ */ new Map(),
|
|
4978
|
+
patternIndexes: /* @__PURE__ */ new Map(),
|
|
4979
|
+
sizeIndexes: /* @__PURE__ */ new Map(),
|
|
5337
4980
|
fieldNameTable,
|
|
5338
4981
|
flatSchemaCache: null,
|
|
5339
4982
|
lastSchemaRef: null,
|
|
@@ -5353,6 +4996,8 @@ function createPartitionIndex(partitionId, trackPositions = true) {
|
|
|
5353
4996
|
state.booleanIndexes.clear();
|
|
5354
4997
|
state.enumIndexes.clear();
|
|
5355
4998
|
state.geoIndexes.clear();
|
|
4999
|
+
state.patternIndexes.clear();
|
|
5000
|
+
state.sizeIndexes.clear();
|
|
5356
5001
|
state.stats.deserialize({ totalDocuments: 0, totalFieldLengths: {}, averageFieldLengths: {}, docFrequencies: {} });
|
|
5357
5002
|
state.flatSchemaCache = null;
|
|
5358
5003
|
state.lastSchemaRef = null;
|
|
@@ -5528,14 +5173,14 @@ function createPartitionIndex(partitionId, trackPositions = true) {
|
|
|
5528
5173
|
sortValues(docId, fields, fieldTypes, modes) {
|
|
5529
5174
|
return sortValuesOf(state, docId, fields, fieldTypes, modes);
|
|
5530
5175
|
},
|
|
5531
|
-
applyFilters(filters, schema) {
|
|
5532
|
-
return applyPartitionFilters(state, filters, schema);
|
|
5176
|
+
applyFilters(filters, schema, meter) {
|
|
5177
|
+
return applyPartitionFilters(state, filters, schema, meter);
|
|
5533
5178
|
},
|
|
5534
|
-
applyFiltersBitset(filters, schema) {
|
|
5535
|
-
return applyPartitionFiltersBitset(state, filters, schema);
|
|
5179
|
+
applyFiltersBitset(filters, schema, meter) {
|
|
5180
|
+
return applyPartitionFiltersBitset(state, filters, schema, meter);
|
|
5536
5181
|
},
|
|
5537
|
-
filterMatches(filters, schema) {
|
|
5538
|
-
return partitionFilterMatches(state, filters, schema);
|
|
5182
|
+
filterMatches(filters, schema, meter) {
|
|
5183
|
+
return partitionFilterMatches(state, filters, schema, meter);
|
|
5539
5184
|
},
|
|
5540
5185
|
computeFacets(matched, config, schema) {
|
|
5541
5186
|
return computeFacets(state, matched, config, schema);
|
|
@@ -5549,10 +5194,6 @@ function createPartitionIndex(partitionId, trackPositions = true) {
|
|
|
5549
5194
|
serialize(indexName, totalPartitions, language, schema) {
|
|
5550
5195
|
return serializePartition(state, partitionId, indexName, totalPartitions, language, schema);
|
|
5551
5196
|
},
|
|
5552
|
-
serializeToBytes(indexName, totalPartitions, language, schema) {
|
|
5553
|
-
const wire = serializePartitionToWirePayloadV2(state, partitionId, indexName, totalPartitions, language, schema);
|
|
5554
|
-
return encodeRawPayloadV2(wire);
|
|
5555
|
-
},
|
|
5556
5197
|
deserialize(data, schema) {
|
|
5557
5198
|
deserializePartition(state, data, clearAll, schema);
|
|
5558
5199
|
}
|
|
@@ -5575,12 +5216,13 @@ function frozenSegmentBytes(segment) {
|
|
|
5575
5216
|
bytes += arrays.documentTable.blob.byteLength + arrays.documentTable.offsets.byteLength;
|
|
5576
5217
|
}
|
|
5577
5218
|
for (const column of arrays.fieldLengthColumns) bytes += column.byteLength;
|
|
5578
|
-
for (const entry of arrays.numeric) bytes += entry.docIds.byteLength + entry.values.byteLength;
|
|
5219
|
+
for (const entry of [...arrays.numeric, ...arrays.sizes]) bytes += entry.docIds.byteLength + entry.values.byteLength;
|
|
5579
5220
|
for (const entry of arrays.boolean) bytes += entry.trueDocs.byteLength + entry.falseDocs.byteLength;
|
|
5580
5221
|
for (const entry of arrays.enums) bytes += entry.offsets.byteLength + entry.docIds.byteLength;
|
|
5581
5222
|
for (const entry of arrays.geo) {
|
|
5582
5223
|
bytes += entry.docIds.byteLength + entry.latitudes.byteLength + entry.longitudes.byteLength;
|
|
5583
5224
|
}
|
|
5225
|
+
for (const reader of segment.patternIndexes.values()) bytes += reader.bytes();
|
|
5584
5226
|
return bytes;
|
|
5585
5227
|
}
|
|
5586
5228
|
|
|
@@ -5658,24 +5300,24 @@ function placeSubBitset(composite, layout, subIndex, subBits) {
|
|
|
5658
5300
|
if (words === 0) return;
|
|
5659
5301
|
composite.set(subBits.length > words ? subBits.subarray(0, words) : subBits, layout.bases[subIndex] >> 5);
|
|
5660
5302
|
}
|
|
5661
|
-
function compositeFiltersBitset(subs, layout, filters, schema) {
|
|
5303
|
+
function compositeFiltersBitset(subs, layout, filters, schema, meter) {
|
|
5662
5304
|
const composite = new Uint32Array(Math.ceil(layout.totalCapacity / WORD_BITS));
|
|
5663
5305
|
for (let i = 0; i < subs.length; i++) {
|
|
5664
|
-
placeSubBitset(composite, layout, i, applyPartitionFiltersBitset(subs[i], filters, schema));
|
|
5306
|
+
placeSubBitset(composite, layout, i, applyPartitionFiltersBitset(subs[i], filters, schema, meter));
|
|
5665
5307
|
}
|
|
5666
5308
|
return composite;
|
|
5667
5309
|
}
|
|
5668
|
-
function compositeFilters(subs, filters, schema) {
|
|
5310
|
+
function compositeFilters(subs, filters, schema, meter) {
|
|
5669
5311
|
const combined = /* @__PURE__ */ new Set();
|
|
5670
5312
|
for (const sub of subs) {
|
|
5671
|
-
for (const docId of applyPartitionFilters(sub, filters, schema)) {
|
|
5313
|
+
for (const docId of applyPartitionFilters(sub, filters, schema, meter)) {
|
|
5672
5314
|
combined.add(docId);
|
|
5673
5315
|
}
|
|
5674
5316
|
}
|
|
5675
5317
|
return combined;
|
|
5676
5318
|
}
|
|
5677
|
-
function compositeFilterMatches(subs, layout, filters, schema) {
|
|
5678
|
-
const bits = compositeFiltersBitset(subs, layout, filters, schema);
|
|
5319
|
+
function compositeFilterMatches(subs, layout, filters, schema, meter) {
|
|
5320
|
+
const bits = compositeFiltersBitset(subs, layout, filters, schema, meter);
|
|
5679
5321
|
let count = 0;
|
|
5680
5322
|
for (let i = 0; i < subs.length; i++) {
|
|
5681
5323
|
const resolver = subs[i].docStore.resolver();
|
|
@@ -6074,10 +5716,6 @@ function createCompositePartition(partitionId, trackPositions = true, existingLi
|
|
|
6074
5716
|
if (frozen.length === 0) return live.serialize(indexName, totalPartitions, language, schema);
|
|
6075
5717
|
return composedScratch().serialize(indexName, totalPartitions, language, schema);
|
|
6076
5718
|
},
|
|
6077
|
-
serializeToBytes(indexName, totalPartitions, language, schema) {
|
|
6078
|
-
if (frozen.length === 0) return live.serializeToBytes(indexName, totalPartitions, language, schema);
|
|
6079
|
-
return composedScratch().serializeToBytes(indexName, totalPartitions, language, schema);
|
|
6080
|
-
},
|
|
6081
5719
|
deserialize(data, schema) {
|
|
6082
5720
|
frozen.length = 0;
|
|
6083
5721
|
invalidateDocFrequencies();
|
|
@@ -6136,14 +5774,14 @@ function createCompositePartition(partitionId, trackPositions = true, existingLi
|
|
|
6136
5774
|
searchFulltextMatches(params) {
|
|
6137
5775
|
return compositeSearchMatches(subs(), layout(), params);
|
|
6138
5776
|
},
|
|
6139
|
-
applyFilters(filters, schema) {
|
|
6140
|
-
return compositeFilters(subs(), filters, schema);
|
|
5777
|
+
applyFilters(filters, schema, meter) {
|
|
5778
|
+
return compositeFilters(subs(), filters, schema, meter);
|
|
6141
5779
|
},
|
|
6142
|
-
applyFiltersBitset(filters, schema) {
|
|
6143
|
-
return compositeFiltersBitset(subs(), layout(), filters, schema);
|
|
5780
|
+
applyFiltersBitset(filters, schema, meter) {
|
|
5781
|
+
return compositeFiltersBitset(subs(), layout(), filters, schema, meter);
|
|
6144
5782
|
},
|
|
6145
|
-
filterMatches(filters, schema) {
|
|
6146
|
-
return compositeFilterMatches(subs(), layout(), filters, schema);
|
|
5783
|
+
filterMatches(filters, schema, meter) {
|
|
5784
|
+
return compositeFilterMatches(subs(), layout(), filters, schema, meter);
|
|
6147
5785
|
},
|
|
6148
5786
|
computeFacets(matched, config, schema) {
|
|
6149
5787
|
const everyValue = everyValueFacetConfig(config);
|
|
@@ -6294,6 +5932,7 @@ function createCapacityChecks(source) {
|
|
|
6294
5932
|
|
|
6295
5933
|
// src/partitioning/manager/index.ts
|
|
6296
5934
|
function createPartitionManager(indexName, config, language, router, initialPartitionCount, vectorIndexes) {
|
|
5935
|
+
requirePatternSearchFor(config.schema);
|
|
6297
5936
|
const count = initialPartitionCount ?? 1;
|
|
6298
5937
|
const analysis = resolveIndexAnalysis(config);
|
|
6299
5938
|
const trackPositions = config.trackPositions ?? true;
|
|
@@ -6507,10 +6146,6 @@ function createPartitionManager(indexName, config, language, router, initialPart
|
|
|
6507
6146
|
validatePartitionId(partitionId);
|
|
6508
6147
|
return partitions[partitionId].serialize(indexName, partitions.length, language.name, config.schema);
|
|
6509
6148
|
},
|
|
6510
|
-
serializePartitionToBytes(partitionId) {
|
|
6511
|
-
validatePartitionId(partitionId);
|
|
6512
|
-
return partitions[partitionId].serializeToBytes(indexName, partitions.length, language.name, config.schema);
|
|
6513
|
-
},
|
|
6514
6149
|
mergeSegment(partitionId, payload, documents) {
|
|
6515
6150
|
validatePartitionId(partitionId);
|
|
6516
6151
|
const partition = partitions[partitionId];
|
|
@@ -6636,6 +6271,9 @@ var WORKER_COPY_MIN_VECTORS = 1024;
|
|
|
6636
6271
|
var BUILD_CHUNK_SIZE = 100;
|
|
6637
6272
|
var REBUILD_REMOVED_RATIO = 0.2;
|
|
6638
6273
|
var VECTOR_FILE_REPLACED_ABOVE_DEAD_SHARE = 0.2;
|
|
6274
|
+
var VECTOR_FILE_FIXED_OVERHEAD_BYTES = 65536;
|
|
6275
|
+
var VECTOR_FILE_STRING_HEADER_BYTES = 5;
|
|
6276
|
+
var VECTOR_FILE_CENTROID_BYTES_PER_COMPONENT = 9;
|
|
6639
6277
|
var OSQ4_MIN_DIMENSION = 384;
|
|
6640
6278
|
var DISK_ADOPTION_YIELD_INTERVAL = 4096;
|
|
6641
6279
|
|
|
@@ -6695,7 +6333,7 @@ function yieldToEventLoop() {
|
|
|
6695
6333
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
6696
6334
|
}
|
|
6697
6335
|
function* allLiveDocIds(state) {
|
|
6698
|
-
for (const
|
|
6336
|
+
for (const docId of state.store.docIds()) {
|
|
6699
6337
|
if (state.tombstones.has(docId)) continue;
|
|
6700
6338
|
yield docId;
|
|
6701
6339
|
}
|
|
@@ -6816,14 +6454,6 @@ async function nodeThreadId() {
|
|
|
6816
6454
|
return 0;
|
|
6817
6455
|
}
|
|
6818
6456
|
}
|
|
6819
|
-
async function isNodeMainThread() {
|
|
6820
|
-
try {
|
|
6821
|
-
const workerThreads = await import('worker_threads');
|
|
6822
|
-
return workerThreads.isMainThread;
|
|
6823
|
-
} catch {
|
|
6824
|
-
return true;
|
|
6825
|
-
}
|
|
6826
|
-
}
|
|
6827
6457
|
|
|
6828
6458
|
// src/workers/entry-point.ts
|
|
6829
6459
|
var DIST_SEGMENT = "/dist/";
|
|
@@ -7588,6 +7218,12 @@ function scheduleBuild(state) {
|
|
|
7588
7218
|
}, 0);
|
|
7589
7219
|
}
|
|
7590
7220
|
|
|
7221
|
+
// src/serialization/constants.ts
|
|
7222
|
+
var CHECKSUM_YIELD_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
7223
|
+
var NATIVE_CRC32_MIN_BYTES = 16;
|
|
7224
|
+
var MAX_FILE_BYTES = 2147483647;
|
|
7225
|
+
var VALID_HNSW_METRICS = /* @__PURE__ */ new Set(["cosine", "dotProduct", "euclidean"]);
|
|
7226
|
+
|
|
7591
7227
|
// src/vector/vector-index/payload.ts
|
|
7592
7228
|
var VECTOR_INDEX_PAYLOAD_VERSION = 3;
|
|
7593
7229
|
var VECTOR_INDEX_PART_VECTORS = 65536;
|
|
@@ -7614,7 +7250,7 @@ function invalid(message, details) {
|
|
|
7614
7250
|
function isRecord(value) {
|
|
7615
7251
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7616
7252
|
}
|
|
7617
|
-
function
|
|
7253
|
+
function isNonNegativeInteger(value) {
|
|
7618
7254
|
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
7619
7255
|
}
|
|
7620
7256
|
function isSerializedGraph(value) {
|
|
@@ -7644,9 +7280,9 @@ function decodeVectorIndexPart(raw) {
|
|
|
7644
7280
|
);
|
|
7645
7281
|
}
|
|
7646
7282
|
if (typeof raw.fieldName !== "string") invalid("fieldName must be a string");
|
|
7647
|
-
if (!
|
|
7648
|
-
if (!
|
|
7649
|
-
if (!
|
|
7283
|
+
if (!isNonNegativeInteger(raw.dimension) || raw.dimension === 0) invalid("dimension must be a positive integer");
|
|
7284
|
+
if (!isNonNegativeInteger(raw.part)) invalid("part must be a non-negative integer");
|
|
7285
|
+
if (!isNonNegativeInteger(raw.parts) || raw.parts === 0 || raw.part >= raw.parts) {
|
|
7650
7286
|
invalid("parts must exceed part", { part: raw.part, parts: raw.parts });
|
|
7651
7287
|
}
|
|
7652
7288
|
if (!Array.isArray(raw.docIds) || !raw.docIds.every((docId) => typeof docId === "string")) {
|
|
@@ -7685,6 +7321,8 @@ function decodeVectorIndexParts(raw) {
|
|
|
7685
7321
|
// src/vector/vector-index/checkpoint-payload.ts
|
|
7686
7322
|
var VECTOR_FILE_PAYLOAD_VERSION = 1;
|
|
7687
7323
|
var VECTOR_GRAPH_PAYLOAD_VERSION = 1;
|
|
7324
|
+
var VECTOR_GRAPH_PART_PAYLOAD_VERSION = 2;
|
|
7325
|
+
var VECTOR_GRAPH_PART_VECTORS = 16384;
|
|
7688
7326
|
var VECTOR_FILE_MAX_VECTORS = VECTOR_INDEX_PART_VECTORS;
|
|
7689
7327
|
var METRICS = ["cosine", "dotProduct", "euclidean"];
|
|
7690
7328
|
function invalid2(payload, message, details) {
|
|
@@ -7735,14 +7373,16 @@ function decodeVectorFilePayload(raw) {
|
|
|
7735
7373
|
function isMetric(value) {
|
|
7736
7374
|
return METRICS.some((metric) => metric === value);
|
|
7737
7375
|
}
|
|
7738
|
-
function decodeNumberedGraph(raw) {
|
|
7376
|
+
function decodeNumberedGraph(raw, minimumM) {
|
|
7739
7377
|
const name = "vector graph";
|
|
7740
7378
|
if (!isRecord2(raw)) invalid2(name, "each graph must be a map");
|
|
7741
7379
|
const entryPoint = raw.entryPoint ?? null;
|
|
7742
7380
|
if (entryPoint !== null && !isIntegerUpTo(entryPoint, 4294967295))
|
|
7743
7381
|
invalid2(name, "entryPoint must be a uint32 or nil");
|
|
7744
7382
|
if (!isIntegerUpTo(raw.maxLayer, 255)) invalid2(name, "maxLayer must be a uint8");
|
|
7745
|
-
if (!isIntegerUpTo(raw.m,
|
|
7383
|
+
if (!isIntegerUpTo(raw.m, MAX_M) || raw.m < minimumM) {
|
|
7384
|
+
invalid2(name, `m must be a whole number from ${minimumM} to ${MAX_M}`);
|
|
7385
|
+
}
|
|
7746
7386
|
if (!isIntegerUpTo(raw.efConstruction, 65535)) invalid2(name, "efConstruction must be a uint16");
|
|
7747
7387
|
if (!isMetric(raw.metric)) invalid2(name, "metric must name a supported metric", { metric: raw.metric });
|
|
7748
7388
|
if (!(raw.levels instanceof Uint8Array)) invalid2(name, "levels must be bytes");
|
|
@@ -7763,7 +7403,56 @@ function decodeVectorGraphPayload(raw) {
|
|
|
7763
7403
|
requireVersion(name, raw.v, VECTOR_GRAPH_PAYLOAD_VERSION);
|
|
7764
7404
|
if (typeof raw.fieldName !== "string") invalid2(name, "fieldName must be a string");
|
|
7765
7405
|
if (!Array.isArray(raw.graphs)) invalid2(name, "graphs must be a list");
|
|
7766
|
-
return {
|
|
7406
|
+
return {
|
|
7407
|
+
v: VECTOR_GRAPH_PAYLOAD_VERSION,
|
|
7408
|
+
fieldName: raw.fieldName,
|
|
7409
|
+
graphs: raw.graphs.map((graph) => decodeNumberedGraph(graph, EARLIER_RELEASE_MIN_M))
|
|
7410
|
+
};
|
|
7411
|
+
}
|
|
7412
|
+
function decodeVectorGraphPartPayload(raw) {
|
|
7413
|
+
const name = "vector graph part";
|
|
7414
|
+
if (!isRecord2(raw)) invalid2(name, "payload must be a map");
|
|
7415
|
+
requireVersion(name, raw.v, VECTOR_GRAPH_PART_PAYLOAD_VERSION);
|
|
7416
|
+
if (typeof raw.fieldName !== "string") invalid2(name, "fieldName must be a string");
|
|
7417
|
+
if (!Array.isArray(raw.graphs)) invalid2(name, "graphs must be a list");
|
|
7418
|
+
return {
|
|
7419
|
+
v: VECTOR_GRAPH_PART_PAYLOAD_VERSION,
|
|
7420
|
+
fieldName: raw.fieldName,
|
|
7421
|
+
graphs: raw.graphs.map((graph) => decodeNumberedGraph(graph, MIN_M))
|
|
7422
|
+
};
|
|
7423
|
+
}
|
|
7424
|
+
function sameGraphHeader(a, b) {
|
|
7425
|
+
return a.entryPoint === b.entryPoint && a.maxLayer === b.maxLayer && a.m === b.m && a.efConstruction === b.efConstruction && a.metric === b.metric;
|
|
7426
|
+
}
|
|
7427
|
+
function assembleGraphParts(parts) {
|
|
7428
|
+
const name = "vector graph part";
|
|
7429
|
+
const first = parts[0];
|
|
7430
|
+
if (first === void 0) invalid2(name, "a field that holds a graph must have at least one part");
|
|
7431
|
+
const partLists = first.graphs.map(() => []);
|
|
7432
|
+
for (let p = 0; p < parts.length; p++) {
|
|
7433
|
+
const part = parts[p];
|
|
7434
|
+
const isLast = p === parts.length - 1;
|
|
7435
|
+
if (part.fieldName !== first.fieldName || part.graphs.length !== first.graphs.length) {
|
|
7436
|
+
invalid2(name, `part ${p} lists other graphs than part 0`, { part: p });
|
|
7437
|
+
}
|
|
7438
|
+
for (let g = 0; g < part.graphs.length; g++) {
|
|
7439
|
+
const graph = part.graphs[g];
|
|
7440
|
+
if (!sameGraphHeader(graph, first.graphs[g]))
|
|
7441
|
+
invalid2(name, `part ${p} gives graph ${g} other headers`, { part: p });
|
|
7442
|
+
const levels = graph.levels.length;
|
|
7443
|
+
const fits = isLast ? levels >= 1 && levels <= VECTOR_GRAPH_PART_VECTORS : levels === VECTOR_GRAPH_PART_VECTORS;
|
|
7444
|
+
if (!fits) invalid2(name, `part ${p} gives graph ${g} ${levels} bytes of levels`, { part: p, levels });
|
|
7445
|
+
partLists[g].push(graph);
|
|
7446
|
+
}
|
|
7447
|
+
}
|
|
7448
|
+
const graphs = partLists.map((list) => ({ partSize: VECTOR_GRAPH_PART_VECTORS, parts: list }));
|
|
7449
|
+
return { fieldName: first.fieldName, graphs };
|
|
7450
|
+
}
|
|
7451
|
+
function graphPartsOfEarlierPayload(payload) {
|
|
7452
|
+
return {
|
|
7453
|
+
fieldName: payload.fieldName,
|
|
7454
|
+
graphs: payload.graphs.map((graph) => ({ partSize: Math.max(graph.levels.length, 1), parts: [graph] }))
|
|
7455
|
+
};
|
|
7767
7456
|
}
|
|
7768
7457
|
|
|
7769
7458
|
// src/vector/vector-index/dead-bits.ts
|
|
@@ -7793,36 +7482,6 @@ function deadBitsWhere(count, isDeadAt) {
|
|
|
7793
7482
|
}
|
|
7794
7483
|
|
|
7795
7484
|
// src/vector/vector-index/persistence.ts
|
|
7796
|
-
function liveDocIds(state) {
|
|
7797
|
-
const docIds = [];
|
|
7798
|
-
for (let ordinal = 0; ordinal < state.store.slots; ordinal++) {
|
|
7799
|
-
const docId = state.store.docIdForOrdinal(ordinal);
|
|
7800
|
-
if (docId === void 0 || state.tombstones.has(docId)) continue;
|
|
7801
|
-
docIds.push(docId);
|
|
7802
|
-
}
|
|
7803
|
-
return docIds;
|
|
7804
|
-
}
|
|
7805
|
-
function partVectors(state, docIds) {
|
|
7806
|
-
const dimension = state.dimension;
|
|
7807
|
-
const vectors = new Float32Array(docIds.length * dimension);
|
|
7808
|
-
for (let i = 0; i < docIds.length; i++) {
|
|
7809
|
-
const entry = state.store.get(docIds[i]);
|
|
7810
|
-
if (entry !== void 0) vectors.set(entry.vector, i * dimension);
|
|
7811
|
-
}
|
|
7812
|
-
return vectors;
|
|
7813
|
-
}
|
|
7814
|
-
function graphsPerPart(state, partOf, parts) {
|
|
7815
|
-
const sliced = Array.from({ length: parts }, () => []);
|
|
7816
|
-
if (state.hnsw === null || partOf.size === 0) return sliced;
|
|
7817
|
-
const graph = state.hnsw.serialize();
|
|
7818
|
-
const nodesPerPart = Array.from({ length: parts }, () => []);
|
|
7819
|
-
for (const node of graph.nodes) {
|
|
7820
|
-
const part = partOf.get(node[0]);
|
|
7821
|
-
if (part !== void 0) nodesPerPart[part].push(node);
|
|
7822
|
-
}
|
|
7823
|
-
for (let part = 0; part < parts; part++) sliced[part].push({ ...graph, nodes: nodesPerPart[part] });
|
|
7824
|
-
return sliced;
|
|
7825
|
-
}
|
|
7826
7485
|
function recordFor(state, quantizer, docId) {
|
|
7827
7486
|
const record = quantizer.recordOf(docId);
|
|
7828
7487
|
if (record !== void 0) return record;
|
|
@@ -7861,36 +7520,6 @@ function partCodes(state, docIds, centroid) {
|
|
|
7861
7520
|
}
|
|
7862
7521
|
return { bits: quantizer.bits, centroid, records };
|
|
7863
7522
|
}
|
|
7864
|
-
function planParts(state) {
|
|
7865
|
-
const docIds = liveDocIds(state);
|
|
7866
|
-
const parts = Math.max(1, Math.ceil(docIds.length / VECTOR_INDEX_PART_VECTORS));
|
|
7867
|
-
const partOf = /* @__PURE__ */ new Map();
|
|
7868
|
-
for (let i = 0; i < docIds.length; i++) partOf.set(docIds[i], Math.floor(i / VECTOR_INDEX_PART_VECTORS));
|
|
7869
|
-
const graphs = graphsPerPart(state, partOf, parts);
|
|
7870
|
-
const centroid = codedCentroid(state);
|
|
7871
|
-
return {
|
|
7872
|
-
parts,
|
|
7873
|
-
readPart(part) {
|
|
7874
|
-
const start = part * VECTOR_INDEX_PART_VECTORS;
|
|
7875
|
-
const slice = docIds.slice(start, start + VECTOR_INDEX_PART_VECTORS);
|
|
7876
|
-
return {
|
|
7877
|
-
v: VECTOR_INDEX_PAYLOAD_VERSION,
|
|
7878
|
-
fieldName: state.fieldName,
|
|
7879
|
-
dimension: state.dimension,
|
|
7880
|
-
part,
|
|
7881
|
-
parts,
|
|
7882
|
-
docIds: slice,
|
|
7883
|
-
graphs: graphs[part],
|
|
7884
|
-
codes: partCodes(state, slice, centroid),
|
|
7885
|
-
vectors: vectorsToBytes(partVectors(state, slice))
|
|
7886
|
-
};
|
|
7887
|
-
}
|
|
7888
|
-
};
|
|
7889
|
-
}
|
|
7890
|
-
function serialize(state) {
|
|
7891
|
-
const plan = planParts(state);
|
|
7892
|
-
return Array.from({ length: plan.parts }, (_, part) => plan.readPart(part));
|
|
7893
|
-
}
|
|
7894
7523
|
function splitSequences(parts) {
|
|
7895
7524
|
const sequences = [];
|
|
7896
7525
|
let current = null;
|
|
@@ -7943,7 +7572,7 @@ function restoreCodes(state, sequences) {
|
|
|
7943
7572
|
}
|
|
7944
7573
|
function restoreGraphs(state, graphs) {
|
|
7945
7574
|
if (graphs.length === 0) {
|
|
7946
|
-
for (const
|
|
7575
|
+
for (const docId of state.store.docIds()) {
|
|
7947
7576
|
if (!state.tombstones.has(docId)) state.buffer.add(docId);
|
|
7948
7577
|
}
|
|
7949
7578
|
return;
|
|
@@ -7966,14 +7595,14 @@ function restoreGraphs(state, graphs) {
|
|
|
7966
7595
|
if (!restoredHnsw.has(nodeDocId) && state.store.has(nodeDocId)) restoredHnsw.insertNode(nodeDocId);
|
|
7967
7596
|
}
|
|
7968
7597
|
}
|
|
7969
|
-
for (const
|
|
7598
|
+
for (const docId of state.store.docIds()) {
|
|
7970
7599
|
if (state.tombstones.has(docId)) continue;
|
|
7971
7600
|
if (!restoredHnsw.has(docId)) state.buffer.add(docId);
|
|
7972
7601
|
}
|
|
7973
7602
|
if (state.buffer.size > restoredHnsw.size) {
|
|
7974
7603
|
adoptGraph(state, null);
|
|
7975
7604
|
state.buffer.clear();
|
|
7976
|
-
for (const
|
|
7605
|
+
for (const docId of state.store.docIds()) {
|
|
7977
7606
|
if (!state.tombstones.has(docId)) state.buffer.add(docId);
|
|
7978
7607
|
}
|
|
7979
7608
|
}
|
|
@@ -8012,6 +7641,17 @@ function deserialize(state, parts) {
|
|
|
8012
7641
|
|
|
8013
7642
|
// src/vector/vector-index/checkpoint-plan.ts
|
|
8014
7643
|
var ORDINAL_HOLDS_NO_NUMBER = -1;
|
|
7644
|
+
function vectorsPerFile(dimension, codeBits) {
|
|
7645
|
+
const recordBytes = codeBits === 1 || codeBits === 2 || codeBits === 4 || codeBits === 8 ? osqRecordBytes(dimension, codeBits) : 0;
|
|
7646
|
+
const docIdBytes = VECTOR_FILE_STRING_HEADER_BYTES + MAX_DOC_ID_LENGTH * 3;
|
|
7647
|
+
const perVector = dimension * 4 + recordBytes + docIdBytes;
|
|
7648
|
+
const overhead = VECTOR_FILE_FIXED_OVERHEAD_BYTES + dimension * VECTOR_FILE_CENTROID_BYTES_PER_COMPONENT;
|
|
7649
|
+
const fitting = Math.floor((MAX_FILE_BYTES - overhead) / perVector);
|
|
7650
|
+
return Math.max(1, Math.min(VECTOR_FILE_MAX_VECTORS, fitting));
|
|
7651
|
+
}
|
|
7652
|
+
function fileCapacityOf(state) {
|
|
7653
|
+
return vectorsPerFile(state.dimension, osqBitsOf(state.quantizationMode));
|
|
7654
|
+
}
|
|
8015
7655
|
function calibrationOf(state) {
|
|
8016
7656
|
if (codedCentroid(state) === null) return null;
|
|
8017
7657
|
return Atomics.load(state.store.handles.header, STORE_CALIBRATION_GENERATION);
|
|
@@ -8051,10 +7691,10 @@ function filesWorthKeeping(state, listed, calibration, liveCounts) {
|
|
|
8051
7691
|
}
|
|
8052
7692
|
return kept;
|
|
8053
7693
|
}
|
|
8054
|
-
function absorbSmallerPartialFiles(kept, liveCounts, vectorsToWrite) {
|
|
7694
|
+
function absorbSmallerPartialFiles(kept, liveCounts, vectorsToWrite, capacity) {
|
|
8055
7695
|
let pending = vectorsToWrite;
|
|
8056
7696
|
for (let index = kept.length - 1; index >= 0 && pending > 0; index--) {
|
|
8057
|
-
if (kept[index].ordinals.length >=
|
|
7697
|
+
if (kept[index].ordinals.length >= capacity) continue;
|
|
8058
7698
|
if (liveCounts[index] > pending) return;
|
|
8059
7699
|
pending += liveCounts[index];
|
|
8060
7700
|
kept.splice(index, 1);
|
|
@@ -8102,13 +7742,14 @@ function numberEveryVector(state, kept, written) {
|
|
|
8102
7742
|
}
|
|
8103
7743
|
return { numberOfOrdinal, ordinalOfNumber };
|
|
8104
7744
|
}
|
|
8105
|
-
function
|
|
8106
|
-
if (state.hnsw === null) return null;
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
7745
|
+
function graphPartsOf(state, numbers) {
|
|
7746
|
+
if (state.hnsw === null || numbers.ordinalOfNumber.length === 0) return null;
|
|
7747
|
+
const parts = state.hnsw.serializeNumbered(
|
|
7748
|
+
numbers.numberOfOrdinal,
|
|
7749
|
+
numbers.ordinalOfNumber,
|
|
7750
|
+
VECTOR_GRAPH_PART_VECTORS
|
|
7751
|
+
);
|
|
7752
|
+
return parts.map((graph) => ({ v: VECTOR_GRAPH_PART_PAYLOAD_VERSION, fieldName: state.fieldName, graphs: [graph] }));
|
|
8112
7753
|
}
|
|
8113
7754
|
function readFile(state, planned, docIds, centroid) {
|
|
8114
7755
|
const dimension = state.dimension;
|
|
@@ -8133,18 +7774,19 @@ function readFile(state, planned, docIds, centroid) {
|
|
|
8133
7774
|
ordinals
|
|
8134
7775
|
};
|
|
8135
7776
|
}
|
|
8136
|
-
function planCheckpoint(state, listedKeys) {
|
|
7777
|
+
function planCheckpoint(state, listedKeys, rewritesGraph = false) {
|
|
8137
7778
|
const signature = signatureOf(state);
|
|
8138
7779
|
const calibration = signature.calibration;
|
|
8139
7780
|
const trusted = registryMatches(state, listedKeys);
|
|
7781
|
+
const capacity = fileCapacityOf(state);
|
|
8140
7782
|
const liveCounts = [];
|
|
8141
7783
|
const keptFiles = filesWorthKeeping(state, trusted ? state.savedFiles : [], calibration, liveCounts);
|
|
8142
|
-
absorbSmallerPartialFiles(keptFiles, liveCounts, countOutside(state, liveCounts));
|
|
7784
|
+
absorbSmallerPartialFiles(keptFiles, liveCounts, countOutside(state, liveCounts), capacity);
|
|
8143
7785
|
const written = ordinalsOutsideOf(state, keptFiles);
|
|
8144
7786
|
const writtenDocIds = written.map((ordinal) => state.store.docIdForOrdinal(ordinal) ?? "");
|
|
8145
7787
|
const centroid = codedCentroid(state);
|
|
8146
7788
|
const listedBefore = trusted && listedKeys !== null;
|
|
8147
|
-
const unchanged = listedBefore && sameSignature(state.savedSignature, signature) && written.length === 0;
|
|
7789
|
+
const unchanged = !rewritesGraph && listedBefore && sameSignature(state.savedSignature, signature) && written.length === 0;
|
|
8148
7790
|
return {
|
|
8149
7791
|
unchanged,
|
|
8150
7792
|
keptFiles,
|
|
@@ -8154,12 +7796,12 @@ function planCheckpoint(state, listedKeys) {
|
|
|
8154
7796
|
count: file.ordinals.length,
|
|
8155
7797
|
dead: deadBitsOf(state, file)
|
|
8156
7798
|
})),
|
|
8157
|
-
newFiles: Math.ceil(written.length /
|
|
8158
|
-
|
|
7799
|
+
newFiles: Math.ceil(written.length / capacity),
|
|
7800
|
+
graphParts: unchanged ? null : graphPartsOf(state, numberEveryVector(state, keptFiles, written)),
|
|
8159
7801
|
signature,
|
|
8160
7802
|
readNewFile(index) {
|
|
8161
|
-
const start = index *
|
|
8162
|
-
const end = start +
|
|
7803
|
+
const start = index * capacity;
|
|
7804
|
+
const end = start + capacity;
|
|
8163
7805
|
return readFile(state, written.slice(start, end), writtenDocIds.slice(start, end), centroid);
|
|
8164
7806
|
}
|
|
8165
7807
|
};
|
|
@@ -8179,13 +7821,22 @@ function sameCentroid2(a, b) {
|
|
|
8179
7821
|
return true;
|
|
8180
7822
|
}
|
|
8181
7823
|
function bufferEveryVectorOutsideTheGraph(state) {
|
|
8182
|
-
for (const
|
|
7824
|
+
for (const docId of state.store.docIds()) {
|
|
8183
7825
|
if (state.tombstones.has(docId)) continue;
|
|
8184
7826
|
if (state.hnsw === null || !state.hnsw.has(docId)) state.buffer.add(docId);
|
|
8185
7827
|
}
|
|
8186
7828
|
}
|
|
7829
|
+
function insertNodesOfLaterGraph(state, graph, later, ordinals) {
|
|
7830
|
+
for (const part of later.parts) requireWholeNeighbourLists(part);
|
|
7831
|
+
for (let number = 0; number < ordinals.length; number++) {
|
|
7832
|
+
const ordinal = ordinals[number];
|
|
7833
|
+
if (ordinal === NO_LIVE_VECTOR_AT_POSITION || topLayerInParts(later, number) < 0) continue;
|
|
7834
|
+
const docId = state.store.docIdForOrdinal(ordinal);
|
|
7835
|
+
if (docId !== void 0 && !graph.has(docId)) graph.insertOrdinal(ordinal);
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
8187
7838
|
function restoreGraph(state, payload, ordinalOfNumber) {
|
|
8188
|
-
const first = payload?.graphs[0];
|
|
7839
|
+
const first = payload?.graphs[0]?.parts[0];
|
|
8189
7840
|
if (payload === null || first === void 0) {
|
|
8190
7841
|
bufferEveryVectorOutsideTheGraph(state);
|
|
8191
7842
|
return;
|
|
@@ -8196,17 +7847,9 @@ function restoreGraph(state, payload, ordinalOfNumber) {
|
|
|
8196
7847
|
{ m: first.m, efConstruction: first.efConstruction, metric: first.metric },
|
|
8197
7848
|
state.osq ?? void 0
|
|
8198
7849
|
);
|
|
8199
|
-
graph.deserializeNumbered(
|
|
7850
|
+
graph.deserializeNumbered(payload.graphs[0], ordinalOfNumber);
|
|
8200
7851
|
adoptGraph(state, graph);
|
|
8201
|
-
for (const later of payload.graphs.slice(1))
|
|
8202
|
-
requireWholeNeighbourLists(later);
|
|
8203
|
-
for (let number = 0; number < later.levels.length && number < ordinalOfNumber.length; number++) {
|
|
8204
|
-
const ordinal = ordinalOfNumber[number];
|
|
8205
|
-
if (later.levels[number] === 0 || ordinal === NO_LIVE_VECTOR_AT_POSITION) continue;
|
|
8206
|
-
const docId = state.store.docIdForOrdinal(ordinal);
|
|
8207
|
-
if (docId !== void 0 && !graph.has(docId)) graph.insertOrdinal(ordinal);
|
|
8208
|
-
}
|
|
8209
|
-
}
|
|
7852
|
+
for (const later of payload.graphs.slice(1)) insertNodesOfLaterGraph(state, graph, later, ordinalOfNumber);
|
|
8210
7853
|
bufferEveryVectorOutsideTheGraph(state);
|
|
8211
7854
|
if (state.buffer.size <= graph.size) return;
|
|
8212
7855
|
adoptGraph(state, null);
|
|
@@ -8887,12 +8530,11 @@ function createVectorIndex(fieldName, dimension, config, workerCopies = VECTOR_W
|
|
|
8887
8530
|
},
|
|
8888
8531
|
maintenanceStatus: () => maintenanceStatus(state),
|
|
8889
8532
|
estimateMemoryBytes: () => estimateMemoryBytes(state),
|
|
8890
|
-
serialize: () => serialize(state),
|
|
8891
8533
|
deserialize: (parts) => {
|
|
8892
8534
|
invalidateWorkerCopies(state);
|
|
8893
8535
|
deserialize(state, parts);
|
|
8894
8536
|
},
|
|
8895
|
-
planCheckpoint: (listedKeys) => planCheckpoint(state, listedKeys),
|
|
8537
|
+
planCheckpoint: (listedKeys, rewritesGraph) => planCheckpoint(state, listedKeys, rewritesGraph),
|
|
8896
8538
|
recordCheckpoint: (plan, written) => recordCheckpoint(state, plan, written),
|
|
8897
8539
|
restoreCheckpoint: (shape) => {
|
|
8898
8540
|
invalidateWorkerCopies(state);
|
|
@@ -9010,11 +8652,46 @@ function createFrozenDocTable(idTable, lengthColumns, documents, tombstones) {
|
|
|
9010
8652
|
}
|
|
9011
8653
|
};
|
|
9012
8654
|
}
|
|
8655
|
+
function removeFieldAtPath(document, fieldPath) {
|
|
8656
|
+
const segments = fieldPath.split(".");
|
|
8657
|
+
let current = document;
|
|
8658
|
+
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
8659
|
+
const next = current[segments[i]];
|
|
8660
|
+
if (next === null || typeof next !== "object" || Array.isArray(next)) return;
|
|
8661
|
+
current = next;
|
|
8662
|
+
}
|
|
8663
|
+
delete current[segments[segments.length - 1]];
|
|
8664
|
+
}
|
|
8665
|
+
function decodeStoredDocument(bytes, discardedFields) {
|
|
8666
|
+
let decoded;
|
|
8667
|
+
try {
|
|
8668
|
+
decoded = decode(bytes);
|
|
8669
|
+
} catch (error) {
|
|
8670
|
+
throw new NarsilError(
|
|
8671
|
+
ErrorCodes.PERSISTENCE_LOAD_FAILED,
|
|
8672
|
+
`A stored document is not MessagePack: ${error instanceof Error ? error.message : String(error)}`
|
|
8673
|
+
);
|
|
8674
|
+
}
|
|
8675
|
+
if (decoded === null || typeof decoded !== "object" || Array.isArray(decoded) || decoded instanceof Uint8Array) {
|
|
8676
|
+
throw new NarsilError(ErrorCodes.PERSISTENCE_LOAD_FAILED, "A stored document decodes to something other than a map");
|
|
8677
|
+
}
|
|
8678
|
+
const document = decoded;
|
|
8679
|
+
if (discardedFields !== void 0) {
|
|
8680
|
+
for (const fieldPath of discardedFields) removeFieldAtPath(document, fieldPath);
|
|
8681
|
+
}
|
|
8682
|
+
return document;
|
|
8683
|
+
}
|
|
8684
|
+
function decodeEveryStoredDocument(data) {
|
|
8685
|
+
const count = data.offsets.length - 1;
|
|
8686
|
+
for (let ordinal = 0; ordinal < count; ordinal++) {
|
|
8687
|
+
decodeStoredDocument(data.blob.subarray(data.offsets[ordinal], data.offsets[ordinal + 1]), data.discardedFields);
|
|
8688
|
+
}
|
|
8689
|
+
}
|
|
9013
8690
|
function encodeDocumentTableData(documents) {
|
|
9014
8691
|
const encoded = new Array(documents.length);
|
|
9015
8692
|
let blobLength = 0;
|
|
9016
8693
|
for (let ordinal = 0; ordinal < documents.length; ordinal++) {
|
|
9017
|
-
const bytes =
|
|
8694
|
+
const bytes = encodeDocument(documents[ordinal]);
|
|
9018
8695
|
encoded[ordinal] = bytes;
|
|
9019
8696
|
blobLength += bytes.length;
|
|
9020
8697
|
}
|
|
@@ -9042,7 +8719,7 @@ function wrapEncodedDocumentTable(data) {
|
|
|
9042
8719
|
docAt(ordinal) {
|
|
9043
8720
|
let document = decoded[ordinal];
|
|
9044
8721
|
if (document === void 0) {
|
|
9045
|
-
document =
|
|
8722
|
+
document = decodeStoredDocument(blob.slice(offsets[ordinal], offsets[ordinal + 1]), data.discardedFields);
|
|
9046
8723
|
decoded[ordinal] = document;
|
|
9047
8724
|
decodedBytes += documentValueBytes(document);
|
|
9048
8725
|
}
|
|
@@ -9346,7 +9023,22 @@ function createFrozenEnumReader(entry) {
|
|
|
9346
9023
|
function rangeOf(value) {
|
|
9347
9024
|
return rangeByValue.get(value) ?? { from: 0, to: 0 };
|
|
9348
9025
|
}
|
|
9026
|
+
let affixes = null;
|
|
9027
|
+
function affixesOf() {
|
|
9028
|
+
affixes ??= new ValueAffixes(() => true, values);
|
|
9029
|
+
return affixes;
|
|
9030
|
+
}
|
|
9031
|
+
function bitsetOfValues(matching, capacity) {
|
|
9032
|
+
const bits = createBitSet(capacity);
|
|
9033
|
+
for (const value of matching) {
|
|
9034
|
+
const { from, to } = rangeOf(value);
|
|
9035
|
+
for (let i = from; i < to; i++) bitsetSet(bits, docIds[i]);
|
|
9036
|
+
}
|
|
9037
|
+
return bits;
|
|
9038
|
+
}
|
|
9349
9039
|
return {
|
|
9040
|
+
queryPrefixBitset: (prefix, capacity) => bitsetOfValues(affixesOf().withPrefix(prefix), capacity),
|
|
9041
|
+
querySuffixBitset: (suffix, capacity) => bitsetOfValues(affixesOf().withSuffix(suffix), capacity),
|
|
9350
9042
|
serialize: () => {
|
|
9351
9043
|
const byValue = /* @__PURE__ */ Object.create(null);
|
|
9352
9044
|
for (let i = 0; i < values.length; i++) {
|
|
@@ -9662,6 +9354,9 @@ function wrapFrozenTokenTable(data) {
|
|
|
9662
9354
|
}
|
|
9663
9355
|
return token;
|
|
9664
9356
|
},
|
|
9357
|
+
byteLengthAt(sortedIndex) {
|
|
9358
|
+
return offsets[sortedIndex + 1] - offsets[sortedIndex];
|
|
9359
|
+
},
|
|
9665
9360
|
payloadSlot(sortedIndex) {
|
|
9666
9361
|
return payloadSlots[sortedIndex];
|
|
9667
9362
|
},
|
|
@@ -9750,6 +9445,34 @@ function sharedSurfaceTable(payload) {
|
|
|
9750
9445
|
if (table.counts.length === 0) return null;
|
|
9751
9446
|
return { blob: sharedUint8(table.blob), offsets: sharedUint32(table.offsets), counts: sharedUint32(table.counts) };
|
|
9752
9447
|
}
|
|
9448
|
+
function segmentArraysOf(payload, documentData) {
|
|
9449
|
+
const tokenTable = encodeFrozenTokenTableData(payload.tokens, payload.docFrequencies);
|
|
9450
|
+
const idTable = encodeExternalIdTableData(payload.docIds);
|
|
9451
|
+
const surfaceTable = payload.surfaceForms === null ? null : encodeSurfaceTable(payload.surfaceForms);
|
|
9452
|
+
return {
|
|
9453
|
+
documentCount: payload.documentCount,
|
|
9454
|
+
fieldNames: payload.fieldNames,
|
|
9455
|
+
fieldLengthNames: payload.fieldLengthNames,
|
|
9456
|
+
fieldLengthColumns: payload.fieldLengthColumns,
|
|
9457
|
+
totalFieldLengths: payload.totalFieldLengths,
|
|
9458
|
+
tokenTable,
|
|
9459
|
+
idTable,
|
|
9460
|
+
documentTable: documentData,
|
|
9461
|
+
postingOffsets: payload.postingOffsets,
|
|
9462
|
+
postingDocIds: payload.postingDocIds,
|
|
9463
|
+
postingFrequencies: payload.postingFrequencies,
|
|
9464
|
+
postingFieldIndices: payload.postingFieldIndices,
|
|
9465
|
+
positionOffsets: payload.positionOffsets,
|
|
9466
|
+
positionValues: payload.positionValues,
|
|
9467
|
+
numeric: payload.numeric,
|
|
9468
|
+
sizes: payload.sizes,
|
|
9469
|
+
boolean: payload.boolean,
|
|
9470
|
+
enums: payload.enums,
|
|
9471
|
+
geo: payload.geo,
|
|
9472
|
+
pattern: payload.pattern,
|
|
9473
|
+
surfaceTable: surfaceTable === null || surfaceTable.counts.length === 0 ? null : surfaceTable
|
|
9474
|
+
};
|
|
9475
|
+
}
|
|
9753
9476
|
function freezeSegmentShared(payload, documents, segmentId) {
|
|
9754
9477
|
if (typeof SharedArrayBuffer !== "function") return null;
|
|
9755
9478
|
return freezeEncodedSegmentShared(payload, encodeDocumentTableData(documents), segmentId);
|
|
@@ -9778,12 +9501,13 @@ function freezeEncodedSegmentShared(payload, documentData, segmentId) {
|
|
|
9778
9501
|
},
|
|
9779
9502
|
documentTable: {
|
|
9780
9503
|
blob: sharedUint8(documentData.blob),
|
|
9781
|
-
offsets: sharedUint32(documentData.offsets)
|
|
9504
|
+
offsets: sharedUint32(documentData.offsets),
|
|
9505
|
+
...documentData.discardedFields === void 0 ? {} : { discardedFields: documentData.discardedFields }
|
|
9782
9506
|
},
|
|
9783
9507
|
postingOffsets: sharedUint32(payload.postingOffsets),
|
|
9784
9508
|
postingDocIds: sharedUint32(payload.postingDocIds),
|
|
9785
9509
|
postingFrequencies: sharedUint16(payload.postingFrequencies),
|
|
9786
|
-
postingFieldIndices:
|
|
9510
|
+
postingFieldIndices: sharedUint16(payload.postingFieldIndices),
|
|
9787
9511
|
positionOffsets: payload.positionOffsets === null ? null : sharedUint32(payload.positionOffsets),
|
|
9788
9512
|
positionValues: payload.positionValues === null ? null : sharedUint32(payload.positionValues),
|
|
9789
9513
|
numeric: payload.numeric.map((entry) => ({
|
|
@@ -9791,6 +9515,11 @@ function freezeEncodedSegmentShared(payload, documentData, segmentId) {
|
|
|
9791
9515
|
docIds: sharedUint32(entry.docIds),
|
|
9792
9516
|
values: sharedFloat64(entry.values)
|
|
9793
9517
|
})),
|
|
9518
|
+
sizes: payload.sizes.map((entry) => ({
|
|
9519
|
+
fieldPath: entry.fieldPath,
|
|
9520
|
+
docIds: sharedUint32(entry.docIds),
|
|
9521
|
+
values: sharedFloat64(entry.values)
|
|
9522
|
+
})),
|
|
9794
9523
|
boolean: payload.boolean.map((entry) => ({
|
|
9795
9524
|
fieldPath: entry.fieldPath,
|
|
9796
9525
|
trueDocs: sharedUint32(entry.trueDocs),
|
|
@@ -9808,6 +9537,13 @@ function freezeEncodedSegmentShared(payload, documentData, segmentId) {
|
|
|
9808
9537
|
latitudes: sharedFloat64(entry.latitudes),
|
|
9809
9538
|
longitudes: sharedFloat64(entry.longitudes)
|
|
9810
9539
|
})),
|
|
9540
|
+
pattern: payload.pattern.map((entry) => ({
|
|
9541
|
+
fieldPath: entry.fieldPath,
|
|
9542
|
+
docIds: sharedUint32(entry.docIds),
|
|
9543
|
+
runs: sharedUint32(entry.runs),
|
|
9544
|
+
offsets: sharedUint32(entry.offsets),
|
|
9545
|
+
postings: sharedUint32(entry.postings)
|
|
9546
|
+
})),
|
|
9811
9547
|
surfaceTable: sharedSurfaceTable(payload)
|
|
9812
9548
|
};
|
|
9813
9549
|
}
|
|
@@ -9854,10 +9590,16 @@ function assembleFrozenSegment(segmentId, source, documentSource, sharedSnapshot
|
|
|
9854
9590
|
const docStore = createFrozenDocTable(source.idTable, lengthColumns, documentSource, tombstones);
|
|
9855
9591
|
const numericIndexes = /* @__PURE__ */ new Map();
|
|
9856
9592
|
for (const entry of source.numeric) numericIndexes.set(entry.fieldPath, createFrozenNumericReader(entry));
|
|
9593
|
+
const sizeIndexes = /* @__PURE__ */ new Map();
|
|
9594
|
+
for (const entry of source.sizes) sizeIndexes.set(entry.fieldPath, createFrozenNumericReader(entry));
|
|
9857
9595
|
const booleanIndexes = /* @__PURE__ */ new Map();
|
|
9858
9596
|
for (const entry of source.boolean) booleanIndexes.set(entry.fieldPath, createFrozenBooleanReader(entry));
|
|
9859
9597
|
const enumIndexes = /* @__PURE__ */ new Map();
|
|
9860
9598
|
for (const entry of source.enums) enumIndexes.set(entry.fieldPath, createFrozenEnumReader(entry));
|
|
9599
|
+
const patternIndexes = /* @__PURE__ */ new Map();
|
|
9600
|
+
for (const entry of source.pattern) {
|
|
9601
|
+
patternIndexes.set(entry.fieldPath, requirePatternSearch(entry.fieldPath).readArrays(entry));
|
|
9602
|
+
}
|
|
9861
9603
|
const fieldNameTable = {
|
|
9862
9604
|
names: [...source.fieldNames],
|
|
9863
9605
|
indexMap: new Map(source.fieldNames.map((name, index) => [name, index]))
|
|
@@ -9875,6 +9617,8 @@ function assembleFrozenSegment(segmentId, source, documentSource, sharedSnapshot
|
|
|
9875
9617
|
booleanIndexes,
|
|
9876
9618
|
enumIndexes,
|
|
9877
9619
|
geoIndexes: buildGeoReaders(source.geo),
|
|
9620
|
+
patternIndexes,
|
|
9621
|
+
sizeIndexes,
|
|
9878
9622
|
fieldNameTable,
|
|
9879
9623
|
trackPositions: source.positionOffsets !== null,
|
|
9880
9624
|
flatSchemaCache: null,
|
|
@@ -9946,6 +9690,133 @@ function createSharedFrozenSegment(snapshot) {
|
|
|
9946
9690
|
snapshot
|
|
9947
9691
|
);
|
|
9948
9692
|
}
|
|
9693
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
9694
|
+
var keyDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
9695
|
+
function headerAt(view, offset) {
|
|
9696
|
+
if (offset >= view.byteLength) return null;
|
|
9697
|
+
const tag = view.getUint8(offset);
|
|
9698
|
+
if (tag <= 127 || tag >= 224) return { headerBytes: 1, bodyBytes: 0, children: 0 };
|
|
9699
|
+
if (tag <= 143) return { headerBytes: 1, bodyBytes: 0, children: (tag & 15) * 2 };
|
|
9700
|
+
if (tag <= 159) return { headerBytes: 1, bodyBytes: 0, children: tag & 15 };
|
|
9701
|
+
if (tag <= 191) return { headerBytes: 1, bodyBytes: tag & 31, children: 0 };
|
|
9702
|
+
const lengthBytes = LENGTH_BYTES_BY_TAG.get(tag);
|
|
9703
|
+
if (lengthBytes !== void 0) {
|
|
9704
|
+
if (offset + 1 + lengthBytes > view.byteLength) return null;
|
|
9705
|
+
const length = lengthBytes === 1 ? view.getUint8(offset + 1) : lengthBytes === 2 ? view.getUint16(offset + 1, false) : view.getUint32(offset + 1, false);
|
|
9706
|
+
const headerBytes = 1 + lengthBytes;
|
|
9707
|
+
if (ARRAY_TAGS.has(tag)) return { headerBytes, bodyBytes: 0, children: length };
|
|
9708
|
+
if (MAP_TAGS.has(tag)) return { headerBytes, bodyBytes: 0, children: length * 2 };
|
|
9709
|
+
return { headerBytes, bodyBytes: length + (EXT_TAGS.has(tag) ? 1 : 0), children: 0 };
|
|
9710
|
+
}
|
|
9711
|
+
const fixedBytes = FIXED_BODY_BYTES_BY_TAG.get(tag);
|
|
9712
|
+
return fixedBytes === void 0 ? null : { headerBytes: 1, bodyBytes: fixedBytes, children: 0 };
|
|
9713
|
+
}
|
|
9714
|
+
var LENGTH_BYTES_BY_TAG = /* @__PURE__ */ new Map([
|
|
9715
|
+
[196, 1],
|
|
9716
|
+
[197, 2],
|
|
9717
|
+
[198, 4],
|
|
9718
|
+
[199, 1],
|
|
9719
|
+
[200, 2],
|
|
9720
|
+
[201, 4],
|
|
9721
|
+
[217, 1],
|
|
9722
|
+
[218, 2],
|
|
9723
|
+
[219, 4],
|
|
9724
|
+
[220, 2],
|
|
9725
|
+
[221, 4],
|
|
9726
|
+
[222, 2],
|
|
9727
|
+
[223, 4]
|
|
9728
|
+
]);
|
|
9729
|
+
var ARRAY_TAGS = /* @__PURE__ */ new Set([220, 221]);
|
|
9730
|
+
var MAP_TAGS = /* @__PURE__ */ new Set([222, 223]);
|
|
9731
|
+
var EXT_TAGS = /* @__PURE__ */ new Set([199, 200, 201]);
|
|
9732
|
+
var FIXED_BODY_BYTES_BY_TAG = /* @__PURE__ */ new Map([
|
|
9733
|
+
[192, 0],
|
|
9734
|
+
[194, 0],
|
|
9735
|
+
[195, 0],
|
|
9736
|
+
[202, 4],
|
|
9737
|
+
[203, 8],
|
|
9738
|
+
[204, 1],
|
|
9739
|
+
[205, 2],
|
|
9740
|
+
[206, 4],
|
|
9741
|
+
[207, 8],
|
|
9742
|
+
[208, 1],
|
|
9743
|
+
[209, 2],
|
|
9744
|
+
[210, 4],
|
|
9745
|
+
[211, 8],
|
|
9746
|
+
[212, 2],
|
|
9747
|
+
[213, 3],
|
|
9748
|
+
[214, 5],
|
|
9749
|
+
[215, 9],
|
|
9750
|
+
[216, 17]
|
|
9751
|
+
]);
|
|
9752
|
+
function endOfValue(view, start) {
|
|
9753
|
+
let offset = start;
|
|
9754
|
+
let pending = 1;
|
|
9755
|
+
while (pending > 0) {
|
|
9756
|
+
const header = headerAt(view, offset);
|
|
9757
|
+
if (header === null) return null;
|
|
9758
|
+
offset += header.headerBytes + header.bodyBytes;
|
|
9759
|
+
if (offset > view.byteLength) return null;
|
|
9760
|
+
pending += header.children - 1;
|
|
9761
|
+
}
|
|
9762
|
+
return offset;
|
|
9763
|
+
}
|
|
9764
|
+
function stringKeyAt(bytes, view, offset) {
|
|
9765
|
+
const header = headerAt(view, offset);
|
|
9766
|
+
if (header === null || header.children !== 0) return null;
|
|
9767
|
+
const tag = view.getUint8(offset);
|
|
9768
|
+
const isString = tag >= 160 && tag <= 191 || tag === 217 || tag === 218 || tag === 219;
|
|
9769
|
+
const end = offset + header.headerBytes + header.bodyBytes;
|
|
9770
|
+
if (!isString || end > bytes.length) return null;
|
|
9771
|
+
try {
|
|
9772
|
+
return { key: keyDecoder.decode(bytes.subarray(offset + header.headerBytes, end)), end };
|
|
9773
|
+
} catch {
|
|
9774
|
+
return null;
|
|
9775
|
+
}
|
|
9776
|
+
}
|
|
9777
|
+
function topLevelMapKeys(bytes) {
|
|
9778
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
9779
|
+
const top = headerAt(view, 0);
|
|
9780
|
+
if (top === null || top.bodyBytes !== 0) return null;
|
|
9781
|
+
const tag = view.getUint8(0);
|
|
9782
|
+
if (!(tag >= 128 && tag <= 143 || MAP_TAGS.has(tag))) return null;
|
|
9783
|
+
const keys = /* @__PURE__ */ new Set();
|
|
9784
|
+
let offset = top.headerBytes;
|
|
9785
|
+
for (let pair = 0; pair < top.children / 2; pair += 1) {
|
|
9786
|
+
const key = stringKeyAt(bytes, view, offset);
|
|
9787
|
+
if (key === null) return null;
|
|
9788
|
+
const valueEnd = endOfValue(view, key.end);
|
|
9789
|
+
if (valueEnd === null) return null;
|
|
9790
|
+
keys.add(key.key);
|
|
9791
|
+
offset = valueEnd;
|
|
9792
|
+
}
|
|
9793
|
+
return keys;
|
|
9794
|
+
}
|
|
9795
|
+
function decodeMapWithoutFields(bytes, skippedFields) {
|
|
9796
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
9797
|
+
const top = headerAt(view, 0);
|
|
9798
|
+
if (top === null || top.children === 0 || top.bodyBytes !== 0) return null;
|
|
9799
|
+
const tag = view.getUint8(0);
|
|
9800
|
+
const isMap = tag >= 128 && tag <= 143 || MAP_TAGS.has(tag);
|
|
9801
|
+
if (!isMap) return null;
|
|
9802
|
+
const document = {};
|
|
9803
|
+
let offset = top.headerBytes;
|
|
9804
|
+
for (let pair = 0; pair < top.children / 2; pair += 1) {
|
|
9805
|
+
const key = stringKeyAt(bytes, view, offset);
|
|
9806
|
+
if (key === null || FORBIDDEN_KEYS.has(key.key)) return null;
|
|
9807
|
+
const valueEnd = endOfValue(view, key.end);
|
|
9808
|
+
if (valueEnd === null) return null;
|
|
9809
|
+
if (!skippedFields.has(key.key)) {
|
|
9810
|
+
try {
|
|
9811
|
+
document[key.key] = decode(bytes.subarray(key.end, valueEnd));
|
|
9812
|
+
} catch {
|
|
9813
|
+
return null;
|
|
9814
|
+
}
|
|
9815
|
+
}
|
|
9816
|
+
offset = valueEnd;
|
|
9817
|
+
}
|
|
9818
|
+
return offset === bytes.length ? document : null;
|
|
9819
|
+
}
|
|
9949
9820
|
|
|
9950
9821
|
// src/core/partition/frozen/merge-fields.ts
|
|
9951
9822
|
function indexByFieldPath(entries) {
|
|
@@ -10002,12 +9873,8 @@ function remapDocIds(inputs, pick) {
|
|
|
10002
9873
|
}
|
|
10003
9874
|
return out;
|
|
10004
9875
|
}
|
|
10005
|
-
function mergeNumeric(inputs) {
|
|
10006
|
-
const byFieldPath = rowsByFieldPath(
|
|
10007
|
-
inputs,
|
|
10008
|
-
(input) => input.segment.arrays.numeric,
|
|
10009
|
-
(entry, at) => [entry.values[at]]
|
|
10010
|
-
);
|
|
9876
|
+
function mergeNumeric(inputs, pick) {
|
|
9877
|
+
const byFieldPath = rowsByFieldPath(inputs, pick, (entry, at) => [entry.values[at]]);
|
|
10011
9878
|
const merged = [];
|
|
10012
9879
|
for (const [fieldPath, rows] of byFieldPath) {
|
|
10013
9880
|
rows.sort((a, b) => a.values[0] - b.values[0] || a.docId - b.docId);
|
|
@@ -10080,12 +9947,32 @@ function mergeEnums(inputs) {
|
|
|
10080
9947
|
}
|
|
10081
9948
|
return merged;
|
|
10082
9949
|
}
|
|
9950
|
+
function mergePattern(inputs) {
|
|
9951
|
+
const byFieldPath = /* @__PURE__ */ new Map();
|
|
9952
|
+
for (const input of inputs) {
|
|
9953
|
+
for (const arrays of input.segment.arrays.pattern) {
|
|
9954
|
+
let entries = byFieldPath.get(arrays.fieldPath);
|
|
9955
|
+
if (entries === void 0) {
|
|
9956
|
+
entries = [];
|
|
9957
|
+
byFieldPath.set(arrays.fieldPath, entries);
|
|
9958
|
+
}
|
|
9959
|
+
entries.push({ arrays, remap: input.remap });
|
|
9960
|
+
}
|
|
9961
|
+
}
|
|
9962
|
+
const merged = [];
|
|
9963
|
+
for (const [fieldPath, entries] of byFieldPath) {
|
|
9964
|
+
merged.push(requirePatternSearch(fieldPath).mergeArrays(fieldPath, entries));
|
|
9965
|
+
}
|
|
9966
|
+
return merged;
|
|
9967
|
+
}
|
|
10083
9968
|
function mergeFieldIndexes2(inputs) {
|
|
10084
9969
|
return {
|
|
10085
|
-
numeric: mergeNumeric(inputs),
|
|
9970
|
+
numeric: mergeNumeric(inputs, (input) => input.segment.arrays.numeric),
|
|
10086
9971
|
boolean: mergeBoolean(inputs),
|
|
10087
9972
|
enums: mergeEnums(inputs),
|
|
10088
|
-
geo: mergeGeo(inputs)
|
|
9973
|
+
geo: mergeGeo(inputs),
|
|
9974
|
+
pattern: mergePattern(inputs),
|
|
9975
|
+
sizes: mergeNumeric(inputs, (input) => input.segment.arrays.sizes)
|
|
10089
9976
|
};
|
|
10090
9977
|
}
|
|
10091
9978
|
|
|
@@ -10192,7 +10079,7 @@ function mergePostings2(inputs) {
|
|
|
10192
10079
|
const postingOffsets = new Uint32Array(tokenCount + 1);
|
|
10193
10080
|
const postingDocIds = new Uint32Array(totalPostings);
|
|
10194
10081
|
const postingFrequencies = new Uint16Array(totalPostings);
|
|
10195
|
-
const postingFieldIndices = new
|
|
10082
|
+
const postingFieldIndices = new Uint16Array(totalPostings);
|
|
10196
10083
|
const positionOffsets = hasPositions ? new Uint32Array(totalPostings + 1) : null;
|
|
10197
10084
|
const positionValues = hasPositions ? new Uint32Array(totalPositions) : null;
|
|
10198
10085
|
let postingCursor = 0;
|
|
@@ -10266,11 +10153,15 @@ function mergePostings2(inputs) {
|
|
|
10266
10153
|
}
|
|
10267
10154
|
|
|
10268
10155
|
// src/core/partition/frozen/merge.ts
|
|
10269
|
-
function
|
|
10156
|
+
function selectionKeeps(selection, ordinal) {
|
|
10157
|
+
return selection.keep === void 0 ? !selection.segment.isTombstoned(ordinal) : selection.keep(ordinal);
|
|
10158
|
+
}
|
|
10159
|
+
function remapOf(selection, docIds) {
|
|
10160
|
+
const { segment } = selection;
|
|
10270
10161
|
const count = segment.arrays.documentCount;
|
|
10271
10162
|
const remap = new Int32Array(count).fill(-1);
|
|
10272
10163
|
for (let ordinal = 0; ordinal < count; ordinal++) {
|
|
10273
|
-
if (
|
|
10164
|
+
if (!selectionKeeps(selection, ordinal)) continue;
|
|
10274
10165
|
remap[ordinal] = docIds.length;
|
|
10275
10166
|
docIds.push(segment.arrays.idTable.idAt(ordinal));
|
|
10276
10167
|
}
|
|
@@ -10283,7 +10174,7 @@ function fieldNameIndex(fieldNames, name) {
|
|
|
10283
10174
|
return fieldNames.length - 1;
|
|
10284
10175
|
}
|
|
10285
10176
|
function fieldIndexRemapOf(segment, fieldNames) {
|
|
10286
|
-
const remap = new
|
|
10177
|
+
const remap = new Uint16Array(segment.arrays.fieldNames.length);
|
|
10287
10178
|
for (let i = 0; i < segment.arrays.fieldNames.length; i++) {
|
|
10288
10179
|
remap[i] = fieldNameIndex(fieldNames, segment.arrays.fieldNames[i]);
|
|
10289
10180
|
}
|
|
@@ -10329,20 +10220,34 @@ function mergeSurfaceForms2(inputs) {
|
|
|
10329
10220
|
}
|
|
10330
10221
|
return registry.size() === 0 ? null : registry.serialize();
|
|
10331
10222
|
}
|
|
10223
|
+
function holdsADiscardedField(bytes, discardedFields) {
|
|
10224
|
+
const keys = topLevelMapKeys(bytes);
|
|
10225
|
+
if (keys === null) return true;
|
|
10226
|
+
for (const fieldPath of discardedFields) {
|
|
10227
|
+
const dot = fieldPath.indexOf(".");
|
|
10228
|
+
if (keys.has(dot < 0 ? fieldPath : fieldPath.slice(0, dot))) return true;
|
|
10229
|
+
}
|
|
10230
|
+
return false;
|
|
10231
|
+
}
|
|
10332
10232
|
function encodedDocumentsOf(input) {
|
|
10333
10233
|
const table = input.segment.arrays.documentTable;
|
|
10334
|
-
if (table !== null) {
|
|
10335
|
-
return (ordinal) => table.blob.subarray(table.offsets[ordinal], table.offsets[ordinal + 1]);
|
|
10336
|
-
}
|
|
10337
10234
|
const encoded = new Array(input.remap.length);
|
|
10338
|
-
|
|
10235
|
+
const encodeOrdinal = (ordinal) => {
|
|
10339
10236
|
let bytes = encoded[ordinal];
|
|
10340
10237
|
if (bytes === void 0) {
|
|
10341
|
-
bytes =
|
|
10238
|
+
bytes = encodeDocument(input.segment.documentSource.docAt(ordinal));
|
|
10342
10239
|
encoded[ordinal] = bytes;
|
|
10343
10240
|
}
|
|
10344
10241
|
return bytes;
|
|
10345
10242
|
};
|
|
10243
|
+
if (table === null) return encodeOrdinal;
|
|
10244
|
+
const discardedFields = table.discardedFields;
|
|
10245
|
+
return (ordinal) => {
|
|
10246
|
+
if (encoded[ordinal] !== void 0) return encodeOrdinal(ordinal);
|
|
10247
|
+
const stored = table.blob.subarray(table.offsets[ordinal], table.offsets[ordinal + 1]);
|
|
10248
|
+
if (discardedFields === void 0 || !holdsADiscardedField(stored, discardedFields)) return stored;
|
|
10249
|
+
return encodeOrdinal(ordinal);
|
|
10250
|
+
};
|
|
10346
10251
|
}
|
|
10347
10252
|
function mergeDocuments2(inputs, documentCount) {
|
|
10348
10253
|
const readers = inputs.map(encodedDocumentsOf);
|
|
@@ -10366,12 +10271,16 @@ function mergeDocuments2(inputs, documentCount) {
|
|
|
10366
10271
|
}
|
|
10367
10272
|
function mergeFrozenSegments(segments) {
|
|
10368
10273
|
if (typeof SharedArrayBuffer !== "function") return null;
|
|
10274
|
+
const merged = mergeSegmentContents(segments.map((segment) => ({ segment })));
|
|
10275
|
+
return freezeEncodedSegmentShared(merged.payload, merged.documents);
|
|
10276
|
+
}
|
|
10277
|
+
function mergeSegmentContents(selections) {
|
|
10369
10278
|
const docIds = [];
|
|
10370
10279
|
const fieldNames = [];
|
|
10371
|
-
const inputs =
|
|
10372
|
-
segment,
|
|
10373
|
-
remap: remapOf(
|
|
10374
|
-
fieldIndexRemap: fieldIndexRemapOf(segment, fieldNames)
|
|
10280
|
+
const inputs = selections.map((selection) => ({
|
|
10281
|
+
segment: selection.segment,
|
|
10282
|
+
remap: remapOf(selection, docIds),
|
|
10283
|
+
fieldIndexRemap: fieldIndexRemapOf(selection.segment, fieldNames)
|
|
10375
10284
|
}));
|
|
10376
10285
|
const documentCount = docIds.length;
|
|
10377
10286
|
const postings = mergePostings2(inputs);
|
|
@@ -10394,87 +10303,10 @@ function mergeFrozenSegments(segments) {
|
|
|
10394
10303
|
surfaceForms: mergeSurfaceForms2(inputs),
|
|
10395
10304
|
...mergeFieldIndexes2(inputs)
|
|
10396
10305
|
};
|
|
10397
|
-
return
|
|
10306
|
+
return { payload, documents: mergeDocuments2(inputs, documentCount) };
|
|
10398
10307
|
}
|
|
10399
10308
|
|
|
10400
|
-
// src/core/partition/
|
|
10401
|
-
var GrowableUint32 = class {
|
|
10402
|
-
values;
|
|
10403
|
-
length = 0;
|
|
10404
|
-
constructor(capacity) {
|
|
10405
|
-
this.values = new Uint32Array(capacity);
|
|
10406
|
-
}
|
|
10407
|
-
growTo(needed, kept) {
|
|
10408
|
-
let capacity = this.values.length * 2;
|
|
10409
|
-
while (capacity < needed) capacity *= 2;
|
|
10410
|
-
const next = new Uint32Array(capacity);
|
|
10411
|
-
next.set(this.values.subarray(0, kept));
|
|
10412
|
-
this.values = next;
|
|
10413
|
-
}
|
|
10414
|
-
reserve(extra) {
|
|
10415
|
-
const needed = this.length + extra;
|
|
10416
|
-
if (needed > this.values.length) this.growTo(needed, this.length);
|
|
10417
|
-
}
|
|
10418
|
-
push(value) {
|
|
10419
|
-
if (this.length === this.values.length) this.growTo(this.length + 1, this.length);
|
|
10420
|
-
this.values[this.length++] = value;
|
|
10421
|
-
}
|
|
10422
|
-
ensureIndex(index) {
|
|
10423
|
-
if (index >= this.values.length) this.growTo(index + 1, this.values.length);
|
|
10424
|
-
}
|
|
10425
|
-
clear() {
|
|
10426
|
-
this.length = 0;
|
|
10427
|
-
}
|
|
10428
|
-
};
|
|
10429
|
-
|
|
10430
|
-
// src/core/partition/segment-builder.ts
|
|
10431
|
-
var TOKEN_TABLE_START = 1 << 12;
|
|
10432
|
-
var POSTING_LOG_START = 1 << 14;
|
|
10433
|
-
var POSITION_POOL_START = 1 << 16;
|
|
10434
|
-
var FIELD_SCRATCH_START = 1 << 10;
|
|
10435
|
-
function indexOf(indexes, fieldPath, create) {
|
|
10436
|
-
let index = indexes.get(fieldPath);
|
|
10437
|
-
if (index === void 0) {
|
|
10438
|
-
index = create();
|
|
10439
|
-
indexes.set(fieldPath, index);
|
|
10440
|
-
}
|
|
10441
|
-
return index;
|
|
10442
|
-
}
|
|
10443
|
-
function indexFilterValue(indexes, ordinal, fieldPath, fieldType, value) {
|
|
10444
|
-
if (fieldType === "number" || fieldType === "number[]") {
|
|
10445
|
-
const index = indexOf(indexes.numericIndexes, fieldPath, createNumericIndex);
|
|
10446
|
-
for (const entry of fieldType === "number" ? [value] : value) index.insert(ordinal, entry);
|
|
10447
|
-
} else if (fieldType === "boolean" || fieldType === "boolean[]") {
|
|
10448
|
-
const index = indexOf(indexes.booleanIndexes, fieldPath, createBooleanIndex);
|
|
10449
|
-
for (const entry of fieldType === "boolean" ? [value] : value)
|
|
10450
|
-
index.insert(ordinal, entry);
|
|
10451
|
-
} else if (fieldType === "enum" || fieldType === "enum[]") {
|
|
10452
|
-
const index = indexOf(indexes.enumIndexes, fieldPath, createEnumIndex);
|
|
10453
|
-
for (const entry of fieldType === "enum" ? [value] : value) index.insert(ordinal, entry);
|
|
10454
|
-
} else if (fieldType === "geopoint") {
|
|
10455
|
-
const point = value;
|
|
10456
|
-
indexOf(indexes.geoIndexes, fieldPath, createGeoIndex).insert(ordinal, point.lat, point.lon);
|
|
10457
|
-
}
|
|
10458
|
-
}
|
|
10459
|
-
function refuseRepeatedDocIds(documents) {
|
|
10460
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10461
|
-
for (const { docId } of documents) {
|
|
10462
|
-
if (seen.has(docId)) {
|
|
10463
|
-
throw new NarsilError(ErrorCodes.DOC_ALREADY_EXISTS, `Document "${docId}" already exists in partition 0`, {
|
|
10464
|
-
docId,
|
|
10465
|
-
partitionId: 0
|
|
10466
|
-
});
|
|
10467
|
-
}
|
|
10468
|
-
seen.add(docId);
|
|
10469
|
-
}
|
|
10470
|
-
}
|
|
10471
|
-
function validateAsAnInsertDoes(documents, schema, options) {
|
|
10472
|
-
if (options?.validate === false) return;
|
|
10473
|
-
for (const { document } of documents) {
|
|
10474
|
-
validateDocument(document, schema, options?.patternValueLimit);
|
|
10475
|
-
if (options?.strict) validateDocumentStrict(document, schema);
|
|
10476
|
-
}
|
|
10477
|
-
}
|
|
10309
|
+
// src/core/partition/segment-postings.ts
|
|
10478
10310
|
var TextPostings = class {
|
|
10479
10311
|
constructor(trackPositions) {
|
|
10480
10312
|
this.trackPositions = trackPositions;
|
|
@@ -10483,22 +10315,22 @@ var TextPostings = class {
|
|
|
10483
10315
|
fieldNames = [];
|
|
10484
10316
|
tokenIds = /* @__PURE__ */ new Map();
|
|
10485
10317
|
fieldNameIndexes = /* @__PURE__ */ new Map();
|
|
10486
|
-
postingsPerToken = new GrowableUint32(
|
|
10487
|
-
docFrequencyPerToken = new GrowableUint32(
|
|
10488
|
-
lastDocPerToken = new GrowableUint32(
|
|
10489
|
-
fieldStampPerToken = new GrowableUint32(
|
|
10490
|
-
fieldSlotPerToken = new GrowableUint32(
|
|
10491
|
-
logToken = new GrowableUint32(
|
|
10492
|
-
logDoc = new GrowableUint32(
|
|
10493
|
-
logFrequency = new GrowableUint32(
|
|
10494
|
-
logField = new GrowableUint32(
|
|
10495
|
-
logPositionStart = new GrowableUint32(
|
|
10496
|
-
positionPool = new GrowableUint32(
|
|
10497
|
-
slotToken = new GrowableUint32(
|
|
10498
|
-
slotCount = new GrowableUint32(
|
|
10499
|
-
slotCursor = new GrowableUint32(
|
|
10500
|
-
eventSlot = new GrowableUint32(
|
|
10501
|
-
eventPosition = new GrowableUint32(
|
|
10318
|
+
postingsPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
|
|
10319
|
+
docFrequencyPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
|
|
10320
|
+
lastDocPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
|
|
10321
|
+
fieldStampPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
|
|
10322
|
+
fieldSlotPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
|
|
10323
|
+
logToken = new GrowableUint32(SEGMENT_POSTING_LOG_START);
|
|
10324
|
+
logDoc = new GrowableUint32(SEGMENT_POSTING_LOG_START);
|
|
10325
|
+
logFrequency = new GrowableUint32(SEGMENT_POSTING_LOG_START);
|
|
10326
|
+
logField = new GrowableUint32(SEGMENT_POSTING_LOG_START);
|
|
10327
|
+
logPositionStart = new GrowableUint32(SEGMENT_POSTING_LOG_START);
|
|
10328
|
+
positionPool = new GrowableUint32(SEGMENT_POSITION_POOL_START);
|
|
10329
|
+
slotToken = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
|
|
10330
|
+
slotCount = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
|
|
10331
|
+
slotCursor = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
|
|
10332
|
+
eventSlot = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
|
|
10333
|
+
eventPosition = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
|
|
10502
10334
|
fieldStamp = 0;
|
|
10503
10335
|
beginField() {
|
|
10504
10336
|
this.fieldStamp++;
|
|
@@ -10585,7 +10417,7 @@ var TextPostings = class {
|
|
|
10585
10417
|
const writeCursor = postingOffsets.slice(0, tokenCount);
|
|
10586
10418
|
const postingDocIds = new Uint32Array(postings);
|
|
10587
10419
|
const postingFrequencies = new Uint16Array(postings);
|
|
10588
|
-
const postingFieldIndices = new
|
|
10420
|
+
const postingFieldIndices = new Uint16Array(postings);
|
|
10589
10421
|
const logRowOfPosting = new Uint32Array(postings);
|
|
10590
10422
|
for (let row = 0; row < postings; row++) {
|
|
10591
10423
|
const target = writeCursor[this.logToken.values[row]]++;
|
|
@@ -10626,11 +10458,56 @@ var TextPostings = class {
|
|
|
10626
10458
|
};
|
|
10627
10459
|
}
|
|
10628
10460
|
};
|
|
10461
|
+
|
|
10462
|
+
// src/core/partition/segment-builder.ts
|
|
10463
|
+
function indexOf(indexes, fieldPath, create) {
|
|
10464
|
+
let index = indexes.get(fieldPath);
|
|
10465
|
+
if (index === void 0) {
|
|
10466
|
+
index = create();
|
|
10467
|
+
indexes.set(fieldPath, index);
|
|
10468
|
+
}
|
|
10469
|
+
return index;
|
|
10470
|
+
}
|
|
10471
|
+
function indexFilterValue(indexes, ordinal, fieldPath, fieldType, value) {
|
|
10472
|
+
if (fieldType === "number" || fieldType === "number[]") {
|
|
10473
|
+
const index = indexOf(indexes.numericIndexes, fieldPath, createNumericIndex);
|
|
10474
|
+
for (const entry of fieldType === "number" ? [value] : value) index.insert(ordinal, entry);
|
|
10475
|
+
} else if (fieldType === "boolean" || fieldType === "boolean[]") {
|
|
10476
|
+
const index = indexOf(indexes.booleanIndexes, fieldPath, createBooleanIndex);
|
|
10477
|
+
for (const entry of fieldType === "boolean" ? [value] : value)
|
|
10478
|
+
index.insert(ordinal, entry);
|
|
10479
|
+
} else if (fieldType === "enum" || fieldType === "enum[]") {
|
|
10480
|
+
const index = indexOf(indexes.enumIndexes, fieldPath, createEnumIndex);
|
|
10481
|
+
for (const entry of fieldType === "enum" ? [value] : value) index.insert(ordinal, entry);
|
|
10482
|
+
} else if (fieldType === "geopoint") {
|
|
10483
|
+
const point = value;
|
|
10484
|
+
indexOf(indexes.geoIndexes, fieldPath, createGeoIndex).insert(ordinal, point.lat, point.lon);
|
|
10485
|
+
}
|
|
10486
|
+
}
|
|
10487
|
+
function refuseRepeatedDocIds(documents) {
|
|
10488
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10489
|
+
for (const { docId } of documents) {
|
|
10490
|
+
if (seen.has(docId)) {
|
|
10491
|
+
throw new NarsilError(ErrorCodes.DOC_ALREADY_EXISTS, `Document "${docId}" already exists in partition 0`, {
|
|
10492
|
+
docId,
|
|
10493
|
+
partitionId: 0
|
|
10494
|
+
});
|
|
10495
|
+
}
|
|
10496
|
+
seen.add(docId);
|
|
10497
|
+
}
|
|
10498
|
+
}
|
|
10499
|
+
function validateAsAnInsertDoes(documents, schema, options) {
|
|
10500
|
+
if (options?.validate === false) return;
|
|
10501
|
+
for (const { document } of documents) {
|
|
10502
|
+
validateDocument(document, schema, options?.patternValueLimit);
|
|
10503
|
+
if (options?.strict) validateDocumentStrict(document, schema);
|
|
10504
|
+
}
|
|
10505
|
+
}
|
|
10629
10506
|
var SurfaceCounts = class {
|
|
10630
10507
|
ids = /* @__PURE__ */ new Map();
|
|
10631
10508
|
surfaces = [];
|
|
10632
10509
|
tokens = [];
|
|
10633
|
-
counts = new GrowableUint32(
|
|
10510
|
+
counts = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
|
|
10634
10511
|
count(surface, token) {
|
|
10635
10512
|
if (surface === void 0 || surface.length === 0) return;
|
|
10636
10513
|
const known = this.ids.get(surface);
|
|
@@ -10690,7 +10567,9 @@ function buildSegmentPayload(documents, schema, language, options, trackPosition
|
|
|
10690
10567
|
numericIndexes: /* @__PURE__ */ new Map(),
|
|
10691
10568
|
booleanIndexes: /* @__PURE__ */ new Map(),
|
|
10692
10569
|
enumIndexes: /* @__PURE__ */ new Map(),
|
|
10693
|
-
geoIndexes: /* @__PURE__ */ new Map()
|
|
10570
|
+
geoIndexes: /* @__PURE__ */ new Map(),
|
|
10571
|
+
patternIndexes: /* @__PURE__ */ new Map(),
|
|
10572
|
+
sizeIndexes: /* @__PURE__ */ new Map()
|
|
10694
10573
|
};
|
|
10695
10574
|
let positionOffset = 0;
|
|
10696
10575
|
const addToken = (token, position, surface) => {
|
|
@@ -10699,9 +10578,11 @@ function buildSegmentPayload(documents, schema, language, options, trackPosition
|
|
|
10699
10578
|
};
|
|
10700
10579
|
for (let ordinal = 0; ordinal < documents.length; ordinal++) {
|
|
10701
10580
|
const document = documents[ordinal].document;
|
|
10702
|
-
for (const { path: fieldPath, base: fieldType } of fields) {
|
|
10581
|
+
for (const { path: fieldPath, base: fieldType, pattern } of fields) {
|
|
10703
10582
|
const value = getNestedValue(document, fieldPath);
|
|
10704
10583
|
if (value === void 0 || value === null) continue;
|
|
10584
|
+
recordValueSize(filters.sizeIndexes, ordinal, fieldPath, fieldType, value);
|
|
10585
|
+
if (pattern) patternIndexOf(filters.patternIndexes, fieldPath).add(ordinal, value);
|
|
10705
10586
|
if (!isWordIndexedBase(fieldType)) {
|
|
10706
10587
|
indexFilterValue(filters, ordinal, fieldPath, fieldType, value);
|
|
10707
10588
|
continue;
|
|
@@ -11152,6 +11033,7 @@ function createDirectExecutor(options) {
|
|
|
11152
11033
|
}
|
|
11153
11034
|
case "query": {
|
|
11154
11035
|
const entry = requireIndex(action.indexName);
|
|
11036
|
+
const patternWork = joinPatternWorkMeter(action.patternWork);
|
|
11155
11037
|
const result = await fanOutQuery(
|
|
11156
11038
|
entry.manager,
|
|
11157
11039
|
action.params,
|
|
@@ -11162,15 +11044,17 @@ function createDirectExecutor(options) {
|
|
|
11162
11044
|
partitionIds: action.partitionIds,
|
|
11163
11045
|
...action.globalStats !== void 0 ? { globalStats: sanitizeGlobalStats(action.globalStats) } : {}
|
|
11164
11046
|
},
|
|
11165
|
-
entry.searchOptions
|
|
11047
|
+
{ ...entry.searchOptions, patternWork }
|
|
11166
11048
|
);
|
|
11049
|
+
result.patternWorkSpent = patternWork.spent();
|
|
11167
11050
|
return result;
|
|
11168
11051
|
}
|
|
11169
11052
|
case "preflight": {
|
|
11170
11053
|
const entry = requireIndex(action.indexName);
|
|
11054
|
+
const searchOptions = { ...entry.searchOptions, patternWork: joinPatternWorkMeter(action.patternWork) };
|
|
11171
11055
|
if (countsWithoutScores(action.params)) {
|
|
11172
11056
|
const count = fanOutMatchCount(entry.manager, action.params, entry.language, entry.config.schema, {
|
|
11173
|
-
searchOptions
|
|
11057
|
+
searchOptions
|
|
11174
11058
|
});
|
|
11175
11059
|
return { count };
|
|
11176
11060
|
}
|
|
@@ -11180,7 +11064,7 @@ function createDirectExecutor(options) {
|
|
|
11180
11064
|
entry.language,
|
|
11181
11065
|
entry.config.schema,
|
|
11182
11066
|
{ scoringMode: entry.config.defaultScoring ?? "local" },
|
|
11183
|
-
|
|
11067
|
+
searchOptions
|
|
11184
11068
|
);
|
|
11185
11069
|
return { count: result.totalMatched };
|
|
11186
11070
|
}
|
|
@@ -11266,4 +11150,4 @@ function createDirectExecutor(options) {
|
|
|
11266
11150
|
};
|
|
11267
11151
|
}
|
|
11268
11152
|
|
|
11269
|
-
export { CONSECUTIVE_TIMEOUTS_BEFORE_DEATH, DEFAULT_BACKPRESSURE_LIMIT, DEFAULT_REQUEST_TIMEOUT_MS, FIELD_FILTER_OPERATORS, FILTER_EXPRESSION_KEYS, NO_LIVE_VECTOR_AT_POSITION, SHUTDOWN_TIMEOUT_MS, VECTOR_FILE_MAX_VECTORS, buildSegmentPayload,
|
|
11153
|
+
export { CHECKSUM_YIELD_CHUNK_BYTES, CONSECUTIVE_TIMEOUTS_BEFORE_DEATH, DEFAULT_BACKPRESSURE_LIMIT, DEFAULT_REQUEST_TIMEOUT_MS, FIELD_FILTER_OPERATORS, FILTER_EXPRESSION_KEYS, MAX_FILE_BYTES, NATIVE_CRC32_MIN_BYTES, NO_LIVE_VECTOR_AT_POSITION, SHUTDOWN_TIMEOUT_MS, VALID_HNSW_METRICS, VECTOR_FILE_MAX_VECTORS, VECTOR_FILE_PAYLOAD_VERSION, assembleGraphParts, buildSegmentPayload, bytesToVectors, countDead, createDirectExecutor, createFrozenSegment, createPartitionIndex, createPartitionManager, createPartitionRouter, createSharedFrozenSegment, deadBitsWhere, deadBytesFor, decodeEveryStoredDocument, decodeMapWithoutFields, decodeStoredDocument, decodeVectorFilePayload, decodeVectorGraphPartPayload, decodeVectorGraphPayload, decodeVectorIndexParts, encodeSegmentState, freezeSegmentShared, generateId, getStopWords, getTokenizer, graphPartsOfEarlierPayload, hasStopWords, hasTokenizer, isCompositePartition, isDead, mergeFrozenSegments, mergeSegmentContents, missingWorkerEntry, nodeThreadId, partitionStateOf, readHeapStatistics, registerPatternSearch, registerStopWords, registerTokenizer, requirePatternSearchFor, resolveIndexAnalysis, resolvePartitionInsertOptions, resolveRequestThreadCount, resolveWorkerCount, resolveWorkerEntry, segmentArraysOf, selectionKeeps, spawnNodeWorker, splitWorkerBudget, survivorDocumentsOf, vectorsPerFile, vectorsToBytes, workerResourceLimits };
|