Skip to content
RetrievalMetadata filtering

Lead · Core path · 6 chapters

Metadata filtering

Pre- vs post-filtering, filtered-ANN recall cliffs, multi-tenancy isolation

A filter that is syntactically correct can still destroy recall or tenant isolation, so measure both before shipping multi-tenant retrieval.

Read these first

What you will know

  • Combine a filter with a vector search and measure recall

    Use a labeled ground-truth set and report recall@k separately for filtered and unfiltered queries; aggregate averages can hide tenant-specific failures.

  • Demonstrate the recall cliff on a high-selectivity filter

    When eligible documents are rarer than the ANN search budget, the index can exhaust candidates before finding enough matches and return short results without an error.

  • Choose pre- or post-filtering with a reason

    Pre-filtering may protect correctness while increasing latency or reducing index efficiency; post-filtering may waste candidates and miss matches, depending on selectivity and the latency target.

  • Guarantee tenant isolation at the index level

    Application predicates are vulnerable to missed code paths, cache contamination, and mis-scoped queries, so cross-tenant probes must fail at the storage or index boundary.

  • Detect a filter silently returning fewer results than it should

    Compare requested k with eligible hits and alert against a trusted count or exhaustive baseline; otherwise truncation is easily mistaken for poor relevance.


Chapters

  1. 1

    A filter can run before the search or after it, and the order changes the answer

    Post-filtering asks the index for its top-k and then discards whatever does not match, so a predicate matching one document in a thousand usually leaves you with zero or one result out of ten blind candidates. Pre-filtering restricts the eligible set first and searches only inside it, paying in latency or lost index efficiency. Both are correct implementations; only one is correct for a given query.

  2. 2

    The recall cliff appears when eligible documents are rarer than the search budget

    A graph or cell traversal explores a bounded number of candidates. When matching documents are scarcer than that budget, the walk exhausts its allowance among ineligible neighbours and returns a short list with no error, no warning and normal latency. This chapter shows how to reproduce the cliff deliberately by sweeping selectivity from 10% down to 0.01% and watching results vanish.

  3. 3

    At high selectivity the exact scan beats the index outright

    Five thousand eligible vectors out of ten million is a brute-force problem: scan them all, get perfect recall, and finish faster than a filtered graph walk that keeps wandering into documents it must throw away. That is the third answer to pre-versus-post, and since selectivity varies query by query, the real design is a router that estimates how many documents the predicate matches and picks a plan from that estimate.

  4. 4

    Filtered recall has to be measured apart from unfiltered recall

    Against a labelled ground-truth set, report recall@k for filtered queries separately from unfiltered ones, and break the filtered figure down by the filter values your traffic actually uses. A single blended average stays comfortable while one tenant, one document type or one date range collapses, because those queries are a small fraction of the total and the mean absorbs them.

  5. 5

    Two results when there should be twenty is truncation, not poor relevance

    Users describe it as bad search, but the signature is a result count below k with normal latency. Detect it by comparing the number returned against an exact count of documents matching the predicate, and alert on the gap rather than waiting for a complaint. Then weigh the cheap fix — over-fetch and re-filter — against the real one, which is routing the query to a plan that respects its selectivity.

  6. 6

    Tenant isolation enforced in the repository layer is not enforced

    A tenant_id predicate added in one code path leaves every other path unguarded: a batch job, a new endpoint, an admin tool, and above all a result or embedding cache keyed without the tenant id, which serves one customer's documents to another with the predicate correctly applied. Push the boundary down to per-tenant namespaces, partitions or row-level security, then prove it with a cross-tenant probe that must fail at the store.

External resources