Vector Databases: Pinecone, Weaviate, Qdrant y pgvector
¿Qué es una Vector Database?
Una base de datos vectorial almacena, indexa y busca embeddings (vectores de alta dimensionalidad) de forma eficiente. Es el componente central de cualquier pipeline RAG.
Texto → Embedding Model → Vector [0.12, -0.45, 0.78, ...] → Vector DB
(1536 dimensiones)
Distancia entre Vectores
| Métrica | Fórmula | Uso |
|---|---|---|
| Cosine Similarity | cos(θ) = A·B / (|A||B|) | Texto, embeddings normalizados |
| Euclidean (L2) | √Σ(aᵢ-bᵢ)² | Clusters, imágenes |
| Dot Product | Σ(aᵢ·bᵢ) | Embeddings no normalizados |
Algoritmos de Indexación
HNSW (Hierarchical Navigable Small World)
El más usado en producción. Crea un grafo multi-capa para búsqueda aproximada.
Capa 3: ○ ──────────── ○ (pocos nodos, saltos largos)
Capa 2: ○ ──── ○ ──── ○ ──── ○ (más nodos)
Capa 1: ○ ─ ○ ─ ○ ─ ○ ─ ○ ─ ○ ─ ○ (todos los nodos)
Búsqueda: Empieza arriba, baja refinando
| Parámetro | Efecto |
|---|---|
M (connections) |
Más = mejor recall, más memoria |
ef_construction |
Más = mejor índice, más lento de construir |
ef_search |
Más = mejor recall, más lento búsqueda |
IVF (Inverted File Index)
Divide el espacio en clusters. Busca solo en los clusters más cercanos.
Cluster 1: [v1, v5, v12, ...]
Cluster 2: [v2, v8, v15, ...]
Cluster 3: [v3, v7, v22, ...]
Query → Encuentra clusters cercanos → Busca dentro de ellos
Pinecone
Base de datos vectorial serverless. Sin infraestructura que administrar.
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key="your-api-key")
# Crear índice
pc.create_index(
name="knowledge-base",
dimension=1536, # Debe coincidir con las dimensiones del modelo de embedding (ej: text-embedding-3-small = 1536)
metric="cosine", # Similitud coseno: ideal para texto, mide ángulo entre vectores (ignora magnitud)
spec=ServerlessSpec( # Configuración del proveedor cloud — Pinecone gestiona la infra por ti
cloud="aws",
region="us-east-1",
),
)
index = pc.Index("knowledge-base")
# Upsert vectores (upsert = insert + update: si el ID ya existe, lo actualiza; si no, lo inserta)
index.upsert(
vectors=[
{
"id": "doc-1",
"values": embedding_vector,
"metadata": {
"source": "manual.pdf",
"page": 5,
"topic": "authentication",
},
},
],
namespace="technical-docs", # Partición lógica dentro del índice — permite separar datos sin crear índices separados
)
# Buscar
results = index.query(
vector=query_embedding,
top_k=5, # Retorna los 5 vectores más similares al query
include_metadata=True, # Incluye la metadata de cada resultado (source, page, topic, etc.)
filter={ # Pre-filtrado por metadata ANTES de la búsqueda vectorial
"topic": {"$eq": "authentication"}, # $eq = igual. Otros: $ne, $gt, $gte, $lt, $lte, $in, $nin
},
namespace="technical-docs",
)
for match in results.matches:
print(f"Score: {match.score:.4f} | {match.metadata}")
Pros: Serverless, fácil de escalar, filtros de metadata potentes Contras: Vendor lock-in, costo puede escalar rápido, no self-hosted
Weaviate
Vector DB open-source con búsqueda híbrida (vectorial + keyword).
import weaviate
from weaviate.classes.config import Configure, Property, DataType
from weaviate.classes.query import MetadataQuery, Filter
client = weaviate.connect_to_local() # o connect_to_weaviate_cloud()
# Crear collection con vectorizer integrado
collection = client.collections.create(
name="Document",
vectorizer_config=Configure.Vectorizer.text2vec_openai(
model="text-embedding-3-large"
),
properties=[
Property(name="content", data_type=DataType.TEXT),
Property(name="source", data_type=DataType.TEXT),
Property(name="page", data_type=DataType.INT),
],
)
# Insertar (vectoriza automáticamente)
collection.data.insert({
"content": "JWT authentication requires...",
"source": "api-docs.md",
"page": 12,
})
# Búsqueda híbrida (vector + BM25 keyword)
results = collection.query.hybrid(
query="JWT authentication setup",
alpha=0.7, # 0=keyword puro, 1=vector puro, 0.7=balance
limit=5,
return_metadata=MetadataQuery(score=True),
filters=Filter.by_property("source").equal("api-docs.md"),
)
Pros: Open-source, búsqueda híbrida, vectorización integrada, GraphQL API Contras: Más complejo de operar, mayor consumo de memoria
Qdrant
Escrito en Rust. Alto rendimiento y filtrado avanzado.
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance, VectorParams, PointStruct,
Filter, FieldCondition, MatchValue,
)
client = QdrantClient(host="localhost", port=6333)
# Crear colección
client.create_collection(
collection_name="knowledge_base",
# VectorParams: configuración del espacio vectorial
# size=1536 → dimensiones del vector (debe coincidir con el modelo de embedding)
# distance=Distance.COSINE → métrica de distancia (COSINE, EUCLID, DOT)
vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)
# Insertar
client.upsert(
collection_name="knowledge_base",
points=[
PointStruct( # PointStruct = un punto en el espacio vectorial: id + vector + payload
id=1,
vector=embedding_vector,
payload={ # payload = metadata almacenada junto al vector (filtrable y retornable)
"content": "JWT authentication...",
"source": "api-docs.md",
"category": "security",
"timestamp": "2026-01-15",
},
),
],
)
# Buscar con filtros
results = client.query_points(
collection_name="knowledge_base",
query=query_vector,
limit=5,
query_filter=Filter(
# must = AND (todos deben cumplirse). También: should = OR, must_not = NOT
must=[
# FieldCondition: filtra por un campo del payload
# MatchValue: coincidencia exacta del valor
FieldCondition(key="category", match=MatchValue(value="security")),
]
),
)
Pros: Muy rápido (Rust), filtrado expresivo, quantización nativa, open-source Contras: Ecosistema más pequeño, menos integraciones cloud-managed
pgvector: Vectores en PostgreSQL
Extensión que añade capacidades vectoriales a PostgreSQL. Ideal cuando ya usas Postgres.
Parámetros clave de pgvector:
- HNSW index:
m = 16controla conexiones por nodo (más = mejor recall, más memoria),ef_construction = 200controla calidad de construcción del índice (más = mejor índice, más lento de construir) vector_cosine_ops: clase de operador que indica a PostgreSQL usar distancia coseno para este índice- Operador
<=>: calcula la distancia coseno entre dos vectores (0 = idénticos, 2 = opuestos) 1 - (embedding <=> ...): convierte distancia en similitud (1 = idénticos, -1 = opuestos), porque<=>retorna distancia, no similitud
-- Habilitar extensión
CREATE EXTENSION vector;
-- Crear tabla con columna vectorial
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
source VARCHAR(255),
embedding vector(1536), -- Vector de 1536 dimensiones
created_at TIMESTAMP DEFAULT NOW()
);
-- Crear índice HNSW para búsqueda eficiente
-- m = 16: conexiones por nodo en el grafo HNSW (default 16, rango típico 12-48)
-- ef_construction = 200: nodos candidatos durante construcción (más alto = mejor recall, más lento)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops) -- vector_cosine_ops: usar distancia coseno para el índice
WITH (m = 16, ef_construction = 200);
-- Insertar
INSERT INTO documents (content, source, embedding)
VALUES ('JWT authentication...', 'api-docs.md', '[0.12, -0.45, ...]');
-- Buscar por similitud coseno
-- <=> es el operador de distancia coseno (no similitud)
-- 1 - distancia = similitud (rango: -1 a 1, donde 1 = idénticos)
SELECT content, source,
1 - (embedding <=> $1::vector) AS similarity
FROM documents
WHERE source = 'api-docs.md'
ORDER BY embedding <=> $1::vector
LIMIT 5;
# Uso con SQLAlchemy
from pgvector.sqlalchemy import Vector
from sqlalchemy import Column, Integer, String, Text, create_engine
from sqlalchemy.orm import declarative_base, Session
Base = declarative_base()
class Document(Base):
__tablename__ = "documents"
id = Column(Integer, primary_key=True)
content = Column(Text)
source = Column(String(255))
embedding = Column(Vector(1536))
# Buscar
with Session(engine) as session:
results = session.query(Document).order_by(
Document.embedding.cosine_distance(query_vector)
).limit(5).all()
Pros: Sin infraestructura adicional, SQL nativo, ACID, joins con datos relacionales Contras: Menos rendimiento a gran escala, sin búsqueda híbrida nativa
Comparativa de Vector Databases
| Característica | Pinecone | Weaviate | Qdrant | pgvector |
|---|---|---|---|---|
| Hosting | Serverless | Self/Cloud | Self/Cloud | Self (Postgres) |
| Lenguaje | Managed | Go | Rust | C (extensión) |
| Búsqueda Híbrida | No | Sí (BM25+vector) | Sparse vectors | No |
| Filtrado | Bueno | Bueno | Excelente | SQL |
| Escalabilidad | Excelente | Buena | Buena | Moderada |
| Latencia (p99) | ~50ms | ~30ms | ~20ms | ~100ms |
| Costo | $$$ | $$ (self-hosted) | $$ (self-hosted) | $ (ya tienes PG) |
| Ideal para | MVP rápido | Búsqueda híbrida | Alto rendimiento | Ya usas Postgres |
Estrategias de Indexación por Escala
< 100K docs → pgvector (simple, económico)
100K - 1M docs → Qdrant o Weaviate self-hosted
1M - 10M docs → Pinecone serverless o Qdrant Cloud
> 10M docs → Pinecone + sharding o solución custom
Mejores Prácticas
- Siempre incluir metadata rica — fuente, fecha, categoría, idioma
- Indexar con namespace/collections — separar por dominio
- Monitorear recall — medir calidad de búsqueda regularmente
- Quantización — reduce memoria 4x con pérdida mínima
- Backup regular — las vector DBs no son inmunes a fallos
- Dimensiones justas — no usar 3072 dims si 512 da similar recall
🧠 Preguntas de Repaso
1. ¿Cuál de las siguientes vector databases tiene la menor latencia p99 y está escrita en Rust?
- A) Pinecone (~50ms, managed)
- B) Weaviate (~30ms, Go)
- C) Qdrant (~20ms, Rust)
- D) pgvector (~100ms, C)
Respuesta: C) — Qdrant, escrita en Rust, ofrece la latencia p99 más baja (~20ms) entre las cuatro options comparadas. Su diseño en Rust le da alto rendimiento y seguridad de memoria.
2. ¿Qué ventaja única ofrece Weaviate sobre las demás vector databases comparadas?
- A) Es la más económica
- B) Tiene búsqueda híbrida nativa (vector + BM25 keyword) con parámetro alpha configurable
- C) Es la única que soporta GPU
- D) Tiene el mayor límite de documentos
Respuesta: B) — Weaviate ofrece búsqueda híbrida nativa combinando vector search con BM25 keyword search. El parámetro
alphacontrola el balance: 0 = keyword puro, 1 = vector puro, 0.7 = balance recomendado.
3. Para un proyecto con menos de 100,000 documentos, ¿cuál es la recomendación de vector database?
- A) Pinecone serverless por su escalabilidad
- B) pgvector, ya que aprovecha PostgreSQL existente y es la más económica
- C) Qdrant Cloud por su baja latencia
- D) Weaviate por su búsqueda híbrida
Respuesta: B) — Para menos de 100K documentos, pgvector es la recomendación porque aprovecha la infraestructura PostgreSQL existente, es la más económica ($), y a esa escala su latencia (~100ms) es aceptable.
4. En el algoritmo HNSW, ¿qué controla el parámetro M?
- A) El número máximo de resultados retornados
- B) El número de conexiones por nodo en el grafo: más M = mejor recall pero más memoria
- C) El tamaño del batch de indexación
- D) La distancia métrica utilizada (cosine, euclidean, etc.)
Respuesta: B) — El parámetro M en HNSW controla el número de conexiones por nodo en el grafo multi-capa. Valores más altos de M mejoran el recall (calidad de búsqueda) pero aumentan el consumo de memoria.