@delali/narsil 0.3.1-next.71 → 0.3.1-next.72

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 (57) hide show
  1. package/dist/adapters/filesystem.mjs +1 -1
  2. package/dist/adapters/indexeddb.mjs +1 -1
  3. package/dist/{capabilities-Dt-7B4Wz.d.ts → capabilities-D6gjjQyA.d.ts} +2 -2
  4. package/dist/{chunk-P4DSQ6M4.mjs → chunk-2X2ZQOAB.mjs} +69 -28
  5. package/dist/chunk-3HQPUCCT.mjs +785 -0
  6. package/dist/{chunk-IOR2K26O.mjs → chunk-7QMTA7AA.mjs} +146 -37
  7. package/dist/{chunk-FVLQ63B7.mjs → chunk-CVPA5Q7R.mjs} +24 -3
  8. package/dist/{chunk-2STVUTNA.mjs → chunk-EDMCZLSJ.mjs} +4 -3
  9. package/dist/{chunk-M76V3OGJ.mjs → chunk-HVJYTKK7.mjs} +940 -647
  10. package/dist/{chunk-HWQLTLNG.mjs → chunk-KZTI25D3.mjs} +1 -1
  11. package/dist/{chunk-YJDQ7NLA.mjs → chunk-LCCL6EEH.mjs} +3 -3
  12. package/dist/{chunk-OI42GWLR.mjs → chunk-LJYTW7GT.mjs} +57 -11
  13. package/dist/{chunk-653BNMAF.mjs → chunk-M6XF5HDE.mjs} +57 -17
  14. package/dist/{chunk-YNVIUGF5.mjs → chunk-P2ZZN32C.mjs} +1 -1
  15. package/dist/{chunk-LMO5TJUS.mjs → chunk-QR4OKRVY.mjs} +637 -196
  16. package/dist/{chunk-USKFU7J5.mjs → chunk-RV77FR2F.mjs} +1 -0
  17. package/dist/{chunk-GIVAGD3Q.mjs → chunk-XZ4PAREY.mjs} +1 -1
  18. package/dist/{chunk-3JGWULDF.mjs → chunk-Y52WZWAU.mjs} +5 -5
  19. package/dist/chunk-ZKRV5M2P.mjs +75 -0
  20. package/dist/{client-COek5GXK.d.ts → client-C2TiW29y.d.ts} +3 -3
  21. package/dist/client.d.ts +8 -8
  22. package/dist/client.mjs +1 -0
  23. package/dist/{config-rBpbgTw8.d.ts → config-BLguB-om.d.ts} +2 -2
  24. package/dist/{config-Dfanyqqt.d.ts → config-C7Ip8Ife.d.ts} +17 -2
  25. package/dist/direct-executor-SVCHXR74.mjs +14 -0
  26. package/dist/distribution/coordinator/etcd.d.ts +2 -2
  27. package/dist/distribution/coordinator/etcd.mjs +2 -2
  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.mjs +1 -1
  31. package/dist/distribution.d.ts +8 -8
  32. package/dist/distribution.mjs +58 -35
  33. package/dist/embeddings/openai.mjs +1 -1
  34. package/dist/{engine-igk0aMpQ.d.ts → engine-Dm4446j1.d.ts} +2 -2
  35. package/dist/index.browser.mjs +2183 -1087
  36. package/dist/index.d.ts +27 -8
  37. package/dist/index.mjs +14 -12
  38. package/dist/invalidation/filesystem.mjs +1 -1
  39. package/dist/{memory-C7UMCMH3.d.ts → memory-CKHTU1CL.d.ts} +66 -25
  40. package/dist/pattern-Cledccbl.d.ts +18 -0
  41. package/dist/pattern.d.ts +22 -0
  42. package/dist/pattern.mjs +7 -0
  43. package/dist/persistence/durability/checkpoint-worker.d.ts +17 -1
  44. package/dist/persistence/durability/checkpoint-worker.mjs +14 -12
  45. package/dist/react.d.ts +7 -7
  46. package/dist/react.mjs +1 -0
  47. package/dist/{schema-Bgzr4g7b.d.ts → schema-BBOCdhND.d.ts} +9 -7
  48. package/dist/server.d.ts +10 -10
  49. package/dist/server.mjs +13 -12
  50. package/dist/{tasks-BZt_zes6.d.ts → tasks-BECd0b__.d.ts} +1 -1
  51. package/dist/{thread-JEVAYETR.mjs → thread-IBSIZZ6W.mjs} +22 -13
  52. package/dist/{types-gOUqhs2F.d.ts → types-BiUmaiWT.d.ts} +1 -1
  53. package/dist/vector/search-worker.d.ts +1 -1
  54. package/dist/vector/search-worker.mjs +4 -4
  55. package/dist/workers/entry.mjs +4 -4
  56. package/package.json +15 -7
  57. package/dist/direct-executor-GYJEKNV5.mjs +0 -12
@@ -1,11 +1,13 @@
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';
1
+ import { cloneProjected, everyValueFacetConfig, mergeFacets, computeFacets, tokenizeOptions, getNestedValue, visitTokens, joinPatternWorkMeter, countsWithoutScores, fanOutMatchCount, fanOutQuery, sanitizeGlobalStats, textFieldsChanged, projectionKeepsField, setNestedValue, fnv1a, kWayMerge, fuzzyPrefixOf, boundedLevenshtein, getFlatSchema, tokenize, getOrCreateFieldNameIndex, tokenizeIterator, getFieldValueByInternalId, undeclaredFilterField, requireValidOperands, polygonContainment, polygonBoxes, radiusBoxes, locationCode, coveringRanges, GEO_UNIT_METRES } from './chunk-QR4OKRVY.mjs';
2
+ import { getLanguage, detectRuntime } from './chunk-KZTI25D3.mjs';
3
+ import { schemaFieldsOf, isWordIndexedBase, validateDocument, validateDocumentStrict, isQuantizationMode, isStorageMode, requireValidFieldTypes, extractVectorFieldsFromSchema, VECTOR_PATTERN, parsedTypeOf, isListBase, VINCENTY_CONVERGENCE_THRESHOLD, EARTH_RADIUS_METERS, VINCENTY_MAX_ITERATIONS, DEG_TO_RAD } from './chunk-M6XF5HDE.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-LCCL6EEH.mjs';
5
+ import { noteFallback } from './chunk-P2ZZN32C.mjs';
6
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';
7
+ import { GrowableUint32, patternSearchModule } from './chunk-3HQPUCCT.mjs';
8
+ import { bitsetHas, createBitSet, bitsetSet, bitsetOrInto, bitsetAndNotInto, bitsetAndInto, PATTERN_RUN_CODE_POINTS, bitsetFromSet, bitsetIsEmpty, bitsetAnd, bitsetNot } from './chunk-ZKRV5M2P.mjs';
9
+ import { compareCodePoints, appendFoldedCodePoint, toReducedSortValue, readSortField, compareComparableValues, defaultSortMode, truncateSortString, SORT_VALUE_MAX_CODE_POINTS } from './chunk-LJYTW7GT.mjs';
10
+ import { NarsilError, ErrorCodes } from './chunk-RV77FR2F.mjs';
9
11
  import { totalmem } from 'os';
10
12
  import { getHeapStatistics } from 'v8';
11
13
  import { encode, decode } from '@msgpack/msgpack';
@@ -398,6 +400,44 @@ function sanitizeSurfaceForms(raw) {
398
400
  }
399
401
  return count > 0 ? result : void 0;
400
402
  }
