Four metrics. Seven index variants.
One write.
Every vector lives inside the same atomic write as the row it describes.
HNSW, IVF, IVF-PQ, binary and PQ quantization, sparse, plus predicate-aware
over-fetch with min_score thresholds
that admit empty as a real result.
Write once. Filter mid-walk. Rank. Fuse.
Four stages, one engine. Because the write is atomic, every stage downstream reads a corpus that agrees with itself. The architecture guide covers why each algorithm works; this is what your request actually does.
row + embedding + postings + edges One transaction stages all four. There is no ingest queue and no sync job, so embedding-vs-row lag is exactly 0 - a vector can never point at a row that has moved on.
WHERE in_stock = true The pre-filter HNSW walker evaluates your metadata predicate on each candidate as the graph walk visits it. Non-matching nodes are pruned mid-walk - never over-fetch-and-discard after the fact.
topk(embedding, :q, 10) Ranked under the metric your model was trained for: Euclidean L2 by default, inner product for pre-normalized embeddings, cosine when only direction matters, Manhattan L1 when outlier dimensions shouldn't dominate.
fuse: rrf Dense, sparse, and structural filters run in the same request against the same snapshot. Reciprocal rank fusion merges the ranked lists by position, so incomparable score scales never touch.
An empty result is a real result.
Plain top-k always returns k rows - even when nothing in the corpus is actually
close. Set MIN_SCORE and the
engine drops everything below the threshold instead of handing your reranker a
confidently-wrong nearest neighbour.
The WHERE clause here never
ran as a post-filter. The walker checked it on every candidate it visited, so a
selective predicate costs pruning, not a wider fetch.
> topk(embedding, :q, 10)
WHERE in_stock = true AND region = 'EU'
MIN_SCORE 0.72
#1 sku-4471 0.89 in_stock · EU
#2 sku-0913 0.81 in_stock · EU
-- sku-2204 0.68 dropped · below min_score
2 rows · predicate applied during traversal Seven variants. One per-table declaration.
The index is declared on the table, not baked into the API - the query you write stays the same across all seven. Measured on the default path: p99 kNN under 30 ms at k=10 over 1M dense vectors.
The default. Graph-walk approximate nearest neighbour with the predicate applied during traversal. Reach for it first; leave it unless memory or corpus shape says otherwise.
Partition first, then quantize. When f32 storage is the constraint, IVF-PQ compresses 64x at D=128. Binary and PQ quantization are the same trade at different points on the curve.
For models that emit high-dimensional sparse vectors, and for retrieval that needs lexical precision beside semantic recall. Fused with the dense list in one request.
Dense, sparse, and filters. Fused, not stitched.
Production retrieval is hybrid: semantic similarity for recall, lexical sparse for precision, structural filters for relevance. Here all three run in a single request against one consistent snapshot - no client-side merging across separate engines, no scores from two systems that were never on the same scale.
Reciprocal rank fusion uses only each list's rank positions, so a document that both retrievers found rises to the top even when their raw scores disagree.
> search docs
dense: embed(:q)
sparse: terms(:q)
filter: lang = 'en'
fuse: rrf
dense sparse fused
doc-118 #1 doc-402 #1 doc-402 #1
doc-402 #2 doc-118 #3 doc-118 #2
doc-077 #3 doc-231 #2 doc-231 #3
one snapshot · rank-position fusion · no client stitching