How to create a Qdrant document store in Haystack

A retrieval application needs its indexed documents to survive beyond one Python process. QdrantDocumentStore connects Haystack to Qdrant, where embeddings and metadata remain available for later semantic searches.

The qdrant-haystack integration supports a disk-backed local mode through the path parameter. Local mode keeps this smoke test self-contained while exercising the same document-store and QdrantEmbeddingRetriever handoff used with a Qdrant server.

The sample uses three-dimensional vectors so it needs neither an embedding model nor an API key. A production index must use the dimension produced by its document and query embedders, and recreate_index=True must be removed before opening a collection whose existing documents must be preserved.

Steps to create a Qdrant document store in Haystack:

  1. Install the qdrant-haystack integration beside haystack-ai in the Python environment that runs the application.
    $ python -m pip install qdrant-haystack
  2. Create qdrant_store_demo.py with the imports and disk-backed document store.
    qdrant_store_demo.py
    from pathlib import Path
     
    from haystack import Document
    from haystack_integrations.components.retrievers.qdrant import QdrantEmbeddingRetriever
    from haystack_integrations.document_stores.qdrant import QdrantDocumentStore
     
     
    document_store = QdrantDocumentStore(
        path="support-qdrant-demo",
        index="support_articles",
        embedding_dim=3,
        similarity="cosine",
        recreate_index=True,
        return_embedding=True,
        progress_bar=False,
    )

    recreate_index=True replaces an existing collection with the same index name. Using this value with a retained collection deletes its indexed documents.

  3. Append the three embedded support documents below the QdrantDocumentStore block.
    documents = [
        Document(
            content="Reset passwords through the identity portal.",
            meta={"topic": "accounts"},
            embedding=[0.99, 0.03, 0.01],
        ),
        Document(
            content="Restart the search service after changing the index.",
            meta={"topic": "operations"},
            embedding=[0.02, 0.98, 0.02],
        ),
        Document(
            content="Archive old invoices from the billing dashboard.",
            meta={"topic": "billing"},
            embedding=[0.01, 0.02, 0.99],
        ),
    ]

    Each embedding has the three values required by embedding_dim=3, and the vectors point in different directions so the retrieval result is unambiguous.

  4. Append the document write and embedding retrieval below the document list.
    written = document_store.write_documents(documents)
     
    retriever = QdrantEmbeddingRetriever(
        document_store=document_store,
        top_k=1,
        return_embedding=True,
    )
    match = retriever.run(query_embedding=[1.0, 0.0, 0.0])["documents"][0]
  5. Append the stored-state and retrieval checks below the match assignment.
    print(f"documents written: {written}")
    print(f"documents stored: {document_store.count_documents()}")
    print(f"top match: {match.content}")
    print(f"match topic: {match.meta['topic']}")
    print(f"embedding returned: {match.embedding is not None}")
    print(f"qdrant path exists: {Path('support-qdrant-demo').is_dir()}")
  6. Confirm the assembled qdrant_store_demo.py matches the complete program before execution.
    qdrant_store_demo.py
    from pathlib import Path
     
    from haystack import Document
    from haystack_integrations.components.retrievers.qdrant import QdrantEmbeddingRetriever
    from haystack_integrations.document_stores.qdrant import QdrantDocumentStore
     
     
    document_store = QdrantDocumentStore(
        path="support-qdrant-demo",
        index="support_articles",
        embedding_dim=3,
        similarity="cosine",
        recreate_index=True,
        return_embedding=True,
        progress_bar=False,
    )
     
    documents = [
        Document(
            content="Reset passwords through the identity portal.",
            meta={"topic": "accounts"},
            embedding=[0.99, 0.03, 0.01],
        ),
        Document(
            content="Restart the search service after changing the index.",
            meta={"topic": "operations"},
            embedding=[0.02, 0.98, 0.02],
        ),
        Document(
            content="Archive old invoices from the billing dashboard.",
            meta={"topic": "billing"},
            embedding=[0.01, 0.02, 0.99],
        ),
    ]
     
    written = document_store.write_documents(documents)
     
    retriever = QdrantEmbeddingRetriever(
        document_store=document_store,
        top_k=1,
        return_embedding=True,
    )
    match = retriever.run(query_embedding=[1.0, 0.0, 0.0])["documents"][0]
     
    print(f"documents written: {written}")
    print(f"documents stored: {document_store.count_documents()}")
    print(f"top match: {match.content}")
    print(f"match topic: {match.meta['topic']}")
    print(f"embedding returned: {match.embedding is not None}")
    print(f"qdrant path exists: {Path('support-qdrant-demo').is_dir()}")
  7. Run the completed smoke test from the directory that will retain the local Qdrant data.
    $ python qdrant_store_demo.py
    documents written: 3
    documents stored: 3
    top match: Reset passwords through the identity portal.
    match topic: accounts
    embedding returned: True
    qdrant path exists: True

    The write and stored counts confirm the collection contains all three documents. The matching account article, returned embedding, and persistent directory confirm that QdrantEmbeddingRetriever can read the created store.