CloudCodeTree LogoCloudCodeTree
AI NewsTutorialsAbout
CloudCodeTree Logo
CloudCodeTree
  • AI News
  • Tutorials
  • About
← Back to AI News
Narrow Your Vector Search to the Right Docs: Qdrant Payload Filtering for RAG

Narrow Your Vector Search to the Right Docs: Qdrant Payload Filtering for RAG

Chris Harper

3 min read

Aug 5, 2026 · 04:05 UTC

AI
Tutorial
RAG
Vectors
Best Practices

Most RAG pipelines over-fetch and post-filter. Qdrant pushes filter conditions into the HNSW graph itself — retrieve only matching docs at the same latency as unfiltered search.

What you'll be able to do after this:

  • Store structured metadata (source, date, category) alongside every vector and query it efficiently
  • Write must/should/must_not filter clauses that run inside the HNSW traversal, not after it
  • Scope your RAG pipeline's context window to only docs that pass both semantic relevance and business rules

Without metadata filtering, your LLM's context fills with the most semantically similar chunks regardless of whether they're from the right source, the right time period, or the right document category. Filtering fixes that — but post-filtering (retrieve 50, then discard 45) bloats latency and hurts recall at scale. Qdrant's payload filtering runs inside the graph traversal itself.

Step-by-step

1. Create a collection and index your filter fields first

Create payload indexes before ingesting data. Without an index Qdrant falls back to a full scan; with one, filtered queries approach unfiltered latency.

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams

client = QdrantClient("localhost", port=6333)

client.create_collection(
    collection_name="docs",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)

# Index filter fields BEFORE inserting data
client.create_payload_index("docs", field_name="category", field_schema="keyword")
client.create_payload_index("docs", field_name="published_ts", field_schema="integer")
client.create_payload_index("docs", field_name="source", field_schema="keyword")

2. Upsert vectors with metadata payloads

from qdrant_client.models import PointStruct

client.upsert(
    collection_name="docs",
    points=[
        PointStruct(
            id=1,
            vector=embed("Firewall configuration best practices"),
            payload={
                "category": "security",
                "source": "internal_wiki",
                "published_ts": 20260601,   # YYYYMMDD int for range queries
                "title": "Firewall Config Guide",
            },
        ),
    ],
)

3. Search with a filter

from qdrant_client.models import Filter, FieldCondition, MatchValue, Range

hits = client.search(
    collection_name="docs",
    query_vector=embed(user_query),
    query_filter=Filter(
        must=[
            FieldCondition(key="category", match=MatchValue(value="security")),
            FieldCondition(key="published_ts", range=Range(gte=20260101)),
        ]
    ),
    limit=5,
)

must = AND. should = OR. must_not = exclusion. Nest them arbitrarily: "from source A or B, published after date X, not tagged draft."

Two gotchas:

  1. If you use LangChain's Qdrant integration, it wraps your payload inside a metadata key automatically. Use key="metadata.category" in your filter, not key="category" — the mismatch causes silent zero-result queries.

  2. Range filters over integer fields need your date stored as an integer (e.g., 20260601). Store timestamps as Unix epoch integers if you need finer granularity.

Next step: Once you have payload filtering working, check out Qdrant's LLM-powered filter automation — let the model extract filter conditions from natural language queries instead of hard-coding them.

Sources: A Complete Guide to Filtering in Vector Search — Qdrant · Qdrant Payload Filtering in Python — YouTube · LLM-Powered Filter Automation — Qdrant Docs · Qdrant Python Tutorial — Medium