Skip to content

Quick Start

CLI

export PGVECTOR_DSN="postgresql://localhost/mydb"
export QDRANT_URL="http://localhost:6333"

# One-shot migration
vecparity migrate --from pgvector://docs --to qdrant://docs

# Live migration: keeps polling for changes until caught up, so your
# app can keep writing to the source the whole time
vecparity migrate --from pgvector://docs --to qdrant://docs --live

# Migrate AND verify retrieval quality survived, gated on a query set
vecparity migrate --from pgvector://docs --to qdrant://docs \
    --live --verify-parity --queries golden_queries.json --min-recall 0.95

A failed parity check exits non-zero, so it can wire into CI or a deploy gate to stop a bad migration from silently shipping.

golden_queries.json is a list of query cases:

[
  {"query_id": "doc-5", "top_k": 5, "label": "doc-5 neighbors"},
  {"query_vector": [0.1, 0.2, 0.3, 0.4], "top_k": 10, "label": "explicit vector"}
]

Either query_id (searches using an existing record's vector from the source) or query_vector (an explicit vector) works. Use real user queries and their expected top hits for a meaningful check; random vectors won't tell you much about actual retrieval quality.

Python

from vecparity.adapters.pgvector import PgVectorAdapter
from vecparity.adapters.qdrant import QdrantAdapter
from vecparity.sync.engine import SyncEngine
from vecparity.verify.parity import verify_parity
from vecparity.types import QueryCase

source = PgVectorAdapter(conn=pg_conn, table="docs")
target = QdrantAdapter(client=qdrant_client, collection="docs")

SyncEngine(source, target).run_until_caught_up()

report = verify_parity(
    source, target,
    queries=[QueryCase(query_id="doc-123", top_k=10, label="spot check")],
    min_recall_at_k=0.9,
)
print(report.summary())
assert report.passed

Reading a parity report

Metric Meaning
recall_at_k Fraction of the source's top-k ids also returned by the target
jaccard_overlap Set overlap of the two top-k result lists
mean_score_drift Average similarity-score difference for ids present in both

ParityReport.passed gates on mean recall@k against the threshold you pass to verify_parity() (or --min-recall on the CLI).