pgvector vs Weaviate vs Qdrant: Self-Hosted Vector Database Comparison 2026

TL;DR — pgvector vs Weaviate vs Qdrant: three self-hosted vector databases with distinct philosophies. KalviumLabs: "Four databases, four design philosophies: pgvector is a Postgres extension, Weaviate is hybrid retrieval first-class, Qdrant is OSS-first with Rust performance." DevTo: "Use Qdrant for fastest filtered search and self-hosted Rust. Weaviate for hybrid BM25+vector. pgvector for Postgres simplicity." CallMissed: "Each represents a distinct philosophy — managed serverless, OSS-first with managed tier, hybrid retrieval first-class, or just use Postgres." Layerbase: "Qdrant is the open-source speed leader in Rust, 10-25% faster than Weaviate or Milvus. pgvector by default with no separate provisioning." StochasticSandbox: "Qdrant: payload index + filterable HNSW, adaptive fallback. Weaviate: roaring bitmap with inverted indexes. pgvector: SQL WHERE." Learn more with pgvector tutorial, pgvector vs Pinecone, vector databases, and hybrid search.

KalviumLabs frames the comparison: "Four databases, four different design philosophies: pgvector is a Postgres extension, Weaviate is hybrid retrieval first-class, Qdrant is OSS-first with Rust performance. Our pgvector and Pinecone numbers come from production systems. For Qdrant and Weaviate, we reference published benchmarks."

CallMissed summarizes the philosophies: "Each represents a distinct philosophy — fully managed serverless, OSS-first with a managed tier, hybrid retrieval as a first-class feature, or just use Postgres."

Three-Way Comparison Architecture

flowchart TD subgraph Pgvector["pgvector (Postgres Extension)"] PG_Ext["PostgreSQL + vector extension"] PG_SQL["SQL Query with JOINs"] PG_Filter["SQL WHERE filtering"] PG_HNSW["HNSW + IVFFlat indexes"] PG_Hybrid["ParadeDB for BM25 hybrid"] PG_Scale["<5M (100M w/pgvectorscale)"] end subgraph Weaviate["Weaviate (Hybrid-First)"] WV_Graph["GraphQL + REST API"] WV_Hybrid["Native BM25 + vector hybrid"] WV_Bitmap["Roaring bitmap filtering"] WV_HNSW["HNSW + composite index"] WV_PQ["PQ quantization (1.22+)"] WV_Scale["100M+ distributed"] end subgraph Qdrant["Qdrant (Rust Performance)"] QD_Rust["Rust engine"] QD_Filter["Payload index + filterable HNSW"] QD_Adaptive["Adaptive filtering fallback"] QD_Multi["Multi-vector + sparse vectors"] QD_Quant["Scalar + binary PQ"] QD_Scale["1B+ with sharding + Raft"] end subgraph Decision["Decision Factors"] D1["Existing Postgres? → pgvector"] D2["Need hybrid BM25+vector? → Weaviate"] D3["Need fastest filtering? → Qdrant"] D4["Need multi-vector? → Qdrant"] D5["Need SQL JOINs? → pgvector"] D6["Need Rust speed? → Qdrant"] end PG_Ext --> PG_SQL --> PG_Filter WV_Graph --> WV_Hybrid --> WV_Bitmap QD_Rust --> QD_Filter --> QD_Adaptive D1 --> Pgvector D2 --> Weaviate D3 --> Qdrant D4 --> Qdrant D5 --> Pgvector D6 --> Qdrant

Feature Comparison

Feature pgvector Weaviate Qdrant
Language C (Postgres extension) Go Rust
Philosophy Just use Postgres Hybrid retrieval first-class OSS speed leader
Index HNSW, IVFFlat HNSW + composite HNSW (custom)
Quantization Binary, halfvec PQ (1.22+) Scalar + binary PQ
Filtering SQL WHERE Roaring bitmap + inverted Payload index + filterable HNSW
Hybrid search ParadeDB BM25 Native BM25 + vector Sparse + dense (RRF/DBSF)
Multi-vector No No Yes (named vectors)
Sparse vectors sparsevec Yes Yes
API SQL GraphQL + REST REST + gRPC
Deployment Postgres extension Docker / K8s / cloud Docker / K8s / cloud
Distributed Postgres replication Sharding Sharding + Raft
ACID Yes (full Postgres) No No
SQL JOINs Yes No No
Managed cloud Supabase, Neon, Aurora Weaviate Cloud Qdrant Cloud
Max scale ~100M (pgvectorscale) 100M+ 1B+
Speed Good (5-20ms at 1M) Good Best (10-25% faster)
Filtered search SQL WHERE Roaring bitmap Adaptive in-graph (fastest)

