pgvector · Pinecone · ANN Search · Embedding Versioning · Production Patterns
Every embedding model turns text, images, or other content into a list of floating-point numbers — a vector — positioned in high-dimensional space so that semantically similar things sit close together. A vector database stores millions or billions of these vectors and answers one core question fast: given this query vector, what are the K closest vectors in the index?
This single operation, approximate nearest neighbor (ANN) search, underpins RAG pipelines, semantic search, recommendation systems, deduplication, anomaly detection, and image similarity search. The "database" part is really index management plus metadata filtering plus storage at scale.
| Term | Meaning |
|---|---|
| Embedding | Numeric vector representation of content, produced by a model (e.g. OpenAI, Cohere, sentence-transformers) |
| ANN search | Approximate Nearest Neighbor — finds "close enough" matches fast, trading perfect recall for speed |
| HNSW | Hierarchical Navigable Small World — graph-based ANN index, most common in production today |
| IVFFlat | Inverted File index — clusters vectors into buckets, searches only relevant buckets |
| Cosine similarity | Measures the angle between two vectors; most common metric for text embeddings |
| Recall@K | Of the K results returned, how many are truly relevant — core retrieval quality metric |
| Hybrid search | Combining dense vector search with sparse keyword search (e.g. BM25) for better precision |
pgvector is an open-source Postgres extension that adds a native vector column type and similarity operators directly into SQL. Instead of standing up new infrastructure, embeddings live in the same table as the rest of your relational data.
-- enable extension CREATE EXTENSION vector; CREATE TABLE documents ( id serial PRIMARY KEY, content text, account_id int, status text, embedding vector(1536) ); -- HNSW index for ANN search CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops); -- query: nearest neighbors + relational filter SELECT content FROM documents WHERE status = 'open' AND account_id = 42 ORDER BY embedding <=> '[0.012, -0.045, ...]' LIMIT 5;
Pinecone is a fully managed, purpose-built vector database delivered as an API. You push vectors via SDK calls; Pinecone handles index construction (HNSW-based), sharding, replication, and scaling — there's no underlying database to administer.
import { Pinecone } from "@pinecone-database/pinecone";
const pc = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
const index = pc.index("support-tickets");
await index.namespace("acct-42").upsert([
{ id: "doc1", values: embedding, metadata: { status: "open" } }
]);
const results = await index.namespace("acct-42").query({
vector: queryEmbedding,
topK: 5,
filter: { status: { "$eq": "open" } }
});
| Situation | Lean toward |
|---|---|
| Already running Postgres, moderate scale, need relational + vector together | pgvector |
| MVP / early-stage product, cost-sensitive | pgvector |
| Billions of vectors, strict low-latency SLA | Pinecone |
| Want zero ops overhead, small team | Pinecone |
| Strict data residency / compliance constraints on a self-hosted stack | pgvector (self-hosted) |
Approximate Nearest Neighbor (ANN) search is what makes vector databases fast. Instead of comparing a query vector against every stored vector (exact search, O(n)), ANN algorithms build index structures that navigate to approximate matches in O(log n) or sub-linear time, trading a small, tunable amount of recall for massive speed gains.
Hierarchical Navigable Small World — builds a multi-layer proximity graph. Upper layers are sparse long-range links for fast navigation; lower layers are dense short-range links for accuracy.
m (edges per node), ef_construction (build-time search width), ef_search (query-time search width)Inverted File Index — k-means clusters vectors into lists buckets; queries only search probes nearest bucket centroids.
probes value — higher = better recall, slower querylists (number of clusters), probes (clusters searched per query)| Parameter | Default | Effect of increasing | Tradeoff |
|---|---|---|---|
m | 16 | More edges per node → better recall | More memory, slower build |
ef_construction | 64 | Wider search at build time → better index quality | Much slower index build, same query speed |
ef_search | 40 | Wider search at query time → better recall | Higher query latency |
| Metric | Operator (pgvector) | When to use |
|---|---|---|
| Cosine similarity | <=> | Text embeddings — cares about direction, not magnitude. Most common for RAG. |
| Dot product | <#> | When vectors are pre-normalized; equivalent to cosine, slightly faster |
| Euclidean (L2) | <-> | Image embeddings, spatial data — magnitude matters |
| Manhattan (L1) | <+> | Sparse or high-dimensional data where L2 suffers from curse of dimensionality |
-- pgvector: HNSW index with tuned parameters CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 24, ef_construction = 100); -- Set query-time search width per session SET hnsw.ef_search = 80; -- IVFFlat alternative for lower memory budget CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 200); -- Tune probes at query time SET ivfflat.probes = 10;
| Scenario | Recommendation |
|---|---|
| Under 1M vectors, need fast inserts | HNSW — better recall, handles incremental inserts well |
| Over 10M vectors, RAM is constrained | IVFFlat — lower memory footprint, tune probes for recall |
| Need best possible recall at any cost | HNSW with high m and ef_search |
| Batch-only workload, large static dataset | IVFFlat — training cost is one-time |
| Production default with no special constraints | HNSW — it's the industry standard for good reason |
Pure dense vector search excels at semantic similarity but misses exact-term matches — a user searching for "GPT-4o-mini" or a specific product SKU might get semantically adjacent results when they need an exact keyword hit. Hybrid search combines dense vector search with sparse keyword search (typically BM25) so you get both semantic understanding and lexical precision.
| Approach | Strengths | Weaknesses |
|---|---|---|
| Dense (vector) only | Semantic understanding, handles synonyms, paraphrases | Misses exact terms, codes, SKUs, proper nouns |
| Sparse (BM25/TF-IDF) only | Exact keyword matching, interpretable, fast | No semantic understanding, misses synonyms |
| Hybrid (both) | Best of both — semantic + lexical precision | More complex pipeline, latency of both searches |
RRF is the most common way to merge dense and sparse result sets. Each document gets a score based on its rank in each list, then scores are summed. It's robust — it doesn't require calibrating score scales between the two systems, which is notoriously hard.
-- RRF formula: score(d) = sum over each list of 1 / (k + rank(d)) -- k=60 is the standard constant (reduces sensitivity to top-rank variance) -- Example: merging dense rank 2 + sparse rank 5 -- RRF score = 1/(60+2) + 1/(60+5) = 0.0161 + 0.0154 = 0.0315
After ANN retrieval gives you a candidate set (e.g. top-50), a cross-encoder re-ranker scores each candidate against the full query in context — much more accurate than bi-encoder similarity, but too slow to run on the full corpus. This two-stage pattern is the production standard for high-precision retrieval.
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
# candidates = [(doc_text, score), ...] from ANN retrieval
pairs = [(query, doc) for doc, score in candidates]
rerank_scores = reranker.predict(pairs)
# Sort by re-ranker score and take top-5
ranked = sorted(zip(candidates, rerank_scores),
key=lambda x: x[1], reverse=True)[:5]
// Pinecone supports native sparse-dense hybrid search
const results = await index.query({
vector: denseEmbedding, // dense component
sparseVector: { // sparse BM25 component
indices: [102, 983, 1204],
values: [0.72, 0.45, 0.31]
},
topK: 10,
alpha: 0.7 // 0 = pure sparse, 1 = pure dense
});
Getting vector search working in a notebook is straightforward. Getting it to stay accurate, fast, and compliant in a production system over months is where most teams underinvest. These are the patterns that matter once you're past the prototype stage.
Vectors from different embedding models live in incomparable semantic spaces. Mixing them silently destroys retrieval quality without any error being thrown.
embedding_model and embedding_version as metadata on every vector-- pgvector: track model version in the same table ALTER TABLE documents ADD COLUMN embedding_model TEXT; ALTER TABLE documents ADD COLUMN embedding_version TEXT; -- Always filter by version at query time SELECT content FROM documents WHERE embedding_model = 'text-embedding-3-small' AND embedding_version = 'v1' ORDER BY embedding <=> $1 LIMIT 5;
tenant_id in a partitioned tabletenant_id metadataOne of the most common silent failures: source data is updated or deleted but the vector index isn't. The model then retrieves stale or incorrect context with high confidence.
| Update pattern | Strategy |
|---|---|
| Document edited | Re-embed updated content → upsert vector with same ID (overwrites) |
| Document deleted | Delete vector by ID immediately — don't wait for a batch job |
| Bulk data refresh | Write to a shadow index, run eval, swap alias to shadow, drop old |
| Embedding model upgrade | Re-embed all documents in new model → new index → eval → cutover |
| What to monitor | Signal | Tool |
|---|---|---|
| Retrieval quality | Sample queries → score precision@K and context relevance | Ragas, DeepEval |
| Embedding drift | Query vector distribution shifting from index distribution | Evidently AI |
| Index staleness | Source doc update lag vs index update lag | Custom metric / Datadog |
| Query latency p95 | ANN search time trending up as index grows | APM / Prometheus |
| Index memory usage | HNSW index RAM approaching instance limit | Cloud metrics |
pgvector and Pinecone are the two most common choices, but the broader landscape has several strong options. Understanding the key differentiators lets you make an informed recommendation — and defend it in an interview.
| Database | Type | Index | Scale | Hosting | Best for |
|---|---|---|---|---|---|
| pgvector | Extension (Postgres) | HNSW, IVFFlat | Low–Mid (tens of millions) | Self-hosted / managed (Supabase, Neon) | Existing Postgres users, relational + vector queries, HIPAA self-hosted |
| Pinecone | Purpose-built managed | HNSW (proprietary) | Billions of vectors | Fully managed SaaS | Large scale, low-ops, multi-tenant RAG with namespaces |
| Weaviate | Purpose-built OSS | HNSW + BM25 hybrid | High | Self-hosted or managed (Weaviate Cloud) | Built-in hybrid search, GraphQL API, multi-modal embeddings |
| Qdrant | Purpose-built OSS | HNSW | High | Self-hosted or managed | Rust-based performance, rich payload filtering, on-prem compliance |
| Chroma | Purpose-built OSS | HNSW (via hnswlib) | Low–Mid | Embedded or self-hosted | Local dev, prototyping, LangChain/LlamaIndex default in tutorials |
| Milvus | Purpose-built OSS | HNSW, IVF, DiskANN | Billions of vectors | Self-hosted (k8s) or Zilliz cloud | Enterprise scale on-prem, multiple index type support |
| Question | If Yes → |
|---|---|
| Already running Postgres in production? | pgvector first — zero new infrastructure |
| Scale > 100M vectors with strict latency SLA? | Pinecone or Milvus |
| Need built-in hybrid dense + sparse search? | Weaviate or Pinecone |
| Air-gapped / strict data residency, not Postgres? | Qdrant or Milvus self-hosted |
| Just prototyping / local dev? | Chroma — simplest setup, no server needed |
| HIPAA, self-hosted, production scale? | pgvector (Postgres infra you already control) |
Start with pgvector. It's free, uses infrastructure you already have, and is sufficient for the majority of production RAG systems. Move to a purpose-built database only when you have a concrete requirement — scale, latency SLA, or operational constraints — that pgvector demonstrably can't meet. Chroma is fine for local development but shouldn't be the first choice for production. Weaviate and Qdrant are strong self-hosted alternatives when you need purpose-built performance without a managed-SaaS dependency. Pinecone is the path of least ops resistance at large scale, but verify compliance posture and model the cost before committing.
Click a question to expand the answer.
m and ef_construction) to balance build time, query latency, and recall.