My hands-on experience with Qdrant and Docling (and Ollama)

Published: (December 13, 2025 at 02:25 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

Using Qdrant for the first time!

Qdrant is an open‑source, high‑performance vector database and similarity‑search engine. It stores, indexes, and searches high‑dimensional vectors, which are essential for modern AI applications such as Retrieval‑Augmented Generation (RAG) systems, recommendation engines, and image recognition. Built in Rust for speed and memory efficiency, Qdrant can be deployed both in the cloud and on‑premises.

Having evaluated various RAG solutions, I wanted to test Qdrant’s advanced metadata filtering and hybrid‑search capabilities. I used the practical RAG pipeline example from the Docling documentation to spin up a local instance and run a hands‑on performance test.

What is Docling?

Docling is an open‑source library that simplifies document processing and parsing across many formats, including advanced PDF understanding. Key features include:

  • Parsing of PDF, DOCX, PPTX, XLSX, HTML, WAV, MP3, VTT, images (PNG, TIFF, JPEG, …) and more
  • Advanced PDF understanding: page layout, reading order, tables, code, formulas, image classification, etc.
  • Unified DoclingDocument representation
  • Export to Markdown, HTML, DocTags, lossless JSON
  • Local execution for sensitive data and air‑gapped environments
  • Plug‑and‑play integrations with LangChain, LlamaIndex, Crew AI, Haystack
  • Extensive OCR support for scanned PDFs and images
  • Support for several Visual Language Models (GraniteDocling)
  • Audio support with Automatic Speech Recognition (ASR) models
  • MCP server for connecting to any agent
  • Simple CLI

Deploying Qdrant locally

The quick‑start page is:

The following Docker (or Podman) commands set up Qdrant in minutes:

docker pull qdrant/qdrant
mkdir -p qdrant_storage

docker run -p 6333:6333 -p 6334:6334 \
    -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
    qdrant/qdrant

When the container starts you’ll see something like:

      _                 _
  __ _| |_ __ __ _ _ __ | |_  
 / _` | __/ _` | '__/ _` | __|
| (_| | || (_| | | | (_| | |_
 \__,_|\__\__,_|_|  \__,_|\__|

Version: 1.16.2, build: d2834de0
Access web UI at http://localhost:6333/dashboard

REST API: http://localhost:6333
GRPC API: http://localhost:6334

Sample Python application

The code below demonstrates basic Qdrant operations: creating a collection, upserting points, and performing both plain and filtered nearest‑neighbor searches.

# qdrant_app.py
from qdrant_client import QdrantClient
from qdrant_client.models import (
    Distance,
    VectorParams,
    PointStruct,
    Filter,
    FieldCondition,
    MatchValue,
    UpdateStatus,
)
import time

# 1. Client initialization
print("1. Initializing Qdrant client...")
client = QdrantClient(url="http://localhost:6333")

# 2. Collection setup
COLLECTION_NAME = "city_vectors"
VECTOR_SIZE = 4
DISTANCE_METRIC = Distance.DOT

# Ensure a clean start
try:
    client.delete_collection(collection_name=COLLECTION_NAME)
    print(f"   Collection '{COLLECTION_NAME}' deleted (if it existed).")
except Exception:
    pass

print(f"   Creating collection '{COLLECTION_NAME}'...")
client.create_collection(
    collection_name=COLLECTION_NAME,
    vectors_config=VectorParams(size=VECTOR_SIZE, distance=DISTANCE_METRIC),
)
print("   Collection created successfully.")

# 3. Upsert points
print("\n2. Upserting data points...")
points_to_insert = [
    PointStruct(id=1, vector=[0.05, 0.61, 0.76, 0.74], payload={"city": "Berlin", "population": 3.7}),
    PointStruct(id=2, vector=[0.19, 0.81, 0.75, 0.11], payload={"city": "London", "population": 8.9}),
    PointStruct(id=3, vector=[0.36, 0.55, 0.47, 0.94], payload={"city": "Moscow", "population": 12.6}),
    PointStruct(id=4, vector=[0.18, 0.01, 0.85, 0.80], payload={"city": "New York", "population": 8.4}),
    PointStruct(id=5, vector=[0.24, 0.18, 0.22, 0.44], payload={"city": "Beijing", "population": 21.5}),
    PointStruct(id=6, vector=[0.35, 0.08, 0.11, 0.44], payload={"city": "Mumbai", "population": 20.4}),
]

operation_info = client.upsert(
    collection_name=COLLECTION_NAME,
    wait=True,
    points=points_to_insert,
)

print(f"   Upsert operation status: {operation_info.status.name}")
if operation_info.status == UpdateStatus.COMPLETED:
    time.sleep(1)  # ensure index is ready
    print(f"   Successfully inserted {len(points_to_insert)} points.")

# 4. Plain nearest‑neighbors search
QUERY_VECTOR_1 = [0.2, 0.1, 0.9, 0.7]
print(f"\n3. Performing nearest neighbors search for query vector: {QUERY_VECTOR_1}")

search_result_1 = client.query_points(
    collection_name=COLLECTION_NAME,
    query=QUERY_VECTOR_1,
    with_payload=True,
    limit=3,
).points

print("   Top 3 closest points:")
for result in search_result_1:
    print(f"   - Score: {result.score:.4f}, City: {result.payload.get('city')}, Vector ID: {result.id}")

# 5. Filtered nearest‑neighbors search (only London)
QUERY_VECTOR_2 = [0.2, 0.1, 0.9, 0.7]
print(f"\n4. Performing filtered search (city='London') for query vector: {QUERY_VECTOR_2}")

search_result_2 = client.query_points(
    collection_name=COLLECTION_NAME,
    query=QUERY_VECTOR_2,
    query_filter=Filter(
        must=[FieldCondition(key="city", match=MatchValue(value="London"))]
    ),
    with_payload=True,
    limit=3,
).points

print("   Closest point filtered by city='London':")
for result in search_result_2:
    print(f"   - Score: {result.score:.4f}, City: {result.payload.get('city')}, Vector ID: {result.id}")

Running the example

python qdrant_app.py

Typical output:

1. Initializing Qdrant client...
   Collection 'city_vectors' deleted (if it existed).
   Creating collection 'city_vectors'...
   Collection created successfully.

2. Upserting data points...
   Upsert operation status: COMPLETED
   Successfully inserted 6 points.

3. Performing nearest neighbors search for query vector: [0.2, 0.1, 0.9, 0.7]
   Top 3 closest points:
   - Score: 1.3620, City: New York, Vector ID: 4
   - Score: 1.2730, City: Berlin, Vector ID: 1
   - Score: 1.2080, City: Moscow, Vector ID: 3

4. Performing filtered search (city='London') for query vector: [0.2, 0.1, 0.9, 0.7]
   Closest point filtered by city='London':
   - Score: 0.8710, City: London, Vector ID: 2
Back to Blog

Related posts

Read more »