Cómo elegir una base de datos vectorial para aplicaciones de IA
Compara Pinecone, Weaviate, Qdrant, Milvus y pgvector para IA: estrategias de indexación, rendimiento, filtrado y complejidad operativa.

Toda aplicación de IA que utiliza embeddings necesita una base de datos vectorial, pero la elección entre un servicio gestionado, una base de datos vectorial independiente y una extensión de Postgres afecta drásticamente tu arquitectura, la carga operativa y la capacidad de combinar la búsqueda vectorial con consultas tradicionales.
El mercado se ha llenado de opciones. Cada una plantea compensaciones distintas entre rendimiento de consultas, capacidades de filtrado, escalabilidad y simplicidad operativa. Elegir la opción equivocada genera problemas de migración que se agravan a medida que crece tu colección de embeddings.
Entendiendo los tipos de índices vectoriales
El principal factor diferenciador entre bases de datos vectoriales es su estrategia de indexación. De ella dependen la velocidad de consulta, la precisión del recall, el uso de memoria y el tiempo de construcción del índice.
// ❌ Brute-force search — O(n) per query
function bruteForceSearch(
query: number[],
vectors: number[][],
k: number
): number[] {
const distances = vectors.map((v, i) => ({
index: i,
distance: cosineSimilarity(query, v),
}));
distances.sort((a, b) => b.distance - a.distance);
return distances.slice(0, k).map((d) => d.index);
}
// Works for < 10K vectors. Unusable at scale.// ✅ HNSW index — O(log n) per query with high recall
interface HNSWConfig {
// Number of connections per node in the graph
// Higher = better recall, more memory
M: number;
// Size of the dynamic candidate list during construction
// Higher = better index quality, slower build
efConstruction: number;
// Size of candidate list during search
// Higher = better recall, slower search
efSearch: number;
}
const balancedConfig: HNSWConfig = {
M: 16,
efConstruction: 200,
efSearch: 100,
};
const highRecallConfig: HNSWConfig = {
M: 32,
efConstruction: 400,
efSearch: 200,
// 99%+ recall, ~2x memory vs balanced
};
const lowLatencyConfig: HNSWConfig = {
M: 12,
efConstruction: 100,
efSearch: 50,
// ~95% recall, fastest queries
};
// IVF index — partition-based approach
interface IVFConfig {
// Number of partitions (clusters)
nlist: number;
// Number of partitions to search per query
nprobe: number;
}
// IVF suits large datasets where memory is constrained
// HNSW suits workloads requiring consistent low latencyLos grafos HNSW (Hierarchical Navigable Small World) predominan porque ofrecen la mejor relación entre recall y latencia para la mayoría de las cargas de trabajo. IVF (Inverted File Index) usa menos memoria, pero requiere ajustar el número de particiones y de sondas (probes). La cuantización de producto (PQ) comprime los vectores para conjuntos de datos masivos a costa de cierta pérdida de recall.
Comparación de opciones de bases de datos vectoriales
Cada base de datos ocupa un punto distinto en el espacio de compensaciones entre simplicidad operativa, funciones de consulta y escalabilidad.
interface VectorDBComparison {
name: string;
type: "managed" | "self-hosted" | "extension";
indexTypes: string[];
filtering: "pre-filter" | "post-filter" | "hybrid";
scalarFields: boolean;
multiTenancy: "native" | "namespace" | "manual";
operationalComplexity: "low" | "medium" | "high";
}
const databases: VectorDBComparison[] = [
{
name: "Pinecone",
type: "managed",
indexTypes: ["proprietary"],
filtering: "hybrid",
scalarFields: true,
multiTenancy: "namespace",
operationalComplexity: "low",
},
{
name: "Weaviate",
type: "self-hosted",
indexTypes: ["HNSW", "flat"],
filtering: "pre-filter",
scalarFields: true,
multiTenancy: "native",
operationalComplexity: "medium",
},
{
name: "Qdrant",
type: "self-hosted",
indexTypes: ["HNSW"],
filtering: "hybrid",
scalarFields: true,
multiTenancy: "manual",
operationalComplexity: "medium",
},
{
name: "pgvector",
type: "extension",
indexTypes: ["IVFFlat", "HNSW"],
filtering: "pre-filter",
scalarFields: true,
multiTenancy: "manual",
operationalComplexity: "low",
},
];pgvector: cuando tus datos ya viven en Postgres
Para aplicaciones en las que la búsqueda vectorial es solo una función más entre muchas, y en las que ya usas Postgres, pgvector elimina por completo una dependencia de infraestructura.
-- Setup pgvector
CREATE EXTENSION IF NOT EXISTS vector;
-- Create table with embedding column
CREATE TABLE documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
content TEXT NOT NULL,
embedding vector(1536),
category TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
tenant_id UUID NOT NULL
);
-- HNSW index for cosine similarity
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
-- Combined vector + scalar query — single database
SELECT
id,
title,
1 - (embedding <=> $1::vector) AS similarity
FROM documents
WHERE tenant_id = $2
AND category = $3
AND created_at > NOW() - INTERVAL '90 days'
ORDER BY embedding <=> $1::vector
LIMIT 10;// TypeScript integration with pgvector
import { Pool } from "pg";
interface SearchResult {
id: string;
title: string;
similarity: number;
}
async function semanticSearch(
pool: Pool,
queryEmbedding: number[],
tenantId: string,
options: {
category?: string;
limit?: number;
minSimilarity?: number;
} = {}
): Promise<SearchResult[]> {
const { category, limit = 10, minSimilarity = 0.7 } =
options;
const embeddingStr = `[${queryEmbedding.join(",")}]`;
const conditions = [
"tenant_id = $2",
`1 - (embedding <=> $1::vector) >= $3`,
];
const params: unknown[] = [
embeddingStr,
tenantId,
minSimilarity,
];
if (category) {
conditions.push(`category = $${params.length + 1}`);
params.push(category);
}
const query = `
SELECT id, title,
1 - (embedding <=> $1::vector) AS similarity
FROM documents
WHERE ${conditions.join(" AND ")}
ORDER BY embedding <=> $1::vector
LIMIT ${limit}
`;
const result = await pool.query(query, params);
return result.rows;
}La ventaja es la consistencia transaccional: puedes insertar un documento y su embedding en la misma transacción, consultar vectores con joins y filtros SQL estándar, y gestionar todo con las herramientas de Postgres que ya usas.
Bases de datos vectoriales dedicadas: cuando la escala lo exige
Cuando tienes millones de vectores, necesitas latencias inferiores a 10 ms o requieres funciones como actualizaciones de índice en tiempo real con alto throughput, una base de datos vectorial dedicada toma la delantera.
// Qdrant client example — dedicated vector DB features
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({
url: "http://localhost:6333",
});
// Create collection with optimized settings
await client.createCollection("documents", {
vectors: {
size: 1536,
distance: "Cosine",
},
optimizers_config: {
indexing_threshold: 20000,
memmap_threshold: 50000,
},
hnsw_config: {
m: 16,
ef_construct: 200,
full_scan_threshold: 10000,
},
});
// Search with payload filtering
const results = await client.search("documents", {
vector: queryEmbedding,
filter: {
must: [
{
key: "tenant_id",
match: { value: tenantId },
},
{
key: "category",
match: { any: ["engineering", "ai"] },
},
],
must_not: [
{
key: "status",
match: { value: "archived" },
},
],
},
limit: 10,
with_payload: true,
score_threshold: 0.7,
});Marco de decisión
La elección se reduce a tu escala, tus patrones de consulta y tu capacidad operativa.
interface DecisionInput {
vectorCount: number;
queryLatencyTarget: string;
existingDatabase: string;
needsJoins: boolean;
teamSize: number;
budget: string;
}
function recommendDatabase(
input: DecisionInput
): string {
// Under 1M vectors + already using Postgres + needs joins
if (
input.vectorCount < 1_000_000 &&
input.existingDatabase === "postgres" &&
input.needsJoins
) {
return "pgvector — minimal operational overhead, " +
"transactional consistency, SQL joins";
}
// Over 1M vectors or sub-10ms latency requirement
if (
input.vectorCount >= 1_000_000 ||
input.queryLatencyTarget === "sub-10ms"
) {
if (input.teamSize <= 3) {
return "Pinecone — managed service, no ops burden";
}
return "Qdrant/Weaviate — self-hosted for control " +
"and cost efficiency at scale";
}
// Default: start simple
return "pgvector — start here, migrate when you " +
"hit measured performance limits";
}Puntos clave
Los índices HNSW ofrecen la mejor relación entre recall y latencia para la mayoría de las cargas de trabajo, pero ajustar parámetros como M, efConstruction y efSearch supone intercambiar directamente memoria y tiempo de construcción por calidad de consulta. pgvector elimina una dependencia de infraestructura cuando la búsqueda vectorial es solo una función más entre muchas: obtienes consistencia transaccional, joins SQL y las herramientas de Postgres que ya conoces. Las bases de datos vectoriales dedicadas, como Qdrant y Weaviate, toman la delantera a partir de millones de vectores, con requisitos de latencia inferiores a 10 ms, actualizaciones de índice en tiempo real y multi-tenancy nativa. La estrategia de filtrado importa: el pre-filtrado aplica condiciones escalares antes de la búsqueda vectorial (preciso, pero puede omitir los vectores más cercanos), el post-filtrado busca en todos los vectores y luego filtra los resultados (puede devolver menos resultados de los solicitados), y los enfoques híbridos equilibran ambos. Empieza con pgvector si ya usas Postgres y tienes menos de un millón de vectores: la migración a una base de datos vectorial dedicada es sencilla cuando alcanzas límites de rendimiento medidos, pero migrar prematuramente añade complejidad operativa sin ningún beneficio.


