Skip to content

SearchableFields caches empty result from initially empty index, breaking ManagedQuery/Search(string) #426

@Shazwazza

Description

@Shazwazza

Bug Description

SearchContext.SearchableFields and TaxonomySearchContext.SearchableFields cache their result after the first read. If the property is accessed before any documents have been indexed (e.g., during application startup when ExamineMultiFieldQueryParser is constructed via CreateQuery()), an empty array is cached permanently for the lifetime of that SearchContext instance.

After documents are indexed and the NRT reader is refreshed, IsSearcherCurrent() returns true (the refreshed reader IS current), so GetSearchContext() reuses the existing SearchContext with its stale empty _searchableFields. This causes ManagedQuery / Search(string) to generate queries with zero fields, returning zero results even though documents exist in the index.

Impact

  • ISearcher.Search(string) returns empty results despite documents being indexed
  • ManagedQuery() without explicit fields generates an empty query
  • Any code path that calls CreateQuery() before the index is populated poisons the cache
  • CreateQuery().All().SelectField().Execute() is not affected (doesn't use SearchableFields)

Affected Versions

  • support/3.x branch (SearchContext.cs)
  • v4 / main (SearchContext.cs and TaxonomySearchContext.cs)

Reproduction

  1. Create an application that registers Examine indexes
  2. During startup, before any documents are indexed, any code that calls CreateQuery() triggers ExamineMultiFieldQueryParser construction which accesses SearchableFields
  3. Index documents (async/timer-based commit)
  4. Call index.Searcher.Search("some text") — returns 0 results
  5. Call index.Searcher.CreateQuery().All().Execute() — returns documents correctly (proving they're in the index)

Root Cause

In SearchContext.SearchableFields:

if (_searchableFields == null)
{
    var searcher = _searcherManager.Acquire();
    // ... reads fields from reader ...
    _searchableFields = fields.Where(...).ToArray(); // caches empty [] if index is empty
}
return _searchableFields; // returns cached [] forever

The GetSearchContext() check uses IsSearcherCurrent() / _searcherManager.IsSearcherCurrent() which returns true after NRT refresh (the new reader IS current), so the SearchContext is never recreated.

Fix

Only cache _searchableFields when the result is non-empty. An empty index has nothing to search anyway, and re-reading on each call has negligible cost:

var filtered = fields.Where(...).ToArray();
if (filtered.Length > 0)
{
    _searchableFields = filtered;
}
return filtered;

Applied to both SearchContext and TaxonomySearchContext.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions