Vector DB Options

Fifteen products, one decision matrix. Pick by constraints, not by hype.

▶ Watch this reel

What you'll learn

  1. pgvector
  2. Azure AI Search
  3. Qdrant / Chroma / FAISS
  4. Pinecone & managed SaaS
  5. Relational built-ins
  6. The selection matrix

Remember this

pgvector

Azure AI Search

Open-source spectrum

Pinecone & managed SaaS

Relational built-ins

Selection matrix

Code: The thin retrieval interface — swap stores without tears

from typing import Protocol, Any

class Hit(BaseModel):
    id: str
    score: float
    payload: dict

class Retriever(Protocol):
    def upsert(self, ids: list[str], vecs, payloads: list[dict]) -> None: ...
    def search(self, vec, k: int = 10, where: dict | None = None) -> list[Hit]: ...
    def delete(self, ids: list[str]) -> None: ...

# --- one implementation per store ---------------------------------
class QdrantRetriever:
    def __init__(self, client, collection: str):
        self.c, self.col = client, collection
    def search(self, vec, k=10, where=None):
        r = self.c.search(self.col, vec, limit=k,
                          query_filter=self._f(where) if where else None)
        return [Hit(id=h.id, score=h.score, payload=h.payload) for h in r]

class PgvectorRetriever:
    def __init__(self, engine): self.engine = engine
    def search(self, vec, k=10, where=None):
        sql = text("""SELECT id, 1-(embedding <=> :q) AS score, payload
                      FROM docs
                      WHERE (:tenant IS NULL OR tenant_id = :tenant)
                      ORDER BY embedding <=> :q LIMIT :k""")
        ...  # same Hit shape

# --- app code depends on the Protocol, never a store --------------
def answer_question(q: str, retriever: Retriever):
    hits = retriever.search(embed(q), k=5, where={"tenant": current_tenant()})
    return generate(context=[h.payload["text"] for h in hits])
# Qdrant today, pgvector for a conservative client, Azure next —
# the app doesn't change.