403
+ function patternIndexesToWire(pattern) {
404
+ if (pattern === void 0) return {};
405
+ const wire = /* @__PURE__ */ Object.create(null);
406
+ for (const [field, index] of Object.entries(pattern)) wire[field] = { doc_ids: index.docIds, runs: index.runs };
407
+ return { pattern: wire };
408
+ }
409
+ function isWireRecord(value) {
410
+ return value !== null && typeof value === "object" && !Array.isArray(value);
411
+ }
412
+ function corruptPatternEntry(field) {
413
+ return new NarsilError(
414
+ ErrorCodes.PERSISTENCE_LOAD_FAILED,
415
+ `The pattern entry of field "${field}" in this partition must hold a list of document IDs and a map of runs`,
416
+ { field }
417
+ );
418
+ }
419
+ function patternIndexesFromWire(raw) {
420
+ if (raw === void 0 || raw === null) return {};
421
+ if (!isWireRecord(raw)) {
422
+ throw new NarsilError(
423
+ ErrorCodes.PERSISTENCE_LOAD_FAILED,
424
+ "The pattern entry of this partition must be a map of fields"
425
+ );
426
+ }
427
+ const pattern = /* @__PURE__ */ Object.create(null);
428
+ for (const [field, index] of Object.entries(raw)) {
429
+ if (!isWireRecord(index) || !isWireRecord(index.runs) || !Array.isArray(index.doc_ids)) {
430
+ throw corruptPatternEntry(field);
431
+ }
432
+ const docIds = [];
433
+ for (const docId of index.doc_ids) {
434
+ if (typeof docId !== "string") throw corruptPatternEntry(field);
435
+ docIds.push(docId);
436
+ }
437
+ pattern[field] = { docIds, runs: index.runs };
438
+ }
439
+ return { pattern };
440
+ }
401
441
  function validateHnswMetric(value) {
402
442
  if (value === void 0 || value === null) return void 0;
403
443
  if (typeof value === "string" && VALID_HNSW_METRICS.has(value)) {
@@ -469,7 +509,8 @@ function wireToPartition(raw) {
469
509
  numeric,
470
510
  boolean,
471
511
  enum: raw.field_indexes?.enum ?? {},
472
- geopoint
512
+ geopoint,
513
+ ...patternIndexesFromWire(raw.field_indexes?.pattern)
473
514
  },
474
515
  surfaceForms: sanitizeSurfaceForms(raw.surface_forms),
475
516
  vectorData,
@@ -576,7 +617,8 @@ function deserializePayloadV2(data) {
576
617
  numeric,
577
618
  boolean,
578
619
  enum: raw.field_indexes?.enum ?? {},
579
- geopoint
620
+ geopoint,
621
+ ...patternIndexesFromWire(raw.field_indexes?.pattern)
580
622
  },
581
623
  surfaceForms: sanitizeSurfaceForms(raw.surface_forms),
582
624
  vectorData,
@@ -1411,8 +1453,55 @@ function createSurfaceRegistry() {
1411
1453
  };
1412
1454
  }
1413
1455
 
1456
+ // src/core/pattern-index/platform.ts
1457
+ function platformPatternSearch() {
1458
+ return patternSearchModule;
1459
+ }
1460
+
1461
+ // src/core/pattern-index/registry.ts
1462
+ var registered = null;
1463
+ function isPatternSearchModule(value) {
1464
+ 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";
1465
+ }
1466
+ function registerPatternSearch(module) {
1467
+ if (!isPatternSearchModule(module)) {
1468
+ throw new NarsilError(
1469
+ ErrorCodes.CONFIG_INVALID,
1470
+ 'registerPatternSearch takes the patternSearch object that "@delali/narsil/pattern" exports'
1471
+ );
1472
+ }
1473
+ registered = module;
1474
+ }
1475
+ function availablePatternSearch() {
1476
+ return registered ?? platformPatternSearch();
1477
+ }
1478
+ function requirePatternSearchFor(schema) {
1479
+ for (const { path, pattern } of schemaFieldsOf(schema)) {
1480
+ if (pattern) requirePatternSearch(path);
1481
+ }
1482
+ }
1483
+ function requirePatternSearch(fieldPath) {
1484
+ const module = availablePatternSearch();
1485
+ if (module !== null) return module;
1486
+ throw new NarsilError(
1487
+ ErrorCodes.CONFIG_INVALID,
1488
+ `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`,
1489
+ { field: fieldPath }
1490
+ );
1491
+ }
1492
+
1493
+ // src/core/pattern-index/host.ts
1494
+ var PATTERN_HOST = { appendFoldedCodePoint };
1495
+ function patternIndexOf(indexes, fieldPath) {
1496
+ let index = indexes.get(fieldPath);
1497
+ if (index === void 0) {
1498
+ index = requirePatternSearch(fieldPath).createIndex(PATTERN_HOST);
1499
+ indexes.set(fieldPath, index);
1500
+ }
1501
+ return index;
1502
+ }
1503
+
1414
1504
  // src/geo/haversine.ts
1415
- var EARTH_RADIUS_METERS = 63710088e-1;
1416
1505
  function haversineDistance(lat1, lon1, lat2, lon2) {
1417
1506
  const phi1 = lat1 * DEG_TO_RAD;
1418
1507
  const phi2 = lat2 * DEG_TO_RAD;
@@ -1475,61 +1564,94 @@ function vincentyDistance(lat1, lon1, lat2, lon2) {
1475
1564
  }
1476
1565
 
1477
1566
  // src/geo/geo-index.ts
1567
+ function firstAtOrAfter(entries, code) {
1568
+ let lo = 0;
1569
+ let hi = entries.length;
1570
+ while (lo < hi) {
1571
+ const mid = lo + hi >>> 1;
1572
+ if (entries[mid].code < code) lo = mid + 1;
1573
+ else hi = mid;
1574
+ }
1575
+ return lo;
1576
+ }
1478
1577
  function createGeoIndex() {
1479
1578
  const entries = [];
1480
- return {
1481
- get entries() {
1482
- return entries;
1483
- },
1579
+ const byDocument = /* @__PURE__ */ new Map();
1580
+ let sorted = true;
1581
+ function ensureSorted() {
1582
+ if (sorted) return;
1583
+ entries.sort((left, right) => left.code - right.code || left.docId - right.docId);
1584
+ sorted = true;
1585
+ }
1586
+ function removeEntry(internalId) {
1587
+ const entry = byDocument.get(internalId);
1588
+ if (entry === void 0) return;
1589
+ byDocument.delete(internalId);
1590
+ ensureSorted();
1591
+ for (let at = firstAtOrAfter(entries, entry.code); at < entries.length; at++) {
1592
+ if (entries[at] !== entry) continue;
1593
+ entries.splice(at, 1);
1594
+ return;
1595
+ }
1596
+ }
1597
+ function matching(boxes, passes, inside) {
1598
+ ensureSorted();
1599
+ const within = /* @__PURE__ */ new Set();
1600
+ for (const range of coveringRanges(boxes)) {
1601
+ for (let at = firstAtOrAfter(entries, range.from); at < entries.length && entries[at].code < range.to; at++) {
1602
+ if (passes(entries[at])) within.add(entries[at].docId);
1603
+ }
1604
+ }
1605
+ if (inside) return within;
1606
+ const outside = /* @__PURE__ */ new Set();
1607
+ for (const entry of entries) {
1608
+ if (!within.has(entry.docId)) outside.add(entry.docId);
1609
+ }
1610
+ return outside;
1611
+ }
1612
+ const index = {
1484
1613
  insert(internalId, lat, lon) {
1485
- entries.push({ lat, lon, docId: internalId });
1614
+ removeEntry(internalId);
1615
+ const entry = { lat, lon, docId: internalId, code: locationCode(lat, lon) };
1616
+ entries.push(entry);
1617
+ byDocument.set(internalId, entry);
1618
+ sorted = false;
1486
1619
  },
1487
1620
  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
- }
1621
+ removeEntry(internalId);
1494
1622
  },
1495
1623
  radiusQuery(lat, lon, distanceMeters, inside, highPrecision) {
1496
- const result = /* @__PURE__ */ new Set();
1497
1624
  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;
1625
+ return matching(
1626
+ radiusBoxes(lat, lon, distanceMeters),
1627
+ (entry) => distanceFn(lat, lon, entry.lat, entry.lon) <= distanceMeters,
1628
+ inside
1629
+ );
1507
1630
  },
1508
1631
  polygonQuery(points, inside) {
1509
- const result = /* @__PURE__ */ new Set();
1510
1632
  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;
1633
+ return matching(polygonBoxes(points), (entry) => contains(entry.lat, entry.lon), inside);
1634
+ },
1635
+ documentsBitset(capacity) {
1636
+ const bits = createBitSet(capacity);
1637
+ for (const entry of entries) bitsetSet(bits, entry.docId);
1638
+ return bits;
1519
1639
  },
1520
1640
  clear() {
1521
1641
  entries.length = 0;
1642
+ byDocument.clear();
1643
+ sorted = true;
1522
1644
  },
1523
1645
  serialize() {
1524
- return entries.map((e) => ({ ...e }));
1646
+ ensureSorted();
1647
+ return entries.map((entry) => ({ lat: entry.lat, lon: entry.lon, docId: entry.docId }));
1525
1648
  },
1526
1649
  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
- }
1650
+ index.clear();
1651
+ for (const entry of data) index.insert(entry.docId, entry.lat, entry.lon);
1531
1652
  }
1532
1653
  };
1654
+ return index;
1533
1655
  }
1534
1656
 
1535
1657
  // src/core/field-index/boolean.ts
@@ -1603,9 +1725,90 @@ function createBooleanIndex() {
1603
1725
  };
1604
1726
  }
1605
1727
 
1728
+ // src/core/field-index/affixes.ts
1729
+ function compareUnits(left, right) {
1730
+ if (left < right) return -1;
1731
+ return left > right ? 1 : 0;
1732
+ }
1733
+ function reversedUnits(value) {
1734
+ let reversed = "";
1735
+ for (let at = value.length - 1; at >= 0; at--) reversed += value[at];
1736
+ return reversed;
1737
+ }
1738
+ function firstAtOrAfter2(sorted, key) {
1739
+ let lo = 0;
1740
+ let hi = sorted.length;
1741
+ while (lo < hi) {
1742
+ const mid = lo + hi >>> 1;
1743
+ if (sorted[mid] < key) lo = mid + 1;
1744
+ else hi = mid;
1745
+ }
1746
+ return lo;
1747
+ }
1748
+ function settledColumn(column, holds) {
1749
+ const { keys, values } = column;
1750
+ const order = [];
1751
+ for (let at = 0; at < keys.length; at++) if (holds(values[at])) order.push(at);
1752
+ order.sort((left, right) => compareUnits(keys[left], keys[right]) || compareUnits(values[left], values[right]));
1753
+ const settled = { keys: [], values: [] };
1754
+ for (const at of order) {
1755
+ const last = settled.values.length - 1;
1756
+ if (last >= 0 && settled.keys[last] === keys[at] && settled.values[last] === values[at]) continue;
1757
+ settled.keys.push(keys[at]);
1758
+ settled.values.push(values[at]);
1759
+ }
1760
+ return settled;
1761
+ }
1762
+ var ValueAffixes = class {
1763
+ constructor(holds, values) {
1764
+ this.holds = holds;
1765
+ for (const value of values) this.add(value);
1766
+ }
1767
+ forward = { keys: [], values: [] };
1768
+ backward = { keys: [], values: [] };
1769
+ settled = true;
1770
+ add(value) {
1771
+ this.forward.keys.push(value);
1772
+ this.forward.values.push(value);
1773
+ this.backward.keys.push(reversedUnits(value));
1774
+ this.backward.values.push(value);
1775
+ this.settled = false;
1776
+ }
1777
+ forget() {
1778
+ this.settled = false;
1779
+ }
1780
+ withPrefix(prefix) {
1781
+ this.settle();
1782
+ return this.matching(this.forward, prefix);
1783
+ }
1784
+ withSuffix(suffix) {
1785
+ this.settle();
1786
+ return this.matching(this.backward, reversedUnits(suffix));
1787
+ }
1788
+ settle() {
1789
+ if (this.settled) return;
1790
+ this.forward = settledColumn(this.forward, this.holds);
1791
+ this.backward = settledColumn(this.backward, this.holds);
1792
+ this.settled = true;
1793
+ }
1794
+ matching(column, start) {
1795
+ const found = [];
1796
+ for (let at = firstAtOrAfter2(column.keys, start); at < column.keys.length; at++) {
1797
+ if (!column.keys[at].startsWith(start)) break;
1798
+ found.push(column.values[at]);
1799
+ }
1800
+ return found;
1801
+ }
1802
+ };
1803
+
1606
1804
  // src/core/field-index/enum.ts
