@yoch/frozenminisearch 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v1.2.1 — `@yoch/frozenminisearch`
6
+
7
+ Patch release: lower search overhead when stored fields are disabled and fewer query-normalization allocations. No API or MSv5 wire-format changes.
8
+
9
+ ### Improved
10
+
11
+ - **Search without `storeFields`** — skip stored-field reads during scoring and result finalization when the index has no stored fields (`storeFields: []`).
12
+ - **String query normalization** — pre-allocated term/spec buffers, hoisted per-query field boosts and match weights, and shared `termToQuerySpec` building (fewer intermediate arrays and closures).
13
+
5
14
  ## v1.2.0 — `@yoch/frozenminisearch`
6
15
 
7
16
  Minor release: configurable MSv5 snapshot compression and Node 20 support.
@@ -260,7 +260,9 @@ function finalizeSearchResults(params) {
260
260
  queryTerms: terms,
261
261
  match,
262
262
  };
263
- Object.assign(result, getStoredFields(docId));
263
+ if (getStoredFields != null) {
264
+ Object.assign(result, getStoredFields(docId));
265
+ }
264
266
  if (filter == null || filter(result)) {
265
267
  results.push(result);
266
268
  }
@@ -4186,56 +4188,76 @@ function normalizeStringQuery(query, searchOptions, params) {
4186
4188
  ...params.globalSearchOptions,
4187
4189
  ...searchOptions,
4188
4190
  };
4189
- const terms = options.tokenize(query)
4190
- .flatMap((term) => options.processTerm(term))
4191
- .filter(term => !!term);
4191
+ const tokens = options.tokenize(query);
4192
+ const terms = [];
4193
+ for (const token of tokens) {
4194
+ const processed = options.processTerm(token);
4195
+ if (Array.isArray(processed)) {
4196
+ for (const term of processed) {
4197
+ if (term)
4198
+ terms.push(term);
4199
+ }
4200
+ }
4201
+ else if (processed) {
4202
+ terms.push(processed);
4203
+ }
4204
+ }
4205
+ const toSpec = termToQuerySpec(options);
4206
+ const specs = new Array(terms.length);
4207
+ for (let i = 0; i < terms.length; i++) {
4208
+ specs[i] = toSpec(terms[i], i, terms);
4209
+ }
4210
+ const { fuzzy: fuzzyWeight, prefix: prefixWeight } = {
4211
+ ...defaultSearchOptions.weights,
4212
+ ...options.weights,
4213
+ };
4192
4214
  return {
4193
4215
  options,
4194
- specs: terms.map(termToQuerySpec(options)),
4216
+ specs,
4195
4217
  operator: options.combineWith,
4218
+ fieldBoosts: fieldBoostsForQuery(options, params.fields),
4219
+ fuzzyWeight,
4220
+ prefixWeight,
4196
4221
  };
4197
4222
  }
4198
4223
  function lazyIndexedTerm(indexView, termIndex) {
4199
4224
  return { kind: 'lazy', resolve: () => indexView.resolveTermByIndex(termIndex) };
4200
4225
  }
