English

Every RAG pipeline eventually hits the same wall: keyword search misses paraphrases, and users don't type the same words that show up in your documents. The fix is semantic search over vector embeddings, but picking where those vectors live—an in-process index, a managed cloud service, or an extension on the Postgres you already run—shapes your latency, ops burden, and bill in very different ways.

How Vector Similarity Search Actually Works

An embedding model turns text (or images, audio, code) into a fixed-length float vector, typically 384 to 3072 dimensions. Semantic similarity becomes a geometry problem: cosine similarity or dot product between vectors. The naive approach—compare a query vector against every stored vector—is exact but O(n), which falls apart past a few hundred thousand rows.

Approximate Nearest Neighbor (ANN) indexes trade a small amount of recall for massive speedups:

  • IVF (Inverted File Index) — clusters vectors into buckets (via k-means), then only searches the nearest buckets at query time
  • HNSW (Hierarchical Navigable Small World) — builds a multi-layer graph of vectors; query time walks the graph greedily toward the nearest neighbors
  • PQ (Product Quantization) — compresses vectors into smaller codes to shrink memory footprint, usually combined with IVF

HNSW dominates in 2026 for its recall-per-millisecond ratio, but it's memory-hungry since the whole graph typically lives in RAM.

FAISS: The Local, Self-Hosted Workhorse

FAISS (Facebook AI Similarity Search) is a C++ library with Python bindings that you embed directly in your process. No network hop, no managed bill—just an index living in memory or on disk.

import faiss
import numpy as np

dimension = 1536
index = faiss.IndexHNSWFlat(dimension, 32)  # 32 = graph connectivity (M)
index.hnsw.efConstruction = 200
index.hnsw.efSearch = 64

embeddings = np.load("doc_embeddings.npy").astype("float32")
index.add(embeddings)

query_vector = embed_query("how do I reset my API key?")
distances, indices = index.search(query_vector.reshape(1, -1), k=5)

faiss.write_index(index, "docs.hnsw")

FAISS shines when:

  • You control the deployment environment and want zero network latency
  • Your corpus is static or updates in batches (rebuilding an index nightly is fine)
  • You need the absolute lowest query latency (sub-millisecond is achievable)

The catch: FAISS gives you an index, not a database. There's no built-in metadata filtering, no replication, no multi-tenancy — you build all of that yourself, usually by keeping a side table mapping index IDs to document metadata.

Pinecone: Managed Vector Search as a Service

Pinecone abstracts the index entirely behind an API. You send vectors, it handles sharding, replication, and filtering.

import { Pinecone } from '@pinecone-database/pinecone';

const pc = new Pinecone({ apiKey: process.env.PINECONE_API_KEY! });
const index = pc.index('support-docs');

await index.upsert([
  {
    id: 'doc-482',
    values: embedding,
    metadata: { source: 'kb-article', team: 'billing', updatedAt: '2026-05-01' },
  },
]);

const results = await index.query({
  vector: queryEmbedding,
  topK: 8,
  filter: { team: { $eq: 'billing' } },
  includeMetadata: true,
});

Pinecone's value is operational: automatic scaling to billions of vectors, metadata filtering that composes cleanly with the ANN search, and serverless pricing based on reads/writes rather than provisioned capacity. The tradeoff is another vendor in your critical path, network latency on every query (typically 20-60ms), and a bill that grows with vector count and query volume independent of your existing infrastructure spend.

pgvector: Semantic Search Where Your Data Already Lives

If your application data is already in Postgres, pgvector lets you add a vector column and index it with the same database that holds your users, orders, and permissions.

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
  id bigserial PRIMARY KEY,
  content text NOT NULL,
  team_id bigint REFERENCES teams(id),
  embedding vector(1536)
);

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

SELECT id, content
FROM documents
WHERE team_id = 42
ORDER BY embedding <=> '[0.012, -0.043, ...]'
LIMIT 5;

The killer feature is that filter (team_id = 42) and the vector search run in the same query plan, transactionally consistent with the rest of your data. No syncing two systems, no eventual consistency between your source of truth and your search index. The cost is that pgvector's HNSW implementation, while solid, doesn't yet match Pinecone's or a tuned FAISS index at extreme scale (100M+ vectors), and you're now responsible for tuning Postgres itself for this workload.

Comparison Table

DimensionFAISSPineconepgvector
Ops burdenHigh (you own everything)Low (fully managed)Medium (you own Postgres anyway)
Query latency<1ms (in-process)20-60ms (network)2-15ms (same DB connection)
Cost modelCompute onlyPer-vector + per-queryMarginal, folded into DB cost
Metadata filteringDIYNative, composableNative SQL, most flexible
Max practical scaleBillions (with sharding effort)Billions (managed)Tens of millions comfortably
Transactional consistency with app dataN/ANoYes

Picking One

  • Prototyping a RAG demo or running search inside a batch job → FAISS
  • Building a product where vector search is a first-class, high-scale feature and you don't want to run infra → Pinecone
  • You already have Postgres, need filtering tied to real permissions, and scale is moderate → pgvector

Most teams I've worked with start on pgvector because it removes an entire category of infrastructure decisions, then migrate specific high-QPS workloads to Pinecone once query volume or corpus size actually justifies the extra vendor. FAISS stays useful even after that migration—as the local index inside offline evaluation scripts, where hitting a network API for every one of ten thousand test queries would make iteration unbearably slow.

Posted onaiwith tags:
0
0
0
0