03 / 07

How would you test that a filter-plus-vector-search feature behaves correctly, beyond just checking that it returns some results?

Assert on exact expected IDs and ordering with a small deterministic fixture

A test that asserts 'results are non-empty' tells you almost nothing: it passes when the filter is ignored, when the vector search returns the wrong ranking, and when the filter is applied but the wrong field is used. The correct approach is to construct a small fixture dataset where you can compute the expected results by hand, and to assert on the exact ordered list of IDs. The fixture should be designed so that the filter and the vector search each have a distinct, observable effect. For example, include points that match the filter but are far from the query vector, points that are close to the query but do not match the filter, and points that both match the filter and are close. Then assert that the result contains only the points that match the filter, ordered by their distance to the query. This catches the two most common bugs: the filter being ignored (which would include the non-matching points) and the filter being applied after a top-k retrieval (which would return fewer results than expected when the filter is selective).

The mechanism that makes this work is that with a small fixture the expected result is computable exactly, so the test is a precise specification of behavior rather than a smoke test. You should write multiple assertions: the exact IDs in the exact order, the count, and the fact that no point outside the filter appears. You should also test the edge cases: a filter that matches nothing should return an empty list; a filter that matches everything should return the same results as the unfiltered search; a filter combined with a small limit should return only matching points even when non-matching points are closer; a filter on an unindexed field should behave the same as a filter on an indexed field. If the filter is applied during graph traversal (as in Qdrant's filter-aware HNSW), the test should also check that the result is not truncated - that the search explored enough of the graph to find the requested number of matching points. This is the case that most often breaks in production: a selective filter combined with a small ef returns fewer results than expected because the traversal stopped early.

  1. 1

    Fixture design: include points that match the filter but are far, points that are close but do not match, and points that do both.

  2. 2

    Exact assertions: assert on the exact ordered list of IDs, not just the count.

  3. 3

    Edge cases: filter matches nothing, filter matches everything, filter combined with a small limit.

  4. 4

    Indexed vs unindexed: the behavior should be the same; test both.

  5. 5

    Selectivity: test a selective filter with a small ef to catch truncation.

  6. 6

    Multiple conditions: test must, should, and must_not combinations.

  7. 7

    Type handling: test that the filter value type matches the payload type.

The trade-off is between the effort of constructing the fixture and the value of the test. A small, carefully designed fixture takes time to build, but it produces tests that are stable, fast, and precise. A large fixture or a test that uses production data produces tests that are slow, flaky, and whose expected results are not verifiable. The common mistake is to use random vectors and random payloads, which makes the expected result unpredictable and the test meaningless. The second mistake is to assert only on the count of results, which does not catch a wrong ranking or a partially ignored filter. The third mistake is to not test the interaction between the filter and the search - the two features may each work correctly in isolation but interact incorrectly when combined. The fourth mistake is to skip the selective-filter case, which is exactly where truncation bugs appear. Version note: the filter-aware HNSW behavior and the way ef is interpreted under a filter have changed across Qdrant releases. A test that asserts exact results under a filter is version-sensitive, so if you upgrade Qdrant, re-run these tests to confirm that the behavior is unchanged or to update the expected results.

javascript

Version-dependent: the filter-aware HNSW behavior and the way ef interacts with filters have changed across Qdrant releases. Some versions automatically expand ef under a selective filter; others require it to be set explicitly. A test that asserts exact results under a filter may need to be updated when upgrading. The filter API itself has also evolved, with new condition types and refinements to nested and array fields.

Difficulty: 5/10
Topics: Filtering, Test Fixtures, Best Practices

Scenario Questions

0-2 years experience
  1. 1

    Your filter-plus-search test asserts that the result is non-empty. Explain what bugs this would miss and how you would improve the test.

  2. 2

    A teammate uses random vectors in the test fixture. Explain why the test is meaningless and how to design a better fixture.

2-5 years experience
  1. 1

    Your test passes in-memory but fails against a real server. Diagnose the likely causes and describe how you would fix the test.

  2. 2

    You need to test a filter on an unindexed field. Describe the test you would write and what behavior you would assert.

5-8 years experience
  1. 1

    Design a property-based test for filter-plus-search that generates fixtures and asserts invariants (e.g. all returned points match the filter).

  2. 2

    You are testing a query builder that constructs filters dynamically. Describe the tests you would write to verify correctness across many combinations.

8+ years experience
  1. 1

    You need to test that filtered search maintains recall under a selective filter. Describe the benchmark, the ground truth, and the assertions that would catch a regression.

  2. 2

    You are designing a testing framework for a search system where filters are a first-class feature. Describe the abstractions, the fixtures, and the invariants you would assert across the test suite.

Follow-up Questions

  • How would you test that a filter is applied during graph traversal rather than after retrieval, given that both can produce the same result for non-selective filters?
  • What additional tests would you write for a filter that combines must, should, and must_not conditions?