4201
- function visitQuerySpecForScoring(query, options, params, visit) {
4226
+ function visitQuerySpecForScoring(query, normalized, params, visit) {
4202
4227
  const { indexView } = params;
4203
- const { weights, maxFuzzy } = options;
4204
- const { fuzzy: fuzzyWeight, prefix: prefixWeight } = { ...defaultSearchOptions.weights, ...weights };
4205
- const maxDistance = maxFuzzyDistance(query, maxFuzzy);
4228
+ const { fuzzyWeight, options, prefixWeight } = normalized;
4229
+ const maxDistance = maxFuzzyDistance(query, options.maxFuzzy);
4206
4230
  const exactTi = indexView.resolveTermIndex(query.term);
4207
4231
  visit(exactTi == null ? undefined : indexView.fieldTermData(exactTi), query.term, 1);
4208
- const seenPrefix = new Set();
4232
+ const seenPrefix = query.prefix && maxDistance ? new Set() : undefined;
4209
4233
  if (query.prefix) {
4210
4234
  for (const { termIndex, length } of indexView.getPrefixMatchesByIndex(query.term)) {
4211
4235
  const distance = length - query.term.length;
4212
4236
  if (!distance)
4213
4237
  continue;
4214
- seenPrefix.add(termIndex);
4238
+ seenPrefix === null || seenPrefix === void 0 ? void 0 : seenPrefix.add(termIndex);
4215
4239
  visit(indexView.fieldTermData(termIndex), lazyIndexedTerm(indexView, termIndex), prefixWeight * length / (length + 0.3 * distance));
4216
4240
  }
4217
4241
  }
4218
4242
  if (!maxDistance)
4219
4243
  return;
4220
4244
  for (const { termIndex, length, distance } of indexView.getFuzzyMatchesByIndex(query.term, maxDistance)) {
4221
- if (!distance || seenPrefix.has(termIndex))
4245
+ if (!distance || (seenPrefix === null || seenPrefix === void 0 ? void 0 : seenPrefix.has(termIndex)))
4222
4246
  continue;
4223
4247
  visit(indexView.fieldTermData(termIndex), lazyIndexedTerm(indexView, termIndex), fuzzyWeight * length / (length + distance));
4224
4248
  }
4225
4249
  }
4226
- function executeQuerySpecInternal(query, searchOptions, params, allowedDocs) {
4227
- const options = { ...params.globalSearchOptions, ...searchOptions };
4228
- const fieldBoosts = fieldBoostsForQuery(options, params.fields);
4250
+ function executeQuerySpecInternal(query, normalized, params, allowedDocs) {
4251
+ const { fieldBoosts, options } = normalized;
4229
4252
  const termOptions = allowedDocs == null ? undefined : { allowedDocs };
4230
4253
  const results = new Map();
4231
- visitQuerySpecForScoring(query, options, params, (data, derivedTerm, termWeight) => {
4254
+ visitQuerySpecForScoring(query, normalized, params, (data, derivedTerm, termWeight) => {
4232
4255
  aggregateTerm(query.term, derivedTerm, termWeight, query.termBoost, data, fieldBoosts, params.aggregateContext, options.boostDocument, options.bm25, results, termOptions);
4233
4256
  });
4234
4257
  return results;
4235
4258
  }
4236
- function collectDocIdsForQuerySpec(query, searchOptions, params, allowedDocs) {
4237
- const options = { ...params.globalSearchOptions, ...searchOptions };
4238
- const fieldBoosts = fieldBoostsForQuery(options, params.fields);
4259
+ function collectDocIdsForQuerySpec(query, normalized, params, allowedDocs) {
4260
+ const { fieldBoosts, options } = normalized;
4239
4261
  const docIds = new Set();
4240
4262
  const { indexView, aggregateContext } = params;
4241
4263
  const maxDistance = maxFuzzyDistance(query, options.maxFuzzy);
@@ -4243,19 +4265,19 @@ function collectDocIdsForQuerySpec(query, searchOptions, params, allowedDocs) {
4243
4265
  if (exactTi != null) {
4244
4266
  indexView.collectDocIds(exactTi, fieldBoosts, aggregateContext, docIds, allowedDocs);
4245
4267
  }
4246
- const seenPrefix = new Set();
4268
+ const seenPrefix = query.prefix && maxDistance ? new Set() : undefined;
4247
4269
  if (query.prefix) {
4248
4270
  for (const { termIndex, length } of indexView.getPrefixMatchesByIndex(query.term)) {
4249
4271
  const distance = length - query.term.length;
4250
4272
  if (!distance)
4251
4273
  continue;
4252
- seenPrefix.add(termIndex);
4274
+ seenPrefix === null || seenPrefix === void 0 ? void 0 : seenPrefix.add(termIndex);
4253
4275
  indexView.collectDocIds(termIndex, fieldBoosts, aggregateContext, docIds, allowedDocs);
4254
4276
  }
4255
4277
  }
4256
4278
  if (maxDistance) {
4257
4279
  for (const { termIndex, distance } of indexView.getFuzzyMatchesByIndex(query.term, maxDistance)) {
4258
- if (!distance || seenPrefix.has(termIndex))
4280
+ if (!distance || (seenPrefix === null || seenPrefix === void 0 ? void 0 : seenPrefix.has(termIndex)))
4259
4281
  continue;
4260
4282
  indexView.collectDocIds(termIndex, fieldBoosts, aggregateContext, docIds, allowedDocs);
4261
4283
  }
@@ -4386,14 +4408,15 @@ function collectDocIdsForQueryInternal(query, searchOptions, params, allowedDocs
4386
4408
  if (typeof query !== 'string') {
4387
4409
  throw new Error('FrozenMiniSearch: invalid query');
4388
4410
  }
4389
- const { options, specs, operator } = normalizeStringQuery(query, searchOptions, params);
4411
+ const normalized = normalizeStringQuery(query, searchOptions, params);
4412
+ const { specs, operator } = normalized;
4390
4413
  const combineWith = (operator !== null && operator !== void 0 ? operator : params.globalSearchOptions.combineWith);
4391
4414
  if (specs.length <= 1) {
4392
4415
  return specs.length === 1
4393
- ? collectDocIdsForQuerySpec(specs[0], options, params, allowedDocs)
4416
+ ? collectDocIdsForQuerySpec(specs[0], normalized, params, allowedDocs)
4394
4417
  : new Set();
4395
4418
  }
4396
- return collectCombinedDocIds(specs, combineWith, (spec, branchAllowed) => collectDocIdsForQuerySpec(spec, options, params, branchAllowed), allowedDocs);
4419
+ return collectCombinedDocIds(specs, combineWith, (spec, branchAllowed) => collectDocIdsForQuerySpec(spec, normalized, params, branchAllowed), allowedDocs);
4397
4420
  }
4398
4421
  function executeWildcardQuery(searchOptions, params) {
4399
4422
  const results = new Map();
@@ -4423,12 +4446,13 @@ function executeQueryInternal(query, searchOptions, params, allowedDocs, run) {
4423
4446
  if (typeof query !== 'string') {
4424
4447
  throw new Error('FrozenMiniSearch: invalid query');
4425
4448
  }
4426
- const { options, specs, operator } = normalizeStringQuery(query, searchOptions, params);
4449
+ const normalized = normalizeStringQuery(query, searchOptions, params);
4450
+ const { specs, operator } = normalized;
4427
4451
  const combineWith = (operator !== null && operator !== void 0 ? operator : params.globalSearchOptions.combineWith);
4428
4452
  if (useGatedEvaluation(run, specs.length, combineWith, false)) {
4429
- return executeCombinedBranches(specs, combineWith, params, (spec, branchAllowed) => executeQuerySpecInternal(spec, options, params, branchAllowed), (spec, branchAllowed) => collectDocIdsForQuerySpec(spec, options, params, branchAllowed), allowedDocs);
4453
+ return executeCombinedBranches(specs, combineWith, params, (spec, branchAllowed) => executeQuerySpecInternal(spec, normalized, params, branchAllowed), (spec, branchAllowed) => collectDocIdsForQuerySpec(spec, normalized, params, branchAllowed), allowedDocs);
4430
4454
  }
4431
- const results = specs.map(spec => executeQuerySpecInternal(spec, options, params, allowedDocs));
4455
+ const results = specs.map(spec => executeQuerySpecInternal(spec, normalized, params, allowedDocs));
4432
4456
  return combineResults(results, combineWith);
4433
4457
  }
4434
4458
  function executeQuery(query, searchOptions, params) {
@@ -4619,6 +4643,7 @@ function materializeOwnedSnapshot(params, mode) {
4619
4643
  function frozenMemoryBreakdown(frozen) {
4620
4644
  return frozen.memoryBreakdown();
4621
4645
  }
4646
+ const noStoredFields = () => undefined;
4622
4647
  function assertFieldsMatchSnapshot(optionsFields, snapFieldIds) {
4623
4648
  const snapNames = Object.keys(snapFieldIds).sort();
4624
4649
  const optNames = [...optionsFields].sort();
@@ -4672,24 +4697,31 @@ class FrozenMiniSearch {
4672
4697
  this._termCount = params.termCount;
4673
4698
  this._postings = params.postings;
4674
4699
  this._fieldTermFlyweight = createFrozenFieldTermFlyweight(this._postings);
4700
+ this._hasStoredFields = this._storedFields.kind !== 'none';
4675
4701
  this._aggregateContext = {
4676
4702
  documentCount: this._documentCount,
4677
4703
  avgFieldLength: this._avgFieldLength,
4678
4704
  fieldIds: this._fieldIds,
4679
4705
  getFieldLength: (docId, fieldId) => this.getFieldLength(docId, fieldId),
4680
4706
  getExternalId: docId => this._externalIds[docId],
4681
- getStoredFields: docId => readStoredFields(this._storedFields, docId),
4707
+ getStoredFields: this._hasStoredFields
4708
+ ? docId => readStoredFields(this._storedFields, docId)
4709
+ : noStoredFields,
4682
4710
  };
4683
4711
  this._queryEngineParams = {
4684
4712
  fields: this._options.fields,
4685
4713
  globalSearchOptions: this._options.searchOptions,
4686
4714
  tokenize: this._options.tokenize,
4687
4715
  processTerm: this._options.processTerm,
4688
- indexView: createFrozenQueryIndexView(this._index, this._postings, this._fieldTermFlyweight, (callback) => {
4689
- forEachLiveShortId(this._nextId, this._externalIds, (shortId, id) => {
4690
- callback(shortId, id, readStoredFields(this._storedFields, shortId));
4691
- });
4692
- }),
4716
+ indexView: createFrozenQueryIndexView(this._index, this._postings, this._fieldTermFlyweight, this._hasStoredFields
4717
+ ? (callback) => {
4718
+ forEachLiveShortId(this._nextId, this._externalIds, (shortId, id) => {
4719
+ callback(shortId, id, readStoredFields(this._storedFields, shortId));
4720
+ });
4721
+ }
4722
+ : (callback) => {
4723
+ forEachLiveShortId(this._nextId, this._externalIds, callback);
4724
+ }),
4693
4725
  aggregateContext: this._aggregateContext,
4694
4726
  };
4695
4727
  }
@@ -4746,7 +4778,9 @@ class FrozenMiniSearch {
4746
4778
  return shortId == null ? undefined : readStoredFields(this._storedFields, shortId);
4747
4779
  }
4748
4780
  search(query, searchOptions = {}) {
4749
- return finalizeRawSearchResults(this.executeQuery(query, searchOptions), query, searchOptions, this._options.searchOptions, docId => this._externalIds[docId], docId => readStoredFields(this._storedFields, docId));
4781
+ return finalizeRawSearchResults(this.executeQuery(query, searchOptions), query, searchOptions, this._options.searchOptions, docId => this._externalIds[docId], this._hasStoredFields
4782
+ ? docId => readStoredFields(this._storedFields, docId)
4783
+ : undefined);
4750
4784
  }
4751
4785
  autoSuggest(queryString, options = {}) {
4752
4786
  const merged = { ...this._options.autoSuggestOptions, ...options };
@@ -600,6 +600,7 @@ declare class FrozenMiniSearch<T = any> {
600
600
  private readonly _fieldTermFlyweight;
601
601
  private readonly _aggregateContext;
602
602
  private readonly _queryEngineParams;
603
+ private readonly _hasStoredFields;
603
604
  constructor(params: FrozenAssembleParams<T>);
604
605
  static readonly wildcard: typeof WILDCARD_QUERY;
605
606
  get documentCount(): number;
package/dist/es/index.js CHANGED
@@ -256,7 +256,9 @@ function finalizeSearchResults(params) {
256
256
  queryTerms: terms,
257
257
  match,
258
258
  };
259
- Object.assign(result, getStoredFields(docId));
259
+ if (getStoredFields != null) {
260
+ Object.assign(result, getStoredFields(docId));
261
+ }
260
262
  if (filter == null || filter(result)) {
261
263
  results.push(result);
262
264
  }
@@ -4182,56 +4184,76 @@ function normalizeStringQuery(query, searchOptions, params) {
4182
4184
  ...params.globalSearchOptions,
4183
4185
  ...searchOptions,
4184
4186
  };
4185
- const terms = options.tokenize(query)
4186
- .flatMap((term) => options.processTerm(term))
4187
- .filter(term => !!term);
4187
+ const tokens = options.tokenize(query);
4188
+ const terms = [];
4189
+ for (const token of tokens) {
4190
+ const processed = options.processTerm(token);
4191
+ if (Array.isArray(processed)) {
4192
+ for (const term of processed) {
4193
+ if (term)
4194
+ terms.push(term);
4195
+ }
4196
+ }
4197
+ else if (processed) {
4198
+ terms.push(processed);
4199
+ }
4200
+ }
4201
+ const toSpec = termToQuerySpec(options);
4202
+ const specs = new Array(terms.length);
4203
+ for (let i = 0; i < terms.length; i++) {
4204
+ specs[i] = toSpec(terms[i], i, terms);
4205
+ }
4206
+ const { fuzzy: fuzzyWeight, prefix: prefixWeight } = {
4207
+ ...defaultSearchOptions.weights,
4208
+ ...options.weights,
4209
+ };
4188
4210
  return {
4189
4211
  options,
4190
- specs: terms.map(termToQuerySpec(options)),
4212
+ specs,
4191
4213
  operator: options.combineWith,
4214
+ fieldBoosts: fieldBoostsForQuery(options, params.fields),
4215
+ fuzzyWeight,
4216
+ prefixWeight,
4192
4217
  };
4193
4218
  }
4194
4219
  function lazyIndexedTerm(indexView, termIndex) {
4195
4220
  return { kind: 'lazy', resolve: () => indexView.resolveTermByIndex(termIndex) };
4196
4221
  }
4197
- function visitQuerySpecForScoring(query, options, params, visit) {
4222
+ function visitQuerySpecForScoring(query, normalized, params, visit) {
4198
4223
  const { indexView } = params;
4199
- const { weights, maxFuzzy } = options;
4200
- const { fuzzy: fuzzyWeight, prefix: prefixWeight } = { ...defaultSearchOptions.weights, ...weights };
4201
- const maxDistance = maxFuzzyDistance(query, maxFuzzy);
4224
+ const { fuzzyWeight, options, prefixWeight } = normalized;
4225
+ const maxDistance = maxFuzzyDistance(query, options.maxFuzzy);
4202
4226
  const exactTi = indexView.resolveTermIndex(query.term);
4203
4227
  visit(exactTi == null ? undefined : indexView.fieldTermData(exactTi), query.term, 1);
4204
- const seenPrefix = new Set();
4228
+ const seenPrefix = query.prefix && maxDistance ? new Set() : undefined;
4205
4229
  if (query.prefix) {
4206
4230
  for (const { termIndex, length } of indexView.getPrefixMatchesByIndex(query.term)) {
4207
4231
  const distance = length - query.term.length;
4208
4232
  if (!distance)
4209
4233
  continue;
4210
- seenPrefix.add(termIndex);
4234
+ seenPrefix === null || seenPrefix === void 0 ? void 0 : seenPrefix.add(termIndex);
4211
4235
  visit(indexView.fieldTermData(termIndex), lazyIndexedTerm(indexView, termIndex), prefixWeight * length / (length + 0.3 * distance));
4212
4236
  }
4213
4237
  }
4214
4238
  if (!maxDistance)
4215
4239
  return;
4216
4240
  for (const { termIndex, length, distance } of indexView.getFuzzyMatchesByIndex(query.term, maxDistance)) {
4217
- if (!distance || seenPrefix.has(termIndex))
4241
+ if (!distance || (seenPrefix === null || seenPrefix === void 0 ? void 0 : seenPrefix.has(termIndex)))
4218
4242
  continue;
4219
4243
  visit(indexView.fieldTermData(termIndex), lazyIndexedTerm(indexView, termIndex), fuzzyWeight * length / (length + distance));
4220
4244
  }
4221
4245
  }
4222
- function executeQuerySpecInternal(query, searchOptions, params, allowedDocs) {
4223
- const options = { ...params.globalSearchOptions, ...searchOptions };
4224
- const fieldBoosts = fieldBoostsForQuery(options, params.fields);
4246
+ function executeQuerySpecInternal(query, normalized, params, allowedDocs) {
4247
+ const { fieldBoosts, options } = normalized;
4225
4248
  const termOptions = allowedDocs == null ? undefined : { allowedDocs };
4226
4249
  const results = new Map();
4227
- visitQuerySpecForScoring(query, options, params, (data, derivedTerm, termWeight) => {
4250
+ visitQuerySpecForScoring(query, normalized, params, (data, derivedTerm, termWeight) => {
4228
4251
  aggregateTerm(query.term, derivedTerm, termWeight, query.termBoost, data, fieldBoosts, params.aggregateContext, options.boostDocument, options.bm25, results, termOptions);
4229
4252
  });
4230
4253
  return results;
4231
4254
  }
4232
- function collectDocIdsForQuerySpec(query, searchOptions, params, allowedDocs) {
4233
- const options = { ...params.globalSearchOptions, ...searchOptions };
4234
- const fieldBoosts = fieldBoostsForQuery(options, params.fields);
4255
+ function collectDocIdsForQuerySpec(query, normalized, params, allowedDocs) {
4256
+ const { fieldBoosts, options } = normalized;
4235
4257
  const docIds = new Set();
4236
4258
  const { indexView, aggregateContext } = params;
4237
4259
  const maxDistance = maxFuzzyDistance(query, options.maxFuzzy);
@@ -4239,19 +4261,19 @@ function collectDocIdsForQuerySpec(query, searchOptions, params, allowedDocs) {
4239
4261
  if (exactTi != null) {
4240
4262
  indexView.collectDocIds(exactTi, fieldBoosts, aggregateContext, docIds, allowedDocs);
4241
4263
  }
4242
- const seenPrefix = new Set();
4264
+ const seenPrefix = query.prefix && maxDistance ? new Set() : undefined;
4243
4265
  if (query.prefix) {
4244
4266
  for (const { termIndex, length } of indexView.getPrefixMatchesByIndex(query.term)) {
4245
4267
  const distance = length - query.term.length;
4246
4268
  if (!distance)
4247
4269
  continue;
4248
- seenPrefix.add(termIndex);
4270
+ seenPrefix === null || seenPrefix === void 0 ? void 0 : seenPrefix.add(termIndex);
4249
4271
  indexView.collectDocIds(termIndex, fieldBoosts, aggregateContext, docIds, allowedDocs);
4250
4272
  }
4251
4273
  }
4252
4274
  if (maxDistance) {
4253
4275
  for (const { termIndex, distance } of indexView.getFuzzyMatchesByIndex(query.term, maxDistance)) {
4254
- if (!distance || seenPrefix.has(termIndex))
4276
+ if (!distance || (seenPrefix === null || seenPrefix === void 0 ? void 0 : seenPrefix.has(termIndex)))
4255
4277
  continue;
4256
4278
  indexView.collectDocIds(termIndex, fieldBoosts, aggregateContext, docIds, allowedDocs);
4257
4279
  }
@@ -4382,14 +4404,15 @@ function collectDocIdsForQueryInternal(query, searchOptions, params, allowedDocs
4382
4404
  if (typeof query !== 'string') {
4383
4405
  throw new Error('FrozenMiniSearch: invalid query');
4384
4406
  }
4385
- const { options, specs, operator } = normalizeStringQuery(query, searchOptions, params);
4407
+ const normalized = normalizeStringQuery(query, searchOptions, params);
4408
+ const { specs, operator } = normalized;
4386
4409
  const combineWith = (operator !== null && operator !== void 0 ? operator : params.globalSearchOptions.combineWith);
4387
4410
  if (specs.length <= 1) {
4388
4411
  return specs.length === 1
4389
- ? collectDocIdsForQuerySpec(specs[0], options, params, allowedDocs)
4412
+ ? collectDocIdsForQuerySpec(specs[0], normalized, params, allowedDocs)
4390
4413
  : new Set();
4391
4414
  }
4392
- return collectCombinedDocIds(specs, combineWith, (spec, branchAllowed) => collectDocIdsForQuerySpec(spec, options, params, branchAllowed), allowedDocs);
4415
+ return collectCombinedDocIds(specs, combineWith, (spec, branchAllowed) => collectDocIdsForQuerySpec(spec, normalized, params, branchAllowed), allowedDocs);
4393
4416
  }
4394
4417
  function executeWildcardQuery(searchOptions, params) {
4395
4418
  const results = new Map();
@@ -4419,12 +4442,13 @@ function executeQueryInternal(query, searchOptions, params, allowedDocs, run) {
4419
4442
  if (typeof query !== 'string') {
4420
4443
  throw new Error('FrozenMiniSearch: invalid query');
4421
4444
  }
4422
- const { options, specs, operator } = normalizeStringQuery(query, searchOptions, params);
4445
+ const normalized = normalizeStringQuery(query, searchOptions, params);
4446
+ const { specs, operator } = normalized;
4423
4447
  const combineWith = (operator !== null && operator !== void 0 ? operator : params.globalSearchOptions.combineWith);
4424
4448
  if (useGatedEvaluation(run, specs.length, combineWith, false)) {
4425
- return executeCombinedBranches(specs, combineWith, params, (spec, branchAllowed) => executeQuerySpecInternal(spec, options, params, branchAllowed), (spec, branchAllowed) => collectDocIdsForQuerySpec(spec, options, params, branchAllowed), allowedDocs);
4449
+ return executeCombinedBranches(specs, combineWith, params, (spec, branchAllowed) => executeQuerySpecInternal(spec, normalized, params, branchAllowed), (spec, branchAllowed) => collectDocIdsForQuerySpec(spec, normalized, params, branchAllowed), allowedDocs);
4426
4450
  }
4427
- const results = specs.map(spec => executeQuerySpecInternal(spec, options, params, allowedDocs));
4451
+ const results = specs.map(spec => executeQuerySpecInternal(spec, normalized, params, allowedDocs));
4428
4452
  return combineResults(results, combineWith);
4429
4453
  }
4430
4454
  function executeQuery(query, searchOptions, params) {
@@ -4615,6 +4639,7 @@ function materializeOwnedSnapshot(params, mode) {
4615
4639
  function frozenMemoryBreakdown(frozen) {
4616
4640
  return frozen.memoryBreakdown();
4617
4641
  }
4642
+ const noStoredFields = () => undefined;
4618
4643
  function assertFieldsMatchSnapshot(optionsFields, snapFieldIds) {
4619
4644
  const snapNames = Object.keys(snapFieldIds).sort();
4620
4645
  const optNames = [...optionsFields].sort();
@@ -4668,24 +4693,31 @@ class FrozenMiniSearch {
4668
4693
  this._termCount = params.termCount;
4669
4694
  this._postings = params.postings;
4670
4695
  this._fieldTermFlyweight = createFrozenFieldTermFlyweight(this._postings);
4696
+ this._hasStoredFields = this._storedFields.kind !== 'none';
4671
4697
  this._aggregateContext = {
4672
4698
  documentCount: this._documentCount,
4673
4699
  avgFieldLength: this._avgFieldLength,
4674
4700
  fieldIds: this._fieldIds,
4675
4701
  getFieldLength: (docId, fieldId) => this.getFieldLength(docId, fieldId),
4676
4702
  getExternalId: docId => this._externalIds[docId],
4677
- getStoredFields: docId => readStoredFields(this._storedFields, docId),
4703
+ getStoredFields: this._hasStoredFields
4704
+ ? docId => readStoredFields(this._storedFields, docId)
4705
+ : noStoredFields,
4678
4706
  };
4679
4707
  this._queryEngineParams = {
4680
4708
  fields: this._options.fields,
4681
4709
  globalSearchOptions: this._options.searchOptions,
4682
4710
  tokenize: this._options.tokenize,
4683
4711
  processTerm: this._options.processTerm,
4684
- indexView: createFrozenQueryIndexView(this._index, this._postings, this._fieldTermFlyweight, (callback) => {
4685
- forEachLiveShortId(this._nextId, this._externalIds, (shortId, id) => {
4686
- callback(shortId, id, readStoredFields(this._storedFields, shortId));
4687
- });
4688
- }),
4712
+ indexView: createFrozenQueryIndexView(this._index, this._postings, this._fieldTermFlyweight, this._hasStoredFields
4713
+ ? (callback) => {
4714
+ forEachLiveShortId(this._nextId, this._externalIds, (shortId, id) => {
4715
+ callback(shortId, id, readStoredFields(this._storedFields, shortId));
4716
+ });
4717
+ }
4718
+ : (callback) => {
4719
+ forEachLiveShortId(this._nextId, this._externalIds, callback);
4720
+ }),
4689
4721
  aggregateContext: this._aggregateContext,
4690
4722
  };
4691
4723
  }
@@ -4742,7 +4774,9 @@ class FrozenMiniSearch {
4742
4774
  return shortId == null ? undefined : readStoredFields(this._storedFields, shortId);
4743
4775
  }
4744
4776
  search(query, searchOptions = {}) {
4745
- return finalizeRawSearchResults(this.executeQuery(query, searchOptions), query, searchOptions, this._options.searchOptions, docId => this._externalIds[docId], docId => readStoredFields(this._storedFields, docId));
4777
+ return finalizeRawSearchResults(this.executeQuery(query, searchOptions), query, searchOptions, this._options.searchOptions, docId => this._externalIds[docId], this._hasStoredFields
4778
+ ? docId => readStoredFields(this._storedFields, docId)
4779
+ : undefined);
4746
4780
  }
4747
4781
  autoSuggest(queryString, options = {}) {
4748
4782
  const merged = { ...this._options.autoSuggestOptions, ...options };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoch/frozenminisearch",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Read-only Node.js full-text search — compact frozen indexes and binary snapshots",
5
5
  "main": "dist/cjs/index.cjs",
6
6
  "module": "dist/es/index.js",