Cómo funciona la búsqueda de texto completo
La función de búsqueda de texto completo de PostgreSQL responde a una pregunta engañosamente simple: ¿contiene este documento estas palabras, en algún sentido lingüístico significativo, y qué tan bien coincide en comparación con otros documentos?
Bajo el capó, esa pregunta se responde mediante un pipeline de búsqueda de texto que analiza, clasifica y normaliza palabras antes de que ocurra cualquier coincidencia o clasificación, que es lo que separa la búsqueda de texto completo de una simple comparación de cadenas.
Esta página construye el modelo conceptual detrás de ese pipeline: cómo el texto sin procesar se convierte en un tsvector, por qué la coincidencia utiliza lemas en lugar de subcadenas y cómo la clasificación convierte una coincidencia booleana en un conjunto de resultados ordenado.
Resumen
- La búsqueda de texto completo convierte el texto del documento y el texto de la consulta en unidades lingüísticas normalizadas llamadas lemas, y luego coincide y clasifica los documentos basándose en esas unidades en lugar de caracteres sin procesar.
- Por qué importa: Los usuarios esperan que "corriendo", "corre" y "corrió" coincidan con una búsqueda de "correr", y la coincidencia de cadenas simple no puede hacer eso sin un paso explícito de normalización lingüística.
- Conceptos clave: parser, token, lexeme, dictionary,
tsvector,tsquery,ts_rank,ts_rank_cd. - Cuándo usar: Utilice este modelo mental cuando una consulta de búsqueda no encuentre documentos que espera que encuentre, o cuando el orden de clasificación parezca incorrecto y necesite razonar sobre por qué.
- Limitaciones / Compensaciones: La misma normalización que hace que la búsqueda sea tolerante a las formas de las palabras también la hace ciega a las subcadenas exactas, por lo que la búsqueda de texto completo no puede reemplazar a
LIKEpara coincidencias de prefijos o fragmentos de código. - Temas relacionados: Mecánica de
tsvector/tsquery, internos del índice GIN, búsqueda híbrida de palabras clave más vector, FTS de Postgres frente a Elasticsearch.
Fundamentos
La búsqueda de texto completo comienza con un parser, un componente que divide el texto sin procesar en un flujo de tokens clasificados en lugar de una lista plana de palabras.
El parser no solo divide por espacios en blanco; reconoce categorías como palabras, números, direcciones de correo electrónico, URL y compuestos con guiones, y etiqueta cada token con su categoría.
Cada categoría de token se enruta a través de un diccionario, un paso de normalización enchufable que puede reducir una palabra a su raíz, descartarla como una palabra vacía (stop word) o plegarla a una ortografía canónica.
La salida de esa etapa de diccionario es un lema (lexeme), la unidad normalizada que la búsqueda de texto completo realmente indexa y coincide, que es por lo que "bases de datos" y "base de datos" se normalizan al mismo lema bajo la configuración en inglés.
Un tsvector es simplemente la colección ordenada y deduplicada de lemas producidos para una pieza de texto, que opcionalmente lleva etiquetas de peso e información de posición para cada lema.
Un tsquery pasa por el mismo pipeline de análisis y normalización, que es exactamente por lo que una cadena sin procesar construida a mano puede producir una consulta que nunca coincide con nada significativo.
La coincidencia en sí, expresada con el operador @@, es una prueba booleana de si la expresión de lemas de la consulta se satisface con los lemas presentes en el tsvector del documento.
Dado que la coincidencia ocurre en lemas normalizados en lugar de texto sin procesar, la búsqueda de texto completo es fundamentalmente una operación lingüística, no una operación de comparación de caracteres.
Mecánica e Interacciones
El pipeline de análisis y normalización se ejecuta en un orden estricto cada vez que se construye un tsvector o tsquery, y comprender ese orden explica la mayoría de las preguntas de "por qué esto no coincidió".
Primero, el parser tokeniza y clasifica el texto sin procesar en tokens tipificados; segundo, cada tipo de token se mapea a una lista ordenada de diccionarios a través de una configuración de búsqueda de texto como english o simple; tercero, el primer diccionario de esa lista que reconoce el token lo normaliza, lo descarta como palabra vacía o lo pasa al siguiente diccionario de la cadena.
Es por esto que cambiar las configuraciones cambia los resultados sin cambiar una sola fila de datos: simple omite por completo la derivación (stemming) y es adecuado para códigos de producto, mientras que english deriva agresivamente y se adapta a la prosa.
Las posiciones también importan, porque un tsvector retiene dónde ocurrió cada lema en el texto fuente, que es lo que hace posibles los operadores de frases como <-> en el momento de la consulta.
Los pesos se superponen a las posiciones: un lema puede etiquetarse como A, B, C o D, típicamente para marcar que provino de un título frente a un cuerpo frente a metadatos, y las funciones de clasificación leen esas etiquetas para sesgar las puntuaciones.
-- Las mismas palabras, dos configuraciones, dos conjuntos de lemas diferentes
SELECT to_tsvector('simple', 'Running the databases');
-- 'databases':3 'running':1 'the':2
SELECT to_tsvector('english', 'Running the databases');
-- 'databas':3 'run':1La clasificación es donde la búsqueda de texto completo pasa de un sí/no booleano a un conjunto de resultados ordenado, y PostgreSQL ofrece dos funciones de clasificación basadas en diferentes teorías de relevancia.
ts_rank es esencialmente una puntuación de frecuencia de términos ponderada: cuenta cuántos lemas de la consulta coincidieron, tiene en cuenta sus pesos de posición y produce una puntuación que trata las coincidencias en cualquier parte del documento de manera aproximadamente igual.
ts_rank_cd, la variante de densidad de cobertura, recompensa además los documentos donde los lemas coincidentes aparecen juntos, con la teoría de que las coincidencias agrupadas indican un pasaje más enfocado y relevante.
Ninguna de las funciones considera la normalización de la longitud del documento o la rareza de los términos en todo el corpus como lo hace el algoritmo BM25 de un motor de recuperación de información completo, lo que es una compensación de simplicidad deliberada a cambio de ejecutarse completamente dentro de SQL.
Consideraciones Avanzadas y Aplicaciones
El operador de coincidencia @@ por sí solo obligaría a un escaneo secuencial de tsvector de cada fila, por lo que la búsqueda de texto completo se basa en un índice GIN para hacer que las coincidencias sean rápidas a escala, y el truco conceptual es el mismo que utiliza cada motor de búsqueda de índice invertido.
Un índice GIN invierte la relación entre documentos y lemas: en lugar de almacenar "este documento contiene estos lemas", almacena "este lema aparece en estos documentos", lo que convierte una consulta de coincidencia en una búsqueda rápida en lugar de un escaneo por fila.
Esa inversión es precisamente lo que distingue la búsqueda de texto completo de LIKE '%término%' o una expresión regular, ambas deben inspeccionar los bytes reales de cada fila candidata porque ninguna tiene una estructura preconstruida que conecte términos con filas.
LIKE y regex son exactos en subcadenas y sensibles a mayúsculas/minúsculas/acentos por defecto, mientras que la búsqueda de texto completo está lingüísticamente normalizada y es consciente de la posición, por lo que las dos técnicas resuelven problemas genuinamente diferentes y a menudo se combinan en lugar de elegirse exclusivamente.
Los motores de búsqueda externos como Elasticsearch se basan en los mismos conceptos de índice invertido y frecuencia de términos, pero agregan un ecosistema de analizadores mucho más rico, fragmentación distribuida y modelos de relevancia más sofisticados, a costa de ejecutar y operar un segundo sistema fuera de la base de datos transaccional.
El punto de decisión práctico suele ser no "qué técnica es más inteligente", sino "el techo de relevancia y escala de un índice invertido dentro de la base de datos satisface las necesidades del producto sin pagar por un segundo clúster".
| Enfoque | Fortaleza | Debilidad | Mejor ajuste |
|---|---|---|---|
Búsqueda de texto completo (tsvector/GIN) | Normalización lingüística, consistencia transaccional, sin sistema adicional | Menor ajuste de relevancia que los motores de búsqueda dedicados | Búsqueda de prosa dentro de una aplicación existente basada en Postgres |
LIKE / regex | Coincidencia exacta de subcadenas o patrones, sin sorpresas de normalización | Sin derivación, sin clasificación, bajo rendimiento sin índices especializados | Códigos, identificadores, búsquedas de fragmentos exactos |
| Motor de búsqueda externo (Elasticsearch/OpenSearch) | Analizadores ricos, escala distribuida, ajuste avanzado de relevancia | Clúster separado para operar, retraso de consistencia de escritura dual o CDC | Grandes corpus con infraestructura de búsqueda dedicada |
| Híbrido (FTS + embeddings) | Combina precisión léxica con recuperación semántica | Dos sistemas de puntuación para reconciliar y ajustar juntos | Búsqueda que también debe capturar sinónimos y paráfrasis |
Conceptos erróneos comunes
- "La búsqueda de texto completo es solo LIKE con un índice" - es un pipeline lingüístico construido sobre lemas analizados y normalizados, mientras que
LIKEes una comparación de subcadenas sin procesar sin ninguna normalización. - "Un tsvector almacena el texto original" - almacena un conjunto deduplicado y ordenado de lemas normalizados más pesos y posiciones opcionales, y las palabras originales no se pueden recuperar de él en general.
- "Una ts_rank más alta siempre significa una mejor coincidencia para el usuario" -
ts_rankrefleja la frecuencia de términos y las etiquetas de peso, no la relevancia semántica, por lo que un documento puede tener una clasificación alta pero no capturar la intención real del usuario. - "ts_rank y ts_rank_cd siempre estarán de acuerdo en el orden" - codifican diferentes teorías de relevancia, frecuencia de términos frente a agrupación de coincidencias, por lo que pueden y producen diferentes órdenes para la misma consulta.
- "La derivación funciona igual para todos los idiomas" - cada configuración de búsqueda de texto tiene su propia cadena de diccionarios, por lo que la derivación, las palabras vacías e incluso los límites de tokenización difieren según la configuración.
Preguntas frecuentes
¿Cuál es la diferencia entre un token y un lema?
Un token es un fragmento de texto sin procesar y clasificado producido por el parser, mientras que un lema es lo que queda después de que un diccionario normaliza, deriva o descarta ese token.
¿Por qué buscar "correr" también coincide con "corriendo" y "corrió"?
El diccionario en la configuración de búsqueda de texto deriva las tres formas de palabras al mismo lema normalizado antes de que se almacene o coincida algo.
¿Por qué la búsqueda de texto completo no encuentra una subcadena como "gres" dentro de "postgres"?
La coincidencia opera sobre lemas completos normalizados, no sobre subcadenas de caracteres, por lo que los fragmentos de palabras parciales requieren una herramienta diferente como pg_trgm o LIKE.
¿Qué determina si una palabra se convierte en una palabra vacía?
La cadena de diccionarios para la configuración de búsqueda de texto activa decide, y las configuraciones comunes como english descartan palabras de alta frecuencia como "the" y "and" por diseño.
¿Cómo sabe la búsqueda de texto completo dónde estaba un lema en el texto original?
El tsvector almacena enteros de posición junto a cada lema, que es lo que permite a los operadores de frases y distancia verificar la adyacencia en el momento de la consulta.
¿Cuál es la diferencia práctica entre ts_rank y ts_rank_cd?
ts_rank puntúa basándose en la frecuencia de términos y las etiquetas de peso, mientras que ts_rank_cd además recompensa los documentos donde los lemas coincidentes aparecen agrupados de cerca.
¿Por qué la consulta necesita pasar por to_tsquery en lugar de una cadena simple?
Un tsquery debe pasar por el mismo pipeline de análisis y normalización que el texto del documento, o las palabras sin procesar de la consulta nunca coincidirán con los lemas normalizados del documento.
¿En qué se diferencia la búsqueda de texto completo de una búsqueda de expresiones regulares?
Una expresión regular coincide con patrones de caracteres literales sin conciencia lingüística, mientras que la búsqueda de texto completo coincide con lemas normalizados que portan significado independientemente de la forma exacta de la palabra.
¿Por qué la gente compara la búsqueda de texto completo de Postgres con Elasticsearch?
Ambos se basan en el mismo concepto de índice invertido que conecta términos con los documentos que los contienen, pero Elasticsearch agrega analizadores más ricos, escala distribuida y modelos de relevancia más avanzados como un sistema separado.
¿La búsqueda de texto completo entiende el significado o los sinónimos por sí sola?
No, entiende las formas de palabras normalizadas a través de la derivación y los diccionarios, no el significado conceptual, que es por lo que los equipos la combinan con incrustaciones vectoriales para la recuperación semántica.
¿Por qué un índice GIN hace que la búsqueda de texto completo sea rápida?
Invierte la relación de almacenamiento de documento-a-lemas a lema-a-documentos, convirtiendo una coincidencia en una búsqueda directa en lugar de un escaneo a través de cada fila.
¿Pueden dos configuraciones diferentes producir resultados de búsqueda diferentes para texto idéntico?
Sí, porque cada configuración tiene sus propias reglas de parser y cadena de diccionarios, por lo que simple y english pueden normalizar la misma entrada en conjuntos de lemas completamente diferentes.
Relacionado
- Fundamentos de la búsqueda de texto completo - la receta paso a paso para crear una tabla indexable que esta página explica la teoría detrás
- tsvector y tsquery - mecánicas más profundas sobre diccionarios, operadores y funciones de clasificación
- Índices GIN para FTS - cómo se construye y mantiene el índice invertido descrito aquí en la práctica
- Búsqueda Híbrida - combinación de este modelo léxico con incrustaciones vectoriales para la recuperación semántica
- Mejores prácticas de búsqueda de texto completo - la lista de verificación operativa que complementa este modelo conceptual
Versiones de Stack: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PostGIS 3.5+, pgbouncer 1.x, y Patroni 3.x.