Decision Matrix

Use Case Recommended Reason
Already on Postgres, <5M vectors pgvector No new infra, SQL JOINs, ACID
Need native hybrid BM25+vector Weaviate First-class hybrid retrieval
Need fastest filtered search Qdrant Adaptive in-graph filtering in Rust
Need multi-vector (ColBERT/ColPali) Qdrant Named vectors + sparse vectors
Need SQL JOINs with vectors pgvector Vectors alongside relational data
Need ACID transactions pgvector Full Postgres transactional guarantees
K8s self-hosted, 100M-1B vectors Qdrant Distributed, sharding + Raft, Rust speed
Need GraphQL API Weaviate GraphQL + REST API built-in
Minimize infrastructure pgvector Use existing Postgres, no new service
Need sparse + dense hybrid Qdrant RRF or DBSF fusion, first-class
Need PQ quantization at scale Weaviate or Qdrant Both support PQ compression
Want Rust performance Qdrant 10-25% faster than Weaviate or Milvus

Implementation

from dataclasses import dataclass
from typing import Optional
from enum import Enum

class SelfHostedVectorDB(Enum):
    PGVECTOR = "pgvector"
    WEAVIATE = "weaviate"
    QDRANT = "qdrant"

@dataclass
class SelfHostedVectorDBComparator:
    """Compare pgvector, Weaviate, and Qdrant for self-hosted RAG."""

    def recommend(self, existing_postgres: bool = False,
                  need_sql_joins: bool = False,
                  need_acid: bool = False,
                  need_native_hybrid: bool = False,
                  need_fastest_filtering: bool = False,
                  need_multi_vector: bool = False,
                  need_graphql: bool = False,
                  vector_count: int = 1_000_000,
                  prefer_rust: bool = False) -> dict:
        """Recommend self-hosted vector database."""
        scores = {SelfHostedVectorDB.PGVECTOR: 0,
                  SelfHostedVectorDB.WEAVIATE: 0,
                  SelfHostedVectorDB.QDRANT: 0}

        if existing_postgres:
            scores[SelfHostedVectorDB.PGVECTOR] += 3
        if need_sql_joins:
            scores[SelfHostedVectorDB.PGVECTOR] += 3
        if need_acid:
            scores[SelfHostedVectorDB.PGVECTOR] += 2
        if vector_count <= 5_000_000:
            scores[SelfHostedVectorDB.PGVECTOR] += 1

        if need_native_hybrid:
            scores[SelfHostedVectorDB.WEAVIATE] += 3
        if need_graphql:
            scores[SelfHostedVectorDB.WEAVIATE] += 2
        if 100_000_000 <= vector_count <= 500_000_000:
            scores[SelfHostedVectorDB.WEAVIATE] += 1

        if need_fastest_filtering:
            scores[SelfHostedVectorDB.QDRANT] += 3
        if need_multi_vector:
            scores[SelfHostedVectorDB.QDRANT] += 3
        if prefer_rust:
            scores[SelfHostedVectorDB.QDRANT] += 2
        if vector_count > 500_000_000:
            scores[SelfHostedVectorDB.QDRANT] += 2

        winner = max(scores, key=scores.get)

        reasons = {
            SelfHostedVectorDB.PGVECTOR: (
                "pgvector: Postgres extension, SQL JOINs, "
                "ACID, no new infrastructure"),
            SelfHostedVectorDB.WEAVIATE: (
                "Weaviate: native hybrid BM25+vector, "
                "GraphQL API, roaring bitmap filtering"),
            SelfHostedVectorDB.QDRANT: (
                "Qdrant: fastest filtered search in Rust, "
                "multi-vector, sparse+dense hybrid, "
                "scalar+binary PQ"),
        }

        return {
            "recommendation": winner.value,
            "reason": reasons[winner],
            "scores": {k.value: v for k, v in scores.items()},
        }

    def get_pgvector_example(self) -> str:
        """pgvector: SQL-based vector search."""
        return (
            "-- pgvector: vector search with SQL JOIN\n"
            "SELECT d.id, d.title, d.content,\n"
            "       d.embedding <=> '[0.1,...]'::vector AS dist\n"
            "FROM documents d\n"
            "JOIN departments dep ON d.dept_id = dep.id\n"
            "WHERE dep.name = 'engineering'\n"
            "ORDER BY d.embedding <=> '[0.1,...]'::vector\n"
            "LIMIT 5;"
        )

    def get_weaviate_example(self) -> str:
        """Weaviate: hybrid search with GraphQL."""
        return (
            "import weaviate\n"
            "\n"
            "client = weaviate.connect_to_local()\n"
            "\n"
            "collection = client.collections.get('Document')\n"
            "\n"
            "# Hybrid search (BM25 + vector)\n"
            "results = collection.query.hybrid(\n"
            "    query='machine learning architecture',\n"
            "    alpha=0.5,  # 0=keyword, 1=vector\n"
            "    limit=5,\n"
            "    filters=Filter.by_property(\n"
            "        'category').equal('tech')\n"
            ")"
        )

    def get_qdrant_example(self) -> str:
        """Qdrant: filtered search with payload index."""
        return (
            "from qdrant_client import QdrantClient\n"
            "from qdrant_client.models import (\n"
            "    Filter, FieldCondition, MatchValue)\n"
            "\n"
            "client = QdrantClient(host='localhost', port=6333)\n"
            "\n"
            "# Filtered vector search\n"
            "results = client.search(\n"
            "    collection_name='documents',\n"
            "    query_vector=[0.1, ...],\n"
            "    query_filter=Filter(\n"
            "        must=[\n"
            "            FieldCondition(\n"
            "                key='category',\n"
            "                match=MatchValue(value='tech')\n"
            "            )\n"
            "        ]\n"
            "    ),\n"
            "    limit=5\n"
            ")"
        )

    def get_filtering_comparison(self) -> dict:
        """Compare filtering approaches."""
        return {
            "pgvector": {
                "approach": "SQL WHERE clauses",
                "mechanism": "Postgres query planner optimizes "
                             "filter + vector search",
                "selective_filters": "May scan more vectors "
                                     "for highly selective filters",
                "best_for": "Moderate selectivity, "
                            "complex relational filters",
            },
            "weaviate": {
                "approach": "Roaring bitmap intersection",
                "mechanism": "Inverted indexes on metadata, "
                             "bitmap intersection for filtering",
                "selective_filters": "Good performance with "
                                     "bitmap optimization",
                "best_for": "Property-based filtering, "
                            "hybrid search",
            },
            "qdrant": {
                "approach": "Payload index + filterable HNSW",
                "mechanism": "Checks filter during graph "
                             "traversal, skips non-matching",
                "selective_filters": "Adaptive: falls back to "
                                     "brute-force for <1% match",
                "best_for": "Fastest filtered search, "
                            "highly selective filters",
            },
        }

    def get_performance_comparison(self) -> dict:
        """Performance comparison at 1M vectors."""
        return {
            "query_latency_1M": {
                "pgvector_hnsw": "5-20ms at 95%+ recall",
                "weaviate_hnsw": "8-25ms",
                "qdrant_hnsw": "5-15ms (Rust advantage)",
            },
            "filtered_search": {
                "pgvector": "Depends on Postgres query planner",
                "weaviate": "Good with roaring bitmap",
                "qdrant": "Fastest with adaptive in-graph filtering",
            },
            "throughput": {
                "pgvector": "Limited by Postgres connections",
                "weaviate": "Good, Go concurrency",
                "qdrant": "Best, Rust + async I/O",
            },
            "notes": (
                "Qdrant is 10-25% faster than Weaviate or Milvus "
                "on common workloads (Layerbase). "
                "pgvector matches at 1M scale with HNSW. "
                "Performance difference is smaller than "
                "embedding API latency in RAG pipelines."
            ),
        }