1607
1805
  function createEnumIndex() {
1608
1806
  const valueMap = /* @__PURE__ */ new Map();
1807
+ let affixes = null;
1808
+ function affixesOf() {
1809
+ affixes ??= new ValueAffixes((value) => valueMap.has(value), valueMap.keys());
1810
+ return affixes;
1811
+ }
1609
1812
  function allDocs() {
1610
1813
  const result = /* @__PURE__ */ new Set();
1611
1814
  for (const docSet of valueMap.values()) {
@@ -1619,6 +1822,7 @@ function createEnumIndex() {
1619
1822
  if (!docSet) {
1620
1823
  docSet = /* @__PURE__ */ new Set();
1621
1824
  valueMap.set(value, docSet);
1825
+ affixes?.add(value);
1622
1826
  }
1623
1827
  docSet.add(internalId);
1624
1828
  },
@@ -1626,7 +1830,15 @@ function createEnumIndex() {
1626
1830
  const docSet = valueMap.get(value);
1627
1831
  if (!docSet) return;
1628
1832
  docSet.delete(internalId);
1629
- if (docSet.size === 0) valueMap.delete(value);
1833
+ if (docSet.size > 0) return;
1834
+ valueMap.delete(value);
1835
+ affixes?.forget();
1836
+ },
1837
+ queryPrefixBitset(prefix, capacity) {
1838
+ return this.queryInBitset(affixesOf().withPrefix(prefix), capacity);
1839
+ },
1840
+ querySuffixBitset(suffix, capacity) {
1841
+ return this.queryInBitset(affixesOf().withSuffix(suffix), capacity);
1630
1842
  },
1631
1843
  queryEq(value) {
1632
1844
  const docSet = valueMap.get(value);
@@ -1702,6 +1914,7 @@ function createEnumIndex() {
1702
1914
  },
1703
1915
  clear() {
1704
1916
  valueMap.clear();
1917
+ affixes = null;
1705
1918
  },
1706
1919
  serialize() {
1707
1920
  const result = /* @__PURE__ */ Object.create(null);
@@ -1712,6 +1925,7 @@ function createEnumIndex() {
1712
1925
  },
1713
1926
  deserialize(data) {
1714
1927
  valueMap.clear();
1928
+ affixes = null;
1715
1929
  for (const value of Object.keys(data)) {
1716
1930
  valueMap.set(value, new Set(data[value]));
1717
1931
  }
@@ -1893,6 +2107,44 @@ function createNumericIndex() {
1893
2107
  };
1894
2108
  }
1895
2109
 
2110
+ // src/core/partition/value-sizes.ts
2111
+ function keepsValueSize(base) {
2112
+ return base === "string" || base === "verbatim" || base.endsWith("[]");
2113
+ }
2114
+ function valueSizeOf(value) {
2115
+ if (Array.isArray(value)) return value.length;
2116
+ return value === "" ? 0 : 1;
2117
+ }
2118
+ function sizeIndexOf(indexes, fieldPath) {
2119
+ let index = indexes.get(fieldPath);
2120
+ if (index === void 0) {
2121
+ index = createNumericIndex();
2122
+ indexes.set(fieldPath, index);
2123
+ }
2124
+ return index;
2125
+ }
2126
+ function recordValueSize(indexes, internalId, fieldPath, base, value) {
2127
+ if (value === void 0 || value === null || !keepsValueSize(base)) return;
2128
+ sizeIndexOf(indexes, fieldPath).insert(internalId, valueSizeOf(value));
2129
+ }
2130
+ function forgetValueSize(indexes, internalId, fieldPath, base, value) {
2131
+ if (value === void 0 || value === null || !keepsValueSize(base)) return;
2132
+ indexes.get(fieldPath)?.remove(internalId, valueSizeOf(value));
2133
+ }
2134
+ function rebuildSizeIndexes(state, schema) {
2135
+ state.sizeIndexes.clear();
2136
+ const fields = schemaFieldsOf(schema).filter((field) => keepsValueSize(field.base));
2137
+ if (fields.length === 0) return;
2138
+ for (const [docId, stored] of state.docStore.all()) {
2139
+ const internalId = state.docStore.getInternalId(docId);
2140
+ if (internalId === void 0) continue;
2141
+ for (const { path, base } of fields) {
2142
+ const value = getNestedValue(stored.fields, path);
2143
+ recordValueSize(state.sizeIndexes, internalId, path, base, value);
2144
+ }
2145
+ }
2146
+ }
2147
+
1896
2148
  // src/core/partition/indexing.ts
1897
2149
  function ensureFieldIndex(state, fieldPath, base) {
1898
2150
  if ((base === "number" || base === "number[]") && !state.numericIndexes.has(fieldPath)) {
@@ -2031,10 +2283,12 @@ function indexDocument(state, docId, document, fields, language, options) {
2031
2283
  const fieldLengths = {};
2032
2284
  const tokensByField = {};
2033
2285
  const internalId = resolveInternalId(state, docId);
2034
- for (const { path: fieldPath, base } of fields) {
2286
+ for (const { path: fieldPath, base, pattern } of fields) {
2035
2287
  const value = getNestedValue(document, fieldPath);
2036
2288
  if (value === void 0 || value === null) continue;
2289
+ recordValueSize(state.sizeIndexes, internalId, fieldPath, base, value);
2037
2290
  ensureFieldIndex(state, fieldPath, base);
2291
+ if (pattern) patternIndexOf(state.patternIndexes, fieldPath).add(internalId, value);
2038
2292
  if (base === "string") {
2039
2293
  indexStringField(state, internalId, fieldPath, value, language, options, fieldLengths, tokensByField);
2040
2294
  } else if (base === "number") {
@@ -2078,9 +2332,11 @@ function removeFromIndexes(state, docId, storedDoc, schemaFields, language, opti
2078
2332
  const fields = storedDoc.fields;
2079
2333
  const internalId = resolveInternalId(state, docId);
2080
2334
  const opts = tokenizeOptions(options);
2081
- for (const { path: fieldPath, base } of schemaFields) {
2335
+ for (const { path: fieldPath, base, pattern } of schemaFields) {
2082
2336
  const value = getNestedValue(fields, fieldPath);
2083
2337
  if (value === void 0 || value === null) continue;
2338
+ forgetValueSize(state.sizeIndexes, internalId, fieldPath, base, value);
2339
+ if (pattern) state.patternIndexes.get(fieldPath)?.remove(internalId, value);
2084
2340
  if (base === "string") {
2085
2341
  const result = tokenize(value, language, opts);
2086
2342
  fieldLengths[fieldPath] = result.tokens.length;
@@ -2152,16 +2408,24 @@ function resolveInternalId2(state, docId) {
2152
2408
  }
2153
2409
  function updateFieldIndexOnly(state, docId, oldFields, newDoc, fields) {
2154
2410
  const internalId = resolveInternalId2(state, docId);
2155
- for (const { path: fieldPath, base: fieldType } of fields) {
2411
+ for (const { path: fieldPath, base: fieldType, pattern } of fields) {
2156
2412
  if (isWordIndexedBase(fieldType)) continue;
2157
2413
  const oldVal = getNestedValue(oldFields, fieldPath);
2158
2414
  const newVal = getNestedValue(newDoc, fieldPath);
2159
2415
  if (oldVal === newVal) continue;
2160
2416
  if (oldVal !== void 0 && oldVal !== null && newVal !== void 0 && newVal !== null && typeof oldVal === typeof newVal && JSON.stringify(oldVal) === JSON.stringify(newVal))
2161
2417
  continue;
2418
+ forgetValueSize(state.sizeIndexes, internalId, fieldPath, fieldType, oldVal);
2419
+ recordValueSize(state.sizeIndexes, internalId, fieldPath, fieldType, newVal);
2162
2420
  if (newVal !== void 0 && newVal !== null) {
2163
2421
  ensureFieldIndex(state, fieldPath, fieldType);
2164
2422
  }
2423
+ if (pattern) {
2424
+ if (oldVal !== void 0 && oldVal !== null) state.patternIndexes.get(fieldPath)?.remove(internalId, oldVal);
2425
+ if (newVal !== void 0 && newVal !== null) {
2426
+ patternIndexOf(state.patternIndexes, fieldPath).add(internalId, newVal);
2427
+ }
2428
+ }
2165
2429
  if (fieldType === "number") {
2166
2430
  const numIdx = state.numericIndexes.get(fieldPath);
2167
2431
  if (numIdx) {
@@ -2223,27 +2487,218 @@ function updateFieldIndexOnly(state, docId, oldFields, newDoc, fields) {
2223
2487
  }
2224
2488
  }
2225
2489
 
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]);
2490
+ // src/filters/pattern-tests.ts
2491
+ function excluding(context, fieldPath, excluded) {
2492
+ const universe = bitsetAnd(context.presentBitset(fieldPath), context.allDocIdsBitset);
2493
+ return bitsetAnd(universe, bitsetNot(excluded, context.capacity));
2494
+ }
2495
+ function textsOf(operand) {
2496
+ const texts = /* @__PURE__ */ new Set();
2497
+ if (!Array.isArray(operand)) return texts;
2498
+ for (const text of operand) if (typeof text === "string") texts.add(text);
2499
+ return texts;
2500
+ }
2501
+ function evaluatePatternTests(fieldPath, filter, context, bitsets) {
2502
+ const caseFold = filter.caseFold === true;
2503
+ const matching = (operator, texts) => {
2504
+ const into = createBitSet(context.capacity);
2505
+ for (const text of texts) context.matchPattern(fieldPath, operator, text, caseFold, into);
2506
+ return into;
2507
+ };
2508
+ if (typeof filter.eq === "string") bitsets.push(matching("eq", [filter.eq]));
2509
+ if (typeof filter.ne === "string") bitsets.push(excluding(context, fieldPath, matching("eq", [filter.ne])));
2510
+ if (filter.in !== void 0) bitsets.push(matching("eq", textsOf(filter.in)));
2511
+ if (filter.nin !== void 0) bitsets.push(excluding(context, fieldPath, matching("eq", textsOf(filter.nin))));
2512
+ if (typeof filter.startsWith === "string") bitsets.push(matching("startsWith", [filter.startsWith]));
2513
+ if (typeof filter.endsWith === "string") bitsets.push(matching("endsWith", [filter.endsWith]));
2514
+ if (typeof filter.contains === "string") bitsets.push(matching("contains", [filter.contains]));
2515
+ }
2516
+
2517
+ // src/filters/field-tests.ts
2518
+ var COMPARISON_OPERATORS = ["eq", "ne", "gt", "gte", "lt", "lte", "between"];
2519
+ var TEXT_RANGE_OPERATORS = ["gt", "gte", "lt", "lte"];
2520
+ var FieldBits = class {
2521
+ constructor(context, fieldPath) {
2522
+ this.context = context;
2523
+ this.fieldPath = fieldPath;
2233
2524
  }
2234
- return result;
2525
+ none() {
2526
+ return createBitSet(this.context.capacity);
2527
+ }
2528
+ present() {
2529
+ return this.context.presentBitset(this.fieldPath);
2530
+ }
2531
+ holdingSomething() {
2532
+ const empty = this.context.sizeIndex(this.fieldPath)?.queryEqBitset(0, this.context.capacity);
2533
+ return empty === void 0 ? this.present() : this.without(this.present(), empty);
2534
+ }
2535
+ without(from, removed) {
2536
+ const kept = from.slice();
2537
+ bitsetAndNotInto(kept, removed);
2538
+ return kept;
2539
+ }
2540
+ complement(bits) {
2541
+ return this.without(this.context.allDocIdsBitset, bits);
2542
+ }
2543
+ };
2544
+ function comparisonBits(index, operator, operand, bits, capacity) {
2545
+ if (operator === "between") {
2546
+ const [low, high] = operand;
2547
+ return index?.queryBetweenBitset(low, high, capacity) ?? bits.none();
2548
+ }
2549
+ const value = operand;
2550
+ if (operator === "ne") return bits.without(bits.present(), index?.queryEqBitset(value, capacity) ?? bits.none());
2551
+ if (index === void 0) return bits.none();
2552
+ if (operator === "eq") return index.queryEqBitset(value, capacity);
2553
+ if (operator === "gt") return index.queryGtBitset(value, capacity);
2554
+ if (operator === "gte") return index.queryGteBitset(value, capacity);
2555
+ if (operator === "lt") return index.queryLtBitset(value, capacity);
2556
+ return index.queryLteBitset(value, capacity);
2557
+ }
2558
+ function elementBits(context, fieldPath, parsed, value) {
2559
+ const { capacity } = context;
2560
+ if (parsed.pattern) {
2561
+ const matches = createBitSet(capacity);
2562
+ context.matchPattern(fieldPath, "eq", value, false, matches);
2563
+ return matches;
2564
+ }
2565
+ if (typeof value === "number")
2566
+ return context.numericIndex(fieldPath)?.queryEqBitset(value, capacity) ?? createBitSet(capacity);
2567
+ if (typeof value === "boolean")
2568
+ return context.booleanIndex(fieldPath)?.queryEqBitset(value, capacity) ?? createBitSet(capacity);
2569
+ return context.enumIndex(fieldPath)?.queryEqBitset(value, capacity) ?? createBitSet(capacity);
2570
+ }
2571
+ function anyElementBits(context, fieldPath, parsed, values) {
2572
+ const { capacity } = context;
2573
+ const union = createBitSet(capacity);
2574
+ if (parsed.pattern) {
2575
+ for (const value of values) context.matchPattern(fieldPath, "eq", value, false, union);
2576
+ return union;
2577
+ }
2578
+ if (parsed.base === "enum[]") {
2579
+ return context.enumIndex(fieldPath)?.queryInBitset(values, capacity) ?? union;
2580
+ }
2581
+ const numbers = context.numericIndex(fieldPath);
2582
+ const booleans = context.booleanIndex(fieldPath);
2583
+ for (const value of values) {
2584
+ if (typeof value === "number") for (const docId of numbers?.queryEq(value) ?? []) bitsetSet(union, docId);
2585
+ if (typeof value === "boolean") for (const docId of booleans?.queryEq(value) ?? []) bitsetSet(union, docId);
2586
+ }
2587
+ return union;
2588
+ }
2589
+ function distinctScalars(values) {
2590
+ return [...new Set(values)];
2235
2591
  }
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]);
2592
+ function narrowByElements(fieldPath, f, parsed, context, bits, narrow) {
2593
+ if (f.matchesAny !== void 0) narrow(anyElementBits(context, fieldPath, parsed, distinctScalars(f.matchesAny)));
2594
+ if (f.containsAll !== void 0) {
2595
+ const all = bits.present().slice();
2596
+ for (const value of distinctScalars(f.containsAll)) {
2597
+ if (bitsetIsEmpty(all)) break;
2598
+ bitsetAndInto(all, elementBits(context, fieldPath, parsed, value));
2599
+ }
2600
+ narrow(all);
2601
+ }
2602
+ if (f.size !== void 0) {
2603
+ const sizes = context.sizeIndex(fieldPath);
2604
+ const comparison = f.size;
2605
+ for (const operator of COMPARISON_OPERATORS) {
2606
+ if (comparison[operator] === void 0) continue;
2607
+ narrow(comparisonBits(sizes, operator, comparison[operator], bits, context.capacity));
2608
+ }
2609
+ }
2610
+ }
2611
+ function narrowByValue(fieldPath, f, parsed, context, bits, narrow) {
2612
+ const { capacity } = context;
2613
+ const base = parsed.base;
2614
+ if (base === "number" || base === "number[]") {
2615
+ const index = context.numericIndex(fieldPath);
2616
+ for (const operator of COMPARISON_OPERATORS) {
2617
+ const operand = f[operator];
2618
+ if (operand === void 0 || typeof operand === "string") continue;
2619
+ if (operator === "between" && typeof operand[0] === "string") continue;
2620
+ narrow(comparisonBits(index, operator, operand, bits, capacity));
2621
+ }
2622
+ return;
2623
+ }
2624
+ if (base === "boolean" || base === "boolean[]") {
2625
+ const index = context.booleanIndex(fieldPath);
2626
+ const equal = (value) => index?.queryEqBitset(value, capacity) ?? bits.none();
2627
+ if (typeof f.eq === "boolean") narrow(equal(f.eq));
2628
+ if (typeof f.ne === "boolean") narrow(bits.without(bits.present(), equal(f.ne)));
2629
+ return;
2630
+ }
2631
+ if (base === "enum" || base === "enum[]") {
2632
+ const index = context.enumIndex(fieldPath);
2633
+ const equal = (value) => index?.queryEqBitset(value, capacity) ?? bits.none();
2634
+ const anyOf = (values) => index?.queryInBitset(values, capacity) ?? bits.none();
2635
+ if (typeof f.eq === "string") narrow(equal(f.eq));
2636
+ if (typeof f.ne === "string") narrow(bits.without(bits.present(), equal(f.ne)));
2637
+ if (Array.isArray(f.in)) narrow(anyOf(f.in));
2638
+ if (Array.isArray(f.nin)) narrow(bits.without(bits.present(), anyOf(f.nin)));
2639
+ if (typeof f.startsWith === "string") narrow(index?.queryPrefixBitset(f.startsWith, capacity) ?? bits.none());
2640
+ if (typeof f.endsWith === "string") narrow(index?.querySuffixBitset(f.endsWith, capacity) ?? bits.none());
2641
+ }
2642
+ }
2643
+ function narrowByLocation(fieldPath, f, context, narrow) {
2644
+ const index = context.geoIndex(fieldPath);
2645
+ if (f.radius !== void 0) {
2646
+ const circle = f.radius;
2647
+ const found = index?.radiusQuery(
2648
+ circle.lat,
2649
+ circle.lon,
2650
+ circle.distance * GEO_UNIT_METRES[circle.unit],
2651
+ circle.inside ?? true,
2652
+ circle.highPrecision ?? false
2653
+ );
2654
+ narrow(bitsetFromSet(found ?? /* @__PURE__ */ new Set(), context.capacity));
2655
+ }
2656
+ if (f.polygon !== void 0) {
2657
+ const polygon = f.polygon;
2658
+ narrow(bitsetFromSet(index?.polygonQuery(polygon.points, polygon.inside ?? true) ?? /* @__PURE__ */ new Set(), context.capacity));
2659
+ }
2660
+ }
2661
+ function narrowByPresence(f, bits, narrow) {
2662
+ if (typeof f.exists === "boolean") narrow(f.exists ? bits.present() : bits.complement(bits.present()));
2663
+ if (typeof f.notExists === "boolean") narrow(f.notExists ? bits.complement(bits.present()) : bits.present());
2664
+ if (typeof f.isNotEmpty === "boolean") {
2665
+ const holding = bits.holdingSomething();
2666
+ narrow(f.isNotEmpty ? holding : bits.complement(holding));
2667
+ }
2668
+ if (typeof f.isEmpty === "boolean") {
2669
+ const holding = bits.holdingSomething();
2670
+ narrow(f.isEmpty ? bits.complement(holding) : holding);
2242
2671
  }
2243
- return result;
2244
2672
  }
2245
- function applyNotBitset(excluded, capacity) {
2246
- return bitsetNot(excluded, capacity);
2673
+ function textBoundsOf(operator, bound) {
2674
+ if (operator === "gt" || operator === "gte") {
2675
+ return { lower: bound, upper: null, lowerInclusive: operator === "gte", upperInclusive: false };
2676
+ }
2677
+ return { lower: null, upper: bound, upperInclusive: operator === "lte", lowerInclusive: false };
2678
+ }
2679
+ function narrowByTextRange(fieldPath, f, context, narrow) {
2680
+ for (const operator of TEXT_RANGE_OPERATORS) {
2681
+ const bound = f[operator];
2682
+ if (typeof bound === "string") narrow(context.textRangeBitset(fieldPath, textBoundsOf(operator, bound)));
2683
+ }
2684
+ if (!Array.isArray(f.between)) return;
2685
+ const [low, high] = f.between;
2686
+ if (typeof low !== "string" || typeof high !== "string") return;
2687
+ narrow(context.textRangeBitset(fieldPath, { lower: low, lowerInclusive: true, upper: high, upperInclusive: true }));
2688
+ }
2689
+ function narrowByFieldTests(fieldPath, f, parsed, context, narrow) {
2690
+ const bits = new FieldBits(context, fieldPath);
2691
+ narrowByPresence(f, bits, narrow);
2692
+ narrowByLocation(fieldPath, f, context, narrow);
2693
+ narrowByTextRange(fieldPath, f, context, narrow);
2694
+ if (parsed.pattern) {
2695
+ const patternBits = [];
2696
+ evaluatePatternTests(fieldPath, f, context, patternBits);
2697
+ for (const matched of patternBits) narrow(matched);
2698
+ } else {
2699
+ narrowByValue(fieldPath, f, parsed, context, bits, narrow);
2700
+ }
2701
+ narrowByElements(fieldPath, f, parsed, context, bits, narrow);
2247
2702
  }
2248
2703
 
2249
2704
  // src/filters/keys.ts
@@ -2260,6 +2715,8 @@ var FIELD_FILTER_OPERATORS = [
2260
2715
  "nin",
2261
2716
  "startsWith",
2262
2717
  "endsWith",
2718
+ "contains",
2719
+ "caseFold",
2263
2720
  "containsAll",
2264
2721
  "matchesAny",
2265
2722
  "size",
@@ -2271,229 +2728,7 @@ var FIELD_FILTER_OPERATORS = [
2271
2728
  "polygon"
2272
2729
  ];
2273
2730
 
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);
2317
- }
2318
-
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);
2361
- }
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));
2366
- }
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);
2372
- }
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);
2378
- }
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);
2384
- }
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);
2390
- }
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);
2396
- }
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);
2402
- }
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));
2411
- }
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);
2454
- }
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
- });
2462
- }
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
- });
2470
- }
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);
2481
- }
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);
2486
- }
2487
-
2488
2731
  // src/filters/evaluator.ts
