Skip to content

Fix BooleanQuery.Filter: close filter searcher and propagate its errors - #2405

Open
OuQiL wants to merge 1 commit into
blevesearch:masterfrom
OuQiL:fix/boolean-filter-close-and-error
Open

OuQiL wants to merge 1 commit into
blevesearch:masterfrom
OuQiL:fix/boolean-filter-close-and-error

Conversation

@OuQiL

@OuQiL OuQiL commented Oct 3, 2026

Copy link
Copy Markdown

Fixes #2384.

What changed

BooleanQuery.Filter had two defects, both stemming from the filter searcher being
captured in a FilterFunc closure that FilteringSearcher neither owned nor could
return errors from:

  1. The filter searcher was never closed. On scorch that leaves
    TotTermSearchersStarted above TotTermSearchersFinished and drops the filter's
    bytes from SearchResult.Cost.
  2. FilterFunc returns only a bool, so an error from the filter searcher's
    Next/Advance (a segment read or decompression failure, for example) was turned
    into "this document does not match" and the search reported success with a partial
    or empty result.

This fixes both without touching any exported API:

  • Added searcher.FilterSearcher and searcher.NewFilterSearcher. It keeps only the
    child documents that are also matched by a caller-supplied filterSearcher, which it
    owns: the filter searcher is closed together with the child, and an error from its
    Next/Advance aborts the search instead of being treated as a non-match.
  • BooleanQuery.Searcher now builds the filter with the disabled-scoring options it
    already used and wraps the child in NewFilterSearcher instead of constructing a
    FilterFunc. The filter still does not contribute to the score.
  • FilteringSearcher and FilterFunc are unchanged, so existing custom FilterFunc
    implementations and the geo searchers keep working as-is.

The matching logic is the same lockstep merge as before (both searchers yield documents
in internal-ID order), so results are unchanged; only ownership and error handling
differ.

Design note

An alternative would be to change FilterFunc to return (bool, error) and give
FilteringSearcher an owned filter searcher. That changes an exported signature, so
this PR keeps the public API stable and moves ownership + error propagation into a
dedicated searcher used only by BooleanQuery.Filter.

How it was verified

Ran the reproducer from #2384 (scorch index plus a query whose searcher fails on
Next/Advance) against master and against this branch:

before after
BooleanQuery{Must,Filter} term searchers started +2, finished +1 started +2, finished +2
error from the filter searcher swallowed, 0 hits returned to the caller

ConjunctionQuery with the same clauses was already correct and stays correct.

New tests in search/searcher/search_filter_test.go cover:

  • FilterSearcher keeps only documents matched by the filter searcher (interleaved,
    superset, disjoint, empty filter, filter ahead), through both Next and Advance;
  • FilterSearcher.Next and Advance return the filter searcher's error;
  • FilterSearcher.Close closes both the child and the filter searcher, preferring the
    child's error;
  • DocumentMatchPoolSize accounts for both searchers.

Existing end-to-end coverage (TestFilteredBooleanQuery, search_nested_test.go,
search_test.go) continues to pass.

gofmt -l ./...   # no output
go vet ./...     # clean
go test ./...    # pass

BooleanQuery.Filter built a FilterFunc closure that captured the filter
searcher. FilteringSearcher never owned that searcher, so it was never
closed, and FilterFunc could only return bool, so errors from the filter
searcher's Next/Advance were silently turned into "no match".

Add searcher.FilterSearcher, which owns the filter searcher (closing it
together with the child) and propagates its errors. BooleanQuery.Searcher
now uses it. FilteringSearcher and FilterFunc are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BooleanQuery.Filter: filter searcher isn't closed, and its errors are swallowed

1 participant