pgvector vs Weaviate vs Qdrant Checklist

  • [ ] pgvector: PostgreSQL extension, C, SQL JOINs, ACID, just use Postgres philosophy
  • [ ] Weaviate: Go, hybrid retrieval first-class, GraphQL + REST, roaring bitmap filtering
  • [ ] Qdrant: Rust, OSS speed leader, fastest filtered search, multi-vector support
  • [ ] Qdrant is 10-25% faster than Weaviate or Milvus on common workloads
  • [ ] pgvector with HNSW queries 1M vectors in 5-20ms at 95%+ recall
  • [ ] Performance difference is smaller than embedding API latency in RAG pipelines
  • [ ] pgvector filtering: SQL WHERE clauses, depends on Postgres query planner
  • [ ] Weaviate filtering: roaring bitmap with inverted indexes on metadata properties
  • [ ] Qdrant filtering: payload index + filterable HNSW, adaptive fallback for <1% match
  • [ ] Qdrant has the fastest filtered search — adaptive in-graph filtering in Rust
  • [ ] Weaviate has native BM25 + vector hybrid search as a first-class feature
  • [ ] Qdrant has sparse + dense hybrid search with RRF or DBSF fusion
  • [ ] pgvector hybrid search requires ParadeDB extension for BM25 inside Postgres
  • [ ] For production hybrid search, Weaviate or Qdrant are better choices
  • [ ] Qdrant supports multi-vector (ColBERT/ColPali) with named vectors
  • [ ] Qdrant supports sparse vectors as first-class
  • [ ] pgvector supports sparsevec type
  • [ ] Weaviate supports sparse vectors
  • [ ] pgvector: HNSW and IVFFlat indexes, binary quantization, halfvec
  • [ ] Weaviate: HNSW + composite index, PQ quantization (1.22+)
  • [ ] Qdrant: HNSW (custom), scalar + binary PQ quantization
  • [ ] pgvector: ACID transactions, point-in-time recovery, standard Postgres backups
  • [ ] Weaviate and Qdrant: no ACID transactions
  • [ ] pgvector: SQL JOINs combining vector search with relational queries
  • [ ] Weaviate and Qdrant: no SQL JOINs — API only
  • [ ] pgvector: max scale ~5M (100M with pgvectorscale StreamingDiskANN)
  • [ ] Weaviate: max scale 100M+ with distributed sharding
  • [ ] Qdrant: max scale 1B+ with sharding + Raft consensus
  • [ ] pgvector: deployment as Postgres extension (Supabase, Neon, Aurora)
  • [ ] Weaviate: deployment via Docker, K8s, or Weaviate Cloud
  • [ ] Qdrant: deployment via Docker, K8s, or Qdrant Cloud
  • [ ] pgvector: no new infrastructure if already on Postgres
  • [ ] Weaviate and Qdrant: separate service to deploy and manage
  • [ ] pgvector: managed by Supabase, Neon, Aurora, Cloud SQL
  • [ ] Weaviate: managed by Weaviate Cloud
  • [ ] Qdrant: managed by Qdrant Cloud
  • [ ] Choose pgvector if already on Postgres and fewer than 5M vectors
  • [ ] Choose pgvector if you need SQL JOINs with vector search
  • [ ] Choose pgvector if you need ACID transactions for vectors
  • [ ] Choose Weaviate if you need native hybrid BM25+vector search
  • [ ] Choose Weaviate if you need GraphQL API
  • [ ] Choose Qdrant if you need the fastest filtered search
  • [ ] Choose Qdrant if you need multi-vector (ColBERT/ColPali) support
  • [ ] Choose Qdrant if you want Rust performance (10-25% faster)
  • [ ] Choose Qdrant for K8s self-hosted at 100M-1B vectors
  • [ ] Choose Qdrant if you need sparse + dense hybrid with RRF/DBSF fusion
  • [ ] Four databases, four design philosophies — operational realities matter
  • [ ] Read pgvector tutorial for setup
  • [ ] Read pgvector vs Pinecone for managed comparison
  • [ ] Read vector databases for fundamentals
  • [ ] Read hybrid search for BM25 + vector
  • [ ] Read what is RAG for architecture
  • [ ] Read embeddings for vector fundamentals
  • [ ] Test: Qdrant filtered search outperforms at high selectivity
  • [ ] Test: Weaviate hybrid search combines BM25 + vector correctly
  • [ ] Test: pgvector SQL JOIN combines vector search with relational data
  • [ ] Test: recommendation logic selects correct database per use case
  • [ ] Document choice, rationale, filtering approach, and scaling strategy

