Blog

Elasticsearch ES|QL lleva la búsqueda de texto completo a datos que nunca indexaste

MATCH y TO_TEXT llevan la búsqueda de texto completo a datos que nunca indexaste. Busca columnas calculadas, campos no mapeados y fuentes federadas en ES|QL.

Get hands-on with Elasticsearch: Dive into our sample notebooks in the Elasticsearch Labs repo, start a free cloud trial, or try Elastic on your local machine now.

ES|QL MATCH ahora ejecuta una búsqueda de texto completo en datos que nunca indexaste. Columnas calculadas, campos no mapeados, textos ensamblados sobre la marcha, incluso datos federados ubicados en S3. La nueva función TO_TEXT le indica a ES|QL que trate cualquier cadena como texto analizable, por lo que MATCH puede tokenizar, normalizar mayúsculas y minúsculas y hacer coincidir términos que existen solo durante el ciclo de vida de una búsqueda. Esto va más allá de la coincidencia de patrones LIKE y RLIKE que la mayoría de los motores de búsqueda ofrecen para textos no indexados: es análisis real. Disponible ahora en Elastic Cloud Serverless y como vista previa técnica en Elasticsearch 9.5.

Cómo MATCH y TO_TEXT habilitan la búsqueda de texto completo en cualquier expresión ES|QL

Empecemos con una búsqueda que era imposible en Elasticsearch 9.4, que usa el comando EVAL:

FROM cooking_blog
| EVAL summary = TO_TEXT(CONCAT(title, description))
| WHERE MATCH(summary, "pancakes")
| KEEP title, author

En este ejemplo, no tiene una configuración de mapeo ni de analizador. Tampoco está asociado con ningún índice invertido. Solo existe durante la vida útil de esta búsqueda, pero ahora puedes realizar una búsqueda de todos modos. Dos adiciones hacen que esto funcione.

Primero, MATCH ahora acepta cualquier expresión como su primer argumento, no solo un campo mapeado. Eso incluye las columnas producidas por EVAL y los resultados de funciones utilizados en línea. También incluye campos no mapeados cargados directamente desde el documento original. Además, todos los tipos de datos normalmente aceptados por MATCH son compatibles en este nuevo caso de uso.

La segunda parte de esto es la nueva función TO_TEXT, que es la primera función de conversión ES|QL que produce la salida de tipo texto. Hasta ahora, las columnas de texto solo podían provenir de campos indexados y mapeados, y todos los textos producidos por las expresiones ES|QL eran valores de palabras clave en lugar de texto. La distinción es importante porque MATCH trata ambos de forma diferente: se analizan los valores de texto, mientras que los valores de palabras clave se comparan exactamente, reflejando cómo una búsqueda MATCH en un campo de palabra clave indexado se reescribe en una búsqueda de término. TO_TEXT(x) es la forma de indicarle a ES|QL: trata esta cadena como texto completo.

Esto se envía como una vista previa técnica en Elasticsearch 9.5 y, como tal, tiene algunas limitaciones:

  • Actualmente solo está filtrando. Una coincidencia en una expresión aún no contribuye a la puntuación de relevancia; solo las coincidencias en los campos indexados afectan la puntuación.

  • Las opciones de búsqueda, como la imprecisión y otras, aún no son compatibles al hacer coincidir una expresión.

  • El texto en tiempo de ejecución se analiza con el analizador estándar. Esto aún no es configurable.

Se está trabajando para solucionar estas limitaciones.

¿Por qué usar la búsqueda de texto completo en lugar de LIKE o RLIKE en ES|QL?

ES|QL ya tenía dos formas de búsqueda de textos sin índice: LIKE (patrones comodines) y RLIKE (expresiones regulares). Ambos funcionan en cualquier expresión de texto, así que es justo preguntar qué agrega MATCH. La respuesta es el análisis, una forma más avanzada de búsqueda que emplea técnicas como el stemming y los sinónimos. También usa la eliminación de palabras vacías.

LIKE es una simple coincidencia de subcadenas, sin ninguna comprensión de las palabras que componen un texto. Digamos, por ejemplo, que estás buscando mensajes de log sobre un oso:

FROM app_logs
| WHERE message LIKE "*fox*"

Esto no detecta “Oso avistado cerca del gallinero” por la mayúscula, mientras que sí coincide con “Un resultado fabuloso para la competencia”, que no tiene nada que ver con un oso. Falla en ambas direcciones: hay falsos negativos por la mayúscula y falsos positivos por subcadenas escondidas dentro de otras palabras.

Las expresiones regulares pueden solucionar el problema de las mayúsculas y minúsculas, pero el problema de los límites de las palabras se complica rápidamente. Algo como:

FROM app_logs
| WHERE message RLIKE "(.* )?[Ff][Oo][Xx]([ ,.:;].*)?"

Y ni siquiera así queda bien. No detecta un oso al final de una oración seguido de ¡ o ¿, y no dice nada sobre tabulaciones, comillas o paréntesis. Cada arreglo hace que el patrón sea más largo, y la próxima persona que lea la búsqueda tiene que hacer ingeniería inversa para entender qué es lo que realmente hace.