2489
- var UNBOUNDED = { lower: null, upper: null };
2490
- function textBoundsOf(operator, bound) {
2491
- if (operator === "gt" || operator === "gte") {
2492
- return { ...UNBOUNDED, lower: bound, lowerInclusive: operator === "gte", upperInclusive: false };
2493
- }
2494
- return { ...UNBOUNDED, upper: bound, upperInclusive: operator === "lte", lowerInclusive: false };
2495
- }
2496
- var TEXT_RANGE_OPERATORS = ["gt", "gte", "lt", "lte"];
2497
2732
  var EXPRESSION_KEYS = new Set(FILTER_EXPRESSION_KEYS);
2498
2733
  var OPERATOR_KEYS = new Set(FIELD_FILTER_OPERATORS);
2499
2734
  function requireKnownKeys(value, known, describe) {
@@ -2503,160 +2738,66 @@ function requireKnownKeys(value, known, describe) {
2503
2738
  }
2504
2739
  }
2505
2740
  }
2741
+ var Narrowing = class {
2742
+ bits = null;
2743
+ narrow = (matched) => {
2744
+ if (this.bits === null) this.bits = matched.slice();
2745
+ else bitsetAndInto(this.bits, matched);
2746
+ };
2747
+ value(universe) {
2748
+ return this.bits ?? universe().slice();
2749
+ }
2750
+ };
2751
+ function narrowByField(fieldPath, filter, context, narrow) {
2752
+ requireKnownKeys(
2753
+ filter,
2754
+ OPERATOR_KEYS,
2755
+ (key) => `Field "${fieldPath}" is tested with "${key}", which is no filter operator`
2756
+ );
2757
+ const type = context.fieldTypes[fieldPath];
2758
+ const parsed = parsedTypeOf(type);
2759
+ if (type === void 0 || parsed === void 0) throw undeclaredFilterField(fieldPath);
2760
+ const f = filter;
2761
+ requireValidOperands(fieldPath, f, type);
2762
+ narrowByFieldTests(fieldPath, f, parsed, context, narrow.narrow);
2763
+ }
2506
2764
  function evaluateFilters(expression, context) {
2507
2765
  requireKnownKeys(
2508
2766
  expression,
2509
2767
  EXPRESSION_KEYS,
2510
2768
  (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
2769
  );
2512
- const bitsets = [];
2770
+ const narrowing = new Narrowing();
2513
2771
  if (expression.fields) {
2514
2772
  for (const [fieldPath, filter] of Object.entries(expression.fields)) {
2515
- bitsets.push(evaluateFieldFilter(fieldPath, filter, context));
2773
+ narrowByField(fieldPath, filter, context, narrowing);
2516
2774
  }
2517
2775
  }
2518
2776
  if (expression.and?.length) {
2519
- const andBitsets = expression.and.map((expr) => evaluateFilters(expr, context));
2520
- bitsets.push(applyAndBitset(andBitsets));
2777
+ for (const nested of expression.and) narrowing.narrow(evaluateFilters(nested, context));
2521
2778
  }
2522
2779
  if (expression.or?.length) {
2523
- const orBitsets = expression.or.map((expr) => evaluateFilters(expr, context));
2524
- bitsets.push(applyOrBitset(orBitsets));
2780
+ const union = createBitSet(context.capacity);
2781
+ for (const nested of expression.or) bitsetOrInto(union, evaluateFilters(nested, context));
2782
+ narrowing.narrow(union);
2525
2783
  }
2526
2784
  if (expression.not) {
2527
2785
  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
- );
2786
+ const kept = context.allDocIdsBitset.slice();
2787
+ bitsetAndNotInto(kept, excluded);
2788
+ narrowing.narrow(kept);
2617
2789
  }
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
- );
2652
- }
2653
- return fieldIndex.index;
2790
+ return narrowing.value(() => context.allDocIdsBitset);
2654
2791
  }