FAQ

How do pgvector, Weaviate, and Qdrant compare for production RAG?

pgvector offers Postgres simplicity with SQL JOINs, Weaviate excels at hybrid BM25+vector search, and Qdrant offers the fastest filtered search in Rust. KalviumLabs: "Four databases, four different design philosophies: pgvector is a Postgres extension, Weaviate is hybrid retrieval first-class, Qdrant is OSS-first with Rust performance. Our pgvector numbers come from production systems. For Qdrant and Weaviate, we reference published benchmarks." DevTo: "Use Qdrant if you need the absolute fastest filtered search, you want to self-host, or you are building in Rust. Their filtering performance is unmatched." CallMissed: "Each represents a distinct philosophy — fully managed serverless, OSS-first with managed tier, hybrid retrieval as first-class feature, or just use Postgres." Layerbase: "Qdrant is the open-source speed leader. Written in Rust, which shows up in the latency numbers. Public benchmarks have Qdrant about 10-25% faster than Weaviate or Milvus on common workloads."

Which has the best filtering performance: pgvector, Weaviate, or Qdrant?

Qdrant has the best filtering performance, using an adaptive approach with payload index and filterable HNSW. DevTo: "Use Qdrant if you need the absolute fastest filtered search. Their filtering performance is unmatched." StochasticSandbox: "Qdrant uses a payload index combined with a filterable HNSW. During graph traversal, it checks filter conditions on each visited node and skips non-matching nodes. When the filter is very selective (<1% match), it falls back to a pre-filtered set with brute-force search. This adaptive approach handles edge cases well. Weaviate uses a roaring bitmap intersection approach with inverted indexes on metadata properties." KalviumLabs: "pgvector filtering uses SQL WHERE clauses — simple but may not be as optimized for highly selective filters at scale." Ranking: (1) Qdrant — adaptive in-graph filtering with payload index. (2) Weaviate — roaring bitmap with inverted indexes. (3) pgvector — SQL WHERE, depends on Postgres query planner.