MATCH hace que el problema desaparezca, porque ejecuta tanto la búsqueda como el valor a través de un analizador, que tokeniza el texto en términos en minúsculas y luego compara término con término:

FROM app_logs
| WHERE MATCH(TO_TEXT(message), "fox")

Esta búsqueda coincidirá con valores como “El rápido oso pardo” y “OSO avistado cerca del gallinero”, pero no con “Un resultado hermoso para la competencia” ni “Protocolo OSOBUCO habilitado”, sin importar la puntuación que rodee a las palabras. Por supuesto, todo esto también funciona con búsquedas de varios términos, como MATCH(TO_TEXT(message), “oso pardo”), tal como uno esperaría.

Se está trabajando para habilitar el uso de los 36 analizadores de lenguaje, con soporte para lenguajes naturales en datos que nunca fueron indexados ni mapeados.

Casos de uso de búsqueda de texto completo para datos no indexados y no mapeados

Los ejemplos anteriores buscaron valores calculados a partir de campos mapeados. Los casos de uso más interesantes para ES|QL MATCH en expresiones involucran datos que nunca se pudieron buscar en absoluto. Vamos a repasar algunos.

Cómo buscar campos sin mapeo en ES|QL sin agregar un mapeo

A veces dejas deliberadamente un campo fuera de tus mapeos, como un rastreo de pila verboso o una carga útil de solicitud sin procesar. Incluso podrías omitir un bloque de depurar. Indexar uno de estos campos costaría espacio en disco y memoria heap en cada documento, y no valdría la pena para un campo que quizás consultes una vez al trimestre.

Esa decisión siempre ha sido definitiva, porque los campos no mapeados eran invisibles para las búsquedas por completo. En Elasticsearch 9.5, puedes usar SET unmapped_fields="load" para hacer que ES|QL cargue campos no mapeados directamente desde el documento de origen como palabras clave. Sigue envolviéndolo en TO_TEXT, y ahora puedes ejecutar una búsqueda de texto completo en él:

SET unmapped_fields="load";
FROM app_logs
| WHERE MATCH(TO_TEXT(stack_trace), "java.lang.NullPointerException")
| KEEP @timestamp, service.name, message

Aquí, stack_trace nunca se mapeó. Cada valor se obtiene de los documentos originales y se analiza sobre la marcha. Están emparejados fila por fila. Eso es un trabajo real, y nunca será tan rápido como una búsqueda de índice invertido. Pero ahora, ese campo que no indexaste ya no es inbuscable. Puedes mantener el mapeo pequeño para el caso cotidiano y seguir respondiendo a la pregunta de una vez por trimestre cuando importa.

Búsqueda de texto completo en un campo de palabras clave sin reindexar

Los campos de palabras clave pueden hacer mucho. Dan coincidencias exactas, agregaciones rápidas y clasificación, razón por la cual tantos campos terminan mapeados de esa manera. Pero los mapeos se deciden cuando llegan los datos, y es fácil terminar en una situación en la que quieres hacer algo diferente con tus datos de lo que originalmente pretendías. Quizás product_name se mapeó como palabra clave porque los paneles de control agregan datos en función de ella, y luego de recibir datos de productos de un año, alguien quiere poder buscar dentro de los valores de product_name.

La respuesta antigua era cambiar el mapeo a texto (o agregar campos múltiples) y reindexar todo. Esto puede llevar mucho tiempo y ser costoso, y en muchos casos, los usuarios simplemente no querrán molestarse con ello. La nueva respuesta es una llamada a una función:

FROM products
| WHERE MATCH(TO_TEXT(product_name), "wireless noise cancelling headphones")
| KEEP product_name, brand, price

TO_TEXT convierte los valores de las palabras clave en texto sobre la marcha, así que MATCH los analiza en vez de compararlos exactamente. Esto te permite consultar un campo de palabra clave sin crear un mapeo o reindexar el documento de origen. Si la búsqueda se convierte en una búsqueda cotidiana, indexar el campo como texto sigue siendo el movimiento correcto a largo plazo, pero TO_TEXT te da una respuesta hoy, sin ningún trabajo adicional.

Buscar el mismo campo en los índices con diferentes mapeos

ES|QL puede abarcar muchos índices, y el mismo campo no tiene por qué verse siempre igual en todos. Cuando el mismo campo tiene diferentes tipos en distintos índices, ES|QL lo trata como un tipo de unión, y una función de conversión resuelve el conflicto. Consideremos un ejemplo en el que el campo message tiene tipo texto en la plantilla de índice de este año, pero era tipo palabra clave en la del año pasado:

FROM logs-2025, logs-2026
| EVAL msg = TO_TEXT(message)
| WHERE MATCH(msg, "connection reset")