2655
2792
 
2656
2793
  // src/core/partition/constants.ts
2657
2794
  var TARGET_BLOCK_ENTRIES = 128;
2658
2795
  var PRUNING_REJECTION_SLACK = 1e-12;
2659
2796
  var SCORE_BUFFER_INITIAL_TOUCHED_CAPACITY = 1024;
2797
+ var SEGMENT_TOKEN_TABLE_START = 1 << 12;
2798
+ var SEGMENT_POSTING_LOG_START = 1 << 14;
2799
+ var SEGMENT_POSITION_POOL_START = 1 << 16;
2800
+ var SEGMENT_FIELD_SCRATCH_START = 1 << 10;
2660
2801
  var SORT_COLUMN_MINIMUM_REBUILD_THRESHOLD = 1024;
2661
2802
  var SORT_COLUMN_REBUILD_FRACTION_SHIFT = 2;
2662
2803
 
@@ -3273,99 +3414,72 @@ function sortColumnBytes(state) {
3273
3414
  }
3274
3415
 
3275
3416
  // 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) {
3417
+ function presentBitsetOf(state, fieldPath, type, capacity) {
3418
+ const base = parsedTypeOf(type)?.base;
3419
+ let present;
3420
+ if (base === "number") present = state.numericIndexes.get(fieldPath)?.getAllDocIdsBitset(capacity);
3421
+ else if (base === "boolean") present = state.booleanIndexes.get(fieldPath)?.getAllDocIdsBitset(capacity);
3422
+ else if (base === "enum") present = state.enumIndexes.get(fieldPath)?.getAllDocIdsBitset(capacity);
3423
+ else if (base === "geopoint") present = state.geoIndexes.get(fieldPath)?.documentsBitset(capacity);
3424
+ else if (base !== void 0 && keepsValueSize(base)) {
3425
+ present = state.sizeIndexes.get(fieldPath)?.getAllDocIdsBitset(capacity);
3426
+ }
3427
+ return present ?? createBitSet(capacity);
3428
+ }
3429
+ function liveDocumentsBitset(state, capacity) {
3430
+ const bits = createBitSet(capacity);
3431
+ for (const internalId of state.docStore.allInternalIds()) bitsetSet(bits, internalId);
3432
+ return bits;
3433
+ }
3434
+ function buildFilterContext(state, schema, meter) {
3281
3435
  const flat = getFlatSchema(state, schema);
3282
- const fieldIndexes = {};
3283
3436
  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
- }
3437
+ const patternFields = /* @__PURE__ */ new Set();
3438
+ for (const { path: fieldPath, pattern } of schemaFieldsOf(schema)) {
3439
+ if (pattern) patternFields.add(fieldPath);
3344
3440
  }
3345
- let cachedAllDocIds = null;
3441
+ const getFieldValue = (internalId, fieldPath) => getFieldValueByInternalId(state.docStore, internalId, fieldPath);
3346
3442
  let cachedAllDocIdsBitset = null;
3443
+ const cachedPresentBitsets = /* @__PURE__ */ new Map();
3347
3444
  return {
3348
3445
  fieldTypes: flat,
3349
- fieldIndexes,
3350
- getFieldValue: (internalId, fieldPath) => getFieldValueByInternalId(state.docStore, internalId, fieldPath),
3446
+ numericIndex: (fieldPath) => state.numericIndexes.get(fieldPath),
3447
+ booleanIndex: (fieldPath) => state.booleanIndexes.get(fieldPath),
3448
+ enumIndex: (fieldPath) => state.enumIndexes.get(fieldPath),
3449
+ geoIndex: (fieldPath) => state.geoIndexes.get(fieldPath),
3450
+ sizeIndex: (fieldPath) => state.sizeIndexes.get(fieldPath),
3351
3451
  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;
3452
+ isPatternField: (fieldPath) => patternFields.has(fieldPath),
3453
+ matchPattern: (fieldPath, operator, text, caseFold, into) => {
3454
+ const index = state.patternIndexes.get(fieldPath);
3455
+ if (index === void 0) return;
3456
+ requirePatternSearch(fieldPath).matchInto({
3457
+ operator,
3458
+ text,
3459
+ caseFold,
3460
+ index,
3461
+ matches: into,
3462
+ meter,
3463
+ host: PATTERN_HOST,
3464
+ valueOf: (internalId) => getFieldValue(internalId, fieldPath)
3465
+ });
3466
+ },
3467
+ presentBitset: (fieldPath) => {
3468
+ const cached = cachedPresentBitsets.get(fieldPath);
3469
+ if (cached !== void 0) return cached;
3470
+ const present = presentBitsetOf(state, fieldPath, flat[fieldPath], capacity);
3471
+ cachedPresentBitsets.set(fieldPath, present);
3472
+ return present;
3357
3473
  },
3358
3474
  capacity,
3359
3475
  get allDocIdsBitset() {
3360
- if (!cachedAllDocIdsBitset) {
3361
- cachedAllDocIdsBitset = bitsetFromSet(this.allDocIds, capacity);
3362
- }
3476
+ cachedAllDocIdsBitset ??= liveDocumentsBitset(state, capacity);
3363
3477
  return cachedAllDocIdsBitset;
3364
3478
  }
3365
3479
  };
3366
3480
  }
