Búsqueda Semántica Vectorial y Otros Temas para Ganar Amigos y Enamorados
El panorama completo de búsqueda: exacta, difusa, semántica, híbrida — y cuándo combinarlas todas.
La búsqueda no es una sola cosa, y la búsqueda semántica no reemplaza al resto.
“Encontrar el usuario con email dan@example.com” y “encuéntrame artículos sobre depuración para ingenieros novatos” se describen ambos como búsqueda, pero tienen muy poco en común como problemas de ingeniería. El primero tiene una respuesta correcta y una búsqueda indexada en O(log n). El segundo no tiene respuesta correcta — solo relevancia — y requiere comprender lenguaje, intención y significado.
Los ingenieros más persuasivos a la hora de tomar decisiones sobre búsqueda — los que ganan los argumentos y construyen el sistema correcto — entienden todo el panorama. Saben qué herramienta usar y por qué, y pueden explicarlo con claridad.
Este artículo cubre la capa semántica: qué hace realmente la búsqueda vectorial, cuándo gana, y dónde debería apartarse. La versión útil no es “incrusta todo”. Es saber cuándo los vectores pertenecen junto a la búsqueda léxica, difusa y de coincidencia exacta en una arquitectura híbrida.
La mitad léxica y difusa del panorama — tsvector, pg_trgm, pg_search — está en Postgres Text Searching Guide 2026.
Términos en Resumen
Embedding (Incrustación) — Una lista densa de números de punto flotante producida por un modelo, que representa un fragmento de texto (o imagen, audio, etc.) como un punto en un espacio de alta dimensionalidad. El contenido semánticamente relacionado aterriza cerca; el contenido no relacionado aterriza lejos.
Búsqueda léxica — Búsqueda basada en coincidencia exacta de palabras y tokens. Rápida, determinista y correcta para términos conocidos. No entiende sinónimos, paráfrasis ni equivalentes entre idiomas.
Búsqueda semántica — Búsqueda basada en significado en lugar de tokens. Una consulta como “cómo manejo los timeouts” puede coincidir con un documento titulado “configuración de políticas de reintento” sin palabras compartidas, porque sus embeddings están geométricamente cerca.
Vector — Una lista de números. En contextos de búsqueda, la salida de un modelo de embedding. La “búsqueda vectorial” encuentra los vectores más cercanos a un vector consulta por distancia geométrica.
FTS (Full-Text Search) — La búsqueda léxica integrada de Postgres, impulsada por tsvector / tsquery. Tokeniza, lematiza e indexa texto para consultas por palabra clave. Fuerte para prosa y búsqueda de términos exactos; ciega al significado.
BM25 — Un algoritmo de ranking para búsqueda léxica (usado por Elasticsearch, Qdrant y otros). Puntúa resultados por frecuencia de términos ponderada por lo raro que es el término en el corpus. Mejor que la coincidencia de palabras clave pura; sigue siendo léxica.
HNSW (Hierarchical Navigable Small World) — El índice estándar de vecino más cercano aproximado para búsqueda vectorial. Construye un grafo de proximidad en capas para consultas de similitud rápidas y de alto recall. pgvector, Qdrant, Weaviate y la mayoría lo usan.
RRF (Reciprocal Rank Fusion) — Un algoritmo para fusionar listas de resultados ranked de múltiples sistemas de recuperación. Usa solo la posición de ranking — sin normalización de puntajes. Un resultado que aparece alto en ambas listas (FTS y vectorial) obtiene un puntaje combinado más fuerte que uno que domina solo una.
Qué Hace Realmente la Búsqueda Semántica
Los embeddings vectoriales convierten texto (o imágenes, audio, etc.) en una lista de números — un punto en un espacio de alta dimensionalidad. Un modelo de embedding se entrena para que el texto semánticamente relacionado aterrice cerca en ese espacio. “Perro” y “canino” quedan cerca. “Correr un maratón” y “correr un script de Python” quedan lejos a pesar de compartir una palabra.
La búsqueda de similitud en ese espacio encuentra documentos cuyo significado está más cerca del significado de la consulta, sin importar la superposición exacta de palabras.
Esto significa:
- “¿Cómo configuro los timeouts de solicitud?” puede coincidir con un artículo titulado “Configuración de límites de conexión y políticas de reintento” — sin palabras clave superpuestas, alta relevancia conceptual
- “Algo ligero para una noche de verano” puede coincidir con una recomendación de vino sin que aparezca ninguna palabra clave en la descripción del producto
- Una consulta en inglés puede coincidir con documentos relevantes en francés, español o japonés si el modelo de embedding fue entrenado en múltiples idiomas
La búsqueda léxica (tsvector, pg_trgm) no puede hacer nada de esto. Opera sobre palabras y caracteres, no sobre significado. Las herramientas no son intercambiables — resuelven problemas diferentes.
Cuándo pgvector Gana
Construir RAG. Retrieval-Augmented Generation (Generación Aumentada con Recuperación) recupera los fragmentos de documento cuyo significado está más cerca de la pregunta del usuario, y luego los pasa a un modelo de lenguaje como contexto. Este paso de recuperación es una operación vectorial. FTS perderá paráfrasis, sinónimos y coincidencias conceptuales que un fragmento relevante podría expresar de otra forma. La ventaja de pgvector sobre un almacén vectorial independiente: se ejecuta dentro de tu instancia de Postgres existente — sin servicio separado que desplegar, operar o sincronizar.
Los usuarios describen lo que quieren, no qué buscar. “Artículos sobre construir confianza como nuevo manager” no tiene palabras clave que aparezcan de forma fiable en los posts relevantes. “Un framework ligero para manejar efectos secundarios” puede no usar esas palabras exactas en la documentación. La búsqueda vectorial coincide con la intención, no con la ortografía.
Encontrar elementos similares. Productos relacionados, tickets de soporte similares, informes de bugs duplicados, artículos que también te pueden gustar. “Encontrar issues similares a este” es una búsqueda de vecino más cercano — incrustar el elemento, encontrar sus vecinos geométricos. Una advertencia importante: la búsqueda vectorial siempre devuelve resultados, incluso cuando nada es genuinamente similar. Para casos de deduplicación y recomendación, filtra por un umbral mínimo de similitud (por ejemplo, similitud coseno ≥ 0.80) para evitar mostrar coincidencias de baja confianza como si fueran significativas.
Deduplicación semántica. Antes de indexar contenido para RAG o búsqueda, a menudo necesitas identificar casi-duplicados en el corpus — artículos revisados varias veces, tickets de soporte presentados dos veces, entradas de base de conocimiento que se solapan significativamente. Incrusta los documentos y filtra por similitud coseno con umbral para marcar o fusionar casi-duplicados antes de que contaminen tu índice. Esto evita que la recuperación devuelva múltiples fragmentos casi idénticos y diluya la ventana de contexto.
Búsqueda multilingüe. Los modelos de embedding multilingües mapean contenido semánticamente equivalente entre idiomas en vectores cercanos. Una consulta en español para “perder peso” puede coincidir con un artículo en inglés sobre “sustainable weight loss habits” — sin tokens compartidos, mismo significado subyacente. FTS requiere configuración de diccionario por idioma y maneja mal las consultas entre idiomas. pg_trgm es independiente del idioma pero ortográfico, no semántico.
Configurar pgvector
Desde la instalación de la extensión hasta la consulta de similitud, la configuración son unas pocas sentencias SQL:
CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE documents ADD COLUMN embedding vector(1536);
-- HNSW suele ser el primer índice a probar para datasets de tamaño moderadoCREATE INDEX documents_embedding_idx ON documents USING hnsw (embedding vector_cosine_ops);
-- Consulta de búsqueda semánticaSELECT id, title, 1 - (embedding <=> $1::vector) AS similarityFROM documentsORDER BY embedding <=> $1::vectorLIMIT 10;<=> es distancia coseno. 1 - cosine_distance da la similitud coseno (1.0 = idéntico, 0.0 = ortogonal). Para ivfflat (la alternativa más antigua, más rápida de construir), usa lists = sqrt(row_count) como punto de partida.
Qué pgvector No Maneja Bien
- Coincidencia exacta de tokens — SKUs de producto, códigos de error, nombres de función.
ORD-12345no es semánticamente similar a nada. Una búsqueda basada en embedding puede devolverORD-12344o nada relevante. Usa FTS o un índice B-tree. - Nombres y sustantivos propios. El espacio de embedding organiza por significado, no por ortografía. El registro de usuario “Micheal Jordan” no necesariamente aterriza cerca de “Michael Jordan” en el espacio vectorial.
- Cadenas cortas donde la similitud a nivel de carácter importa más que el significado.
pg_trgmmaneja esto. - Consultas donde el término exacto debe aparecer. BM25 y FTS son más fiables para coincidencia de términos conocidos.
Búsqueda Híbrida: El Caso de Ambas
La documentación técnica es el ejemplo más claro donde ninguna herramienta basta sola.
Los usuarios que buscan “cómo configurar timeouts” necesitan coincidencia conceptual: un artículo titulado “Configuración de políticas de reintento y límites de conexión” no tiene palabras clave superpuestas pero es exactamente lo que necesitan.
Los mismos usuarios también buscan withRetry(), ECONNRESET y ERR_SOCKET_TIMEOUT. Estas cadenas exactas deben aparecer — la coincidencia semántica puede no encontrarlas de forma fiable, y un falso positivo (conceptualmente similar pero no la API correcta) es activamente engañoso.
La búsqueda vectorial maneja las consultas conceptuales. FTS maneja los términos exactos. Ninguna maneja bien ambas por sí sola.
La solución es búsqueda híbrida: ejecutar ambas y fusionar los resultados.
Reciprocal Rank Fusion
Reciprocal Rank Fusion (RRF) es el algoritmo estándar para combinar listas ranked de diferentes sistemas de recuperación. No requiere normalizar puntajes entre sistemas — solo usa posiciones de ranking. Un resultado que aparece alto en ambas listas obtiene un puntaje combinado más fuerte que uno que domina solo una.
WITH fts_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, query) DESC) AS rank FROM documents, to_tsquery('english', $1) query WHERE search_vector @@ query LIMIT 50),vector_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank FROM documents ORDER BY embedding <=> $2::vector LIMIT 50),rrf AS ( SELECT COALESCE(f.id, v.id) AS id, COALESCE(1.0 / (60 + f.rank), 0) + COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score FROM fts_results f FULL OUTER JOIN vector_results v ON f.id = v.id)SELECT d.id, d.title, rrf.rrf_scoreFROM rrfJOIN documents d ON d.id = rrf.idORDER BY rrf_score DESCLIMIT 10;El 60 en el denominador es la constante RRF. Valores más altos suavizan las diferencias de posición; valores más bajos las amplifican. El valor por defecto de 60 funciona bien para la mayoría de los tipos de contenido.
RRF evita el problema más difícil de normalizar ts_rank (un puntaje de frecuencia logarítmica) contra distancia coseno (una medida geométrica). No son comparables. RRF solo pregunta: “¿qué tan alto apareció este resultado en cada lista?”
Búsqueda Híbrida con Trigramas También
Para búsqueda orientada al usuario sobre contenido mixto — donde los usuarios pueden buscar un nombre de persona, un concepto o un término exacto en la misma sesión — la fusión de tres capas maneja todos los casos:
WITH trgm_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY similarity(title, $1) DESC) AS rank FROM documents WHERE title % $1 LIMIT 50),fts_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, to_tsquery('english', $1)) DESC) AS rank FROM documents WHERE search_vector @@ to_tsquery('english', $1) LIMIT 50),vector_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank FROM documents ORDER BY embedding <=> $2::vector LIMIT 50),rrf AS ( SELECT COALESCE(t.id, f.id, v.id) AS id, COALESCE(1.0 / (60 + t.rank), 0) + COALESCE(1.0 / (60 + f.rank), 0) + COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score FROM trgm_results t FULL OUTER JOIN fts_results f ON t.id = f.id FULL OUTER JOIN vector_results v ON COALESCE(t.id, f.id) = v.id)SELECT d.id, d.title, rrf.rrf_scoreFROM rrfJOIN documents d ON d.id = rrf.idORDER BY rrf_score DESCLIMIT 10;Esto maneja: coincidencias difusas de nombres (trigramas), coincidencias exactas de palabras clave (FTS), y consultas conceptuales (vectorial). Una única caja de búsqueda puede servir los tres tipos de intención del usuario.
Arquitecturas Híbridas Multicapa
Las aplicaciones reales rara vez tienen una única superficie de búsqueda. Tienen múltiples, cada una con una necesidad diferente:
| Superficie | Qué consulta el usuario | Capas recomendadas |
|---|---|---|
| Búsqueda de blog / documentación | Palabras clave + conceptos | FTS + pgvector (RRF) |
| Búsqueda de nombres de usuario/cliente | Nombres con erratas | pg_trgm |
| Búsqueda de productos | Nombres, descripciones, “similar a” | pg_trgm + FTS + pgvector |
| Deduplicación de tickets de soporte | ”Issues similares a este” | Solo pgvector |
| Búsqueda interna de SKU/pedido | Identificadores exactos | Índice B-tree |
| RAG sobre base de conocimiento grande | Preguntas en lenguaje natural | pgvector (documentos fragmentados) |
| E-commerce “también te puede gustar” | Similitud comportamental + semántica | pgvector |
| Autocompletado | Prefijo, tolerante a erratas | pg_trgm |
Estos no son hipotéticos. La mayoría de aplicaciones con mucho contenido necesitan al menos dos superficies de búsqueda distintas con formas de consulta diferentes. La tentación es elegir un enfoque y usarlo en todas partes — normalmente búsqueda vectorial ahora, porque es la opción de moda. Eso lleva a embeddings caros para problemas donde un índice de trigramas habría sido más rápido, más barato y más correcto.
La Regla de Oro
Añade una capa cuando aparece un modo de fallo que la capa actual no puede arreglar:
- Los usuarios se quejan de que las erratas no coinciden → añade
pg_trgm - Los usuarios buscan por concepto y pierden resultados relevantes → añade pgvector
- Los usuarios buscan símbolos o códigos exactos y obtienen resultados conceptuales → añade FTS o revisa si estás dependiendo demasiado de la búsqueda vectorial
- La latencia se convierte en un problema → evalúa pre-filtrado, índices aproximados, o un almacén dedicado
Si Necesitas un Almacén Vectorial Dedicado
pgvector maneja mucha búsqueda de aplicación antes de que necesites otra base de datos. El límite aproximado depende del conteo de vectores, configuración de índices, tasa de escritura, filtros, hardware y concurrencia, así que trata cualquier regla de “menos de 10M vectores” como una suposición inicial a benchmarkear, no como un límite del producto. Cuando realmente lo superas — concurrencia muy alta, requisitos de latencia p99 muy bajos, miles de millones de vectores, o necesidades serias de aislamiento multi-tenant — el panorama de bases de datos vectoriales dedicadas es amplio y vale la pena entenderlo.
Qué Significan Realmente las Columnas de la Matriz
Búsqueda híbrida significa que la búsqueda de palabras clave BM25 y la similitud vectorial se ejecutan en una sola consulta, fusionadas mediante RRF. Sin ella, o eliges un modo de búsqueda o fusionas dos consultas tú mismo.
Vectores dispersos van más allá de BM25. Un vector SPLADE disperso tiene ~30.000 dimensiones (una por término de vocabulario), ~98% de ceros. Las posiciones no nulas te dicen qué términos importan y cuánto. Una consulta para “dogs” también pondera “canine” y “pet” — precisión a nivel BM25 más expansión de términos dentro de un índice vectorial. Si esta columna es falsa, necesitas una capa FTS separada para consultas de términos exactos.
# SPLADE: ~30.000 dims, ~60 no nulos — solo dispara el vocabulario relevantedef encode_splade(text: str) -> dict: tokens = tokenizer(text, return_tensors="pt", truncation=True, max_length=512) with torch.no_grad(): output = model(**tokens) vec = torch.log1p(torch.relu(output.logits)).max(dim=1).values.squeeze() return {"indices": vec.nonzero().squeeze().tolist(), "values": vec[vec != 0].tolist()}SQL / similar a SQL trata realmente sobre filtrado. La búsqueda vectorial sin filtrado es una demo. Todavía necesitas alcance de tenant, rangos de fechas, permisos y filtros de categoría. El SQL completo (pgvector, LanceDB) expresa esto junto a tus joins existentes. Las bases de datos de propósito propio usan objetos de filtro JSON (Qdrant, Pinecone), un DSL de consultas (Elasticsearch, Milvus), o GraphQL (Weaviate). Funcionan; SQL se vuelve más atractivo a medida que la lógica de filtrado se complica.
-- pgvector: la similitud vectorial es solo otra expresiónSELECT id, title, 1 - (embedding <=> $1) AS scoreFROM documentsWHERE tenant_id = $2 AND category = ANY($3::text[]) AND created_at > NOW() - INTERVAL '90 days'ORDER BY embedding <=> $1LIMIT 10;# Qdrant: filtro equivalente como objeto Python — mismo resultado, más ceremoniaresults = client.query_points( collection_name="documents", query=query_embedding, query_filter=models.Filter(must=[ models.FieldCondition(key="tenant_id", match=models.MatchValue(value=tenant_id)), models.FieldCondition(key="category", match=models.MatchAny(any=categories)), models.FieldCondition(key="created_at", range=models.DatetimeRange(gte=cutoff)), ]), limit=10,)Multimodal nativo significa que la base de datos incluye modelos de embedding para contenido no textual. Le pasas una URL de imagen cruda; ella se encarga de la vectorización. La mayoría de las bases de datos son agnósticas al embedding — tú eres dueño del pipeline de embedding. Marqo y Weaviate (vía módulos CLIP/ImageBind) cierran este bucle.
# Marqo: POST imágenes crudas, consulta con texto — sin paso de embedding externomq.index("products").add_documents( [{"id": "shoe-001", "image": "https://cdn.example.com/shoes/001.jpg"}], tensor_fields=["image"])results = mq.index("products").search(q="lightweight shoes for summer")# Devuelve shoe-001 a pesar de cero superposición de palabras clave — CLIP maneja la coincidencia cross-modalÍndice basado en disco es una palanca de coste. Los índices HNSW en RAM pueden requerir varios GB de RAM por millón de vectores de 1536 dimensiones una vez contados los vectores crudos, la sobrecarga del grafo y los metadatos. Las alternativas nativas en disco (Milvus DiskANN, Elasticsearch DiskBBQ, el formato Lance de LanceDB, el tier de almacenamiento de objetos de Turbopuffer) suelen intercambiar algo de latencia de consulta por menor coste de infraestructura. Para cargas de trabajo RAG donde la latencia del modelo ya domina, ese intercambio suele merecer la pena benchmarkear.
Dimensiones máximas es una migración oculta en tu arquitectura. text-embedding-3-large usa 3072 dims, Jina v3 puede emitir embeddings más grandes, y los modelos de investigación siguen subiendo. Algunos servicios gestionados publican límites duros de dimensión; otros documentan límites altos o ningún límite práctico para modelos de embedding típicos. Revisa la documentación actual antes de comprometerte. Elige algo con margen; migrar un índice vectorial porque alcanzaste un techo de dimensiones es un sprint doloroso.
Verificado por última vez contra la documentación pública y las páginas de producto el 8 de mayo de 2026. Trata la tabla de abajo como una ayuda de decisión, no como un sustituto de verificar límites actuales, precios y flags de función de servicios gestionados.
El Panorama
| Base de datos | Despliegue | Licencia | Búsqueda Híbrida | Vectores Dispersos | SQL / similar a SQL | Multimodal | Índice en Disco | Dims Máx | Punto Fuerte |
|---|---|---|---|---|---|---|---|---|---|
| pgvector | Self-host / gestionado (Supabase, Neon, RDS) | OSS (PostgreSQL) | Manual (RRF vía SQL) | ❌ | ✅ SQL completo | ❌ | ✅ HNSW en disco | 16.000 almacenamiento; 2.000 indexado vector | Ya en Postgres; conteo moderado de vectores |
| Qdrant | Self-host / Cloud | Apache 2.0 | ✅ BM25 nativo | ✅ Soporte maduro | ❌ (REST/gRPC) | ❌ | ✅ | 65.535 | Consultas filtradas a escala; metadatos complejos |
| Weaviate | Self-host / Cloud | BSD 3 | ✅ BM25 nativo + RRF | ✅ | ❌ (GraphQL / gRPC) | ✅ vía módulos | ✅ | 65.535 | Patrones de acceso GraphQL; vectorización integrada |
| Pinecone | Solo Cloud | Propietaria | ✅ (añadido 2024) | ✅ | ❌ | ❌ | ✅ (serverless) | 20.000 | Simplicidad gestionada; sin equipo de ops |
| Milvus / Zilliz | Self-host / Cloud (Zilliz) | Apache 2.0 | ✅ Nativo | ✅ | ✅ Similar a SQL (Milvus Query Language) | ✅ | ✅ DiskANN | 32.768 | Escala de miles de millones; enterprise on-prem |
| Chroma | Embedded / self-host | Apache 2.0 | ❌ | ❌ | ❌ | ❌ | ❌ | 65.535 | Solo dev local y prototipado |
| LanceDB | Embedded / Cloud | Apache 2.0 | ✅ | ❌ | ✅ SQL vía DataFusion | ✅ Nativo | ✅ (formato Lance) | Ilimitado | Edge / serverless; lakehouse multimodal |
| Orama | Embedded / Cloud | Apache 2.0 | ✅ Texto completo + vector | ❌ | ❌ | ❌ | ❌ | Variable | Apps JS/edge; búsqueda ligera de sitio/app |
| Turbopuffer | Solo Cloud (serverless) | Propietaria | ✅ BM25 + vector | ❌ | ❌ | ❌ | ✅ (almacenamiento de objetos) | 16.000 | SaaS multi-tenant; millones de namespaces |
| Elasticsearch | Self-host / Elastic Cloud | SSPL / AGPLv3 | ✅ RRF + ELSER disperso | ✅ (ELSER) | ✅ Query DSL | ❌ | ✅ DiskBBQ | 4.096 | Ya en stack Elastic; búsqueda híbrida enterprise |
| OpenSearch | Self-host / AWS gestionado | Apache 2.0 | ✅ RRF + Neural Search | ✅ | ✅ Query DSL | ❌ | ✅ FAISS + HNSW | 16.000 | Nativo AWS; alternativa open-source a Elastic |
| Vespa | Self-host / Cloud | Apache 2.0 | ✅ Nativo | ✅ Tensores / ranking léxico | ✅ YQL | ✅ Tensores | ✅ | Efectivamente ilimitado | Sistemas de búsqueda + ranking + recomendación |
| ClickHouse | Self-host / Cloud | Apache 2.0 | Manual | ❌ | ✅ SQL completo | ❌ | ✅ Columnar + HNSW | Variable | Analítica/logs con búsqueda vectorial junto a OLAP |
| MongoDB Atlas | Cloud / self-host | SSPL | ✅ Integrado | ❌ | ✅ MQL + agregación | ❌ | ✅ HNSW | 8.192 | Ya en MongoDB; documento + vector en uno |
| Redis (VSS) | Self-host / Redis Cloud | RSALv2 / SSPL | ✅ (RediSearch) | ✅ | ❌ | ❌ | ❌ Solo RAM | 32.768 | Ultra-baja latencia; búsqueda vectorial en capa de caché |
| Marqo | Cloud / self-host | Apache 2.0 | ✅ | ❌ | ❌ | ✅ Enfoque nativo | ✅ | Variable | Multimodal end-to-end: imagen + texto + vídeo |
Unas Cuantas Cosas que No Caben en la Tabla
Multi-tenencia de Turbopuffer está construida alrededor de conteos muy altos de namespaces. Su posicionamiento público y casos de cliente enfatizan cargas de trabajo como el corpus grande y de alto uso de namespaces de Notion. Si cada usuario u organización necesita búsqueda vectorial aislada, esa arquitectura puede cambiar la economía, pero sigue benchmarkando tu propia forma de tenencia.
Modo embedded de LanceDB es lo más cercano a “SQLite para búsqueda vectorial.” Se ejecuta en proceso, no requiere servidor, y funciona en Lambda, Cloudflare Workers y entornos edge. El formato columnar Lance hace que la operación embedded sea práctica a escala real.
Chroma es más fuerte en dev/test y despliegues de app pequeños. Si apuntas a corpus muy grandes, HA, operación pesada en disco, o búsqueda híbrida de primera clase, evalúa un almacén orientado a producción antes de promover el prototipo a infraestructura.
Vespa es a lo que acudes cuando la recuperación es solo la mitad del producto. Combina recuperación léxica, búsqueda de vecino más cercano, tensores, expresiones de ranking, agrupamiento y serving online. Ese poder es real, pero también lo es la complejidad operativa y de modelado. Encaja más con equipos de búsqueda/recomendación que con “añadir búsqueda semántica a mi app CRUD.”
ClickHouse pertenece a la conversación cuando la búsqueda está adjunta a analytics. Si tu fuente de verdad son eventos, logs, traces o métricas, ClickHouse mantiene distancia vectorial, filtrado, agregación e indexación de texto completo seria en un único motor SQL. No es una base de datos vectorial de propósito propio, pero suele ser la respuesta aburrida-correcta para recuperación analítica.
Los vectores dispersos son cómo obtienes coincidencia de palabras clave de calidad BM25 dentro de un índice vectorial — sin ejecutar un motor de texto completo separado. Qdrant y Elasticsearch tienen implementaciones especialmente maduras aquí. Si la búsqueda híbrida es crítica y una arquitectura de dos sistemas es un deal-breaker, el soporte de vectores dispersos es lo que hay que buscar.
Elegir Cuando Has Superado pgvector
- Producto SaaS con aislamiento por tenant → Turbopuffer
- Filtrado complejo de metadatos a escala → Qdrant
- Ya en stack Elastic/ELK → Elasticsearch con DiskBBQ
- Equipo AWS que quiere open-source → OpenSearch
- Plataforma de búsqueda/recomendación con necesidades serias de ranking → Vespa
- Analytics, observabilidad, búsqueda de logs/eventos → ClickHouse
- Escala de miles de millones on-prem / self-hosted → Milvus
- Edge / serverless / multimodal → LanceDB
- App JS pequeña, sitio de docs, o UX de búsqueda edge-native → Orama
- Cero ops, el coste es secundario → Pinecone
- Multimodal primero (imágenes, vídeo, audio) → Marqo
- Ya en MongoDB → Atlas Vector Search
- Ya en Postgres, necesitas más margen → Supabase Vector o Neon (ambos pgvector gestionado, con mejor tooling)
La Única Cosa que No Debes Hacer
No uses búsqueda vectorial como búsqueda difusa de texto para cosas que tienen respuestas correctas.
“Encuéntrame el usuario con email dan@example.com” no es un problema de búsqueda vectorial. “Encuentra la orden con ID ORD-12345” tampoco lo es. Incrustar ORD-12345 y buscar por similitud coseno devolverá algo — pero puede ser incorrecto. Un identificador tiene una respuesta correcta. Una coincidencia aproximada sobre un identificador es un bug.
La búsqueda vectorial devuelve lo más similar en tu dataset, incluso cuando nada es realmente relevante. No sabe cuándo no existe una buena respuesta. Eso está bien para documentos relacionados. Es un problema serio para búsqueda exacta de registros, donde una respuesta incorrecta pero segura es peor que un resultado vacío.
Lo mismo aplica en la otra dirección: no uses FTS para consultas donde el usuario está describiendo un concepto. “artículos sobre tomar decisiones difíciles bajo incertidumbre” no contiene palabras clave fiables. FTS devolverá ruido o nada. Usa la herramienta correcta para la forma de consulta.
La Imagen Completa
La mayoría de sistemas de búsqueda en producción necesitan más de una capa:
pg_trgmpara nombres, erratas, autocompletado- FTS /
pg_searchpara búsqueda de prosa basada en palabras clave - pgvector para consultas semánticas y conceptuales
- Fusión RRF para superficies donde los usuarios mezclan tipos de consulta
- Índices regulares para identificadores exactos, filtros y listas ordenadas
Estos no son herramientas competidoras. Son complementarias. Un sistema de búsqueda bien construido elige la capa correcta para cada forma de consulta — y cuando las formas de consulta se solapan, ejecuta múltiples capas y fusiona los resultados.
Los equipos que construyen buenas funcionalidades de búsqueda entienden todo el stack. Los que no, eligen una base de datos vectorial, incrustan todo, y se preguntan por qué las búsquedas exactas a veces devuelven el registro equivocado.