Tacnode™
Glossary
Machine Learning

What Is Filtered Vector Search?

Filtered vector search is nearest-neighbor search restricted to items that satisfy structured predicates, such as category = 'shoes' AND in_stock, so that the result is the k most similar items among those that match, not the k most similar overall. Engines implement it by filtering before the search (pre-filtering), after it (post-filtering), or during the traversal of the vector index. The strategy decides whether a query returns k correct results, fewer than k, or results that miss closer matches.

Updated

How filtered vector search works

There are three strategies:

StrategyHow it runsStrengthFailure mode
Pre-filterApply the predicate, then compute distances over the matchesExact resultsSlow when many rows match
Post-filterScan the approximate index, then drop non-matching resultsFast for loose filtersToo few results for strict filters
In-indexSkip non-matching nodes while traversing the indexGood across selectivitiesGraph can fragment under very strict or combined filters

PostgreSQL with pgvector shows the trade-off concretely. With a B-tree index on the filter column, the planner can fetch matching rows and sort them by exact distance, which the pgvector documentation says works well "for conditions that match a low percentage of rows." With an HNSW index, filtering is post-filtering: "If a condition matches 10% of rows, with HNSW and the default hnsw.ef_search of 40, only 4 rows will match on average." Since version 0.8.0, iterative index scans keep scanning "until enough results are found (or it reaches hnsw.max_scan_tuples or ivfflat.max_probes)":

sql
SET hnsw.iterative_scan = strict_order;   -- pgvector 0.8.0+

SELECT p.id, p.name
FROM   products p
WHERE  p.category_id = 123
  AND  p.in_stock
ORDER  BY p.embedding <=> $1               -- cosine distance to the query vector
LIMIT  10;

For a filter with few distinct values, pgvector suggests a partial index per value; for many values, partitioning. It also warns that in a shared approximate index, one tenant's vectors "can affect recall (and speed) for other tenants."

Why filtered vector search matters in real-time systems

The filter is often the part of the query that changes fastest. Embeddings of a product description or a support article change rarely. Stock, price, account status, permissions and "already shown to this user" change with every transaction or session event. When the vectors live in a separate store, those attributes are usually copied into each vector's metadata and synced from the system that owns them, so the predicate is evaluated against a copy. Each sync interval leaves a window in which the search returns an item that just sold out or a document a user just lost access to.

If the filter attributes are stable, such as document type, language or tenant, and there is no freshness or concurrency requirement, this is pgvector territory and nothing more is needed. The harder case is a predicate on state that changes under load, often one that needs a join rather than a copied field. Tacnode runs vector similarity, SQL predicates and joins in the same query against one snapshot, so the filter and the ranking read the same version of the rows rather than metadata copied into each vector. For how similarity fits among the other retrieval patterns a decision uses, see retrieval patterns.

FAQ

Related terms

Further reading