3367
- function applyPartitionFilters(state, filters, schema) {
3368
- const context = buildFilterContext(state, schema);
3481
+ function applyPartitionFilters(state, filters, schema, meter) {
3482
+ const context = buildFilterContext(state, schema, meter);
3369
3483
  const resultBitset = evaluateFilters(filters, context);
3370
3484
  const resolver = state.docStore.resolver();
3371
3485
  const externalResult = /* @__PURE__ */ new Set();
@@ -3385,12 +3499,12 @@ function applyPartitionFilters(state, filters, schema) {
3385
3499
  }
3386
3500
  return externalResult;
3387
3501
  }
3388
- function applyPartitionFiltersBitset(state, filters, schema) {
3389
- const context = buildFilterContext(state, schema);
3502
+ function applyPartitionFiltersBitset(state, filters, schema, meter) {
3503
+ const context = buildFilterContext(state, schema, meter);
3390
3504
  return evaluateFilters(filters, context);
3391
3505
  }
3392
- function partitionFilterMatches(state, filters, schema) {
3393
- const accepted = applyPartitionFiltersBitset(state, filters, schema);
3506
+ function partitionFilterMatches(state, filters, schema, meter) {
3507
+ const accepted = applyPartitionFiltersBitset(state, filters, schema, meter);
3394
3508
  const docStore = state.docStore;
3395
3509
  const resolver = docStore.resolver();
3396
3510
  let count = 0;
@@ -3547,6 +3661,8 @@ function estimatePartitionBytes(state) {
3547
3661
  bytes += docCount * state.booleanIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
3548
3662
  bytes += docCount * state.enumIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
3549
3663
  bytes += docCount * state.geoIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
3664
+ bytes += docCount * state.sizeIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
3665
+ for (const index of state.patternIndexes.values()) bytes += index.bytes();
3550
3666
  return bytes + sortColumnBytes(state);
3551
3667
  }
3552
3668
 
@@ -3606,12 +3722,12 @@ function mergeSurfaceForms(target, source) {
3606
3722
  target.surfaceRegistry.add(surface, value[1], value[0]);
3607
3723
  }
3608
3724
  }
3609
- function mergeFieldIndexes(target, source, idMapping) {
3610
- for (const [fieldPath, index] of source.numericIndexes) {
3611
- let targetIndex = target.numericIndexes.get(fieldPath);
3725
+ function mergeNumericIndexes(target, source, idMapping) {
3726
+ for (const [fieldPath, index] of source) {
3727
+ let targetIndex = target.get(fieldPath);
3612
3728
  if (targetIndex === void 0) {
3613
3729
  targetIndex = createNumericIndex();
3614
- target.numericIndexes.set(fieldPath, targetIndex);
3730
+ target.set(fieldPath, targetIndex);
3615
3731
  }
3616
3732
  for (const entry of index.serialize()) {
3617
3733
  const targetInternalId = idMapping.get(entry.docId);
@@ -3619,6 +3735,10 @@ function mergeFieldIndexes(target, source, idMapping) {
3619
3735
  targetIndex.insert(targetInternalId, entry.value);
3620
3736
  }
3621
3737
  }
3738
+ }
3739
+ function mergeFieldIndexes(target, source, idMapping) {
3740
+ mergeNumericIndexes(target.numericIndexes, source.numericIndexes, idMapping);
3741
+ mergeNumericIndexes(target.sizeIndexes, source.sizeIndexes, idMapping);
3622
3742
  for (const [fieldPath, index] of source.booleanIndexes) {
3623
3743
  let targetIndex = target.booleanIndexes.get(fieldPath);
3624
3744
  if (targetIndex === void 0) {
@@ -3660,6 +3780,12 @@ function mergeFieldIndexes(target, source, idMapping) {
3660
3780
  targetIndex.insert(targetInternalId, entry.lat, entry.lon);
3661
3781
  }
3662
3782
  }
3783
+ for (const [fieldPath, index] of source.patternIndexes) {
3784
+ patternIndexOf(target.patternIndexes, fieldPath).addArrays(
3785
+ index.toArrays(fieldPath, null),
3786
+ (sourceInternalId) => idMapping.get(sourceInternalId) ?? -1
3787
+ );
3788
+ }
3663
3789
  }
3664
3790
  function mergeSegmentState(target, source) {
3665
3791
  if (source.stats.totalDocuments === 0 && source.docStore.count() === 0) return;
@@ -4680,19 +4806,23 @@ function searchFulltext(state, params) {
4680
4806
  }
4681
4807
 
4682
4808
  // 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));
4809
+ function encodeNumericIndexes(indexes, remap) {
4810
+ const encoded = [];
4811
+ for (const [fieldPath, index] of indexes) {
4812
+ const entries = index.serialize().filter((entry) => remap[entry.docId] >= 0);
4688
4813
  const docIds = new Uint32Array(entries.length);
4689
4814
  const values = new Float64Array(entries.length);
4690
4815
  for (let i = 0; i < entries.length; i++) {
4691
4816
  docIds[i] = remap[entries[i].docId];
4692
4817
  values[i] = entries[i].value;
4693
4818
  }
4694
- numeric.push({ fieldPath, docIds, values });
4819
+ encoded.push({ fieldPath, docIds, values });
4695
4820
  }
4821
+ return encoded;
4822
+ }
4823
+ function encodeFieldIndexes(state, remap) {
4824
+ const survives = (internalId) => remap[internalId] >= 0;
4825
+ const numeric = encodeNumericIndexes(state.numericIndexes, remap);
4696
4826
  const booleans = [];
4697
4827
  for (const [fieldPath, index] of state.booleanIndexes) {
4698
4828
  const { trueDocs, falseDocs } = index.serialize();
@@ -4733,7 +4863,9 @@ function encodeFieldIndexes(state, remap) {
4733
4863
  }
4734
4864
  geo.push({ fieldPath, docIds, latitudes, longitudes });
4735
4865
  }
4736
- return { numeric, boolean: booleans, enums, geo };
4866
+ const pattern = [];
4867
+ for (const [fieldPath, index] of state.patternIndexes) pattern.push(index.toArrays(fieldPath, remap));
4868
+ return { numeric, boolean: booleans, enums, geo, pattern, sizes: encodeNumericIndexes(state.sizeIndexes, remap) };
4737
4869
  }
4738
4870
 
4739
4871
  // src/core/partition/segment-payload.ts
@@ -4890,15 +5022,19 @@ function mergePayloadPostings(target, payload, ordinalBase) {
4890
5022
  }
4891
5023
  }
4892
5024
  }
4893
- function mergePayloadFieldIndexes(target, payload, ordinalBase) {
4894
- for (const entry of payload.numeric) {
4895
- let index = target.numericIndexes.get(entry.fieldPath);
5025
+ function mergePayloadNumericIndexes(target, entries, ordinalBase) {
5026
+ for (const entry of entries) {
5027
+ let index = target.get(entry.fieldPath);
4896
5028
  if (index === void 0) {
4897
5029
  index = createNumericIndex();
4898
- target.numericIndexes.set(entry.fieldPath, index);
5030
+ target.set(entry.fieldPath, index);
4899
5031
  }
4900
5032
  for (let i = 0; i < entry.docIds.length; i++) index.insert(ordinalBase + entry.docIds[i], entry.values[i]);
4901
5033
  }
5034
+ }
5035
+ function mergePayloadFieldIndexes(target, payload, ordinalBase) {
5036
+ mergePayloadNumericIndexes(target.numericIndexes, payload.numeric, ordinalBase);
5037
+ mergePayloadNumericIndexes(target.sizeIndexes, payload.sizes, ordinalBase);
4902
5038
  for (const entry of payload.boolean) {
4903
5039
  let index = target.booleanIndexes.get(entry.fieldPath);
4904
5040
  if (index === void 0) {
@@ -4929,6 +5065,9 @@ function mergePayloadFieldIndexes(target, payload, ordinalBase) {
4929
5065
  index.insert(ordinalBase + entry.docIds[i], entry.latitudes[i], entry.longitudes[i]);
4930
5066
  }
4931
5067
  }
5068
+ for (const entry of payload.pattern) {
5069
+ patternIndexOf(target.patternIndexes, entry.fieldPath).addArrays(entry, (ordinal) => ordinalBase + ordinal);
5070
+ }
4932
5071
  }
4933
5072
  function mergeSegmentPayload(target, payload, documents) {
4934
5073
  if (payload.documentCount === 0) return;
@@ -4973,6 +5112,123 @@ function mergeSegmentPayload(target, payload, documents) {
4973
5112
  target.scoreBuffer = null;
4974
5113
  }
4975
5114
 
5115
+ // src/core/partition/pattern-snapshot.ts
5116
+ function loadFailed(fieldPath, problem) {
5117
+ return new NarsilError(
5118
+ ErrorCodes.PERSISTENCE_LOAD_FAILED,
5119
+ `The pattern index of field "${fieldPath}" in this partition is corrupt: ${problem}`,
5120
+ { field: fieldPath }
5121
+ );
5122
+ }
5123
+ function serializePatternIndex(state, fieldPath, index) {
5124
+ const resolver = state.docStore.resolver();
5125
+ const arrays = index.toArrays(fieldPath, null);
5126
+ const positionOf = /* @__PURE__ */ new Map();
5127
+ const docIds = [];
5128
+ for (const ordinal of arrays.docIds) {
5129
+ const externalId = resolver.toExternal(ordinal);
5130
+ if (externalId === void 0) continue;
5131
+ positionOf.set(ordinal, docIds.length);
5132
+ docIds.push(externalId);
5133
+ }
5134
+ const runs = /* @__PURE__ */ Object.create(null);
5135
+ for (let run = 0; run + 1 < arrays.offsets.length; run++) {
5136
+ const positions = [];
5137
+ for (let at = arrays.offsets[run]; at < arrays.offsets[run + 1]; at++) {
5138
+ const position = positionOf.get(arrays.postings[at]);
5139
+ if (position !== void 0) positions.push(position);
5140
+ }
5141
+ if (positions.length === 0) continue;
5142
+ const base = run * PATTERN_RUN_CODE_POINTS;
5143
+ runs[String.fromCodePoint(arrays.runs[base], arrays.runs[base + 1], arrays.runs[base + 2])] = positions;
5144
+ }
5145
+ return { docIds, runs };
5146
+ }
5147
+ function serializePatternIndexes(state, schema) {
5148
+ const patternFields = schemaFieldsOf(schema).filter((field) => field.pattern);
5149
+ if (patternFields.length === 0) return void 0;
5150
+ const serialized = /* @__PURE__ */ Object.create(null);
5151
+ for (const { path: fieldPath } of patternFields) {
5152
+ const index = state.patternIndexes.get(fieldPath);
5153
+ serialized[fieldPath] = index === void 0 ? { docIds: [], runs: {} } : serializePatternIndex(state, fieldPath, index);
5154
+ }
5155
+ return serialized;
5156
+ }
5157
+ function appendRunCodePoints(fieldPath, key, out) {
5158
+ const start = out.length;
5159
+ for (const character of key) out.push(character.codePointAt(0) ?? 0);
5160
+ const count = out.length - start;
5161
+ if (count !== PATTERN_RUN_CODE_POINTS) {
5162
+ throw loadFailed(
5163
+ fieldPath,
5164
+ `the key "${key}" holds ${count} code points, while each key must hold ${PATTERN_RUN_CODE_POINTS}`
5165
+ );
5166
+ }
5167
+ }
5168
+ function requirePositions(fieldPath, key, list, documentCount) {
5169
+ if (!Array.isArray(list)) throw loadFailed(fieldPath, `the run "${key}" must map to a list of positions`);
5170
+ let previous = -1;
5171
+ for (const position of list) {
5172
+ if (!Number.isInteger(position) || position < 0 || position >= documentCount) {
5173
+ throw loadFailed(fieldPath, `the run "${key}" names position ${String(position)} outside its document list`);
5174
+ }
5175
+ if (position <= previous) {
5176
+ throw loadFailed(fieldPath, `the positions of the run "${key}" are out of ascending order`);
5177
+ }
5178
+ previous = position;
5179
+ }
5180
+ return list;
5181
+ }
5182
+ function requireDistinctDocuments(fieldPath, docIds) {
5183
+ const seen = /* @__PURE__ */ new Set();
5184
+ for (const docId of docIds) {
5185
+ if (seen.has(docId)) throw loadFailed(fieldPath, `the document list names "${docId}" twice`);
5186
+ seen.add(docId);
5187
+ }
5188
+ }
5189
+ function decodePatternEntry(fieldPath, entry) {
5190
+ requireDistinctDocuments(fieldPath, entry.docIds);
5191
+ const runs = [];
5192
+ const offsets = [0];
5193
+ const postings = [];
5194
+ for (const [key, list] of Object.entries(entry.runs)) {
5195
+ appendRunCodePoints(fieldPath, key, runs);
5196
+ for (const position of requirePositions(fieldPath, key, list, entry.docIds.length)) postings.push(position);
5197
+ offsets.push(postings.length);
5198
+ }
5199
+ const positions = new Uint32Array(entry.docIds.length);
5200
+ for (let position = 0; position < positions.length; position++) positions[position] = position;
5201
+ return {
5202
+ fieldPath,
5203
+ docIds: positions,
5204
+ runs: Uint32Array.from(runs),
5205
+ offsets: Uint32Array.from(offsets),
5206
+ postings: Uint32Array.from(postings)
5207
+ };
5208
+ }
5209
+ function rebuildPatternIndex(state, fieldPath) {
5210
+ for (const [docId, stored] of state.docStore.all()) {
5211
+ const value = getNestedValue(stored.fields, fieldPath);
5212
+ if (value === void 0 || value === null) continue;
5213
+ const internalId = state.docStore.getInternalId(docId);
5214
+ if (internalId !== void 0) patternIndexOf(state.patternIndexes, fieldPath).add(internalId, value);
5215
+ }
5216
+ }
5217
+ function loadPatternIndexes(state, serialized, schema) {
5218
+ const resolver = state.docStore.resolver();
5219
+ for (const { path: fieldPath, pattern } of schemaFieldsOf(schema)) {
5220
+ if (!pattern) continue;
5221
+ const entry = serialized !== void 0 && Object.hasOwn(serialized, fieldPath) ? serialized[fieldPath] : void 0;
5222
+ if (entry === void 0) {
5223
+ rebuildPatternIndex(state, fieldPath);
5224
+ continue;
5225
+ }
5226
+ const decoded = decodePatternEntry(fieldPath, entry);
5227
+ const ordinals = entry.docIds.map((docId) => resolver.toInternal(docId) ?? -1);
5228
+ patternIndexOf(state.patternIndexes, fieldPath).addArrays(decoded, (position) => ordinals[position]);
5229
+ }
5230
+ }
5231
+
4976
5232
  // src/core/partition/serialization.ts
4977
5233
  function serializePartition(state, partitionId, indexName, totalPartitions, language, schema) {
4978
5234
  const flatSchema = getFlatSchema(state, schema);
@@ -5045,6 +5301,7 @@ function serializePartition(state, partitionId, indexName, totalPartitions, lang
5045
5301
  }));
5046
5302
  }
5047
5303
  const serializedStats = state.stats.serialize();
5304
+ const serializedPattern = serializePatternIndexes(state, schema);
5048
5305
  return {
5049
5306
  indexName,
5050
5307
  partitionId,
@@ -5059,7 +5316,8 @@ function serializePartition(state, partitionId, indexName, totalPartitions, lang
5059
5316
  numeric: serializedNumeric,
5060
5317
  boolean: serializedBoolean,
5061
5318
  enum: serializedEnum,
5062
- geopoint: serializedGeo
5319
+ geopoint: serializedGeo,
5320
+ ...serializedPattern === void 0 ? {} : { pattern: serializedPattern }
5063
5321
  },
5064
5322
  surfaceForms: state.surfaceRegistry.serialize(),
5065
5323
  statistics: serializedStats
@@ -5137,6 +5395,8 @@ function deserializePartition(state, data, clearFn, schema) {
5137
5395
  throw new Error(`Schema mismatch: serialized data contains unknown field "${field}" not present in schema`);
5138
5396
  }
5139
5397
  }
5398
+ loadPatternIndexes(state, data.fieldIndexes.pattern, schema);
5399
+ rebuildSizeIndexes(state, schema);
5140
5400
  }
5141
5401
 
5142
5402
  // src/core/partition/suggestions.ts
@@ -5299,7 +5559,8 @@ function serializePartitionToWirePayloadV2(state, partitionId, indexName, totalP
5299
5559
  numeric: wireNumeric,
5300
5560
  boolean: wireBoolean,
5301
5561
  enum: wireEnum,
5302
- geopoint: wireGeo
5562
+ geopoint: wireGeo,
5563
+ ...patternIndexesToWire(serializePatternIndexes(state, schema))
5303
5564
  },
5304
5565
  surface_forms: state.surfaceRegistry.serialize(),
5305
5566
  statistics: {
@@ -5334,6 +5595,8 @@ function createPartitionIndex(partitionId, trackPositions = true) {
5334
5595
  booleanIndexes: /* @__PURE__ */ new Map(),
5335
5596
  enumIndexes: /* @__PURE__ */ new Map(),
5336
5597
  geoIndexes: /* @__PURE__ */ new Map(),
5598
+ patternIndexes: /* @__PURE__ */ new Map(),
5599
+ sizeIndexes: /* @__PURE__ */ new Map(),
5337
5600
  fieldNameTable,
5338
5601
  flatSchemaCache: null,
5339
5602
  lastSchemaRef: null,
@@ -5353,6 +5616,8 @@ function createPartitionIndex(partitionId, trackPositions = true) {
5353
5616
  state.booleanIndexes.clear();
5354
5617
  state.enumIndexes.clear();
5355
5618
  state.geoIndexes.clear();
5619
+ state.patternIndexes.clear();
5620
+ state.sizeIndexes.clear();
5356
5621
  state.stats.deserialize({ totalDocuments: 0, totalFieldLengths: {}, averageFieldLengths: {}, docFrequencies: {} });
5357
5622
  state.flatSchemaCache = null;
5358
5623
  state.lastSchemaRef = null;
@@ -5528,14 +5793,14 @@ function createPartitionIndex(partitionId, trackPositions = true) {
5528
5793
  sortValues(docId, fields, fieldTypes, modes) {
5529
5794
  return sortValuesOf(state, docId, fields, fieldTypes, modes);
5530
5795
  },
5531
- applyFilters(filters, schema) {
5532
- return applyPartitionFilters(state, filters, schema);
5796
+ applyFilters(filters, schema, meter) {
5797
+ return applyPartitionFilters(state, filters, schema, meter);
5533
5798
  },
5534
- applyFiltersBitset(filters, schema) {
5535
- return applyPartitionFiltersBitset(state, filters, schema);
5799
+ applyFiltersBitset(filters, schema, meter) {
5800
+ return applyPartitionFiltersBitset(state, filters, schema, meter);
5536
5801
  },
5537
- filterMatches(filters, schema) {
5538
- return partitionFilterMatches(state, filters, schema);
5802
+ filterMatches(filters, schema, meter) {
5803
+ return partitionFilterMatches(state, filters, schema, meter);
5539
5804
  },
5540
5805
  computeFacets(matched, config, schema) {
5541
5806
  return computeFacets(state, matched, config, schema);
@@ -5575,12 +5840,13 @@ function frozenSegmentBytes(segment) {
5575
5840
  bytes += arrays.documentTable.blob.byteLength + arrays.documentTable.offsets.byteLength;
5576
5841
  }
5577
5842
  for (const column of arrays.fieldLengthColumns) bytes += column.byteLength;
5578
- for (const entry of arrays.numeric) bytes += entry.docIds.byteLength + entry.values.byteLength;
5843
+ for (const entry of [...arrays.numeric, ...arrays.sizes]) bytes += entry.docIds.byteLength + entry.values.byteLength;
5579
5844
  for (const entry of arrays.boolean) bytes += entry.trueDocs.byteLength + entry.falseDocs.byteLength;
5580
5845
  for (const entry of arrays.enums) bytes += entry.offsets.byteLength + entry.docIds.byteLength;
5581
5846
  for (const entry of arrays.geo) {
5582
5847
  bytes += entry.docIds.byteLength + entry.latitudes.byteLength + entry.longitudes.byteLength;
5583
5848
  }
5849
+ for (const reader of segment.patternIndexes.values()) bytes += reader.bytes();
5584
5850
  return bytes;
5585
5851
  }
5586
5852
 
@@ -5658,24 +5924,24 @@ function placeSubBitset(composite, layout, subIndex, subBits) {
5658
5924
  if (words === 0) return;
5659
5925
  composite.set(subBits.length > words ? subBits.subarray(0, words) : subBits, layout.bases[subIndex] >> 5);
5660
5926
  }
5661
- function compositeFiltersBitset(subs, layout, filters, schema) {
5927
+ function compositeFiltersBitset(subs, layout, filters, schema, meter) {
5662
5928
  const composite = new Uint32Array(Math.ceil(layout.totalCapacity / WORD_BITS));
5663
5929
  for (let i = 0; i < subs.length; i++) {
5664
- placeSubBitset(composite, layout, i, applyPartitionFiltersBitset(subs[i], filters, schema));
5930
+ placeSubBitset(composite, layout, i, applyPartitionFiltersBitset(subs[i], filters, schema, meter));
5665
5931
  }
5666
5932
  return composite;
5667
5933
  }
5668
- function compositeFilters(subs, filters, schema) {
5934
+ function compositeFilters(subs, filters, schema, meter) {
5669
5935
  const combined = /* @__PURE__ */ new Set();
5670
5936
  for (const sub of subs) {
5671
- for (const docId of applyPartitionFilters(sub, filters, schema)) {
5937
+ for (const docId of applyPartitionFilters(sub, filters, schema, meter)) {
5672
5938
  combined.add(docId);
5673
5939
  }
5674
5940
  }
5675
5941
  return combined;
5676
5942
  }
5677
- function compositeFilterMatches(subs, layout, filters, schema) {
5678
- const bits = compositeFiltersBitset(subs, layout, filters, schema);
5943
+ function compositeFilterMatches(subs, layout, filters, schema, meter) {
5944
+ const bits = compositeFiltersBitset(subs, layout, filters, schema, meter);
5679
5945
  let count = 0;
5680
5946
  for (let i = 0; i < subs.length; i++) {
5681
5947
  const resolver = subs[i].docStore.resolver();
@@ -6136,14 +6402,14 @@ function createCompositePartition(partitionId, trackPositions = true, existingLi
6136
6402
  searchFulltextMatches(params) {
6137
6403
  return compositeSearchMatches(subs(), layout(), params);
6138
6404
  },
6139
- applyFilters(filters, schema) {
6140
- return compositeFilters(subs(), filters, schema);
6405
+ applyFilters(filters, schema, meter) {
6406
+ return compositeFilters(subs(), filters, schema, meter);
6141
6407
  },
6142
- applyFiltersBitset(filters, schema) {
6143
- return compositeFiltersBitset(subs(), layout(), filters, schema);
6408
+ applyFiltersBitset(filters, schema, meter) {
6409
+ return compositeFiltersBitset(subs(), layout(), filters, schema, meter);
6144
6410
  },
6145
- filterMatches(filters, schema) {
6146
- return compositeFilterMatches(subs(), layout(), filters, schema);
6411
+ filterMatches(filters, schema, meter) {
6412
+ return compositeFilterMatches(subs(), layout(), filters, schema, meter);
6147
6413
  },
6148
6414
  computeFacets(matched, config, schema) {
6149
6415
  const everyValue = everyValueFacetConfig(config);
@@ -6294,6 +6560,7 @@ function createCapacityChecks(source) {
6294
6560
 
6295
6561
  // src/partitioning/manager/index.ts
6296
6562
  function createPartitionManager(indexName, config, language, router, initialPartitionCount, vectorIndexes) {
6563
+ requirePatternSearchFor(config.schema);
6297
6564
  const count = initialPartitionCount ?? 1;
6298
6565
  const analysis = resolveIndexAnalysis(config);
6299
6566
  const trackPositions = config.trackPositions ?? true;
@@ -9346,7 +9613,22 @@ function createFrozenEnumReader(entry) {
9346
9613
  function rangeOf(value) {
9347
9614
  return rangeByValue.get(value) ?? { from: 0, to: 0 };
9348
9615
  }
9616
+ let affixes = null;
9617
+ function affixesOf() {
9618
+ affixes ??= new ValueAffixes(() => true, values);
9619
+ return affixes;
9620
+ }
9621
+ function bitsetOfValues(matching, capacity) {
9622
+ const bits = createBitSet(capacity);
9623
+ for (const value of matching) {
9624
+ const { from, to } = rangeOf(value);
9625
+ for (let i = from; i < to; i++) bitsetSet(bits, docIds[i]);
9626
+ }
9627
+ return bits;
9628
+ }
9349
9629
  return {
9630
+ queryPrefixBitset: (prefix, capacity) => bitsetOfValues(affixesOf().withPrefix(prefix), capacity),
9631
+ querySuffixBitset: (suffix, capacity) => bitsetOfValues(affixesOf().withSuffix(suffix), capacity),
9350
9632
  serialize: () => {
9351
9633
  const byValue = /* @__PURE__ */ Object.create(null);
9352
9634
  for (let i = 0; i < values.length; i++) {
@@ -9791,6 +10073,11 @@ function freezeEncodedSegmentShared(payload, documentData, segmentId) {
9791
10073
  docIds: sharedUint32(entry.docIds),
9792
10074
  values: sharedFloat64(entry.values)
9793
10075
  })),
10076
+ sizes: payload.sizes.map((entry) => ({
10077
+ fieldPath: entry.fieldPath,
10078
+ docIds: sharedUint32(entry.docIds),
10079
+ values: sharedFloat64(entry.values)
10080
+ })),
9794
10081
  boolean: payload.boolean.map((entry) => ({
9795
10082
  fieldPath: entry.fieldPath,
9796
10083
  trueDocs: sharedUint32(entry.trueDocs),
@@ -9808,6 +10095,13 @@ function freezeEncodedSegmentShared(payload, documentData, segmentId) {
9808
10095
  latitudes: sharedFloat64(entry.latitudes),
9809
10096
  longitudes: sharedFloat64(entry.longitudes)
9810
10097
  })),
10098
+ pattern: payload.pattern.map((entry) => ({
10099
+ fieldPath: entry.fieldPath,
10100
+ docIds: sharedUint32(entry.docIds),
10101
+ runs: sharedUint32(entry.runs),
10102
+ offsets: sharedUint32(entry.offsets),
10103
+ postings: sharedUint32(entry.postings)
10104
+ })),
9811
10105
  surfaceTable: sharedSurfaceTable(payload)
9812
10106
  };
9813
10107
  }
@@ -9854,10 +10148,16 @@ function assembleFrozenSegment(segmentId, source, documentSource, sharedSnapshot
9854
10148
  const docStore = createFrozenDocTable(source.idTable, lengthColumns, documentSource, tombstones);
9855
10149
  const numericIndexes = /* @__PURE__ */ new Map();
9856
10150
  for (const entry of source.numeric) numericIndexes.set(entry.fieldPath, createFrozenNumericReader(entry));
10151
+ const sizeIndexes = /* @__PURE__ */ new Map();
10152
+ for (const entry of source.sizes) sizeIndexes.set(entry.fieldPath, createFrozenNumericReader(entry));
9857
10153
  const booleanIndexes = /* @__PURE__ */ new Map();
9858
10154
  for (const entry of source.boolean) booleanIndexes.set(entry.fieldPath, createFrozenBooleanReader(entry));
9859
10155
  const enumIndexes = /* @__PURE__ */ new Map();
9860
10156
  for (const entry of source.enums) enumIndexes.set(entry.fieldPath, createFrozenEnumReader(entry));
10157
+ const patternIndexes = /* @__PURE__ */ new Map();
10158
+ for (const entry of source.pattern) {
10159
+ patternIndexes.set(entry.fieldPath, requirePatternSearch(entry.fieldPath).readArrays(entry));
10160
+ }
9861
10161
  const fieldNameTable = {
9862
10162
  names: [...source.fieldNames],
9863
10163
  indexMap: new Map(source.fieldNames.map((name, index) => [name, index]))
@@ -9875,6 +10175,8 @@ function assembleFrozenSegment(segmentId, source, documentSource, sharedSnapshot
9875
10175
  booleanIndexes,
9876
10176
  enumIndexes,
9877
10177
  geoIndexes: buildGeoReaders(source.geo),
10178
+ patternIndexes,
10179
+ sizeIndexes,
9878
10180
  fieldNameTable,
9879
10181
  trackPositions: source.positionOffsets !== null,
9880
10182
  flatSchemaCache: null,
@@ -10002,12 +10304,8 @@ function remapDocIds(inputs, pick) {
10002
10304
  }
10003
10305
  return out;
10004
10306
  }
10005
- function mergeNumeric(inputs) {
10006
- const byFieldPath = rowsByFieldPath(
10007
- inputs,
10008
- (input) => input.segment.arrays.numeric,
10009
- (entry, at) => [entry.values[at]]
10010
- );
10307
+ function mergeNumeric(inputs, pick) {
10308
+ const byFieldPath = rowsByFieldPath(inputs, pick, (entry, at) => [entry.values[at]]);
10011
10309
  const merged = [];
10012
10310
  for (const [fieldPath, rows] of byFieldPath) {
10013
10311
  rows.sort((a, b) => a.values[0] - b.values[0] || a.docId - b.docId);
@@ -10080,12 +10378,32 @@ function mergeEnums(inputs) {
10080
10378
  }
10081
10379
  return merged;
10082
10380
  }
10381
+ function mergePattern(inputs) {
10382
+ const byFieldPath = /* @__PURE__ */ new Map();
10383
+ for (const input of inputs) {
10384
+ for (const arrays of input.segment.arrays.pattern) {
10385
+ let entries = byFieldPath.get(arrays.fieldPath);
10386
+ if (entries === void 0) {
10387
+ entries = [];
10388
+ byFieldPath.set(arrays.fieldPath, entries);
10389
+ }
10390
+ entries.push({ arrays, remap: input.remap });
10391
+ }
10392
+ }
10393
+ const merged = [];
10394
+ for (const [fieldPath, entries] of byFieldPath) {
10395
+ merged.push(requirePatternSearch(fieldPath).mergeArrays(fieldPath, entries));
10396
+ }
10397
+ return merged;
10398
+ }
10083
10399
  function mergeFieldIndexes2(inputs) {
10084
10400
  return {
10085
- numeric: mergeNumeric(inputs),
10401
+ numeric: mergeNumeric(inputs, (input) => input.segment.arrays.numeric),
10086
10402
  boolean: mergeBoolean(inputs),
10087
10403
  enums: mergeEnums(inputs),
10088
- geo: mergeGeo(inputs)
10404
+ geo: mergeGeo(inputs),
10405
+ pattern: mergePattern(inputs),
10406
+ sizes: mergeNumeric(inputs, (input) => input.segment.arrays.sizes)
10089
10407
  };
10090
10408
  }
10091
10409
 
@@ -10397,84 +10715,7 @@ function mergeFrozenSegments(segments) {
10397
10715
  return freezeEncodedSegmentShared(payload, mergeDocuments2(inputs, documentCount));
10398
10716
  }
10399
10717
 
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
- }
10718
+ // src/core/partition/segment-postings.ts
10478
10719
  var TextPostings = class {
10479
10720
  constructor(trackPositions) {
10480
10721
  this.trackPositions = trackPositions;
@@ -10483,22 +10724,22 @@ var TextPostings = class {
10483
10724
  fieldNames = [];
10484
10725
  tokenIds = /* @__PURE__ */ new Map();
10485
10726
  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);
10727
+ postingsPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
10728
+ docFrequencyPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
10729
+ lastDocPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
10730
+ fieldStampPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
10731
+ fieldSlotPerToken = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
10732
+ logToken = new GrowableUint32(SEGMENT_POSTING_LOG_START);
10733
+ logDoc = new GrowableUint32(SEGMENT_POSTING_LOG_START);
10734
+ logFrequency = new GrowableUint32(SEGMENT_POSTING_LOG_START);
10735
+ logField = new GrowableUint32(SEGMENT_POSTING_LOG_START);
10736
+ logPositionStart = new GrowableUint32(SEGMENT_POSTING_LOG_START);
10737
+ positionPool = new GrowableUint32(SEGMENT_POSITION_POOL_START);
10738
+ slotToken = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
10739
+ slotCount = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
10740
+ slotCursor = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
10741
+ eventSlot = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
10742
+ eventPosition = new GrowableUint32(SEGMENT_FIELD_SCRATCH_START);
10502
10743
  fieldStamp = 0;
10503
10744
  beginField() {
10504
10745
  this.fieldStamp++;
@@ -10626,11 +10867,56 @@ var TextPostings = class {
10626
10867
  };
10627
10868
  }
10628
10869
  };
10870
+
10871
+ // src/core/partition/segment-builder.ts
10872
+ function indexOf(indexes, fieldPath, create) {
10873
+ let index = indexes.get(fieldPath);
10874
+ if (index === void 0) {
10875
+ index = create();
10876
+ indexes.set(fieldPath, index);
10877
+ }
10878
+ return index;
10879
+ }
10880
+ function indexFilterValue(indexes, ordinal, fieldPath, fieldType, value) {
10881
+ if (fieldType === "number" || fieldType === "number[]") {
10882
+ const index = indexOf(indexes.numericIndexes, fieldPath, createNumericIndex);
10883
+ for (const entry of fieldType === "number" ? [value] : value) index.insert(ordinal, entry);
10884
+ } else if (fieldType === "boolean" || fieldType === "boolean[]") {
10885
+ const index = indexOf(indexes.booleanIndexes, fieldPath, createBooleanIndex);
10886
+ for (const entry of fieldType === "boolean" ? [value] : value)
10887
+ index.insert(ordinal, entry);
10888
+ } else if (fieldType === "enum" || fieldType === "enum[]") {
10889
+ const index = indexOf(indexes.enumIndexes, fieldPath, createEnumIndex);
10890
+ for (const entry of fieldType === "enum" ? [value] : value) index.insert(ordinal, entry);
10891
+ } else if (fieldType === "geopoint") {
10892
+ const point = value;
10893
+ indexOf(indexes.geoIndexes, fieldPath, createGeoIndex).insert(ordinal, point.lat, point.lon);
10894
+ }
10895
+ }
10896
+ function refuseRepeatedDocIds(documents) {
10897
+ const seen = /* @__PURE__ */ new Set();
10898
+ for (const { docId } of documents) {
10899
+ if (seen.has(docId)) {
10900
+ throw new NarsilError(ErrorCodes.DOC_ALREADY_EXISTS, `Document "${docId}" already exists in partition 0`, {
10901
+ docId,
10902
+ partitionId: 0
10903
+ });
10904
+ }
10905
+ seen.add(docId);
10906
+ }
10907
+ }
10908
+ function validateAsAnInsertDoes(documents, schema, options) {
10909
+ if (options?.validate === false) return;
10910
+ for (const { document } of documents) {
10911
+ validateDocument(document, schema, options?.patternValueLimit);
10912
+ if (options?.strict) validateDocumentStrict(document, schema);
10913
+ }
10914
+ }
10629
10915
  var SurfaceCounts = class {
10630
10916
  ids = /* @__PURE__ */ new Map();
10631
10917
  surfaces = [];
10632
10918
  tokens = [];
10633
- counts = new GrowableUint32(TOKEN_TABLE_START);
10919
+ counts = new GrowableUint32(SEGMENT_TOKEN_TABLE_START);
10634
10920
  count(surface, token) {
10635
10921
  if (surface === void 0 || surface.length === 0) return;
10636
10922
  const known = this.ids.get(surface);
@@ -10690,7 +10976,9 @@ function buildSegmentPayload(documents, schema, language, options, trackPosition
10690
10976
  numericIndexes: /* @__PURE__ */ new Map(),
10691
10977
  booleanIndexes: /* @__PURE__ */ new Map(),
10692
10978
  enumIndexes: /* @__PURE__ */ new Map(),
10693
- geoIndexes: /* @__PURE__ */ new Map()
10979
+ geoIndexes: /* @__PURE__ */ new Map(),
10980
+ patternIndexes: /* @__PURE__ */ new Map(),
10981
+ sizeIndexes: /* @__PURE__ */ new Map()
10694
10982
  };
10695
10983
  let positionOffset = 0;
10696
10984
  const addToken = (token, position, surface) => {
@@ -10699,9 +10987,11 @@ function buildSegmentPayload(documents, schema, language, options, trackPosition
10699
10987
  };
10700
10988
  for (let ordinal = 0; ordinal < documents.length; ordinal++) {
10701
10989
  const document = documents[ordinal].document;
10702
- for (const { path: fieldPath, base: fieldType } of fields) {
10990
+ for (const { path: fieldPath, base: fieldType, pattern } of fields) {
10703
10991
  const value = getNestedValue(document, fieldPath);
10704
10992
  if (value === void 0 || value === null) continue;
10993
+ recordValueSize(filters.sizeIndexes, ordinal, fieldPath, fieldType, value);
10994
+ if (pattern) patternIndexOf(filters.patternIndexes, fieldPath).add(ordinal, value);
10705
10995
  if (!isWordIndexedBase(fieldType)) {
10706
10996
  indexFilterValue(filters, ordinal, fieldPath, fieldType, value);
10707
10997
  continue;
@@ -11152,6 +11442,7 @@ function createDirectExecutor(options) {
11152
11442
  }
11153
11443
  case "query": {
11154
11444
  const entry = requireIndex(action.indexName);
11445
+ const patternWork = joinPatternWorkMeter(action.patternWork);
11155
11446
  const result = await fanOutQuery(
11156
11447
  entry.manager,
11157
11448
  action.params,
@@ -11162,15 +11453,17 @@ function createDirectExecutor(options) {
11162
11453
  partitionIds: action.partitionIds,
11163
11454
  ...action.globalStats !== void 0 ? { globalStats: sanitizeGlobalStats(action.globalStats) } : {}
11164
11455
  },
11165
- entry.searchOptions
11456
+ { ...entry.searchOptions, patternWork }
11166
11457
  );
11458
+ result.patternWorkSpent = patternWork.spent();
11167
11459
  return result;
11168
11460
  }
11169
11461
  case "preflight": {
11170
11462
  const entry = requireIndex(action.indexName);
11463
+ const searchOptions = { ...entry.searchOptions, patternWork: joinPatternWorkMeter(action.patternWork) };
11171
11464
  if (countsWithoutScores(action.params)) {
11172
11465
  const count = fanOutMatchCount(entry.manager, action.params, entry.language, entry.config.schema, {
11173
- searchOptions: entry.searchOptions
11466
+ searchOptions
11174
11467
  });
11175
11468
  return { count };
11176
11469
  }
@@ -11180,7 +11473,7 @@ function createDirectExecutor(options) {
11180
11473
  entry.language,
11181
11474
  entry.config.schema,
11182
11475
  { scoringMode: entry.config.defaultScoring ?? "local" },
11183
- entry.searchOptions
11476
+ searchOptions
11184
11477
  );
11185
11478
  return { count: result.totalMatched };
11186
11479
  }
@@ -11266,4 +11559,4 @@ function createDirectExecutor(options) {
11266
11559
  };
11267
11560
  }
11268
11561
 
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 };
11562
+ 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, registerPatternSearch, registerStopWords, registerTokenizer, requirePatternSearchFor, resolveIndexAnalysis, resolvePartitionInsertOptions, resolveRequestThreadCount, resolveWorkerCount, resolveWorkerEntry, serializeMetadata, spawnNodeWorker, splitWorkerBudget, workerResourceLimits };