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
What is filtered vector search?
Standard approximate nearest neighbor indexes such as HNSW and IVFFlat are built over vectors alone. They know nothing about tenant, price, date, permissions or stock. Many real queries carry those constraints, so every vector engine has to answer the same question: how do you combine a graph or cluster index with an arbitrary WHERE clause?
Research has treated this as its own problem. The ACORN paper (Patel, Kraft, Guestrin and Zaharia, 2024) notes that earlier methods "either suffer from poor performance or support a severely restricted set of search predicates," and proposes predicate subgraph traversal over HNSW. Some vector databases build filtering into the index. Qdrant's documentation describes extending "the HNSW graph with additional edges based on indexed payload values" so filters can be applied during graph search, aimed at the middle range of selectivity, where neither a plain HNSW search nor a full scan of the matches works well.
The usual measure of quality is recall: the fraction of the true top k matching items the query actually returns.
How filtered vector search works
There are three strategies:
| Strategy | How it runs | Strength | Failure mode |
|---|---|---|---|
| Pre-filter | Apply the predicate, then compute distances over the matches | Exact results | Slow when many rows match |
| Post-filter | Scan the approximate index, then drop non-matching results | Fast for loose filters | Too few results for strict filters |
| In-index | Skip non-matching nodes while traversing the index | Good across selectivities | Graph 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)":
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
Stale embeddings are stored vectors computed from an older version of their source: the row or document changed, but the embedding was not recomputed.
Embedding drift is a shift over time in the embeddings a system produces, from changing input data or a changed embedding model. It degrades retrieval silently.
Semantic context is the meaning-bearing layer of decision context — aggregations, vector similarity, LLM-derived signals — computed from the same events.
Shared context means every service and agent in a decision system reads one internally coherent snapshot of state, instead of private copies that diverge.
Decision coherence is the property that concurrent automated decisions act on the same version of shared state, instead of contradicting each other.
Context lag is the delay between a fact changing at its source and the system an automated decision reads from reflecting it. Also called stale context.
Temporal RAG is retrieval-augmented generation that accounts for when facts were true, so a model gets evidence from the moment the question is about.
