@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.
Files changed (69) hide show
  1. package/README.md +2 -2
  2. package/dist/adapters/filesystem.mjs +1 -1
  3. package/dist/adapters/indexeddb.mjs +1 -1
  4. package/dist/{capabilities-Dt-7B4Wz.d.ts → capabilities-CYeAWYxQ.d.ts} +19 -4
  5. package/dist/{chunk-3JGWULDF.mjs → chunk-2WLD2NFW.mjs} +5 -5
  6. package/dist/{chunk-LMO5TJUS.mjs → chunk-3YKWSO54.mjs} +642 -196
  7. package/dist/{chunk-HWQLTLNG.mjs → chunk-4MQ5F4IL.mjs} +1 -1
  8. package/dist/{chunk-FVLQ63B7.mjs → chunk-AFKH4ZK3.mjs} +30 -6
  9. package/dist/{chunk-653BNMAF.mjs → chunk-AK3BLGCI.mjs} +195 -25
  10. package/dist/{chunk-IOR2K26O.mjs → chunk-CVFAFZSN.mjs} +8000 -4391
  11. package/dist/{chunk-USKFU7J5.mjs → chunk-I64LXKO3.mjs} +1 -1
  12. package/dist/{chunk-OI42GWLR.mjs → chunk-LJYTW7GT.mjs} +57 -11
  13. package/dist/{chunk-2STVUTNA.mjs → chunk-LPBWCHBE.mjs} +194 -13
  14. package/dist/{chunk-YNVIUGF5.mjs → chunk-PU43WDY4.mjs} +4 -2
  15. package/dist/{chunk-M76V3OGJ.mjs → chunk-THMSFOJO.mjs} +1289 -1405
  16. package/dist/{chunk-YJDQ7NLA.mjs → chunk-WVDFG45M.mjs} +93 -95
  17. package/dist/chunk-XW25X6AU.mjs +614 -0
  18. package/dist/chunk-ZKRV5M2P.mjs +75 -0
  19. package/dist/{chunk-P4DSQ6M4.mjs → chunk-ZULSEZCE.mjs} +68 -28
  20. package/dist/{client-COek5GXK.d.ts → client-D698E5ge.d.ts} +77 -26
  21. package/dist/client.d.ts +9 -9
  22. package/dist/client.mjs +45 -22
  23. package/dist/{config-rBpbgTw8.d.ts → config-Bv8etwUZ.d.ts} +11 -4
  24. package/dist/{config-Dfanyqqt.d.ts → config-QXU3HMpq.d.ts} +26 -2
  25. package/dist/direct-executor-5SRDFZAS.mjs +13 -0
  26. package/dist/distribution/coordinator/etcd.d.ts +29 -16
  27. package/dist/distribution/coordinator/etcd.mjs +163 -76
  28. package/dist/distribution/coordinator/in-memory.d.ts +2 -2
  29. package/dist/distribution/coordinator/in-memory.mjs +1 -1
  30. package/dist/distribution/transport/grpc.d.ts +2 -2
  31. package/dist/distribution/transport/grpc.mjs +22 -2
  32. package/dist/distribution/transport/in-memory.d.ts +2 -2
  33. package/dist/distribution/transport/in-memory.mjs +6 -3
  34. package/dist/distribution/transport/tcp.d.ts +2 -2
  35. package/dist/distribution/transport/tcp.mjs +40 -16
  36. package/dist/distribution.d.ts +9 -9
  37. package/dist/distribution.mjs +682 -388
  38. package/dist/embeddings/openai.mjs +1 -1
  39. package/dist/{engine-igk0aMpQ.d.ts → engine-D_OzKZ-6.d.ts} +97 -188
  40. package/dist/index.browser.mjs +11671 -7427
  41. package/dist/index.d.ts +27 -8
  42. package/dist/index.mjs +13 -14
  43. package/dist/invalidation/filesystem.mjs +1 -1
  44. package/dist/{memory-C7UMCMH3.d.ts → memory-B4jMmygw.d.ts} +279 -27
  45. package/dist/pattern-Cledccbl.d.ts +18 -0
  46. package/dist/pattern.d.ts +22 -0
  47. package/dist/pattern.mjs +7 -0
  48. package/dist/persistence/durability/checkpoint-worker.d.ts +41 -17
  49. package/dist/persistence/durability/checkpoint-worker.mjs +24 -16
  50. package/dist/react.d.ts +7 -7
  51. package/dist/react.mjs +1 -1
  52. package/dist/{schema-Bgzr4g7b.d.ts → schema-BBOCdhND.d.ts} +9 -7
  53. package/dist/server.d.ts +11 -11
  54. package/dist/server.mjs +213 -34
  55. package/dist/{tasks-BZt_zes6.d.ts → tasks-DpgCpW1x.d.ts} +3 -3
  56. package/dist/{thread-JEVAYETR.mjs → thread-3LNVA43V.mjs} +94 -27
  57. package/dist/{types-BQDlIvfS.d.ts → types-B3b68nfF.d.ts} +1 -1
  58. package/dist/{types-gOUqhs2F.d.ts → types-BiUmaiWT.d.ts} +1 -1
  59. package/dist/{types-D5OwhtsQ.d.ts → types-Cb9kiY5P.d.ts} +1 -1
  60. package/dist/vector/search-worker.d.ts +1 -1
  61. package/dist/vector/search-worker.mjs +4 -4
  62. package/dist/workers/entry.mjs +4 -4
  63. package/package.json +15 -7
  64. package/dist/chunk-3L4U6EJQ.mjs +0 -49
  65. package/dist/chunk-GIVAGD3Q.mjs +0 -25
  66. package/dist/chunk-IFBKLBGB.mjs +0 -7
  67. package/dist/direct-executor-GYJEKNV5.mjs +0 -12
  68. package/dist/serialization/crc32-worker.d.ts +0 -20
  69. package/dist/serialization/crc32-worker.mjs +0 -62