Cada valor se analiza en el momento de la búsqueda, ya sea que provenga del índice de texto o del de la palabra clave. Los valores de palabra clave de los índices más antiguos se tokenizan y convierten a minúsculas como todo lo demás, así que “connection reset” encuentra “Connection RESET by peer”, sin importar en qué índice se encuentre.

Otro caso interesante es cuando un campo está mapeado en un solo índice pero también presente (y no mapeado) en el otro:

SET unmapped_fields="load";
FROM logs-2025, logs-2026
| WHERE MATCH(TO_TEXT(error_details), "timeout")

Hay un matiz que vale la pena destacar aquí. Si error_details está mapeado en logs-2026 pero no en logs-2025, Elasticsearch no puede enviar esta búsqueda a Lucene, porque los índices donde el campo no está mapeado no devolverían coincidencias silenciosamente. En cambio, el planificador se da cuenta de que el campo está potencialmente no mapeado y evalúa todo MATCH fila por fila, de donde provengan las filas. No es necesario que sepas en cuál de tus índices está mapeado el campo; la búsqueda simplemente responde a la pregunta.

Cómo ES|QL analiza el texto en tiempo de búsqueda sin un índice invertido

Cuando ES|QL planifica un MATCH contra una expresión, analiza el texto de búsqueda una vez, de antemano, en un conjunto de términos. La forma en que se evalúa cada fila depende del tipo de expresión:

Tipo de expresión

Procesamiento

Comportamiento de coincidencia

texto (mediante TO_TEXT)

El analizador tokeniza el valor en términos en minúsculas

Comparación token-contra-token; una fila coincide si cualquier token es igual a cualquier término de búsqueda (O semántica)

palabra clave, ip, fecha, numérico

Sin análisis; constante de búsqueda convertida una vez al tipo nativo

Comparación exacta por fila

Ambas rutas eluden Lucene por completo y evalúan los valores fila por fila. La ruta no textual refleja exactamente lo que hace una búsqueda de coincidencia cuando se delega a Lucene contra esos tipos de campo, por lo que la semántica sigue siendo consistente independientemente de si la búsqueda llega a un índice.

Una búsqueda de índice invertido hace su trabajo en el momento de la ingesta, y nunca procesa documentos que no coinciden en el momento de la búsqueda. Un MATCH en tiempo de ejecución realiza ese análisis en el momento de la búsqueda, por cada fila que llega a él. Uno es rápido porque el trabajo ya ocurrió; el otro es flexible porque los datos no necesitan haber sido indexados en absoluto.

¿Qué novedades hay para la búsqueda de texto completo en ES|QL?

Todo lo que se menciona en esta publicación es la primera entrega de un esfuerzo mayor para lograr que la búsqueda en ES|QL funcione con cualquier tipo de contenido, no solo con lo que se indexó previamente. Las limitaciones señaladas anteriormente se abordan de forma activa, y la roadmap va más allá:

  • Puntuación. Las coincidencias en tiempo de ejecución contribuirán a _score, por lo que puedes ordenar por relevancia incluso cuando los datos nunca se indexaron.

  • MATCH_PHRASE en las expresiones. Ya disponible en Elastic Cloud Serverless, y llegará a Elastic Stack en la versión 9.6.

  • Analizadores configurables. Compatibilidad del analizador con MATCH y MATCH_PHRASE en expresiones, lo que permite el uso de analizadores de lenguaje, derivación y sinónimos en el momento de la búsqueda.

  • Opciones de coincidencia. Opciones como la coincidencia inexacta y el operador para coincidencias en tiempo de ejecución.

  • Búsqueda vectorial. Generar incrustaciones por fila y ejecutar kNN (vecinos más cercanos) en expresiones dense_vector en tiempo de ejecución, llevando también la búsqueda semántica a datos no indexados.

Prueba la búsqueda de texto completo ES|QL en expresiones hoy

Puedes probar la búsqueda en tiempo de ejecución hoy mismo. Ya está disponible en Elastic Cloud Serverless, donde las nuevas capacidades de ES|QL llegan primero y se lanza como vista previa técnica en Elasticsearch 9.5. Comienza con la referencia de funciones de búsqueda y consulta la página de limitaciones de ES|QL para conocer los límites actuales. Es una vista previa técnica porque queremos tus comentarios: si realizas una búsqueda de algo que nunca fue indexado y te sorprende, ya sea positiva o negativamente, nos encantaría saberlo.

Contenido relacionado

Acceso instantáneo al dashboard en menos de un minuto, 5 veces más económico: dashboards con IA y gráficos personalizados de Vega-Lite en Kibana

Marta Bondyra

LINQ a Elasticsearch ES|QL: escribir en C#, buscar en Elasticsearch

Florian Bernd

Estadísticas ES|QL más rápidas con tablas hash de estilo suizo

Chris Hegarty

Introducción del soporte de Elasticsearch en Google MCP Toolbox for Databases

Enrico Zimuel

ES|QL en la versión 9.2: incorporación de la búsqueda inteligente (Lookup Joins) y compatibilidad con series temporales

Tyler Perkins