Which is best for hybrid search: pgvector, Weaviate, or Qdrant?

Weaviate and Qdrant both offer first-class hybrid search, while pgvector requires ParadeDB for BM25. CallMissed: "Weaviate represents hybrid retrieval as a first-class feature." StochasticSandbox: "Weaviate uses a roaring bitmap intersection approach. It maintains inverted indexes on metadata properties, computes a bitmap. Qdrant uses sparse + dense vector hybrid search with RRF or DBSF fusion." KalviumLabs: "pgvector hybrid search requires ParadeDB for BM25 inside Postgres. Weaviate and Qdrant handle hybrid natively." Hybrid search comparison: (1) Weaviate — native BM25 + vector hybrid with fused scoring. (2) Qdrant — sparse + dense hybrid with RRF or DBSF fusion. (3) pgvector — requires ParadeDB extension for BM25, or manual fusion in SQL. For production hybrid search, Weaviate or Qdrant are better choices.

When should you choose Qdrant over pgvector and Weaviate?

Choose Qdrant when you need the fastest filtered search, want self-hosted with Rust performance, or need multi-vector (ColBERT) support. DevTo: "Use Qdrant if you need the absolute fastest filtered search, you want to self-host, or you are building in Rust." Layerbase: "Qdrant is the open-source speed leader. Written in Rust. 10-25% faster than Weaviate or Milvus on common workloads." StochasticSandbox: "Qdrant: named vectors + sparse vectors. Multi-vector (ColBERT-style late interaction) is first class. Scalar + binary PQ. Payload index + ranged filtering. Distributed cluster (sharding + Raft)." Choose Qdrant when: (1) You need the fastest filtered search performance. (2) You want self-hosted with Rust speed. (3) You need multi-vector (ColBERT/ColPali) support. (4) You need sparse + dense hybrid search. (5) You want scalar + binary quantization. (6) You need distributed clustering with sharding.

When should you choose pgvector over Weaviate and Qdrant?

Choose pgvector when you already use PostgreSQL, need SQL JOINs with vector search, want ACID transactions, or want to minimize infrastructure. KalviumLabs: "pgvector is a Postgres extension — vector search alongside relational data. Our pgvector numbers come from production systems." CallMissed: "pgvector represents the just use Postgres philosophy." Layerbase: "Layerbase Cloud hosts pgvector as part of the regular Postgres engine, so you get pgvector by default with no separate provisioning." Choose pgvector when: (1) You already use PostgreSQL — no new infrastructure. (2) You need SQL JOINs combining vector search with relational queries. (3) You need ACID transactions for vectors. (4) You have fewer than 5M vectors (or 100M with pgvectorscale). (5) You want to minimize infrastructure and operational burden. (6) You want point-in-time recovery and standard Postgres backups. (7) You want to avoid managing a separate vector database.


Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →