Semantic Search with Qdrant Vector Database Integration in Code-Graph-RAG

Code-Graph-RAG implements pluggable semantic search by storing code embeddings in Qdrant, with configurable retry logic, project-scoped filtering, and graceful lifecycle management.

The vitali87/code-graph-rag repository provides a retrieval-augmented generation (RAG) system for codebases that leverages semantic search with Qdrant vector database integration as its default vector storage backend. This architecture enables fast similarity search over code embeddings while remaining agnostic to the underlying vector store through a clean abstraction layer.


How Qdrant Backend Selection Works

The vector store system uses runtime configuration to determine which backend to instantiate. In codebase_rag/vector_store.py, the _selected_backend() function inspects settings.VECTOR_STORE_BACKEND and returns either QdrantVectorStore or MilvusVectorStore via _get_vector_store().

The optional dependency on qdrant-client is guarded by has_qdrant_client() in codebase_rag/utils/dependencies.py. This allows the package to import and inspect the vector store module even when Qdrant support is not installed.

Key configuration parameters from codebase_rag/config.py include:

  • settings.QDRANT_URL — Remote Qdrant server endpoint
  • settings.QDRANT_DB_PATH — Local persistent storage directory
  • settings.QDRANT_COLLECTION_NAME — Target collection identifier
  • settings.QDRANT_VECTOR_DIM — Embedding dimension (must match your encoder)
  • settings.QDRANT_UPSERT_RETRIES — Maximum retry attempts for upserts
  • settings.QDRANT_RETRY_BASE_DELAY — Base delay for exponential back-off

Qdrant Client Management and Lifecycle

The get_qdrant_client() function in codebase_rag/vector_store.py implements lazy singleton initialization with automatic collection creation.

from codebase_rag.vector_store import get_qdrant_client, close_vector_store_client
import atexit

# Client is created on first call

client = get_qdrant_client()

# Automatically registered at interpreter shutdown

atexit.register(close_vector_store_client)

The client creation logic distinguishes between:

  • Remote mode: When QDRANT_URL is set, connects to a Qdrant server instance
  • Local mode: When QDRANT_DB_PATH is set, creates a persistent local client

On first access, the system creates the collection with cosine distance metric and the configured vector dimension. An atexit hook ensures proper cleanup, avoiding the portalocker import-time issue that can occur during Python interpreter shutdown.


Storing Code Embeddings with Retry Logic

The store_embedding_batch() function converts code node embeddings into Qdrant PointStruct objects and persists them with configurable retry behavior.

from codebase_rag.vector_store import store_embedding_batch

# Each tuple: (node_id, embedding_vector, qualified_name)

points = [
    (123, [0.12, 0.34, 0.56, 0.78], "myproject.module.Class.method"),
    (124, [0.98, 0.01, 0.33, 0.44], "myproject.other.Module.function"),
]

stored = store_embedding_batch(points)
print(f"{stored} points stored in Qdrant")

Under the hood, _upsert_with_retry() in vector_store.py implements exponential back-off for transient failures. The retry policy respects settings.QDRANT_UPSERT_RETRIES and settings.QDRANT_RETRY_BASE_DELAY, making the system resilient to network interruptions or temporary Qdrant unavailability.


Performing Semantic Searches

The search_embeddings() method executes similarity queries against stored code embeddings.

from codebase_rag.vector_store import search_embeddings

query_vector = [0.11, 0.22, 0.33, 0.44]  # Must match QDRANT_VECTOR_DIM

top_k = 10

results = search_embeddings(
    query_vector,
    top_k=top_k,
    project="myproject"  # Optional: scope to specific project

)

for node_id, score, qualified_name in results:
    print(f"{qualified_name} – similarity {score:.4f}")

The implementation calls query_points() on the Qdrant client and extracts structured results containing node_id, similarity score, and qualified_name from each point's payload.


Project-Scoped Retrieval with Automatic Expansion

When the project parameter is provided, Code-Graph-RAG applies client-side filtering to ensure results belong to the specified codebase. The _search_project_scoped() function in vector_store.py implements an adaptive fetch strategy:

  1. Issue initial query with top_k results
  2. Filter hits by project prefix using _filter_by_project()
  3. If insufficient matches, exponentially expand fetch window up to a hard limit
  4. Return the best project-matching results

This approach balances query efficiency with result relevance, avoiding excessive network traffic while guaranteeing useful results for multi-project deployments.


Managing Embedded Code Data

The system provides utility functions for common maintenance operations:

Delete embeddings for a specific project:

from codebase_rag.vector_store import delete_project_embeddings

node_ids = [123, 124, 125]  # IDs belonging to "myproject"

delete_project_embeddings("myproject", node_ids)

Clear entire collection (testing/reset):

from codebase_rag.vector_store import clear_all_embeddings

clear_all_embeddings()  # Drops and recreates the Qdrant collection

Verify which IDs are persisted:

from codebase_rag.vector_store import verify_stored_ids

expected = {123, 124, 125}
found = verify_stored_ids(expected)
missing = expected - found
assert len(missing) == 0, f"IDs not found: {missing}"

Key Implementation Files

File Purpose
codebase_rag/vector_store.py QdrantVectorStore class, client management, upsert retry, search logic, project-scoped filtering
codebase_rag/config.py AppConfig with all Qdrant settings (QDRANT_DB_PATH, QDRANT_URL, vector dimension, retry policies)
codebase_rag/utils/dependencies.py has_qdrant_client() guards optional qdrant-client import
codebase_rag/constants.py VectorStoreBackend.QDRANT enum for backend selection

Summary

  • Pluggable architecture — Qdrant backend selected via settings.VECTOR_STORE_BACKEND with clean fallback handling
  • Resilient ingestion — _upsert_with_retry() provides exponential back-off for reliable embedding storage
  • Adaptive search — _search_project_scoped() expands result windows until project-relevant hits are found
  • Zero hard dependencies — Optional qdrant-client import guarded by has_qdrant_client()
  • Graceful cleanup — atexit registration prevents shutdown issues with portalocker

Frequently Asked Questions

What Qdrant deployment modes does Code-Graph-RAG support?

Code-Graph-RAG supports both local persistent storage via QDRANT_DB_PATH and remote server connections via QDRANT_URL. The get_qdrant_client() function automatically detects which mode to use based on configuration, with local mode suitable for development and remote mode for production deployments.

How does the project-scoped search handle insufficient matches?

The _search_project_scoped() function implements an expanding window strategy. It starts with the requested top_k, filters by project prefix, and if too few matches remain, it re-queries with progressively larger fetch sizes up to a hard limit. This ensures useful results without requiring per-project collections.

What happens if Qdrant is temporarily unavailable during embedding upsert?

The _upsert_with_retry() function catches transient failures and retries with exponential back-off based on settings.QDRANT_RETRY_BASE_DELAY. The maximum attempt count is controlled by settings.QDRANT_UPSERT_RETRIES. Permanent failures propagate as exceptions after retries are exhausted.

Can I use Code-Graph-RAG without installing Qdrant dependencies?

Yes. The has_qdrant_client() check in dependencies.py guards all Qdrant imports. If qdrant-client is not installed, the system can still load and use alternative backends like Milvus, or operate in modes that don't require vector storage. The package remains importable and inspectable regardless of optional dependencies.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →