Qdrant Hybrid Search — Named Vectors, Payload Filters, and Fusion
Qdrant ハイブリッド検索 — 名前付きベクトル、ペイロードフィルタ、フュージョン
Qdrant supports multiple vector heads per point and structured payload filters in a single query_points call. Hybrid retrieval lives inside Qdrant rather than on top of it, which collapses three network trips into one and preserves score semantics across heads.
article technology en Qdrant supports multiple vector heads per point and structured payload filters in a single query_points call. Hybrid retrieval lives inside Qdrant rather than on top of it, which collapses three network trips into one and preserves score semantics across heads.Qdrant Hybrid Search — Named Vectors, Payload Filters, and Fusion
Qdrant’s hybrid search reduces three separate retrieval hops — vector search, lexical search, reranking — to one server-side query. The design rests on three features: named vectors, sparse vector support, and payload filtering inside the same query plan.
Named vectors
A Qdrant collection stores multiple vectors per point under distinct names. A typical hybrid layout is dense (1024-dim cosine), sparse (IDF-modified inverted list), and colbert (multi-vector MAX_SIM). All three live on the same point, so a single document upsert populates every retrieval mode. The operational cost is one collection, one backup, one drift check — versus three separate indices in a Solr/Elasticsearch-plus-FAISS setup.
Payload indexes
A payload is Qdrant’s per-point structured metadata (JSON). A payload index lets the query filter category = "finance" or tags contains "rag" before the vector scan instead of after, which in turn dramatically narrows the candidate set. Nine index types are supported — keyword, integer, float, geo, bool, datetime, uuid, text (BM25), and nested. For a knowledge base, the common set is keyword on category/language/type/status, keyword on tags and relation_ids (list-valued), and datetime on date_updated for recency filters.
Hybrid query in one call
query_points accepts a prefetch pipeline, letting the server fuse heads without a client round-trip:
client.query_points(
collection_name="magi_knowledge",
prefetch=[
Prefetch(query=dense_vec, using="dense", limit=50),
Prefetch(query=sparse_vec, using="sparse", limit=50),
],
query=FusionQuery(fusion=Fusion.RRF),
query_filter=Filter(must=[FieldCondition(key="language", match=MatchValue(value="en"))]),
limit=10,
)
The server scores both heads over the same 50-candidate window and returns the fused top-10. The filter is evaluated before scoring, so the result set is always in-language. No application-side merge logic, no triple-paging, no rank stitching.
Score semantics across fusion modes
Fusion choice affects how the final score reads:
- RRF — the returned score is
1 / (rank + k)summed across heads. Not comparable to cosine similarity. Good for sorting, meaningless for thresholding. - Weighted Sum — requires pre-normalisation per head; the final score retains cosine-ish shape. Allows threshold cutoffs.
- Dense-only — the score is raw cosine similarity in [0, 1]. Cleanest semantics when only one head is populated.
For most personal-scale corpora, RRF is the right default — it degrades gracefully as the sparse head matures and doesn’t require tuning.
Drift detection
Qdrant’s collection schema is mutable at the API level (add a new payload index, delete one) but silently drifting between your provisioner and the live collection is the common operational failure. A good practice is a verify_collection_schema step at startup that compares the live CollectionInfo against a pinned spec and raises on any mismatch in vector dim, distance metric, multivector comparator, sparse modifier, or payload index data type. Warn on extras, fail on deviations — this catches accidental double-provisioning before it corrupts a retrieval run.
Backlinks
No backlinks yet