@@ -1,14 +1,15 @@
1
- import { getLanguage, detectRuntime } from './chunk-HWQLTLNG.mjs';
2
- import { cloneProjected, everyValueFacetConfig, mergeFacets, computeFacets, tokenizeOptions, getNestedValue, visitTokens, countsWithoutScores, fanOutMatchCount, fanOutQuery, sanitizeGlobalStats, textFieldsChanged, projectionKeepsField, setNestedValue, fnv1a, bitsetHas, createBitSet, kWayMerge, fuzzyPrefixOf, boundedLevenshtein, getFlatSchema, bitsetSet, tokenize, bitsetFromSet, getAllInternalDocIds, getFieldValueByInternalId, getOrCreateFieldNameIndex, tokenizeIterator, polygonContainment, requireValidOperands, bitsetAnd, bitsetOr, bitsetNot, requirePolygonRing, VINCENTY_CONVERGENCE_THRESHOLD, VINCENTY_MAX_ITERATIONS, DEG_TO_RAD } from './chunk-LMO5TJUS.mjs';
3
- import { schemaFieldsOf, isWordIndexedBase, validateDocument, validateDocumentStrict, isQuantizationMode, isStorageMode, requireValidFieldTypes, extractVectorFieldsFromSchema, VECTOR_PATTERN, parsedTypeOf, isListBase } from './chunk-653BNMAF.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, requireWholeNeighbourLists, STORE_CALIBRATION_GENERATION, entryForOrd, toDistance, sharedMemoryAvailable, 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-YJDQ7NLA.mjs';
5
- import { noteFallback } from './chunk-YNVIUGF5.mjs';
6
- import { VALID_HNSW_METRICS } from './chunk-IFBKLBGB.mjs';
7
- import { compareCodePoints, toReducedSortValue, readSortField, compareComparableValues, defaultSortMode, truncateSortString, SORT_VALUE_MAX_CODE_POINTS } from './chunk-OI42GWLR.mjs';
8
- import { NarsilError, ErrorCodes } from './chunk-USKFU7J5.mjs';
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 { encode, decode } from '@msgpack/msgpack';
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 Uint8Array(POSTING_LIST_INITIAL_CAPACITY),
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 Uint8Array(newCap);
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 Uint8Array(count);
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
- return {
1481
- get entries() {
1482
- return entries;
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
- entries.push({ lat, lon, docId: internalId });
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
- for (let i = entries.length - 1; i >= 0; i--) {
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
- for (let i = 0; i < entries.length; i++) {
1499
- const entry = entries[i];
1500
- const dist = distanceFn(lat, lon, entry.lat, entry.lon);
1501
- const withinRadius = dist <= distanceMeters;
1502
- if (inside ? withinRadius : !withinRadius) {
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
- for (let i = 0; i < entries.length; i++) {
1512
- const entry = entries[i];
1513
- const withinPolygon = contains(entry.lat, entry.lon);
1514
- if (inside ? withinPolygon : !withinPolygon) {
1515
- result.add(entry.docId);
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
- return entries.map((e) => ({ ...e }));
1133
+ ensureSorted();
1134
+ return entries.map((entry) => ({ lat: entry.lat, lon: entry.lon, docId: entry.docId }));
1525
1135
  },
1526
1136
  deserialize(data) {
1527
- entries.length = 0;
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 === 0) valueMap.delete(value);
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/combinators.ts
2227
- function applyAndBitset(bitsets) {
2228
- if (bitsets.length === 0) return new Uint32Array(0);
2229
- if (bitsets.length === 1) return bitsets[0];
2230
- let result = bitsets[0];
2231
- for (let i = 1; i < bitsets.length; i++) {
2232
- result = bitsetAnd(result, bitsets[i]);
2233
- }
2234
- return result;
2235
- }
2236
- function applyOrBitset(bitsets) {
2237
- if (bitsets.length === 0) return new Uint32Array(0);
2238
- if (bitsets.length === 1) return bitsets[0];
2239
- let result = bitsets[0];
2240
- for (let i = 1; i < bitsets.length; i++) {
2241
- result = bitsetOr(result, bitsets[i]);
2242
- }
2243
- return result;
2244
- }
2245
- function applyNotBitset(excluded, capacity) {
2246
- return bitsetNot(excluded, capacity);
2247
- }
2248
-
2249
- // src/filters/keys.ts
2250
- var FILTER_EXPRESSION_KEYS = ["fields", "and", "or", "not"];
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/operators/bitset.ts
2320
- function resolveBitset(bs) {
2321
- return typeof bs === "function" ? bs() : bs;
2322
- }
2323
- function scanToBitset(docIds, capacity, getValue, predicate) {
2324
- const resolved = resolveBitset(docIds);
2325
- const result = createBitSet(capacity);
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
- if (fieldIndex?.type === "enum" && typeof value === "string") {
2363
- const all = fieldIndex.index.allDocIdsBitset(capacity);
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
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => lacksValue(v, value));
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
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v > value);
2374
- }
2375
- function applyLtBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
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
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v < value);
2380
- }
2381
- function applyGteBitset(value, allDocsBitset, capacity, getValue, fieldIndex) {
2382
- if (fieldIndex?.type === "numeric") {
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
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v >= value);
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
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v <= value);
2392
- }
2393
- function applyBetweenBitset(range, allDocsBitset, capacity, getValue, fieldIndex) {
2394
- if (fieldIndex?.type === "numeric") {
2395
- return fieldIndex.index.betweenBitset(range[0], range[1], capacity);
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
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => typeof v === "number" && v >= range[0] && v <= range[1]);
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 valSet = new Set(values);
2404
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => holdsStringIn(v, valSet));
2405
- }
2406
- function applyNinBitset(values, allDocsBitset, capacity, getValue, fieldIndex) {
2407
- if (fieldIndex?.type === "enum") {
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
- const valSet = new Set(values);
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 applyIsEmptyBitset(allDocsBitset, capacity, getValue) {
2456
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
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 applyIsNotEmptyBitset(allDocsBitset, capacity, getValue) {
2464
- return scanToBitset(allDocsBitset, capacity, getValue, (v) => {
2465
- if (v === void 0 || v === null) return false;
2466
- if (typeof v === "string" && v === "") return false;
2467
- if (Array.isArray(v) && v.length === 0) return false;
2468
- return true;
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 applyGeoRadiusBitset(filter, geoIndex, capacity) {
2472
- const distanceMeters = convertToMeters(filter.distance, filter.unit);
2473
- const setResult = geoIndex.radiusQuery(
2474
- filter.lat,
2475
- filter.lon,
2476
- distanceMeters,
2477
- filter.inside ?? true,
2478
- filter.highPrecision ?? false
2479
- );
2480
- return bitsetFromSet(setResult, capacity);
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 applyGeoPolygonBitset(filter, geoIndex, capacity) {
2483
- requirePolygonRing(filter.points);
2484
- const setResult = geoIndex.polygonQuery(filter.points, filter.inside ?? true);
2485
- return bitsetFromSet(setResult, capacity);
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 { ...UNBOUNDED, lower: bound, lowerInclusive: operator === "gte", upperInclusive: false };
2162
+ return { lower: bound, upper: null, lowerInclusive: operator === "gte", upperInclusive: false };
2493
2163
  }
2494
- return { ...UNBOUNDED, upper: bound, upperInclusive: operator === "lte", lowerInclusive: false };
2164
+ return { lower: null, upper: bound, upperInclusive: operator === "lte", lowerInclusive: false };
2495
2165
  }
2496
- var TEXT_RANGE_OPERATORS = ["gt", "gte", "lt", "lte"];
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 bitsets = [];
2257
+ const narrowing = new Narrowing();
2513
2258
  if (expression.fields) {
2514
2259
  for (const [fieldPath, filter] of Object.entries(expression.fields)) {
2515
- bitsets.push(evaluateFieldFilter(fieldPath, filter, context));
2260
+ narrowByField(fieldPath, filter, context, narrowing);
2516
2261
  }
2517
2262
  }
2518
2263
  if (expression.and?.length) {
2519
- const andBitsets = expression.and.map((expr) => evaluateFilters(expr, context));
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 orBitsets = expression.or.map((expr) => evaluateFilters(expr, context));
2524
- bitsets.push(applyOrBitset(orBitsets));
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 universe = context.allDocIdsBitset;
2529
- bitsets.push(applyAndBitset([universe, applyNotBitset(excluded, context.capacity)]));
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 fieldIndex.index;
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
- var EMPTY_GEO_INDEX = {
3277
- radiusQuery: () => /* @__PURE__ */ new Set(),
3278
- polygonQuery: () => /* @__PURE__ */ new Set()
3279
- };
3280
- function buildFilterContext(state, schema) {
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
- for (const { path: fieldPath, base: fieldType } of schemaFieldsOf(schema)) {
3285
- if (fieldType === "number" || fieldType === "number[]") {
3286
- const numIdx = state.numericIndexes.get(fieldPath);
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
- let cachedAllDocIds = null;
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
- fieldIndexes,
3350
- getFieldValue: (internalId, fieldPath) => getFieldValueByInternalId(state.docStore, internalId, fieldPath),
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
- get allDocIds() {
3353
- if (!cachedAllDocIds) {
3354
- cachedAllDocIds = getAllInternalDocIds(state.docStore);
3355
- }
3356
- return cachedAllDocIds;
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
- if (!cachedAllDocIdsBitset) {
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 mergeFieldIndexes(target, source, idMapping) {
3610
- for (const [fieldPath, index] of source.numericIndexes) {
3611
- let targetIndex = target.numericIndexes.get(fieldPath);
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.numericIndexes.set(fieldPath, targetIndex);
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 encodeFieldIndexes(state, remap) {
4684
- const survives = (internalId) => remap[internalId] >= 0;
4685
- const numeric = [];
4686
- for (const [fieldPath, index] of state.numericIndexes) {
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
- numeric.push({ fieldPath, docIds, values });
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
- return { numeric, boolean: booleans, enums, geo };
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 Uint8Array(postings);
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 mergePayloadFieldIndexes(target, payload, ordinalBase) {
4894
- for (const entry of payload.numeric) {
4895
- let index = target.numericIndexes.get(entry.fieldPath);
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.numericIndexes.set(entry.fieldPath, index);
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
- const wireGeo = {};
5276
- for (const [path, idx] of state.geoIndexes) {
5277
- wireGeo[path] = idx.serialize().map((e) => ({
5278
- lat: e.lat,
5279
- lon: e.lon,
5280
- doc_id: resolver.toExternal(e.docId) ?? ""
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 serializedStats = state.stats.serialize();
5284
- return {
5285
- v: 2,
5286
- index_name: indexName,
5287
- partition_id: partitionId,
5288
- total_partitions: totalPartitions,
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 [docId] of state.store.entries()) {
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 isNonNegativeInteger2(value) {
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 (!isNonNegativeInteger2(raw.dimension) || raw.dimension === 0) invalid("dimension must be a positive integer");
7648
- if (!isNonNegativeInteger2(raw.part)) invalid("part must be a non-negative integer");
7649
- if (!isNonNegativeInteger2(raw.parts) || raw.parts === 0 || raw.part >= raw.parts) {
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, 65535) || raw.m === 0) invalid2(name, "m must be a positive uint16");
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 { v: VECTOR_GRAPH_PAYLOAD_VERSION, fieldName: raw.fieldName, graphs: raw.graphs.map(decodeNumberedGraph) };
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 [docId] of state.store.entries()) {
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 [docId] of state.store.entries()) {
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 [docId] of state.store.entries()) {
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 >= VECTOR_FILE_MAX_VECTORS) continue;
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 graphOf(state, numbers) {
8106
- if (state.hnsw === null) return null;
8107
- return {
8108
- v: VECTOR_GRAPH_PAYLOAD_VERSION,
8109
- fieldName: state.fieldName,
8110
- graphs: [state.hnsw.serializeNumbered(numbers.numberOfOrdinal, numbers.ordinalOfNumber)]
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 / VECTOR_FILE_MAX_VECTORS),
8158
- graph: unchanged ? null : graphOf(state, numberEveryVector(state, keptFiles, written)),
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 * VECTOR_FILE_MAX_VECTORS;
8162
- const end = start + VECTOR_FILE_MAX_VECTORS;
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 [docId] of state.store.entries()) {
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(first, ordinalOfNumber);
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 = encode(documents[ordinal]);
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 = decode(blob.slice(offsets[ordinal], offsets[ordinal + 1]));
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: sharedUint8(payload.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 Uint8Array(totalPostings);
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 remapOf(segment, docIds) {
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 (segment.isTombstoned(ordinal)) continue;
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 Uint8Array(segment.arrays.fieldNames.length);
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
- return (ordinal) => {
10235
+ const encodeOrdinal = (ordinal) => {
10339
10236
  let bytes = encoded[ordinal];
10340
10237
  if (bytes === void 0) {
10341
- bytes = encode(input.segment.documentSource.docAt(ordinal));
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 = segments.map((segment) => ({
10372
- segment,
10373
- remap: remapOf(segment, docIds),
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 freezeEncodedSegmentShared(payload, mergeDocuments2(inputs, documentCount));
10306
+ return { payload, documents: mergeDocuments2(inputs, documentCount) };
10398
10307
  }
10399
10308
 
10400
- // src/core/partition/growable-uint32.ts
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(TOKEN_TABLE_START);
10487
- docFrequencyPerToken = new GrowableUint32(TOKEN_TABLE_START);
10488
- lastDocPerToken = new GrowableUint32(TOKEN_TABLE_START);
10489
- fieldStampPerToken = new GrowableUint32(TOKEN_TABLE_START);
10490
- fieldSlotPerToken = new GrowableUint32(TOKEN_TABLE_START);
10491
- logToken = new GrowableUint32(POSTING_LOG_START);
10492
- logDoc = new GrowableUint32(POSTING_LOG_START);
10493
- logFrequency = new GrowableUint32(POSTING_LOG_START);
10494
- logField = new GrowableUint32(POSTING_LOG_START);
10495
- logPositionStart = new GrowableUint32(POSTING_LOG_START);
10496
- positionPool = new GrowableUint32(POSITION_POOL_START);
10497
- slotToken = new GrowableUint32(FIELD_SCRATCH_START);
10498
- slotCount = new GrowableUint32(FIELD_SCRATCH_START);
10499
- slotCursor = new GrowableUint32(FIELD_SCRATCH_START);
10500
- eventSlot = new GrowableUint32(FIELD_SCRATCH_START);
10501
- eventPosition = new GrowableUint32(FIELD_SCRATCH_START);
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 Uint8Array(postings);
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(TOKEN_TABLE_START);
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: entry.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
- entry.searchOptions
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, countDead, createCompositePartition, createDirectExecutor, createFrozenSegment, createPartitionIndex, createPartitionManager, createPartitionRouter, createSharedFrozenSegment, deadBitsWhere, deadBytesFor, decodeVectorFilePayload, decodeVectorGraphPayload, decodeVectorIndexParts, deserializeMetadata, deserializePayloadV1, deserializePayloadV2, documentsOfPayloadV2, encodeSegmentState, freezeSegmentShared, generateId, getStopWords, getTokenizer, hasStopWords, hasTokenizer, isCompositePartition, isNodeMainThread, mergeFrozenSegments, missingWorkerEntry, nodeThreadId, readHeapStatistics, registerStopWords, registerTokenizer, resolveIndexAnalysis, resolvePartitionInsertOptions, resolveRequestThreadCount, resolveWorkerCount, resolveWorkerEntry, serializeMetadata, spawnNodeWorker, splitWorkerBudget, workerResourceLimits };
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 };