Apply the per-query filter conditions a dataset query set carries - #168
Merged
Conversation
A `tar` dataset's tests.jsonl pairs each query with the conditions it was
answered under:
{"query": [..], "conditions": {"and": [{"similarity": {"range": {"gt": .34}}}]},
"closest_ids": [..]}
Those closest_ids are the ground truth for the FILTERED query. bfb read the
query vector and the ids but dropped the conditions, so it searched unfiltered
and scored the result against the answers to a different question. On
laion-small-clip, half of whose 5000 queries are conditioned, that reads as
recall 0.63 where vector-db-benchmark reports 0.99 — measured on one
collection, same server, same ef: 0.632 before, 0.995 after.
Parse the conditions into a filter and apply each query's own. The dialect is
the one vector-db-benchmark defines in engine/base_client/parser.py, so the
numbers stay comparable: `and` -> must, `or` -> should, over match / range /
geo. Unrecognized operators, condition types and empty ranges are errors, not
silent drops — dropping one silently is the bug being fixed here.
Conditions are parsed once when the query set is opened, so a malformed set
fails at startup and the timed path only clones a ready-made filter. Query
sets without conditions ({} in the -no-filters datasets, absent in h5 and
sparse) are untouched: laion-small-clip-no-filters-1 scores 0.9969591 before
and after.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
A
tardataset'stests.jsonlpairs each query with the filter it was answered under:{"query": [...], "conditions": {"and": [{"similarity": {"range": {"gt": 0.349}}}]}, "closest_ids": [...], "closest_scores": [...]}Those
closest_idsare the ground truth for the filtered query. bfb read the query vector and the ids but droppedconditions— the field was explicitly excluded fromQueryRow— so it searched unfiltered and scored the result against the answers to a different question.On
laion-small-clip, half of whose 5000 queries are conditioned, that reads as recall 0.63 where vector-db-benchmark reports 0.99. It affects every filtered dataset in the catalog:arxiv-titles-*-filters,h-and-m-*-filters, therandom-*-filtersfamily,random-768-100-tenants.Exact search over the corpus isolates it — the approximation is not involved:
The change
Parse
conditionsinto aFilterand apply each query's own. The dialect is the one vector-db-benchmark defines inengine/base_client/parser.py, so numbers stay comparable with its results:and→must,or→should. One entry may name several fields and a field several condition types; each becomes its own condition, as increate_condition_subfilters.Unknown operators, unknown condition types, an empty
rangeand ageomissing a coordinate are errors, not silent drops — silently dropping a condition is exactly the bug being fixed, and it fails quietly as a plausible-looking recall number.Conditions are parsed once when the query set is opened, so a malformed set fails at startup and the timed request path only clones a ready-made filter. When a dataset supplies a filter it wins over a
filters:block on the same request; bfb prints how many queries carry conditions:Verification
A/B on one collection, same server, same
hnsw_ef=128, only the binary differs:qdrant/bfb:devlaion-small-clip(half conditioned)laion-small-clip-no-filters-1(conditions: {})0.995 lands where the exact-search analysis predicted (0.996); the remainder is HNSW on filtered search. The unfiltered twin is identical to seven decimals, and no "carry filter conditions" line is printed for it — sets without conditions take the same path as before.
6 new unit tests cover the laion shape,
and/or→must/should, geo, multi-bound ranges, multi-condition entries, and that each malformed shape errors. 161 tests pass;cargo fmt --checkandclippy --all-targetsclean.Follow-up
benchmarks/dataset-searchin benchmark-skills currently reportsaccuracy: nullfor filtered datasets because bfb could not measure them. That guard should be dropped once this lands andqdrant/bfb:devis rebuilt — I will send that separately rather than have it go stale against the published image.🤖 Generated with Claude Code