<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[ES|QL - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[ES|QL - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/es/search-labs/blog/category/esql</link>
    </image>
    <link>https://www.elastic.co/es/search-labs/blog/category/esql</link>
    <atom:link href="https://www.elastic.co/es/search-labs/rss/category/esql.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[es]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 04:22:20 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Elasticsearch ES|QL lleva la búsqueda de texto completo a datos que nunca indexaste]]></title>
    <description><![CDATA[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.]]></description>
    <content:encoded><![CDATA[<p>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.</p><h2>Cómo MATCH y TO_TEXT habilitan la búsqueda de texto completo en cualquier expresión ES|QL</h2><p>Empecemos con una búsqueda que era imposible en Elasticsearch 9.4, que usa <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval">el comando EVAL</a>:</p><p>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.</p><p>Primero, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/search-functions/match">MATCH</a> 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.</p><p>La segunda parte de esto es la nueva función <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/type-conversion-functions/to_text">TO_TEXT</a>, 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: <em>trata esta cadena como texto completo</em>.</p><p>Esto se envía como una vista previa técnica en Elasticsearch 9.5 y, como tal, tiene algunas limitaciones:</p><ul><li><p>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.</p></li><li><p>Las opciones de búsqueda, como la imprecisión y otras, aún no son compatibles al hacer coincidir una expresión.</p></li><li><p>El texto en tiempo de ejecución se analiza con el analizador estándar. Esto aún no es configurable.</p></li></ul><p>Se está trabajando para solucionar estas limitaciones.</p><h2>¿Por qué usar la búsqueda de texto completo en lugar de LIKE o RLIKE en ES|QL?</h2><p>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 <a href="https://www.elastic.co/docs/manage-data/data-store/text-analysis">el análisis</a>, 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.</p><p>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:</p><p>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.</p><p>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:</p><p>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.</p><p>MATCH hace que el problema desaparezca, porque ejecuta tanto la búsqueda como el valor a través de un <a href="https://www.elastic.co/docs/reference/text-analysis/analyzer-reference">analizador</a>, que tokeniza el texto en términos en minúsculas y luego compara término con término:</p><p>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.</p><p>Se está trabajando para habilitar el uso de los <a href="https://www.elastic.co/docs/reference/text-analysis/analysis-lang-analyzer">36 analizadores de lenguaje</a>, con soporte para lenguajes naturales en datos que nunca fueron indexados ni mapeados.</p><h2>Casos de uso de búsqueda de texto completo para datos no indexados y no mapeados</h2><p>Los ejemplos anteriores buscaron valores calculados a <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">partir de campos mapeados</a>. 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.</p><h3>Cómo buscar campos sin mapeo en ES|QL sin agregar un mapeo</h3><p>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.</p><p>Esa decisión siempre ha sido definitiva, porque los <a href="https://www.elastic.co/search-labs/blog/esql-unmapped-fields">campos no mapeados</a> 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:</p><p>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 <a href="https://www.elastic.co/docs/manage-data/data-store/index-basics">índice invertido</a>. 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.</p><h3>Búsqueda de texto completo en un campo de palabras clave sin reindexar</h3><p>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.</p><p>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:</p><p>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.</p><h3>Buscar el mismo campo en los índices con diferentes mapeos</h3><p><a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-multi-index">ES|QL puede abarcar muchos índices</a>, 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:</p><p>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.</p><p>Otro caso interesante es cuando un campo está mapeado en un solo índice pero también presente (y no mapeado) en el otro:</p><p>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 <a href="https://lucene.apache.org/">Lucene</a>, 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.</p><h2>Cómo ES|QL analiza el texto en tiempo de búsqueda sin un índice invertido</h2><p>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:</p><p><strong>Tipo de expresión</strong></p><p><strong>Procesamiento</strong></p><p><strong>Comportamiento de coincidencia</strong></p><p>texto (mediante TO_TEXT)</p><p>El analizador tokeniza el valor en términos en minúsculas</p><p>Comparación token-contra-token; una fila coincide si cualquier token es igual a cualquier término de búsqueda (O semántica)</p><p>palabra clave, ip, fecha, numérico</p><p>Sin análisis; constante de búsqueda convertida una vez al tipo nativo</p><p>Comparación exacta por fila</p><p>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.</p><p>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.</p><h2>¿Qué novedades hay para la búsqueda de texto completo en ES|QL?</h2><p>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á:</p><ul><li><p><strong>Puntuación.</strong> 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.</p></li><li><p><strong>MATCH_PHRASE</strong><strong> en las expresiones.</strong> Ya disponible en Elastic Cloud Serverless, y llegará a Elastic Stack en la versión 9.6.</p></li><li><p><strong>Analizadores configurables.</strong> 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.</p></li><li><p><strong>Opciones de coincidencia.</strong> Opciones como la coincidencia inexacta y el operador para coincidencias en tiempo de ejecución.</p></li><li><p><strong>Búsqueda vectorial.</strong> 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.</p></li></ul><h2>Prueba la búsqueda de texto completo ES|QL en expresiones hoy</h2><p>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 <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/search-functions">funciones de búsqueda</a> y consulta la página de <a href="https://www.elastic.co/docs/reference/query-languages/esql/limitations">limitaciones de ES|QL</a> 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, <a href="https://www.elastic.co/es/community">nos encantaría saberlo</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/full-text-search-unindexed-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/full-text-search-unindexed-data</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Mapeos]]></category>
    <dc:creator><![CDATA[Kevin Corcoran,Ioana Tagirta]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a74e633d05585a8/6a730774b8c2e64c3ebe0fd4/image1.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[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]]></title>
    <description><![CDATA[Describe tus métricas en lenguaje natural y el chat de IA de Kibana genera dashboards respaldados por ES|QL y gráficos Vega-Lite, desde gráficos de dispersión hasta formato condicional y consejos de herramientas personalizados.]]></description>
    <content:encoded><![CDATA[<p>El<a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/chat"> chat de IA</a> de Kibana ahora construye dashboards completos<a href="https://www.elastic.co/docs/explore-analyze/visualize/esorql"> respaldados por el lenguaje de búsqueda de Elasticsearch (ES|QL)</a> desde una indicación en lenguaje natural en menos de un minuto. En Elastic 9.5, esto pasa a disponibilidad general (GA) (<a href="https://www.elastic.co/es/search-labs/blog/ai-dashboard-generation-elastic-agent-kibana">vista previa técnica en 9.4</a>) con recuperación de errores que reintenta consultas fallidas, costos de generación de ES|QL 5 veces menores gracias al enrutamiento por niveles de modelo, y controles interactivos de filtros. Esta versión también agrega la creación de gráficos<a href="https://www.elastic.co/docs/explore-analyze/visualize/custom-visualizations-with-vega"> Vega-Lite</a> a través del lenguaje natural, incluidos gráficos de dispersión, gráficos de caja, formato condicional e información sobre herramientas personalizadas que normalmente tendrías que codificar a mano en JSON.</p><h2>Novedades en la creación del dashboard de IA de Kibana</h2><p><strong>Capacidad</strong></p><p><strong>Vista previa técnica (9.4)</strong></p><p><strong>GA (9.5)</strong></p><p>Manejo de errores</p><p>No se permiten reintentos en consultas ES|QL fallidas</p><p>Reintento automático hasta tres veces con inspección y ajuste de consultas</p><p>Costo de generación de ES|QL</p><p>Todas las consultas enrutadas a través del modelo principal</p><p>Enrutamiento por niveles, hasta 5 veces más económico</p><p>Intervalo de tiempo</p><p>Ventana predeterminada fija</p><p>Selección automática basada en la distribución temporal de los datos</p><p>Controles de filtro</p><p>Sin soporte</p><p>Se agrega automáticamente para los campos más relevantes</p><p>Gráficos de Vega-Lite</p><p>Sin soporte</p><p>Creación en lenguaje natural, incluyendo gráficos de dispersión, gráficos de caja, formato condicional, sugerencias de herramientas personalizadas</p><p>Edición de gráficos</p><p>Sin soporte</p><p>Edita paneles Vega-Lite existentes a través del lenguaje natural</p><h3>Recuperación automática de errores para la generación del dashboard de IA</h3><p>En la versión preliminar técnica, el agente no volvió a intentar las consultas ES|QL fallidas. En la versión 9.5, detecta errores de consulta y vuelve a intentarlo hasta tres veces, inspeccionando cada error y ajustando la consulta antes de darte por vencido. En la práctica, esto elimina la mayoría de los problemas de paneles vacíos y produce dashboards que se renderizan correctamente en el primer intento.</p><h3>¿Por qué es más barato crear dashboards con IA en Elastic 9.5?</h3><p>No todos los pasos en la generación del dashboard requieren el mismo nivel de razonamiento. En la versión 9.5, la generación de ES|QL emplea un modelo más ligero de manera predeterminada y solo recurre al modelo principal cuando es necesario. Si tu <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/connectors">conector</a> emplea Claude Opus 4.8 de Anthropic, eso significa que la generación de ES|QL será 5 veces más económica en todos los paneles.</p><h3>Selección automática de intervalos de tiempo basada en tus datos</h3><p>Los dashboards solo son útiles cuando muestran la ventana de datos correcta. El agente ahora aplica una lógica mejorada para elegir un intervalo de tiempo que tenga sentido para los datos que está consultando, a menos que el usuario solicite un intervalo de tiempo específico. Considera la distribución temporal de los datos y ajusta en consecuencia, ya sea la última hora para un incidente en tiempo real o los últimos 90 días para un análisis de tendencias, en lugar de recurrir de manera predeterminada a una ventana fija.</p><h3>Control automático de filtros en dashboards generados por IA</h3><p>La creación de dashboards ahora admite controles; es decir, filtros interactivos que permiten a los usuarios refinar un dashboard por valores de campo sin editar las consultas subyacentes. Al generar un dashboard, el agente agrega automáticamente controles en la parte superior para los campos más relevantes para filtrar.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4a5520e66ae76001/6a719a218a155220ed6498e7/image3.png" alt="Kibana AI chat generating an ES|QL-backed host metrics dashboard with automatic filter controls in 71 seconds" /><h2>Gráficos Vega-Lite de lenguaje natural: tipos de gráficos y formato más allá de los valores predeterminados</h2><p><a href="https://vega.github.io/vega/">Vega</a> y <a href="https://vega.github.io/vega-lite/examples/">Vega-Lite</a> admiten una amplia variedad de tipos de gráficos y personalizaciones en Kibana. Con 9.5, puedes construirlos a partir de un lenguaje sencillo en lugar de escribir el código tú mismo. </p><h3>Gráficos de dispersión, gráficos de caja y más tipos de gráficos Vega-Lite</h3><p>Los diagramas de dispersión, diagramas de caja, múltiplos pequeños facetados, gráficos de burbujas y diagramas de composición (como combinar histogramas con mapas de calor), entre muchos otros, se admiten en <a href="https://vega.github.io/vega-lite/examples/">Vega-Lite</a>. Una indicación como <em>Muéstrame un gráfico de dispersión del tiempo de respuesta frente al tamaño de la solicitud, coloreado por nombre de servicio</em>, produce un panel Vega-Lite con los mapeos de datos correctos. Usan las paletas de colores predeterminadas de Kibana para combinarse con el resto del dashboard.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1651fe7ac903d47/6a719a49f124649f746fc1b4/image5.png" alt="Kibana dashboard with four Vega-Lite charts: box plot, bubble chart, faceted small multiples, and heatmap." /><h3>Formato condicional, información sobre herramientas personalizadas y etiquetas en gráficos estándar</h3><p>Incluso para tipos de gráficos que ya son nativos de los dashboards, como barra, línea o área, a veces necesitas más control del que ofrecen las capacidades predeterminadas. Vega-Lite, a través del chat, cubre esa necesidad. Algunos ejemplos incluyen:</p><ul><li><p><strong>Formato de color condicional:</strong> colorea los puntos de datos por encima de un umbral de manera diferente; por ejemplo, marca en rojo los puntos de datos en una línea o barras cuando alguna métrica supera tu objetivo de nivel de servicio (SLO). Pregúntale al agente algo como <em>Marca en rojo cualquier punto por encima de 500 ms para mi gráfico de líneas</em>. </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc1211784e9033557/6a719a6ab966e1736163d7d1/image1.png" alt="Vega-Lite line and bar charts in Kibana with conditional colour formatting showing data points above a threshold in red" /><p></p></li><li><p><strong>Marcas y etiquetas personalizadas:</strong> agrega emojis, símbolos o etiquetas de texto en línea a los puntos de datos para obtener indicadores de estado de un vistazo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3c59c3748484bc1/6a719aa02888394fdc07bac1/image2.png" alt="Lite horizontal bar chart in Kibana with emoji flag labels and custom tooltip showing requests by country" /><p></p></li><li><p><strong>Información sobre herramientas personalizada:</strong> enriquece los estados del cursor con métricas adicionales, contexto o valores calculados que no forman parte de los ejes del gráfico. Pregunta algo como <em>Agrega una descripción emergente que muestre el total de registros y el porcentaje por barra.</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt43f241f4382b5b90/6a719ab7ded0cf3367f49275/image4.png" alt="Vega-Lite stacked bar chart in Kibana with custom tooltip showing total records and percentage of total by extension" /><p></p></li></ul><p>Esto también funciona para editar gráficos de Vega existentes. Si tienes un panel Vega-Lite que necesita un ajuste como cambiar una escala de color, ajustar un eje o cambiar el tipo de marca, describe el cambio en el chat en lugar de indagar en el código JSON.</p><h2>Cómo construimos la generación en lenguaje natural de Vega-Lite en Kibana</h2><p>Generar un gráfico Vega-Lite a partir de una oración no es una simple indicación de <em>solicitud JSON al modelo</em>. Construimos un pequeño pipeline agéntico que convierte la intención del lenguaje natural en un gráfico validado y respaldado por datos.</p><p>Cuando llega una solicitud, el agente primero determina si Vega-Lite es la opción adecuada. Para las solicitudes de Vega-Lite, establece la visualización en una consulta ES|QL real contra Elasticsearch y luego usa un modelo para generar el código Vega-Lite. Antes de renderizar, el resultado pasa por una capa de normalización que corrige el esquema y vincula la consulta canónica. También aplica transformaciones de seguridad de renderizado. </p><p>Algunas decisiones de diseño hacen que este flujo de trabajo sea confiable:</p><ul><li><p><strong>Llamadas a herramientas tipificadas</strong>: la creación de gráficos es una invocación de herramienta estructurada en lugar de Vega-Lite de forma libre pegada en la conversación.</p></li><li><p><strong>Generación con restricciones</strong>: el modelo genera código Vega-Lite dentro de un esquema definido, haciendo que la salida sea más previsible y fácil de validar.</p></li><li><p><strong>Ejemplos seleccionados:</strong> los patrones estructurales, como el facetado, las marcas superpuestas y los mapas de calor, proporcionan orientación sin copiar los datos subyacentes.</p></li><li><p><strong>Ejecutar y verificar bucles</strong>: las consultas se ejecutan antes de la creación de gráficos y los errores de validación desencadenan reintentos correctivos para la generación de ES|QL.</p></li></ul><h2>Prueba la creación de dashboard con IA y gráficos Vega-Lite en Kibana</h2><p>Para probar la creación de dashboards en lenguaje natural y gráficos Vega-Lite, actualiza a <strong>Elastic 9.5</strong> (o <a href="https://cloud.elastic.co/registration">inicia una prueba gratis</a>), y abre el <strong>chat</strong> en Kibana. Luego pídele que cree un dashboard a partir de tus datos. Para Vega-Lite, intenta pedir un tipo de gráfico que hayas querido pero que nunca hayas construido, como un diagrama de dispersión o un gráfico de burbujas. Si el resultado no es del todo correcto, dile al agente qué cambiar. El agente itera contigo.</p><p>Esto requiere una licencia Empresarial. <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/chat#get-started">Comencemos</a>.</p><p><em>El lanzamiento y el momento de cualquier característica o funcionalidad descrita en esta publicación quedan a exclusivo criterio de Elastic. Es posible que alguna característica o funcionalidad que no esté disponible en este momento no se lance a tiempo o no se lance en absoluto.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-dashboards-kibana-vega-lite</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-dashboards-kibana-vega-lite</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Marta Bondyra,Teresa Alvarez Soler]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt54406ad0378bc5fc/6a7199ffed03ccee0dac9d7c/image6.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LINQ a Elasticsearch ES|QL: escribir en C#, buscar en Elasticsearch]]></title>
    <description><![CDATA[Explorar el nuevo proveedor de LINQ a Elasticsearch ES|QL en el cliente .NET de Elasticsearch, que te permite escribir código en C# que se traduce automáticamente en búsquedas ES|QL.]]></description>
    <content:encoded><![CDATA[<p>A partir de <strong>v9.3.4</strong> y <strong>v8.19.18</strong>, el cliente de Elasticsearch para .NET incluye un <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">proveedor de Language Integrated Query (LINQ) </a>que traduce las expresiones LINQ de C# a búsquedas del <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">lenguaje de búsqueda de Elasticsearch (ES|QL)</a> en tiempo de ejecución. En lugar de escribir textos de ES|QL manualmente, compones búsquedas con <code>Where</code>, <code>Select</code>, <code>OrderBy</code>, <code>GroupBy</code> y otros operadores estándar. El proveedor se encarga de la traducción, la parametrización y la deserialización de los resultados, incluido el streaming fila por fila, lo que mantiene el uso de memoria constante independientemente del tamaño del conjunto de resultados.</p><h2>Tu primera búsqueda</h2><p>Comienza por definir un objeto CLR (POCO) simple que se mapea a tu índice de Elasticsearch. Los nombres de las propiedades se resuelven a nombres de columnas ES|QL a través de atributos <code>System.Text.Json</code> estándar, como <code>[JsonPropertyName]</code>, o a través de un <code>JsonNamingPolicy</code> configurado. Las mismas reglas de <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization">serialización de origen</a> que se aplican en el resto del cliente también se aplican aquí.</p>using System.Text.Json.Serialization;

public class Product
{
    [JsonPropertyName("product_id")]
    public string Id { get; set; }

    public string Name { get; set; }

    public string Brand { get; set; }

    [JsonPropertyName("price_usd")]
    public double Price { get; set; }

    [JsonPropertyName("in_stock")]
    public bool InStock { get; set; }
}<p>Con el tipo ya definido, una consulta se ve así:</p>var minPrice = 100.0;
var brand = "TechCorp";

await foreach (var product in client.Esql.QueryAsync&lt;Product&gt;(q =&gt; q
    .From("products")
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10)))
{
    Console.WriteLine($"{product.Name}: ${product.Price}");
}<p>El proveedor traduce esto al siguiente ES|QL:</p><p>Algunos detalles a tener en cuenta:</p><ul><li><p><strong>Resolución de nombres de propiedades:</strong> <code>p.Price</code> se vuelve <code>price_usd</code> debido al atributo <code>[JsonPropertyName]</code>, y <code>p.Brand</code> se convierte en <code>brand</code> siguiendo la política predeterminada de nombres camelCase.</p></li><li><p><strong>Captura de parámetros:</strong> Las variables C# <code>minPrice</code> y <code>brand</code> se capturan como parámetros nombrados (<code>?minPrice</code>, <code>?brand</code>). Se envían por separado del texto de búsqueda en la carga útil JSON, lo que previene la inyección y habilita el almacenamiento en caché del plan de búsqueda del lado del servidor.</p></li><li><p><strong>Streaming:</strong> <code>QueryAsync&lt;T&gt;</code> devuelve <code>IAsyncEnumerable&lt;T&gt;</code>. Las filas se materializan una a la vez a medida que llegan desde Elasticsearch.</p></li></ul><p>También puedes inspeccionar la búsqueda generada y sus parámetros sin ejecutarla:</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE (in_stock == true AND price_usd &gt;= 100) | SORT price_usd DESC | LIMIT 10

Console.WriteLine(query.ToEsqlString(inlineParameters: false));
// FROM products | WHERE (in_stock == true AND price_usd &gt;= ?minPrice AND brand == ?brand) | SORT price_usd DESC | LIMIT 10

var parameters = query.GetParameters();
// { "minPrice": 100.0, "brand": "TechCorp" }<h2>¿Cómo funciona esto? Un repaso rápido de LINQ</h2><p>El mecanismo que hace posibles los proveedores LINQ es la distinción entre <code>IEnumerable&lt;T&gt;</code> y <code>IQueryable&lt;T&gt;</code>.</p><p>Cuando llamas a <code>.Where(p =&gt; p.Price &gt; 100)</code> en un <code>IEnumerable&lt;T&gt;</code>, la lambda se compila en un <code>Func&lt;Product, bool&gt;</code>, un delegado común que el runtime ejecuta en proceso. Esto es LINQ a objetos.</p><p>Cuando llamas al mismo método en un <code>IQueryable&lt;T&gt;</code>, el compilador de C# encapsula la expresión lambda en un <code>Expression&lt;Func&lt;Product, bool&gt;&gt;</code> en su lugar. Esta es una estructura de datos que representa la <em>estructura</em> del código en lugar de su forma ejecutable. El árbol de expresión puede inspeccionarse, analizarse y traducirse a otro idioma en tiempo de ejecución.</p>// IEnumerable: the lambda is a compiled delegate
IEnumerable&lt;Product&gt; local = products.Where(p =&gt; p.Price &gt; 100);

// IQueryable: the lambda is an expression tree, a data structure
IQueryable&lt;Product&gt; remote = queryable.Where(p =&gt; p.Price &gt; 100);<p>La interfaz <code>IQueryProvider</code> es el punto de extensión. Cualquier proveedor puede implementar <code>CreateQuery&lt;T&gt;</code> y <code>Execute&lt;T&gt;</code> para traducir estos árboles de expresiones a un idioma destino. Entity Framework usa esto para emitir SQL. El proveedor de LINQ a ES|QL lo usa para emitir ES|QL.</p><p>El árbol de expresión para la búsqueda anterior se ve así:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt521838e8b9c36649/6a1705b1839dfa5f40dcfdfe/f864cd18a390831f8d28503a29b5835efb1842f7-1000x720.png" alt="Árbol de expresiones para la búsqueda de ejemplo." /><p><em>Árbol de expresiones para la búsqueda de ejemplo.</em></p><p>El árbol está anidado al revés: <code>Take</code> envuelve <code>OrderByDescending</code>, que envuelve <code>Where</code>, que envuelve <code>From</code>, que envuelve la constante raíz <code>EsqlQueryable&lt;Product&gt;</code>. El predicado <code>Where</code> es en sí mismo un subárbol de <code>BinaryExpression</code> nodos para los operadores <code>&amp;&amp;</code>, <code>&gt;=</code> y <code>==</code>, con <code>MemberExpression</code> hojas para accesos a propiedades y capturas de cierre para las variables <code>minPrice</code> y <code>brand</code>. Esta es la estructura de datos que el proveedor recorre para producir el ES|QL final.</p><h2>En detalle: el pipeline de traducción</h2><p>La ruta de una expresión LINQ a los resultados de la búsqueda sigue un pipeline de seis etapas:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt930670a505dd61ea/6a1705b3b339d58a54769ecf/2a2c772b63d720f61fc9a28b2f85668fa2db8d38-1999x1036.png" alt="Visión general del pipeline de traducción." /><p><em>Visión general del pipeline de traducción.</em></p><h3>1. Captura del árbol de expresiones</h3><p>Cuando se encadenan <code>.Where()</code>, <code>.OrderBy()</code>, <code>.Take()</code> y otros operadores en un <code>IQueryable&lt;T&gt;</code>, la infraestructura LINQ estándar crea un árbol de expresiones. <code>EsqlQueryable&lt;T&gt;</code> implementa <code>IQueryable&lt;T&gt;</code> y delega a <code>EsqlQueryProvider</code>.</p><h3>2. Traducción</h3><p>Cuando se ejecuta la búsqueda (al enumerar, llamar a <code>ToList()</code> o usar <code>await foreach)</code>, <code>EsqlExpressionVisitor</code> recorre el árbol de expresiones de adentro hacia afuera. Envía cada llamada al método LINQ a un visitante especializado:</p><p>Visitante</p><p>Traduce</p><p>En</p><p>whereClauseVisitor</p><p>.Where(predicado)</p><p>Condición WHERE</p><p>SelectProjectionVisitor</p><p>.Select(selector)</p><p>EVAL + KEEP + RENAME</p><p>GroupByVisitor</p><p>.GroupBy().Select()</p><p>STATS ... BY</p><p>OrderByVisitor</p><p>.OrderBy() / .ThenBy()</p><p>Campo SORT [ASC\|DESC]</p><p>EsqlFunctionTranslator</p><p>EsqlFunctions.*, Math.*, métodos de texto</p><p>Más de 80 funciones ES|QL</p><p>Durante la traducción, las variables de C# a las que se hace referencia en las expresiones se capturan como parámetros con nombre.</p><h3>3. Modelo de búsqueda</h3><p>Los visitantes no producen textos directamente. En cambio, producen objetos <code>QueryCommand</code>, una representación intermedia inmutable. Un <code>FromCommand</code>, un <code>WhereCommand</code>, un <code>SortCommand</code> y un <code>LimitCommand</code>, cada uno representa un comando de procesamiento de ES|QL. Estos se recopilan en un modelo <code>EsqlQuery</code>.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt788c9936976f2f62/6a1705b50e2e4910da419ff0/2adc349b6cf655b96b7b3e826a134e8a17fe42fd-1999x1036.png" alt="Modelo de búsqueda y patrón de comandos." /><p><em>Modelo de búsqueda y patrón de comandos.</em></p><p>Este modelo intermedio está desacoplado tanto del árbol de expresiones como del formato de salida. Se puede inspeccionar, interceptar (vía <code>IEsqlQueryInterceptor</code>) o modificar antes de dar formato.</p><h3>4. Formato</h3><p><code>EsqlFormatter</code> visita cada <code>QueryCommand</code> en orden y produce el texto final de ES|QL. Cada comando se convierte en una línea, separada por el operador de barra vertical (|) que ES|QL usa para encadenar comandos de procesamiento. Los identificadores que contienen caracteres especiales se escapan automáticamente con comillas invertidas.</p><h3>5. Ejecución</h3><p>El texto ES|QL formateado y los parámetros capturados se envían al endpoint <code>/_query</code> de Elasticsearch como carga útil JSON. La interfaz <code>IEsqlQueryExecutor</code> abstrae la capa de transporte, que es donde entra en juego la arquitectura de paquetes en capas.</p><h3>6. Materialización</h3><p><code>EsqlResponseReader</code> transmite la respuesta JSON sin almacenar en memoria todo el conjunto de resultados. Un árbol <code>ColumnLayout</code>, precomputado una vez por búsqueda, mapea nombres de columnas planas de ES|QL (como <code>address.street</code>, <code>address.city</code>) a propiedades anidadas de POCO. Cada fila se ensambla en una instancia <code>T</code> y se genera una a la vez a través de <code>IEnumerable&lt;T&gt;</code> o <code>IAsyncEnumerable&lt;T&gt;</code>.</p><h2>La arquitectura en capas</h2><p>La funcionalidad de LINQ a ES|QL se divide en tres paquetes:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt662bd0dd8861b6b6/6a1705b7a929cf7086ae08a2/41b8aae860ecdc2480edcb1c1d4cc9b03cfb78c9-1999x1036.png" alt="Arquitectura de paquetes." /><p><em>Arquitectura de paquetes.</em>
<a href="https://www.nuget.org/packages/Elastic.Esql"><strong><code>Elastic.Esql</code></strong></a> es el motor puro de traducción. No tiene dependencias HTTP y contiene los visitantes de expresiones, el modelo de búsqueda, el formateador y el lector de respuestas. Puedes usarlo de forma independiente para crear e inspeccionar búsquedas de ES|QL sin una conexión de Elasticsearch, lo que es útil para pruebas, logging de búsquedas o para crear tu propia capa de ejecución.</p>// Translation-only: no Elasticsearch connection needed
var provider = new EsqlQueryProvider();
var query = new EsqlQueryable&lt;Product&gt;(provider)
    .From("products")
    .Where(p =&gt; p.InStock)
    .OrderByDescending(p =&gt; p.Price);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE in_stock == true | SORT price_usd DESC<p><a href="https://www.nuget.org/packages/Elastic.Clients.Esql"><strong><code>Elastic.Clients.Esql</code></strong></a> es un cliente ES|QL ligero e independiente. Añade ejecución HTTP sobre <code>Elastic.Esql</code> a través de <code>Elastic.Transport</code>. Si tu aplicación solo necesita ES|QL y ninguna de las otras API de Elasticsearch, esta es la opción de dependencia mínima.</p><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch"><strong><code>Elastic.Clients.Elasticsearch</code></strong></a> es el cliente completo de Elasticsearch .NET. También se basa en <code>Elastic.Esql</code> y expone al proveedor LINQ a través del espacio de nombres <code>client.Esql</code>. Este es el punto de entrada recomendado para la mayoría de las aplicaciones.</p><p>Ambos paquetes de capa de ejecución proporcionan su propia implementación de <code>IEsqlQueryExecutor</code>, la interfaz estratégica que une la traducción y el transporte.</p><p>Los tres paquetes son compatibles con Native AOT cuando se usan con un <code>JsonSerializerContext</code> generado por el código fuente. Para el cliente completo, consulta la <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization#native-aot">documentación de Native AOT</a>.</p><h2>Mas allá de los conceptos básicos</h2><p>El ejemplo anterior cubrió el filtrado, la clasificación y la paginación. El proveedor admite un conjunto más amplio de operaciones.</p><h3>Agregaciones</h3><p><code>GroupBy</code>, combinado con funciones agregadas en <code>Select</code>, se traduce a ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS ... BY</code></a>:</p>var stats = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .GroupBy(p =&gt; p.Brand)
    .Select(g =&gt; new
    {
        Brand = g.Key,
        Count = g.Count(),
        AvgPrice = g.Average(p =&gt; p.Price),
        MaxPrice = g.Max(p =&gt; p.Price)
    }));

// -&gt; FROM products | STATS COUNT(*), AVG(price_usd), MAX(price_usd) BY brand<h3>Proyecciones</h3><p><code>Select</code>, con tipos anónimos genera comandos <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval"><code>EVAL</code></a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/keep"><code>KEEP</code></a>, y <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/rename"><code>RENAME</code></a>:</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Select(p =&gt; new { ProductName = p.Name, p.Price, p.InStock });

// -&gt; FROM products | KEEP name, price_usd, in_stock | RENAME name AS ProductName<h3>Biblioteca de funciones enriquecida</h3><p>Hay más de 80 funciones ES|QL disponibles a través de la clase <code>EsqlFunctions</code>, que abarcan fecha/hora, texto, matemáticas, IP, coincidencia de patrones y puntuación. También se traducen los métodos estándar <code>Math.*</code> y <code>string.*</code>:</p>.Where(p =&gt; p.Name.Contains("Pro"))       // -&gt; WHERE name LIKE "*Pro*"
.Where(p =&gt; EsqlFunctions.CidrMatch(      // -&gt; WHERE CIDR_MATCH(ip, "10.0.0.0/8")
    p.IpAddress, "10.0.0.0/8"))<h3>LOOKUP JOIN</h3><p>Las consultas cruzadas de índices se traducen a ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join"><code>LOOKUP JOIN</code></a>:</p>var enriched = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .LookupJoin&lt;Product, CategoryLookup, string, object&gt;(
        "category-lookup-index",
        product =&gt; product.Id,
        category =&gt; category.CategoryId,
        (product, category) =&gt; new { product.Name, category!.CategoryLabel }));<h3>Acceso directo a ES|QL sin procesar</h3><p>Para las características de ES|QL que aún no están cubiertas por el proveedor de LINQ, puedes anexar fragmentos sin procesar:</p>var results = client.Esql.Query&lt;Product&gt;(q =&gt; q
    .Where(p =&gt; p.InStock)
    .RawEsql("| EVAL discounted = price_usd * 0.9"));<h3>Búsquedas asíncronas del lado del servidor</h3><p>Para búsquedas de ejecución prolongada, envíalas para procesamiento en segundo plano en el servidor:</p>await using var asyncQuery = await client.Esql.SubmitAsyncQueryAsync&lt;Product&gt;(
    q =&gt; q.Where(p =&gt; p.InStock),
    asyncQueryOptions: new EsqlAsyncQueryOptions
    {
        WaitForCompletionTimeout = TimeSpan.FromSeconds(5),
        KeepAlive = TimeSpan.FromMinutes(10)
    });

await asyncQuery.WaitForCompletionAsync();
await foreach (var product in asyncQuery.AsAsyncEnumerable())
    Console.WriteLine(product.Name);<p>Las búsquedas asíncronas del lado del servidor son especialmente útiles para búsquedas analíticas de larga duración/procesamiento de grandes sets de datos que pueden superar los umbrales típicos de tiempo de espera, o en entornos sensibles al tiempo de espera con balanceadores de carga, gateways API o proxies que imponen tiempos de espera HTTP estrictos. Las búsquedas asíncronas evitan las caídas de conexión al separar el envío de la solicitud de la recuperación de los resultados.</p><h2>Primeros pasos</h2><p>LINQ a ES|QL está disponible a partir de:</p><ul><li><p><strong>Elastic.Clients.Elasticsearch v9.3.4</strong> (rama 9.x)</p></li><li><p><strong>Elastic.Clients.Elasticsearch v8.19.18</strong> (rama 8.x)</p></li></ul><p>Instalar desde NuGet:</p><p><code>dotnet add package Elastic.Clients.Elasticsearch</code></p><p>Los puntos de entrada están en <code>client.Esql</code>:</p><p>Método</p><p>Devuelve</p><p>Caso de uso</p><p>Query&lt;T&gt;(...)</p><p>IEnumerable&lt;T&gt;</p><p>Ejecución sincrónica</p><p>QueryAsync&lt;T&gt;(...)</p><p>IAsyncEnumerable&lt;T&gt;</p><p>Transmisión asíncrona</p><p>CreateQuery&lt;T&gt;()</p><p>IEsqlQueryable&lt;T&gt;</p><p>Composición e inspección avanzadas</p><p>SubmitAsyncQueryAsync&lt;T&gt;(...)</p><p>EsqlAsyncQuery&lt;T&gt;</p><p>Búsquedas de larga ejecución del lado del servidor</p><p>Para consultar la referencia completa de características, incluidas las opciones de búsqueda, el acceso a múltiples campos, los objetos anidados y el manejo de campos de valores múltiples, consulta la <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/linq-to-esql">documentación de LINQ a ES|QL</a>.</p><h2>Conclusión</h2><p>LINQ a ES|QL aporta toda la expresividad de LINQ de C# al lenguaje de búsqueda ES|QL de Elasticsearch, lo que te permite realizar búsquedas con tipado fuerte y combinables sin crear manualmente cadenas de texto. Con captura automática de parámetros, materialización de streaming y una arquitectura de paquetes en capas que escala desde el paquete de traducción autónomo hasta el cliente completo de Elasticsearch, se adapta naturalmente a aplicaciones .NET de cualquier tamaño. Instala el cliente más reciente, dirige tus expresiones LINQ a un índice y deja que el proveedor se encargue del resto.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Base de datos vectorial]]></category>
    <dc:creator><![CDATA[Florian Bernd,Martijn Laarman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa35fbcbbf4959f/6a1705b9dc55de19a4e00d07/e54132e915217063e9ed0ec45059c6cfc38e31dd-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Estadísticas ES|QL más rápidas con tablas hash de estilo suizo]]></title>
    <description><![CDATA[Cómo el hashing inspirado en Suiza y el diseño compatible con SIMD ofrecen mejoras consistentes y medibles en el lenguaje de búsqueda de Elasticsearch (ES|QL).]]></description>
    <content:encoded><![CDATA[<p>Recientemente reemplazamos partes clave de la implementación de tablas hash de Elasticsearch por un diseño de estilo suizo y observamos tiempos de construcción e iteración hasta 2–3 veces más rápidos en cargas de trabajo uniformes y de alta cardinalidad. El resultado es una latencia más baja, un mejor rendimiento y un desempeño más predecible para las operaciones de estadísticas y análisis del lenguaje de búsqueda de Elasticsearch (ES|QL).</p><h2>¿Por qué importa esto?</h2><p>La mayoría de los flujos de trabajo analíticos típicos acaban reduciéndose a agrupar datos. Ya sea para calcular el promedio de bytes por host, contar eventos por usuario o agregar métricas en diferentes dimensiones, la operación de núcleo es la misma: asignar claves a grupos y actualizar los agregados en ejecución.</p><p>A pequeña escala, casi cualquier tabla hash razonable funciona bien. A gran escala (cientos de millones de documentos y millones de grupos distintos), los detalles empiezan a importar. Los factores de carga, la estrategia de sondeo, el diseño de la memoria y el comportamiento de la memoria caché pueden marcar la diferencia entre un rendimiento lineal y una barrera de fallos de caché.</p><p>Elasticsearch ha soportado estas cargas de trabajo durante años, pero siempre estamos buscando oportunidades para modernizar los algoritmos de núcleo. Por lo tanto, evaluamos un enfoque más reciente inspirado en las tablas suizas y lo aplicamos a cómo ES|QL calcula las estadísticas.</p><h2>¿Qué son realmente las tablas suizas?</h2><p>Las tablas suizas son una familia de tablas hash modernas popularizadas por la SwissTable de Google y posteriormente adoptadas en Abseil y otras bibliotecas.</p><p>Las tablas hash tradicionales pasan mucho tiempo persiguiendo punteros o cargando claves solo para descubrir que no coinciden. La característica definitoria de las tablas suizas es la capacidad de rechazar la mayoría de las sondas usando una pequeña estructura de matriz residente en caché, almacenada separadamente de las claves y valores, llamadas <em>bytes de control</em>, para reducir significativamente el tráfico de memoria.</p><p>Cada byte de control representa un solo slot y, en nuestro caso, codifica dos cosas: si el slot está vacío y una huella corta derivada del hash. Estos bytes de control están dispuestos de forma continua en la memoria, típicamente en grupos de 16, lo que los hace ideales para el <a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">procesamiento de una sola instrucción y múltiples datos</a> (SIMD).</p><p>En lugar de sondear una ranura a la vez, las tablas suizas escanean todo un bloque de bytes de control utilizando instrucciones vectoriales. En una sola operación, la CPU compara la huella digital de la clave entrante con 16 ranuras y filtra las entradas vacías. Solo los pocos candidatos que sobreviven a esta ruta rápida requieren cargar y comparar las claves reales.</p><p>Este diseño intercambia una pequeña cantidad de metadatos adicionales por una mejor localización de caché y muchas menos cargas aleatorias. A medida que la tabla crece y las cadenas de sonda se alargan, esas propiedades se vuelven cada vez más valiosas.</p><h2>SIMD en el centro</h2><p>La verdadera estrella del espectáculo es SIMD.</p><p>Los bytes de control no solo son compactos, sino que también están diseñados explícitamente para ser procesados con instrucciones vectoriales. Una sola comparación SIMD puede verificar 16 huellas dactilares a la vez, lo que convierte lo que normalmente sería un bucle en un puñado de operaciones amplias. Por ejemplo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f710e87dd749ab3/6a170cc46234e052dadb1a49/bd418778f0c6144f8f5f18419f6220ac0c935c7a-903x407.png" alt="SIMD en el centro de Elasticsearch" /><p>En la práctica, esto significa:</p><ul><li><p>Menos ramas.</p></li><li><p>Cadenas de sondeo más cortas.</p></li><li><p>Menos cargas de la memoria de claves y valores.</p></li><li><p>Mucho mejor utilización de las unidades de ejecución de la CPU.</p></li></ul><p>La mayoría de las búsquedas nunca pasan del escaneo de bytes de control. Cuando lo hacen, el trabajo restante es enfocado y previsible. Este es precisamente el tipo de carga de trabajo en el que destacan las CPU modernas.</p><h2>SIMD bajo el capó</h2><p>Para los lectores a quienes les gusta echar un vistazo por dentro, aquí está lo que sucede al insertar una nueva clave en la tabla. Utilizamos la API Panama Vector con vectores de 128 bits, por lo que opera en 16 bytes de control en paralelo.</p><p>El siguiente fragmento muestra el código generado en un Intel Rocket Lake con AVX-512. Aunque las instrucciones reflejan ese entorno, el diseño no depende de AVX-512. Las mismas operaciones vectoriales de alto nivel se emiten en otras plataformas usando instrucciones equivalentes (por ejemplo, AVX2, SSE o NEON).</p>; Load 16 control bytes from the control block
vmovdqu xmm0, XMMWORD PTR [r9+r10*1+0x10]

; Broadcast the 7-bit fingerprint of the new key across the vector
vpbroadcastb xmm1, r11d

; Compare all 16 control bytes to the new fingerprint
vpcmpeqb k7, xmm0, xmm1
kmovq rbx, k7

; Check if any matches were found
test rbx, rbx
jne &lt;handle_match&gt;<p>Cada instrucción tiene una función clara en el proceso de inserción:</p><ul><li><p><code>vmovdqu</code>: Carga 16 bytes de control consecutivos en el registro <code>xmm0</code> de 128 bits.</p></li><li><p><code>vpbroadcastb</code>:Replica la huella digital de 7 bits de la nueva clave en todos los carriles del registro <code>xmm1</code>.</p></li><li><p><code>vpcmpeqb</code>: Compara cada byte de control con la huella digital transmitida, lo que genera una máscara de posibles coincidencias.</p></li><li><p><code>kmovq</code> + <code>test</code>: Mueve la máscara a un registro de propósito general y comprueba rápidamente si existe una coincidencia.</p></li></ul><p>Finalmente, decidimos sondear grupos de 16 bytes de control a la vez, ya que las pruebas de rendimiento mostraron que expandirse a 32 o 64 bytes con registros más amplios no proporcionaba ningún beneficio de rendimiento medible.</p><h2>Integración en ES|QL</h2><p>Adoptar el hash al estilo suizo en Elasticsearch no fue solo un reemplazo inmediato. ES|QL tiene exigencias estrictas en cuanto a contabilidad de memoria, seguridad e integración con el resto del motor de cálculo.</p><p>Integramos la nueva tabla hash estrechamente con la gestión de memoria de Elasticsearch, que incluye el reciclador de páginas y la contabilidad del interruptor de circuito, lo que garantiza que las asignaciones permanezcan visibles y limitadas. Las agregaciones de Elasticsearch se almacenan densamente y se indexan por un ID de grupo, lo que mantiene el diseño de memoria compacto y rápido para la iteración, además de habilitar ciertas optimizaciones de rendimiento al permitir el acceso aleatorio.</p><p>Para las claves de bytes de longitud variable, almacenamos en caché el hash completo junto con el ID del grupo. Esto evita la recomputación de costosos códigos hash durante el sondeo y mejora la localidad de la caché al mantener los metadatos relacionados juntos. Durante el reprocesamiento, podemos confiar en el hash en caché y en los bytes de control sin inspeccionar los valores en sí, lo que mantiene bajos los costos de redimensionamiento.</p><p>Una simplificación importante en nuestra implementación es que las entradas nunca se eliminan. Esto elimina la necesidad de <em>marcadores</em> (marcadores para identificar ranuras previamente ocupadas) y permite que las ranuras vacías permanezcan verdaderamente vacías, lo que mejora aún más el comportamiento de la sonda y mantiene eficientes los escaneos de bytes de control.</p><p>El resultado es un diseño que se ajusta naturalmente al modelo de ejecución de Elasticsearch a la vez que preserva las características de rendimiento que hacen atractivas a las tablas suizas.</p><h2>¿Cómo funciona?</h2><p>En cardinalidades pequeñas, las tablas suizas rinden aproximadamente al mismo nivel que la implementación existente. Esto es lo que se espera: cuando las tablas son pequeñas, los efectos de la caché tienen menos importancia y hay poco que optimizar.</p><p>A medida que aumenta la cardinalidad, la imagen cambia rápidamente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09b13af2fe162f59/6a170cc66f7f04485c9148b8/24900afc47ab07b0e9933f6117b99d0f4613f794-962x599.png" alt="Estadísticas ES|QL con tablas hash de estilo suizo" /><p>El mapa de calor anterior traza los factores de mejora del tiempo para diferentes tamaños de clave (8, 32, 64 y 128 bytes) en cardinalidades desde 1,000 hasta 10,000,000 de grupos. A medida que aumenta la cardinalidad, el factor de mejora aumenta constantemente, y llega a hasta 2–3x para distribuciones uniformes.</p><p>Esta tendencia es exactamente lo que predice el diseño. Una cardinalidad más alta conduce a cadenas de sondeo más largas en las tablas hash tradicionales, mientras que el sondeo de estilo suizo continúa resolviendo la mayoría de las búsquedas dentro de los bloques de bytes de control amigables con SIMD.</p><h2>El comportamiento de la caché cuenta la historia</h2><p>Para comprender mejor las aceleraciones, ejecutamos el mismo JMH <a href="https://github.com/elastic/elasticsearch/pull/139343/files#diff-d0e0cc91a7495bf36b2d44eacce95f5185d01879e5f6c38089ac7a89aad17da7"><code>benchmarks</code></a> en Linux <code>perf</code> y capturamos estadísticas de caché y TLB.</p><p>En comparación con la implementación original, la versión suiza realiza aproximadamente un 60% menos de referencias de caché en general. Las cargas de la caché de último nivel disminuyen más de 4 veces, y los fallos de carga de la LLC caen en más de 6 veces. Dado que las omisiones de LLC a menudo se traducen directamente en accesos a la memoria principal, esta reducción por sí sola explica una gran parte de la mejora de extremo a extremo.</p><p>Más cerca de la CPU, vemos menos pérdidas de caché de datos L1 y casi 6 veces menos pérdidas TLB de datos, lo que apunta a una localidad espacial más estrecha y patrones de acceso a la memoria más predecibles.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb987a5bd98c0d7eb/6a170cc8a929cf9655ae0a25/6e49b7609fba83e33692cb9834552b6ca7e42a83-998x499.png" alt="Comportamiento de la caché: Estadísticas originales frente a ES|QL con tablas hash al estilo suizo" /><p>Esta es la recompensa práctica de los bytes de control compatibles con SIMD. En lugar de cargar repetidamente claves y valores desde ubicaciones de memoria dispersas, la mayoría de las pruebas se resuelven escaneando una estructura compacta residente en la caché. Menos memoria afectada significa menos fallos, y menos fallos significan consultas más rápidas.</p><h2>Resumen</h2><p>Al adoptar un diseño de tabla hash al estilo suizo y al inclinarnos por el sondeo amigable con SIMD, logramos una velocidad de 2 a 3 veces mayor para cargas de trabajo de estadísticas ES|QL de alta cardinalidad, junto con un rendimiento más estable y predecible.</p><p>Este trabajo destaca cómo las estructuras de datos modernas con conocimiento de CPU pueden desbloquear ganancias sustanciales, incluso para problemas bien conocidos, como las tablas hash. Hay más para explorar aquí, como especializaciones adicionales de tipo primitivo y el uso en otras rutas de alta cardinalidad, como las uniones, que son solo parte del esfuerzo más amplio y continuo para modernizar continuamente los internos de Elasticsearch.</p><p>Si te interesan los detalles o quieres seguir el trabajo, echa un vistazo a esta <a href="https://github.com/elastic/elasticsearch/pull/139343">solicitud de extracción</a> y <a href="https://github.com/elastic/elasticsearch/issues/138799">problema meta</a> en Github.</p><p>¡Feliz hash!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-swiss-hash-stats</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-swiss-hash-stats</guid>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Matthew Alp,Nik Everett]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf76dd688c5b737e6/6a170cc9839dfa7fc7dcff40/21036e031070f14faccb2b53b22723de2750c391-1280x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 19 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Introducción del soporte de Elasticsearch en Google MCP Toolbox for Databases]]></title>
    <description><![CDATA[Descubre cómo el soporte de Elasticsearch ya está disponible en Google MCP Toolbox for Databases y aprovecha las herramientas ES|QL para integrar de forma segura tu índice con cualquier cliente MCP.]]></description>
    <content:encoded><![CDATA[<p>En este artículo, explicaremos cómo usar Google MCP Toolbox con <a href="https://github.com/elastic/elasticsearch">Elasticsearch</a> para crear una herramienta sencilla que permita extraer información de un índice de Elasticsearch.</p><p>Recientemente contribuimos al proyecto open source <a href="https://github.com/googleapis/genai-toolbox">Google MCP Toolbox for Databases</a> agregando soporte para Elasticsearch como base de datos.</p><p>Con esta nueva característica, ahora puedes usar Google MCP Toolbox para conectarte a Elasticsearch y “conversar” directamente con tus datos.</p><h2>Elasticsearch</h2><p>Es necesario tener una instancia de Elasticsearch en funcionamiento. Puedes activar una prueba gratuita en <a href="https://www.elastic.co/cloud">Elastic Cloud</a> o instalarla localmente utilizando el script <a href="https://github.com/elastic/start-local">start-local</a>:</p>curl -fsSL https://elastic.co/start-local | sh<p>Esto instalará Elasticsearch y Kibana en tu computadora y generará una clave API que se utilizará para configurar Google MCP Toolbox.</p><p>La clave de API se mostrará como salida del comando anterior y se almacenará en un archivo .env. en la carpeta elastic-start-local.</p><h2>Instala el set de datos de ejemplo</h2><p>Tras la instalación, puedes iniciar sesión en Kibana con el nombre de usuario <em>elastic</em> y la contraseña generada por el script start-local (almacenada en un archivo .env).</p><p>Puedes instalar el conjunto de datos de <strong>pedidos de comercio electrónico </strong>disponible desde Kibana. Incluye un único índice llamado <strong>kibana_sample_data_ecommerce</strong> que contiene información sobre 4675 pedidos de un sitio web de comercio electrónico. Para cada pedido, tenemos la siguiente información:</p><ul><li><p>Información del cliente (nombre, identificación, fecha de nacimiento, correo electrónico, etc.)</p></li><li><p>Fecha del pedido</p></li><li><p>ID de pedido</p></li><li><p>Productos (lista de todos los productos con precio, cantidad, identificación, categoría, descuento, etc.)</p></li><li><p>SKU</p></li><li><p>Precio total (sin impuestos, con impuestos)</p></li><li><p>Cantidad total</p></li><li><p>Información geográfica (ciudad, país, continente, ubicación, región)</p></li></ul><p>Para instalar los datos de muestra, abre la página <strong>Integraciones</strong> en Kibana (busca “Integración” en la barra superior de búsqueda) e instala los “Datos de muestra”. Para obtener más detalles, consulta la documentación aquí: <a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana</a>.</p><p>El objetivo de este artículo es mostrar lo fácil que es configurar Google MCP Toolbox para conectarse a Elasticsearch e interactuar con el <strong>índice de kibana_sample_data_ecommerce</strong> usando lenguaje natural.</p><h2>Google MCP Toolbox</h2><p>Google MCP Toolbox es un servidor MCP open source diseñado para facilitar la interacción segura y eficiente de aplicaciones y agentes de IA con bases de datos. El proyecto, anteriormente conocido como el “GenAI Toolbox for Databases”, cambió su denominación después de adoptar la compatibilidad total con el <a href="https://www.anthropic.com/news/model-context-protocol">Protocolo de contexto de modelo</a> (MCP). Su propósito es eliminar el trabajo pesado que tradicionalmente se requiere al conectar agentes con bases de datos, gestionando la agrupación de conexiones, autenticación, observabilidad y otras preocupaciones operativas en segundo plano.</p><p>Esencialmente, Toolbox permite a los desarrolladores definir herramientas reutilizables de alto nivel que encapsulan las interacciones con la base de datos. Estas herramientas pueden ser invocadas por cualquier cliente compatible con MCP (como un agente de IA) sin requerir que el cliente implemente consultas SQL de bajo nivel o administre conexiones de base de datos. Este enfoque reduce drásticamente la cantidad de código repetitivo necesario para crear agentes compatibles con bases de datos, lo que permite integrar operaciones de datos avanzadas en solo unas pocas líneas de lógica de aplicación. Una vez definida una herramienta, se puede compartir entre varios agentes, marcos de trabajo o lenguajes (Figura 1).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte90070297ee83546/6a16fa29964cea694b08b972/137cea290bb70ad5da21853f9a6358cef4cf7451-1248x1056.png" alt="" /><p>Una de las ventajas principales de usar la Toolbox es el modelo de seguridad integrado. Los flujos de autenticación como OAuth2 y OIDC son compatibles de forma nativa, lo que permite a los desarrolladores evitar manejar o almacenar credenciales confidenciales de bases de datos en agentes. La plataforma también ofrece características de observabilidad (como métricas y rastreo) a través de OpenTelemetry, que es esencial para la depuración, la supervisión y los despliegues de producción. En conjunto, MCP Toolbox sirve como una interfaz unificada, segura y extensible para interactuar con tus datos desde cualquier sistema compatible con MCP.</p><h2>Cómo instalar MCP Toolbox</h2><p>Puedes instalar el servidor MCP Toolbox en Linux usando el siguiente comando:</p>export VERSION=0.21.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox<p>Si quieres instalarlo en macOS o Windows, puedes seguir las instrucciones <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/#installing-the-server">detalladas aquí</a>.</p><h2>Configura la Toolbox para Elasticsearch</h2><p>Para configurar el MCP Toolbox para Elasticsearch, necesitamos crear un archivo <strong>tools.yaml</strong>, de la siguiente manera:</p>sources:
  my-cluster:
    kind: elasticsearch
    addresses:
      - http://localhost:9200
    apikey: &lt;insert-here-api-key&gt;

tools:
  customer-orders:
    kind: elasticsearch-esql
    source: my-cluster
    description: Get the orders made by a customer identified by name.
    query: |
    	FROM kibana_sample_data_ecommerce | WHERE MATCH(customer_full_name, ?name, {"operator": "AND"})
    parameters:
      - name: name
        type: string
        description: The customer name.

toolsets:
  elasticsearch-tools:
    - customer-orders<p>Debes reemplazar el valor <strong>&lt;insert-here-api-key&gt;</strong> por una clave API válida de Elasticsearch. Si estás ejecutando Elasticsearch localmente usando start-local, puedes encontrar la clave de API en el archivo.env generado por start-local, bajo la variable <strong>ES_LOCAL_API_KEY</strong> . Si usas Elastic Cloud, puedes generar una clave API siguiendo el procedimiento <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys">descrito aquí</a>.</p><p>Las herramientas anteriores contienen la siguiente consulta ES|QL para Elasticsearch:</p><p>Si no estás familiarizado con ES|QL, es un lenguaje de búsqueda desarrollado por Elastic, similar a SQL, que puedes usar para buscar en uno o más índices. Puedes leer más sobre ES|QL en la documentación oficial <a href="https://www.elastic.co/docs/reference/query-languages/esql">aquí</a>.</p><p>La búsqueda anterior busca todos los pedidos almacenados en el <strong>índice kibana_sample_data_ecommerce</strong> que contienen el nombre del cliente especificado, usando el parámetro <strong>?name</strong> (el signo de interrogación indica un parámetro).</p><p>El nombre del cliente se define en la configuración YAML anterior empleando el texto de tipo y la descripción "El nombre del cliente".</p><p>Esta herramienta se puede usar para responder preguntas sobre los pedidos de un cliente, por ejemplo: <em>¿Cuántos pedidos realizó el cliente Foo en octubre de 2025?</em></p><p>Las descripciones de las herramientas y sus parámetros son esenciales para extraer la información relevante de la solicitud en lenguaje natural del usuario. Esta extracción se realiza utilizando la capacidad de <strong>llamada de función</strong> de un modelo de lenguaje grande (LLM). En la práctica, un LLM puede determinar qué función (herramienta) debe ejecutar para obtener la información necesaria, junto con los parámetros apropiados para esa función.</p><p>Para más información sobre las llamadas a funciones, sugerimos leer el artículo de <a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic">OpenAI sobre llamadas a funciones con Elasticsearch</a> de Ashish Tiwari.</p><h2>Ejecuta el servidor de Toolbox</h2><p>Puedes ejecutar la MCP Toolbox usando el archivo tools.yaml anterior con el siguiente comando:</p>./toolbox --tools-file tools.yaml --ui<p>El parámetro<strong> –ui</strong> ejecuta una aplicación sitio web en <a href="http://127.0.0.1:5000/ui">http://127.0.0.1:5000/ui</a> (Figura 2).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0fb3953fae603572/6a16fa2aa6c2b92763e794fb/3caf2339b632bafd5847af1ed8b33b518a25b8a2-1600x314.png" alt="" /><p>Puedes seleccionar la <strong>Herramientas</strong> &gt; <strong>pedidos-clientes</strong> e insertar un nombre de cliente en el parámetro <strong>nombre</strong> (por ejemplo, Gwen Sanders) y haz clic en el botón <strong>Ejecutar herramienta</strong>. Deberías ver una respuesta JSON como se indica en la Figura 3.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca02df8c78cb39ff/6a16fa2c961e6909b5c4cd22/b167e0142afb8919d9cedf6d0fa431d33d0e55f8-1600x933.png" alt="" /><p>La configuración se ha completado y MCP Toolbox puede ejecutar la herramienta de <strong>pedidos de clientes</strong> para comunicarse con Elasticsearch, ejecutando la consulta ES|QL.</p><h2>Usar la herramienta MCP Toolbox con Gemini CLI</h2><p>Podemos usar cualquier cliente del MCP para comunicarnos con MCP Toolbox for Database. Por ejemplo, podemos usar <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a>, una herramienta de línea de comandos, para usar Gemini. Puedes instalar Gemini CLI siguiendo las instrucciones indicadas <a href="https://geminicli.com/docs/get-started/installation/">aquí</a>.</p><p>Gemini CLI ofrece una extensión preconfigurada para MCP Toolbox, disponible en <a href="https://github.com/gemini-cli-extensions/mcp-toolbox">gemini-cli-extensions/mcp-toolbox</a>. Puedes instalar esta extensión ejecutando el comando siguiente:</p>gemini extensions install https://github.com/gemini-cli-extensions/mcp-toolbox<p>Tras la instalación, debes ir al directorio donde almacenaste el archivo de configuración tools.yaml para MCP Toolbox y ejecutar la CLI Gemini de la siguiente manera (este paso es necesario para que la CLI Gemini se configure automáticamente con MCP Toolbox):</p>gemini<p>Deberías ver un anuncio de salida como el que se muestra en la Figura 4.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf7245c10b9e32cd6/6a16fa2d964cea073208b976/0f22df6d3da13c1dc50dcb560414fa7c630eb9a7-1434x341.png" alt="" /><p>Puedes comprobar si MCP Toolbox está conectada usando el siguiente comando:</p>/mcp list<p>Deberías ver el <strong>mcp_toolbox</strong> con las herramientas de<strong> customer-orders</strong> listadas (Figura 5).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte857b42dbe604203/6a16fa2f8b73cb531b189e33/97edbc40de9e44f469f6f3a09427532be167de0e-493x155.png" alt="" /><p>Si el MCP Toolbox está conectado a la CLI de Gemini, ahora podemos intentar hacer algunas preguntas, como: “<em>Dame los pedidos del cliente Gwen Sanders</em>”. La CLI de Gemini solicitará entonces permiso para ejecutar la herramienta de pedidos de clientes desde el servidor mcp_toolbox (ver Figura 6).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfb7ea752c1a39da7/6a16fa30cdacbfdf937d27ff/c052f3b5e49436903b804280c0065f67ee02444b-1432x284.png" alt="" /><p>Tras la confirmación, Gemini CLI ejecutará la solicitud a MCP Toolbox, obteniendo una respuesta JSON como resultado y utilizándola para dar formato a la respuesta (Figura 7).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9f6e04987137a9f/6a16fa320811ae2297e9fef7/7ea5128f1705951c2757af6da4b456d394d4a080-1432x734.png" alt="" /><p>La respuesta de Gemini CLI será un reporte que indica que Gwen Sanders hizo solo un pedido de 2 productos, por un precio total de 132 euros.</p><h2>SDK de MCP Toolbox</h2><p>Google MCP Toolbox también ofrece un SDK para acceder a todas las funcionalidades desde un programa escrito en Go, Python y Javascript.</p><p>Por ejemplo, el SDK de Python está disponible en Github en la siguiente página: <a href="https://github.com/googleapis/mcp-toolbox-sdk-python">https://github.com/googleapis/mcp-toolbox-sdk-python</a>.</p><p>Es necesario crear un agente simple para conectarnos a MCP Toolbox. Debemos instalar los siguientes paquetes:</p>pip install toolbox-core
pip install google-adk<p>Además, crear un nuevo proyecto de agente usando los siguientes comandos:</p>adk create my_agent<p>Esto creará un nuevo directorio llamado <strong>my_agent</strong> con un <strong>archivo agent.py</strong>.</p><p>Actualiza <strong>my_agent/agent.py</strong> con el siguiente contenido para conectar con Toolbox:</p>from google.adk import Agent
from google.adk.apps import App
from toolbox_core import ToolboxSyncClient

client = ToolboxSyncClient("http://127.0.0.1:5000")

root_agent = Agent(
    name='root_agent',
    model='gemini-2.5-flash',
    instruction="You are a helpful AI assistant designed to search information about a dataset of ecommerce orders.",
    tools=client.load_toolset(),
)

app = App(root_agent=root_agent, name="my_agent")<p>Crea un archivo <strong>.env</strong> con tu clave de API de Google:</p>echo 'GOOGLE_API_KEY="YOUR_API_KEY"' &gt; my_agent/.env<p>Finalmente, podemos ejecutar el agente y observar los resultados. Para ejecutar el agente, puedes ejecutar el siguiente comando:</p>adk run my_agent<p>O bien, puedes servirlo a través de una interfaz web:</p>adk web --port 8000<p>En ambos casos, puedes interactuar con MCP Toolbox usando una interfaz de preguntas frecuentes. Por ejemplo, puedes hacer la pregunta anterior: <em>Dame las órdenes de la cliente Gwen Sanders</em>.</p><p>Para más información sobre los diferentes SDK, puedes consultar <a href="https://googleapis.github.io/genai-toolbox/sdks/">esta página de documentación</a>.</p><h2>Conclusión</h2><p>En este artículo, hemos mostrado la integración de Elasticsearch con Google MCP Toolbox for Databases. Mediante un sencillo archivo de configuración YAML, podemos definir un conjunto de herramientas que traducen preguntas en lenguaje natural a consultas de Elasticsearch utilizando el lenguaje ES|QL.</p><p>Mostramos cómo interactuar con el set de datos kibana_sample_data_ecommerce, que contiene pedidos de un sitio web de comercio electrónico. Con este archivo de configuración, podemos simplemente ejecutar el servidor MCP Toolbox y conectarnos a él desde cualquier cliente MCP.</p><p>Por último, mostramos cómo utilizar la CLI de Gemini como cliente para conectarse a MCP Toolbox for Databases y consultar los datos de comercio electrónico almacenados en Elasticsearch. Ejecutamos una consulta en lenguaje natural para recuperar información sobre pedidos para un cliente específico identificado por su nombre.</p><p>A medida que el ecosistema MCP sigue creciendo, este patrón (definiciones ligeras de herramientas respaldadas por infraestructuras seguras y listas para producción) crea nuevas oportunidades para construir agentes cada vez más capaces y conscientes de los datos con un esfuerzo mínimo. Ya sea que experimentes localmente con los sets de datos de muestra de Elastic o integres capacidades de búsqueda en una aplicación más amplia, MCP Toolbox ofrece una base fiable y extensible para interactuar con tus datos de Elasticsearch usando lenguaje natural.</p><p>Para obtener más información sobre el desarrollo de aplicaciones de IA agentic, puedes leer el artículo <a href="https://search-labs-redesign.vercel.app/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Creación de flujos de trabajo de IA agentic con Elasticsearch</a> de Anish Mathur y Dana Juratoni.</p><p>Para obtener más información sobre Google MCP Toolbox, puedes visitar <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/">https://googleapis.github.io/genai-toolbox/getting-started/introduction/</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/google-mcp-toolbox-elasticsearch-support</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/google-mcp-toolbox-elasticsearch-support</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Enrico Zimuel,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72d49893c51407cf/6a16fa33cf4f2502bab2cf7e/425a48691f436ed47c9bdfaf5d561ac122b2c472-1062x668.png" length="0" type="image/png"/>
    <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ES|QL en la versión 9.2: incorporación de la búsqueda inteligente (Lookup Joins) y compatibilidad con series temporales]]></title>
    <description><![CDATA[Explora tres actualizaciones separadas de ES|QL en Elasticsearch 9.2: un LOOKUP JOIN mejorado para una correlación de datos más expresiva, el nuevo comando TS para análisis de series temporales y el comando flexible INLINE STATS para la agregación.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch 9.2, lanzado en octubre, está repleto de avances significativos que hacen que el análisis de tus datos sea más rápido, más flexible y más accesible que nunca. En el corazón de esta versión se encuentran importantes mejoras a ES|QL, nuestro lenguaje de búsqueda canalizado, diseñado para brindar aún más valor directamente a los usuarios finales.</p><p>A continuación, se muestran las características de Elasticsearch 9.2 que transformarán tus flujos de trabajo de análisis de datos con ES|QL.</p><h2>Revolucionando la correlación de datos: Lookup Join más inteligente, rápido y flexible</h2><p>El comando <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join">LOOKUP JOIN</a> en ES|QL experimentó una transformación significativa en Elasticsearch 9.2, y se volvió mucho más eficiente y versátil. Lookup JOIN combina datos de la tabla de resultados de búsquedas ES|QL con registros coincidentes de un índice de modo de consulta especificado. Agrega campos del índice de búsqueda como nuevas columnas a la tabla de resultados en función de los valores coincidentes en el campo de combinación. Anteriormente, la unión de datos se limitaba a un solo campo y a una igualdad simple. ¡Ya no! Estas mejoras te permiten abordar escenarios complejos de correlación de datos con facilidad.</p><p><strong>Las mejoras clave de Lookup Join incluyen:</strong></p><ul><li><p><strong>Uniones de múltiples campos:</strong> únete fácilmente a varios campos. Por ejemplo, para unir <code>application_logs</code> con <code>service_registry</code> en <code>service_name</code>, <code>environment</code> y <code>version:</code></p></li></ul>FROM application_logs
| LOOKUP JOIN service_registry ON service_name, environment, version<ul><li><p><strong>Utilización de predicados de unión complejos con expresiones (vista previa técnica):</strong></p></li></ul><p>Ya no estás limitado a la igualdad simple. LOOKUP JOIN ahora permite especificar <strong>múltiples criterios</strong> de correlación e incorporar una variedad de <strong>operadores binarios,</strong> como ==, !=, &lt;, &gt;, &lt;= y &gt;=. Esto significa que puedes crear condiciones de unión muy matizadas, lo que te permite plantear preguntas mucho más complejas sobre tus datos.</p><p>Ejemplo 1: Búsqueda de métricas de aplicaciones con umbrales de SLA por servicio</p>FROM application_metrics
| LOOKUP JOIN sla_thresholds
      ON service_name == sla_service AND response_time &gt; sla_response_time<p>Ejemplo 2: Esta búsqueda calcula el monto adeudado, basado en políticas de precios regionales que cambian con el tiempo. Une tres sets de datos basados en condiciones complejas de rango de fechas e igualdad para calcular un <code>due_amount</code> final. La segunda unión de búsqueda utiliza el campo <code>measurement_date</code> del índice de <code>meter_readings</code> y el campo <code>region_id</code> del índice de <code>customers</code> para unirse al índice de <code>pricing_policies</code> y encontrar la política de precios correcta según la <code>region</code> y la <code>measurement_date</code>particular.</p>FROM meter_readings
| LOOKUP JOIN customers
      ON meter_id
| LOOKUP JOIN pricing_policies
      ON
        region_id == region AND
          measurement_date &gt;= policy_begin_date AND
          measurement_date &lt; policy_end_date
| EVAL due_amount = (kwh_consumed * rate_per_kwh + base_charge) * (1 + tax_rate)
| EVAL period = policy_name
| KEEP customer_name, period, due_amount, measurement_date, kwh_consumed,
    rate_per_kwh, base_charge, tax_rate
| SORT measurement_date<ul><li><p><strong>Grandes ganancias de rendimiento para uniones filtradas: </strong></p></li></ul><p>Hemos mejorado el rendimiento de las "uniones en expansión" que se filtran al utilizar condiciones de tabla de búsqueda. Las uniones expansivas producen múltiples coincidencias por fila de entrada, lo que puede generar grandes conjuntos de resultados intermedios. Esto empeora cuando muchas de esas filas se descartan mediante un filtro posterior. En la versión 9.2, optimizamos estas uniones al filtrar las filas innecesarias cuando se aplica un filtro a los datos de búsqueda, lo que evita procesar filas que se descartarían. ¡En algunos casos, estas uniones pueden ser hasta <strong>1000 veces más rápidas</strong>!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltce2c948a9a00a348/6a17f1a20b0bed8c08dd368b/002c014ee29b1aaf9ddeb8c554bb76efe3ed180c-1572x954.png" alt="Mejoras en el rendimiento de las uniones filtradas" /><p>Esta optimización es crucial cuando se trata de "uniones en expansión", en las que una búsqueda podría generar inicialmente muchas coincidencias potenciales. Al aplicar filtros de forma inteligente, solo se procesan los datos relevantes, lo que reduce drásticamente el tiempo de ejecución de las consultas y permite realizar análisis en tiempo real en sets de datos masivos. Esto significa que obtienes tu información mucho más rápido, incluso con operaciones de unión muy grandes o complejas.</p><p><strong>Compatibilidad de la búsqueda de agrupación con Cross-Cluster Search (CCS):</strong></p><p>Cuando Lookup Join se lanzó al mercado en las versiones 8.19 y 9.1, carecía de compatibilidad con Cross-Cluster Search (CCS). Para organizaciones que operan en múltiples agrupaciones, LOOKUP JOIN ahora se integra perfectamente con CCS en la versión 9.2. Simplemente coloca tu índice de búsqueda en todos los clústeres remotos donde desees realizar una unión, y ES|QL aprovechará automáticamente estos índices de búsqueda para unirse a sus datos remotos. Esto simplifica el análisis distribuido de datos y garantiza un enriquecimiento consistente en todo el despliegue de Elasticsearch.</p><p>Estas mejoras permiten correlacionar diversos conjuntos de datos con una precisión, velocidad y facilidad sin precedentes, lo que permite obtener información más profunda y útil sin necesidad de soluciones alternativas complejas ni pasos de preprocesamiento.</p><h2>Enriquece tus datos con facilidad: Kibana Discover UX para índices de búsqueda</h2><p>El enriquecimiento de datos debe ser sencillo, no un obstáculo. Introdujimos una experiencia de usuario fantástica en Discover de Kibana para crear y gestionar índices de búsqueda.</p><p><strong>Flujo de trabajo intuitivo:</strong> el autocompletado integral de Discover te guiará a través del proceso y te sugerirá índices de búsqueda y campos de unión en el editor ES|QL, lo que hace que sea increíblemente fácil conectar tus datos de monitoreo del tiempo de actividad con índices existentes. Escribe el nombre de un índice de búsqueda que no exista y obtén acceso directo al editor de búsqueda con un clic para crear el índice. Escribe el nombre de un índice de búsqueda existente y te sugeriremos una opción para editarlo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20a75bcfd25eb156/6a17f1a46864a4864db688a1/d36fd6ffd6bc0bf8d31067f6266445c68d15c71c-1400x184.png" alt="" /><p><strong>Gestión en línea (CRUD):</strong> mantén actualizados tus sets de datos de referencia con las funciones de edición en línea (crear, leer, actualizar, eliminar) directamente en Discover.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6263c5dd8c2c9d7e/6a17f1a5af47b614dbcde095/a0e4aa66540b1f725c24ccb0519d978415073bb6-1453x842.png" alt="Ejemplo de una consulta LOOKUP" /><p><strong>Carga de archivos sin esfuerzo: </strong>ahora puedes subir archivos directamente, como CSVs, dentro de Discover y usarlos instantáneamente en <code>LOOKUP JOIN</code>. ¡Ya no es necesario cambiar de contexto al saltar de un área a otra de Kibana!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltddfcc3580af0e5c6/6a17f1a73e9e45965aba1587/0f5dc2c712af4c4cada50292a7c8b836eb02aa67-1600x748.png" alt="Agrega fácilmente datos a un índice de búsqueda y úsalos en LOOKUP JOINs" /><p>Ya sea que estés usando mapping de IDs de usuario a nombres, agregando metadatos empresariales o uniendo archivos de referencia estáticos, esta característica democratiza el enriquecimiento de datos, lo que pone el poder de las uniones directamente en manos de cada usuario de forma rápida, sencilla y en un solo lugar.</p><h2>Preserva tu contexto: presentación de INLINE STATS (versión preliminar de tecnología)</h2><p>La agregación de datos es crucial, pero a veces necesitas ver los agregados <em>junto a</em> tus datos originales. Nos complace presentar <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by">INLINE STATS</a> como una <strong>característica de vista previa técnica</strong>.</p><p>A diferencia del comando <code>STATS</code>, que reemplaza tus campos de entrada por una salida agregada, <code>INLINE STATS</code> conserva todos tus campos de entrada originales y simplemente agrega los nuevos campos agregados. Esto te permite realizar más operaciones en tus campos de entrada originales <em>después</em> de la agregación, lo que genera un flujo de trabajo de análisis más continuo y flexible.</p><p>Por ejemplo, para calcular la distancia promedio de vuelo mientras se mantienen las filas de vuelo individuales:</p>FROM kibana_sample_data_flights
 | KEEP Carrier, Dest, DistanceMiles
 | INLINE STATS avgDist = ROUND(AVG(DistanceMiles))
       BY Dest
 | WHERE DistanceMiles &gt; avgDist<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9e0c7209db8a67ad/6a17f1a9e8fbcee5233a1a53/6eea943035e0ab371270084c504a06bb89f8b82b-1496x290.png" alt="Filtrar resultados de vuelos con una distancia mayor que el promedio mediante avgDist " /><p>En esta consulta, se agrega <code>avgDist</code> a cada fila con el <code>Dest</code>correspondiente (ination) por el que agrupamos y, como aún tenemos las columnas de información de vuelo, podemos filtrar los resultados a los vuelos con una distancia mayor que la media.</p><h2>Compatibilidad con series temporales en ES|QL (vista previa técnica)</h2><p>Elasticsearch usa <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds">flujos de datos temporales</a> para almacenar métricas. Estamos agregando soporte para agregaciones de series temporales en ES|QL, a través del comando fuente <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a>. Esto está disponible en Elastic Cloud Serverless y la versión 9.2 básica como vista previa técnica.</p><p>El análisis de series temporales se basa en gran medida en consultas de agregación que resumen los valores métricos a lo largo de las cubetas de tiempo, divididos por una o más dimensiones de filtrado. La mayoría de las consultas de agregación se basan en un procesamiento de dos pasos, con (a) una función de agregación interna que resume los valores por serie temporal y (b) una función de agregación externa, que combina los resultados de (a) en todas las series temporales.</p><p>El comando de origen <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a>, combinado con <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS</code></a>, proporciona una forma concisa, pero efectiva de expresar tales consultas sobre series temporales. Más concretamente, considera el siguiente ejemplo para calcular la tasa total de solicitudes por host y hora:</p>TS my_metrics
| WHERE @timestamp &gt; NOW() - 1 day
| STATS SUM(RATE(requests))
      BY host, TBUCKET(1h)<p>En este caso, la función de agregación de seriales temporales <code>RATE</code> se evalúa primero por series temporales y hora. Los agregados parciales producidos se combinan luego al usar <code>SUM</code> para calcular los valores agregados finales por host y por hora.</p><p>Puedes consultar la lista de funciones de agregación de series temporales disponibles <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/time-series-aggregation-functions">aquí</a>. Ahora se admite la tasa <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds#time-series-metric">de contador</a>, posiblemente la función de agregación más importante para procesar contadores.</p><p>El comando fuente <code>TS</code> está diseñado para combinarse con <code>STATS</code>, con ejecución ajustada para soportar eficientemente agregaciones de series temporales. Por ejemplo, los datos se ordenan antes de pasar a las <code>STATS</code>. Actualmente, no se permiten comandos de procesamiento que puedan enriquecer o alterar los datos temporales o su orden, como <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork"><code>FORK</code></a> o <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by"><code>INLINE STATS</code></a>, entre <code>TS</code> y <code>STATS</code>. Esta limitación podría eliminarse en el futuro.</p><p>La salida tabular <code>STATS</code> se puede procesar aún más con cualquier comando aplicable. Por ejemplo, la siguiente búsqueda calcula la relación del promedio de <code>cpu_usage</code> por host hospedado y hora con el valor máximo por host:</p>TS my_metrics
| STATS avg_usage = AVG(AVG_OVER_TIME(cpu_usage))
      BY host, time_bucket = TBUCKET(1h)
| INLINE STATS max_avg_usage = MAX(avg_usage)
      BY host
| EVAL ratio = avg_usage / max_avg_usage
| KEEP host, time_bucket, ratio
| SORT host, time_bucket DESC<p>Los datos temporales se almacenan en nuestro motor de almacenamiento columnar subyacente que funciona con los valores de documentos de Lucene. El comando TS agrega ejecución de consultas vectorizadas a través del motor de cómputo ES|QL. El rendimiento de las búsquedas a menudo se mejora en más de un orden de magnitud, en comparación con las consultas <a href="https://www.elastic.co/docs/reference/query-languages/querydsl">DSL</a> equivalentes, y está a la par con los sistemas establecidos específicos de métricas. En el futuro ofreceremos un análisis detallado de arquitectura y rendimiento, así que mantente alerta.</p><h2>Ampliación de tu conjunto de herramientas: funciones nuevas de ES|QL</h2><p>Para mejorar aún más la utilidad y versatilidad de ES|QL, agregamos un conjunto de <a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-functions-operators">funciones</a> nuevas:</p><p><strong>Manipulación de cadenas: </strong><a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-contains">CONTAINS</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/mv-functions#esql-mv_contains">MV_CONTAINS</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_encode">URL_ENCODE</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_encode_component">URL_ENCODE_COMPONENT</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_decode">URL_DECODE</a> para un procesamiento más robusto de texto y URL.</p><p><strong>Serie temporal y geoespacial:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/grouping-functions#esql-tbucket">TBUCKET</a> para cubetas de tiempo flexibles, TO_DENSE_VECTOR para operaciones vectoriales y un conjunto completo de <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/spatial-functions">funciones geoespaciales</a> como <code>ST_GEOHASH</code>, <code>ST_GEOTILE</code>, <code>ST_GEOHEX</code>, <code>TO_GEOHASH</code>, <code>TO_GEOTILE</code>, <code>TO_GEOHEX</code> para un análisis avanzado basado en la ubicación.</p><p><strong>Formato de fechas:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/date-time-functions#esql-day_name">DAY_NAME</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/date-time-functions#esql-month_name">MONTH_NAME</a> para representaciones de fechas más legibles.</p><p>Estas funciones te proporcionan un conjunto más completo de herramientas para manipular y analizar tus datos directamente dentro de ES|QL.</p><h2>Bajo el capó: Más rendimiento y eficiencia</h2><p>Más allá de las características destacadas, Elasticsearch 9.2 incluye varias optimizaciones de rendimiento en ES|QL. Aceleramos <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/where#like-and-rlike">RLIKE (LIST</a>) con pushdown en casos en los que la función reemplaza múltiples consultas RLIKE similares. Con <code>RLIKE</code> (LIST), podemos fusionar esas búsquedas en un único autómata y aplicar un autómata en vez de varios. También tenemos una carga más rápida de los campos de palabras clave con ordenamientos de índice y optimizaciones generales de consultas; estas mejoras aseguran que tus consultas ES|QL se ejecuten más eficientemente que nunca.</p><h2>¡Comienza hoy mismo!</h2><p>Elasticsearch 9.2 representa un avance significativo para ES|QL, ya que brinda un poder y flexibilidad sin precedentes a sus flujos de trabajo de análisis de datos. Te invitamos a explorar estas funciones nuevas y a experimentar la diferencia que generan.</p><p>Para obtener una lista completa de todos los cambios y mejoras de Elasticsearch 9.2, consulte las <a href="https://www.elastic.co/guide/en/elasticsearch/reference/9.2/release-notes-9.2.0.html">notas de lanzamiento oficiales</a>. ¡Feliz búsqueda!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-elasticsearch-9-2-multi-field-joins-ts-command</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-elasticsearch-9-2-multi-field-joins-ts-command</guid>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Tyler Perkins,Kostas Krikellas,Julian Kiryakov]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd30ea8cac3809cc3/6a17f1aa4b055dd8734322f8/415894e21e7758c907d6e60d4efc94230349beef-2012x1164.png" length="0" type="image/png"/>
    <pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ES| de ElasticsearchExperiencia en el editor QL frente al analizador de eventos PPL de OpenSearch]]></title>
    <description><![CDATA[Descubre cómo ES|Las funciones avanzadas de QL Editor aceleran tu flujo de trabajo, en contraste directo con el enfoque manual del PPL Event Analyzer de OpenSearch. 
]]></description>
    <content:encoded><![CDATA[<p>El <a href="https://www.elastic.co/blog/getting-started-elasticsearch-query-language">Lenguaje de Consultas Elasticsearch</a> (ES|QL), disponible de forma general desde la versión 8.14, introduce un lenguaje de consulta y un motor diseñados específicamente para búsqueda, observabilidad e investigaciones de seguridad. A diferencia del Lenguaje de Procesamiento por Tuberías (PPL) de OpenSearch, que toma mucho prestado de lenguajes por tuberías existentes, ES|QL se construyó desde cero para centrar en el pulido, la usabilidad y una integración fluida en toda la plataforma Kibana.</p><p>En este blog, exploraremos la experiencia de desarrollador del ES|QL Editor en Elasticsearch 9.1 comparándolo con PPL en el Event Analyzer (PPL para abreviar) en OpenSearch 3.2.</p><p>Las diferencias se hacen evidentes rápidamente: el ES|QL Editor ofrece autocompletado inteligente, ayuda contextual, consultas recomendadas y soporte para consultas entre clústeres que empoderan no solo a usuarios principiantes, sino también a expertos en nivel profesional. El diseño pensado para ES|La autoría QL se observa también en la inspección integrada de consultas y la integración holística a través de flujos de trabajo Kibana, por ejemplo, con Consultas Recientes.</p><p>PPL, en cambio, carece de soporte comparable para autocompletado, guía contextual y consultas distribuidas, lo que crea una curva de aprendizaje más pronunciada y más prueba y error.</p><h2>Creación de ES|QL es más fácil de aprender y usar</h2><p>Empezar con un nuevo lenguaje de consulta a menudo puede resultar abrumador. El ES|QL Editor<strong>, </strong>integrado directamente en <strong>Kibana Discover</strong>, está diseñado para facilitar ese proceso apoyando no solo la creación y depuración de consultas, sino también acelerando la rapidez con la que te familiarizas y te sientes cómodo con el lenguaje. Como el editor ayuda a reducir la fricción en las tareas cotidianas, puedes cambiar tu enfoque de la sintaxis y el ensayo y error a la solución. Puedes leer más sobre estos principios y cómo los integramos en <a href="https://www.elastic.co/search-labs/blog/improving-esql-editor-experience-in-kibana">el editor aquí</a>.</p><p>Esta experiencia como editor no se limita a Discover; es un módulo de código reutilizable que estamos trabajando en <strong>integrar en otras partes de Kibana</strong>, como paneles de control, alertas de Kibana y mapas de Kibana.</p><h3>Autocompletado inteligente: acelerando la creación de tu consulta</h3><p>El autocompletado en ES|QL Editor es completo, ofreciendo sugerencias para funciones, argumentos, literales e incluso funciones anidadas compatibles, una capacidad notablemente ausente en PPL. De hecho, fue reconstruida desde cero, como <a href="https://www.elastic.co/search-labs/blog/esql-autocomplete-rebuilt">se explica aquí</a>.</p><p>La validación se ejecuta a medida que el usuario escribe, como se describe <a href="https://www.elastic.co/search-labs/blog/improving-esql-editor-experience-in-kibana">aquí</a>, y sugerirá campos y también notificará al usuario sobre errores. Esto reduce la carga mental de los usuarios y ayuda a prevenir errores al principio del proceso de creación de la consulta.</p><p>Ejemplo: Se sugieren campos y funciones compatibles en este anidamiento:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb186fc80dcc66f5/6a17f311e9ea8737a5a9c720/a4d7b2819c34fab31bced7873257b8932b623fba-1502x473.png" alt="Sugerencia de anidamiento de campos y funciones compatibles." /><p>Algo que PPL no soporta:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt250ae79e8f33b598/6a17f3127f6f150d3dc09c74/6f3a89b1255b8a3a762022a2704fdd1c2987e5f9-1013x335.png" alt="Una función que PPL no soporta." /><p>Incluso con un autocompletado inteligente guiándote a través de funciones compatibles, argumentos y funciones anidadas, puede que aún quieras entender más a fondo las opciones disponibles. Aquí es precisamente donde ES|La ayuda contextual de QL Editor se vuelve invaluable, ofreciendo asistencia inmediata dentro del editor para aclarar y mejorar el desarrollo de tus consultas.</p><h3>Ayuda contextual al alcance de tu mano</h3><p>Información adicional sobre un comando generado por autocompletado está a un clic Ctrl-Espacio de distancia. Aparece inmediatamente un panel con detalles sobre la función, argumento o campo en cuestión. Esta interacción ligera mantiene a los desarrolladores en el flujo, proporcionando orientación justo a tiempo sin obligarles a abandonar el editor ni buscar en documentación externa. Esto reduce el tiempo perdido en búsquedas de sintaxis y ayuda a prevenir errores comunes antes de que ocurran.</p><p>Así es como se ve en acción:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt407e42acc7f53f15/6a17f3134b055d128d43231a/2797f9b5e002dbd83c46475c4ed4dcdc86144a01-1343x522.gif" alt="Un comando generado por autocompletación con espacio Ctrl para contexto adicional." /><p>PPL carece de este nivel de guía integrada, lo que obliga a los usuarios a depender de documentos externos o de prueba y error. Esa ausencia no es solo una característica que falta; Pone de manifiesto una disparidad más amplia en la filosofía del diseño. ES|QL prioriza una experiencia reflexiva y consciente del contexto que se adapta a los datos y al flujo de trabajo del usuario. Esta diferencia se hace más pronunciada a medida que las consultas se vuelven más complejas, haciendo que ES|QL Editor es un entorno más eficiente y fiable tanto para el aprendizaje como para el uso en producción.</p><h3>Consultas recomendadas que sean conscientes del contexto de los datos</h3><p>El ES|QL Editor proporciona consultas recomendadas que se adaptan automáticamente a los datos con los que trabajas, como los registros. En lugar de presentar un editor en blanco, pone a la luz los puntos de partida más relevantes para casos de uso comunes. Seleccionar una consulta recomendada genera una consulta canónica que es inmediatamente utilizable y puede refinar según sea necesario. Este enfoque acelera el desarrollo de consultas, especialmente para nuevos usuarios que aún no conocen la sintaxis completa.</p><p>Aquí tienes un ejemplo en el que un usuario selecciona la consulta "Detectar punto de cambio":</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc6c41f9e1e3cb40/6a17f3156864a43791b688d0/3284c9340d41298820fbf8c7702abad946b48248-925x370.gif" alt="¿Qué ocurre cuando un usuario selecciona la consulta &quot;Detectar Punto de Cambio&quot;." /><p>Compáralo con la experiencia de PPL:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6511bb51659feaf3/6a17f3166864a427c2b688d4/5c3e59dadc6210aede3366bdd081887bcbae7a54-969x798.png" alt="La experiencia PPL, con solo autocompletado básico." /><p>En cambio, PPL aquí solo ofrece autocompletado básico, dejándote armar las consultas sin contexto ni estructura. Esta falta de orientación puede provocar frustración y prueba y error.
Con ES|Consultas recomendadas con conocimiento de datos de QL Editor, puedes evitar empezar desde cero o memorizar la sintaxis para tareas rutinarias. El editor reduce la carga cognitiva, ayuda a prevenir errores y te permite centrarte en la resolución de problemas y en objetivos más amplios, como realizar búsquedas entre clústeres en lugar de lidiar con la construcción de consultas.</p><h2>Consulta intuitiva entre clústeres</h2><p>ES|El autocompletado del editor QL sigue siendo superior, incluso cuando se trabaja con múltiples clústeres <a href="https://elastic.aiops.work/search-labs/blog/esql-cross-cluster-search">remotos con CCS</a>. He aquí por qué:</p><h3>ES|QL Editor ofrece un autocompletado fluido incluso entre clústeres</h3><p>Autocompletado en el ES|QL Editor soporta no solo nombres de clústeres sino también<strong> índices locales y remotos</strong>. Como se explica <a href="https://www.elastic.co/search-labs/blog/esql-cross-cluster-search">aquí</a>, esto funciona gracias a una arquitectura de nodos coordinadores, que ayuda a validar y generar el plan de consulta para enviar a los nodos locales, ejecutar la consulta y agregar los resultados antes de enviarlos de vuelta al usuario. Sin introducir el nombre completo del clúster remoto, escribir ":" inicia el proceso de autocompletado para el índice remoto. Y no estás limitado al prefijo.</p><p>Esto facilita descubrir y consultar entre conjuntos de datos distribuidos sin memorizar convenciones de nombres ni cambiar de contexto.</p><p>Aquí tienes un ejemplo en el que el usuario simplemente escribe "clu:g" para localizar un índice remoto:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4fadd61aa6d2a212/6a17f3186864a4a385b688d8/bae1fbacb2320e4d07f41291ea57c9bcf15bf8a5-1092x523.gif" alt="Un ejemplo donde el usuario simplemente escribe &quot;clu:g&quot; para localizar un índice remoto." /><p>En marcado contraste, la PPL solo proporciona completitud básica para índices locales, con sugerencias restringidas a coincidencias con prefijos. Los clústeres remotos deben ser tipados manualmente, lo que aumenta la probabilidad de errores y ralentiza la creación de consultas.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3949402084cf303a/6a17f31a6864a482b1b688dc/e38793c0cc7c6cc7dc0fd4779a3e24ffbb6e0838-1094x263.gif" alt="Un ejemplo de cómo PPL solo proporciona completitud básica para índices locales, con sugerencias restringidas a coincidencias con prefijos." /><p>PPL solo proporciona completitud para índices locales y las sugerencias se restringen al prefijo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcaa81c403f354e1f/6a17f31c6864a4e620b688e0/5310f824942f94485cace2558ea72c56a0971e22-862x197.png" alt="Otro ejemplo de cómo PPL proporciona completitud solo para índices y sugerencias locales están restringidas al prefijo." /><p>ES|QL va más allá <a href="https://www.elastic.co/docs/solutions/search/cross-cluster-search#exclude-problematic-clusters">permitiendo exclusiones</a> directamente usando un signo negativo, dándote un control detallado sobre qué clústeres participan en tu exploración. Esta capacidad es especialmente valiosa al trabajar con entornos híbridos, donde puede ser necesario incluir u omitir conjuntos de datos específicos durante investigaciones entre clústeres.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf0ca78bfbceb60ae/6a17f31d6864a4199bb688e4/f23ca17f58fbf8e6d27419c028274cb91f30a549-937x78.png" alt="Un ejemplo de codificación de una investigación entre conglomerados." /><p>Estas mejoras reflejan el enfoque más amplio de Elasticsearch en reducir la fricción en la búsqueda entre clústeres. Al facilitar la construcción y gestión de consultas distribuidas, ES|QL Editor permite a analistas y desarrolladores centrar en los insights en lugar de la sintaxis, mientras que PPL deja mayor parte de esa carga al usuario. Y igual que el ES|QL Editor simplifica la creación de consultas entre clústeres y también proporciona herramientas para inspeccionar cómo se ejecutan esas consultas, garantizando transparencia y monitorización del rendimiento en múltiples clústeres.</p><h3>Uso de la herramienta Inspect para analizar los detalles de búsqueda entre clústeres</h3><p>La Herramienta de Inspección, accesible desde el ES|QL Editor está diseñado para proporcionar metadatos con información explícita sobre la ejecución de consultas en todos los clústeres. Esta funcionalidad está habilitada en Kibana Discover y es accesible directamente en el inspector de consultas, permitiéndote analizar el progreso y los detalles de la búsqueda, algo especialmente crucial para <strong>la búsqueda entre clústeres</strong> (<a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-cross-clusters">CCS</a>). Esta capacidad te ayuda a monitorizar el progreso de las búsquedas y a entender cómo funcionan las consultas entre conjuntos de datos distribuidos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf79eaeedac3600b9/6a17f31fabe0f26f06dfeb4b/5d1c204f70171526fff924c30ea8ad08121a0f8d-919x523.gif" alt="La herramienta de inspección para analizar detalles de búsqueda entre clústeres." /><p>Esta visibilidad detallada de la ejecución de consultas, especialmente para búsquedas distribuidas complejas, te permite garantizar un rendimiento y resolución de problemas óptimos.</p><p>Más allá de entender la mecánica de las consultas individuales, ES|QL Editor mejora aún más el recorrido del usuario al integrar profundamente funcionalidades esenciales en toda la plataforma Kibana, fomentando un flujo de trabajo fluido e ininterrumpido.</p><h2>Experiencia unificada de consultas con ES|QL y Kibana</h2><p>Una de las fuentes más comunes de fricción en el análisis guiado por consultas es el cambio de contexto. A menudo necesitas recordar consultas que ya escribiste. Cada interrupción rompe el foco y ralentiza las investigaciones. ES|QL Editor aborda esto integrando el historial de consultas en Kibana.</p><h3>Consultas recientes</h3><p>La función <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">de Consultas Recientes</a> en ES|QL Editor te ayuda a mantener el flujo haciendo que el trabajo pasado sea instantáneamente accesible. Dentro del ES|En el Editor QL en Discover, puedes ver, volver a ejecutar y poner estrellas en tus últimas 20 consultas, cerciorando que las consultas frecuentes o complejas estén a solo un clic de distancia. Estas consultas almacenadas también se transmiten a Kibana, integrar con paneles, visualizaciones, alertas y mapas, así que no necesitas salir de la pantalla actual ni volver a escribir comandos desde cero. Esto reduce el trabajo repetitivo, acelera las investigaciones y minimiza el riesgo de errores.</p><p>Por ejemplo, un usuario puede emplear las Consultas Recientes en ES|Editor de QL en Discover (y ponles la estrella):</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt49033a46a0805ec8/6a17f321e9ea87a505a9c724/eb0f9fe37b92dec421c394d31ae7d90afebe062e-1421x793.png" alt="Un ejemplo de cómo usar las consultas recientes en ES|Editor QL en Discover (y cómo ponerlos con una estrella)." /><p>Las consultas recientes están integradas en el Panel de Control:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta2f7c745cdc5e5f1/6a17f323a2929932a1d02da9/b84cd3a9bdec58812360d2aba4fc7713363ee3cc-1411x797.png" alt=" Consultas recientes integradas en un panel de control." /><p>PPL no ofrece una capacidad comparable, por lo que los usuarios dependen de copiar y pegar manualmente o notas externas para reutilizar consultas. La diferencia es más que la comodidad; refleja la estrategia de Elastic de construir ES|QL como un lenguaje verdaderamente integrado dentro del ecosistema Kibana. Con funciones como Consultas Recientes, ES|QL Editor no solo agiliza los flujos de trabajo diarios, sino que también sienta las bases para funcionalidades más avanzadas que ahora están en vista previa técnica, cerciorando que la experiencia siga evolucionando.</p><h2>Conclusión</h2><p>ES|QL es más que una sintaxis; refleja la estrategia de Elastic para mejorar la forma en que los usuarios buscan, exploran y analizan los datos. Con autocompletado inteligente, consultas recomendadas que consigan el contexto, guía en el editor y herramientas como Inspect, ES|QL Editor acelera el aprendizaje, reduce errores y simplifica flujos de trabajo complejos como el análisis entre clústeres. Integrado en Kibana, conecta consultas de forma fluida con paneles, alertas y visualizaciones para un flujo de trabajo ininterrumpido.</p><p>En resumen, ES|QL no es simplemente otro lenguaje canalizado; es un motor de consultas cuidadosamente diseñado combinado con una interfaz intuitiva que redefine fundamentalmente cómo interactúas con tus datos, ofreciendo una experiencia integrada, inteligente y en constante evolución que contrasta fuertemente con la naturaleza a menudo secuencial y menos guiada de OpenSearch PPL.</p><h2>¿Qué viene después?</h2><p>Este blog solo rasca la superficie de ES|QL. Las futuras publicaciones profundizarán en comparaciones con OpenSearch PPL y explorarán funciones geoespaciales, de visualización y de próximos editores como <a href="https://www.elastic.co/docs/explore-analyze/dashboards/add-controls">los Controles</a> (ya disponibles en Dashboards), pestañas de exploración de múltiples datos, búsqueda en segundo plano, historial de consultas más completo y FUSE.</p><h2>Prueba ES|QL hoy</h2><p>Puedes echar un vistazo a ES|QL en proyectos <a href="https://www.elastic.co/cloud/serverless">Serverless</a> de Elasticsearch totalmente gestionados con <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project">una prueba gratis</a>. También está disponible en versiones que abarcan desde la 8.11, pero se experimenta mejor en <a href="https://www.elastic.co/blog/whats-new-elastic-9-1-0">la 8.19 y la 9.1</a>.</p><p>Empieza en minutos en tu entorno local con un solo comando:</p>curl -fsSL https://elastic.co/start-local | sh]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/opensearch-vs-elasticsearch-ppl-esql</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/opensearch-vs-elasticsearch-ppl-esql</guid>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Libby Lin,George Kobar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte51b193824ff8084/6a17f3257f6f154b7bc09c7c/f1ff4ff4a00b3e5b084d4116cea6cabc82a2d816-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 18 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Presentamos el ES|Generador de consultas QL para el cliente Ruby de Elasticsearch]]></title>
    <description><![CDATA[Aprende a usar el recientemente lanzado ES|Generador de consultas QL para el cliente Ruby de Elasticsearch. Una herramienta para construir ES|QL consulta más fácilmente con código Ruby.]]></description>
    <content:encoded><![CDATA[<p>Recientemente lanzamos <a href="https://github.com/elastic/esql-ruby/"><code>elastic-esql</code></a>, una joya Ruby publicada bajo la licencia Apache 2. Esta gema te permite construir el <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES| de ElasticConsultas QL</a> en Ruby idiomático, que luego puedes usar con el ES|API de consulta QL. ES|QL permite a los desarrolladores filtrar, transformar y analizar los datos almacenados en Elasticsearch mediante consultas. Emplea "tuberías" ( <code>|</code> ) para trabajar paso a paso con los datos. La gema emplea funciones Ruby en su lugar, que puedes encadenar al objeto original para crear consultas más complejas:</p><p><strong>ESQL:</strong></p><p><strong>Rubí:</strong></p>Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending<h2>Instalación</h2><p>La gema puede instalar desde RubyGems con:</p>gem install elastic-esql<p>O puede agregar al archivo de gemas de un proyecto:</p>gem 'elastic-esql'<h2>Uso</h2><p>Puedes construir una consulta completa de una vez o crear un objeto de consulta con un comando fuente como <code>from</code> o <code>row</code> y luego encadenar ES|QL métodos para construir sobre ella.</p>query = Elastic::ESQL.from('sample_data')
query.limit(2).sort('@timestamp')<p>La gema traduce el código a ES|QL en el método <code>to_s</code> , así que devuelve el ES|Consulta QL cuando se imprime o se convierte en una cadena:</p>query = Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending
query.to_s
# =&gt; "FROM sample_data | LIMIT 2 | SORT @timestamp DESC"<p>Puedes instanciar un objeto de consulta y mutar su estado inicial usando los equivalentes <code>!</code> de cada función:</p>query = Elastic::ESQL.from('sample_data')
query.to_s
# =&gt; "FROM sample_data"
query.limit!(2).sort!('@timestamp')
query.to_s
# =&gt; "FROM sample_data | LIMIT 2 | SORT @timestamp"<p>La herramienta ofrece formas cómodas de encadenar pasos extra a un ES|Función QL, como <code>enrich</code> y <code>sort</code>. Una vez que llamas <code>enrich</code> a un objeto <code>Elastic::ESQL</code> , puedes encadenar <code>on</code> y <code>with</code> a él:</p>esql.enrich!('policy').on('a').with({ name: 'language_name' })<p>También puedes encadenar <code>desc</code>, <code>asc</code>, <code>nulls_first</code> y <code>nulls_last</code> a tu consulta tras usar <code>sort</code>:</p>Elastic::ESQL.from('sample_data').sort('@timestamp').asc.to_s
# =&gt; 'FROM sample_data | SORT @timestamp ASC'

Elastic::ESQL.from('sample_data').sort('@timestamp').desc.nulls_first.to_s
# =&gt; 'FROM sample_data | SORT @timestamp DESC NULLS FIRST'<p>También soporta cadenas personalizadas, por si quieres escribir el ES|Consulta QL tú mismo, o usa una función que aún no se agregó a la biblioteca. <code>custom</code> se unirán a las cadenas al final de la consulta. Los agregará a medida que se envían a la función, sin agregar ningún carácter de la tubería. Se combinarán con el resto de la consulta mediante un carácter espacio.</p>esql = Elastic::ESQL.from('sample_data')
esql.custom('| MY_VALUE = "test value"').to_s
# =&gt; 'FROM sample_data | MY_VALUE = "test value"'<p>También puedes encadenar <code>custom</code> funciones:</p>esql.custom('| MY_VALUE = "test value"').custom('| ANOTHER, VALUE')
'FROM sample_data | MY_VALUE = "test value" | ANOTHER, VALUE'<h2>Usando el ES|QL Query Builder con el cliente Ruby</h2><p>Puedes usar el constructor de consultas directamente con <a href="https://github.com/elastic/elasticsearch-ruby">elasticsearch-ruby</a> y la API <code>esql.query</code> enviando el objeto de consulta:</p>require 'elasticsearch'
require 'elastic/esql'

client = Elasticsearch::Client.new
index = 'sample_data'

query = Elastic::ESQL.from(index)
                     .sort('@timestamp')
                     .desc
                     .where('event_duration &gt; 5000000')
                     .limit(3)
                     .eval({ duration_ms: 'ROUND(event_duration/1000000.0, 1)' })
client.esql.query(body: { query: query })<p>También puedes usarlo con el ES|QL Helper del cliente Ruby de Elasticsearch, <a href="https://www.elastic.co/search-labs/blog/esql-ruby-helper-elasticsearch">para saber más</a>:</p>require 'elasticsearch/helpers/esql_helper'

Elasticsearch::Helpers::ESQLHelper.query(client, query)<h2>Como herramienta independiente</h2><p>La gema está diseñada como una herramienta independiente para construir ES|QL consulta de forma idiomática. No tiene dependencias en tiempo de ejecución; puedes usarlo con el cliente oficial de Elasticsearch Ruby, o por separado.</p><p>La consulta generada puede usar con la API <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-esql-query"><code>esql.query</code></a> de cualquier forma que una aplicación interactúe con la API de Elasticsearch (Ruby o no). Una vez que una consulta se construye con <code>elastic-esql</code>, la cadena generada puede enviar a la API como el parámetro <code>query</code> en el cuerpo de la solicitud. </p><p>Anteriormente escribí sobre <a href="https://www.elastic.co/search-labs/blog/elasticsearch-ruby-tools">el uso de Elasticsearch con las herramientas Ruby populares</a>. Esta gema puede usar con cualquiera de las herramientas Ruby populares para consultar Elasticsearch con ES|QL.</p><h2>Conclusión</h2><p>Esta biblioteca está en desarrollo activo y la API final aún no se completó. Actualmente está lanzado como un avance técnico. Si tienes algún comentario sobre la API actual o su uso general, no dudes en <a href="https://github.com/elastic/esql-ruby/issues">abrir un nuevo número</a>. Por favor, consulta <a href="https://github.com/elastic/esql-ruby/?tab=readme-ov-file#ruby-esql-query-builder">el README</a> para saber más sobre el Ruby ES|Constructor de consultas QL.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-query-builder-elasticsearch-ruby-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-query-builder-elasticsearch-ruby-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Ruby]]></category>
    <dc:creator><![CDATA[Fernando Briano]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt85d112ccca541b9e/6a17dccb4b055d6bfd4320cc/f8e1263ab53d356824a4fc539084151be80899db-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Ingirir datos geoespaciales en Elasticsearch con Kibana para su uso en ES|QL]]></title>
    <description><![CDATA[Cómo usar Kibana y el procesador de ingesta csv para ingirse datos geoespaciales en Elasticsearch para usarlos con la búsqueda en el lenguaje de consultas de Elasticsearch (ES|QL). Elasticsearch cuenta con poderosas características de búsqueda geoespacial, que ahora están llegando a ES|QL por una facilidad de uso mucho mejorada y familiaridad con el OGC. Pero para usar estas características, necesitamos datos geoespaciales.]]></description>
    <content:encoded><![CDATA[<p>Recientemente publicamos un blog describiendo cómo emplear las nuevas <a href="https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one">características</a> <a href="https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one">de búsqueda geoespacial</a> <a href="https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one">en ES|QL,</a> el nuevo y poderoso <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">lenguaje de consultas por tubes</a> de Elasticsearch. Para usar estas características, necesitas tener datos geoespaciales en Elasticsearch. Así que en este blog, te mostraremos cómo ingerir datos geoespaciales y cómo usarlos en ES|Consultas QL.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc041ebca11476c6/6a16f70560084b31b93c4344/01fde3b1d714f12bf8673140c9f2f940d443de31-1440x823.jpg" alt="Búsqueda Geoespacial ESQL" /><h2>Importación de datos geoespaciales usando Kibana</h2><p>Los datos que usamos para los ejemplos del blog anterior se basaban en datos que usamos internamente para pruebas de integración. Para tu comodidad, lo incluimos aquí en forma de algunos archivos CSV que se pueden importar fácilmente usando Kibana. Los datos son una mezcla de aeropuertos, ciudades y límites urbanos. Puedes descargar los datos desde:</p><ul><li><p><a href="https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/main/supporting-blog-content/geospatial-data-ingest/airports/airports.csv">airports.csv</a></p><ul><li><p>Este contiene una fusión de tres conjuntos de datos:</p><ul><li><p>Aeropuertos (nombres, ubicaciones y datos relacionados) de <a href="https://www.naturalearthdata.com/downloads/10m-cultural-vectors/airports/">Natural Earth</a></p></li><li><p>Ubicaciones de las ciudades según <a href="https://simplemaps.com/data/world-cities">SimpleMaps</a></p></li><li><p>Elevaciones de aeropuertos según <a href="https://www.partow.net/miscellaneous/airportdatabase/">la base de datos global de aeropuertos</a></p></li></ul></li></ul></li><li><p><a href="https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/main/supporting-blog-content/geospatial-data-ingest/airports/airport_city_boundaries.csv">airport_city_boundaries.csv</a></p><ul><li><p>Esto contiene una fusión de nombres de aeropuertos y ciudades mencionados anteriormente con una fuente nueva:</p><ul><li><p>Límites de la ciudad según <a href="https://www.openstreetmap.org/">OpenStreetMap</a></p></li></ul></li></ul></li></ul><p>Como puedes imaginar, dedicamos un tiempo a combinar estas fuentes de datos en los dos archivos anteriores, con el objetivo de poder probar las características geoespaciales de ES|QL. Puede que esto no sea exactamente lo mismo que tus necesidades específicas de datos, pero espero que esto te dé una idea de lo que es posible. En individuo, queremos demostrar algunas cosas interesantes:</p><ul><li><p>Importación de datos con campos geoespaciales junto con otros datos indexables</p></li><li><p>Importar datos <code>geo_point</code> y <code>geo_shape</code> y usarlos juntos en consultas</p></li><li><p>Importación de datos en dos índices que pueden unir mediante una relación espacial</p></li><li><p>Crear una canalización de ingestión para facilitar futuras importaciones (más allá de Kibana)</p></li><li><p>Algunos ejemplos de procesadores de ingest, como <code>csv</code>, <code>convert</code> y <code>split</code></p></li></ul><p>Aunque en este blog hablaremos sobre cómo trabajar con datos CSV, es importante entender que existen <a href="https://www.elastic.co/guide/en/kibana/current/import-geospatial-data.html">varias formas</a> <a href="https://www.elastic.co/guide/en/kibana/current/import-geospatial-data.html">de</a> <a href="https://www.elastic.co/guide/en/kibana/current/import-geospatial-data.html">agregar datos geográficos usando</a> <a href="https://www.elastic.co/guide/en/kibana/current/import-geospatial-data.html">Kibana</a>. Dentro de la aplicación de mapas puedes subir datos delimitados como archivos de forma CSV, GeoJSON y ESRI, y también puedes dibujar formas directamente en el mapa. Para este blog nos centraremos en importar archivos CSV desde la página principal de Kibana.</p><h3>Importación de aeropuertos</h3><p>El primer expediente <a href="https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/main/supporting-blog-content/geospatial-data-ingest/airports/airports.csv">, airports.csv</a>, tiene algunas rarezas interesantes con las que tenemos que lidiar. En primer lugar, las columnas tienen espacios en blanco adicionales que las separan, algo que no es típico de los archivos CSV. En segundo lugar, el campo <code>type</code> es un campo de varios valores, que necesitamos separar en cuerpos separados. Por último, algunos campos no son cadenas y necesitan convertir al tipo correcto. Todo esto se puede hacer empleando la instalación de importación CSV de Kibana.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt393bc416bf425301/6a16f707839dfa1e86dcfca9/b1afd8c95973bec32f229a9adfaef14680b09a8e-1944x478.png" alt="Kibana Upload - Vista previa" /><p>Empieza en la página principal de Kibana. Hay una sección llamada "Empezar agregando integraciones", que tiene un enlace llamado "Subir un archivo":</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte12fc741edab20d3/6a16f709acf088600fbe98bf/996372bb3a52859cc840bd1d8f984a33a0e7ada3-1180x410.png" alt="Kibana Home - Sube un archivo" /><p>Haz clic en este enlace y serás llevado a la página "Subir archivo". Aquí puedes arrastrar y soltar el archivo <code>airports.csv</code> , y Kibana analizará el archivo y te mostrará una vista previa de los datos. Debería detectar automáticamente el delimitador como una coma, y la primera fila como la fila de cabecera. Sin embargo, probablemente no recortó el espacio en blanco extra entre las columnas, ni determinó los tipos de campos, suponiendo que todos los campos sean <code>text</code> o <code>keyword</code>. Tenemos que arreglar esto.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf65a09ec0c7a61ad/6a16f70ad7c0227aedde626f/1d9ffa6cdbc0d67a228be1a747ec1ebda6a63099-1800x538.png" alt="Kibana Upload - Vista previa" /><p>Haz clic <code>Override settings</code> y marca la casilla para <code>Should trim fields</code>y <code>Apply</code> para cerrar la configuración. Ahora necesitamos fijar los tipos de los campos. Esto está disponible en la página siguiente, así que adelante y haz clic <code>Import</code>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda9a5f85d8e693dc/6a16f70c60084b72f53c4348/a97aceffb1858eae26a213336913505ae9923b03-1800x526.png" alt="Kibana Upload - Importación" /><p>Primero elige un nombre de índice y luego selecciona <code>Advanced</code> para acceder a la página de mapeo de campos e ingesta del procesador.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt56705083cdd9eb18/6a16f70d66c4f93350f8bdbb/058b0fb09da0b8d7f9c9b0a9c83b8b485ce43925-1440x629.png" alt="Kibana Upload - Mapeos de campos" /><p>Aquí necesitamos hacer cambios tanto en los mapeos de campos del índice como en la canalización de ingesta para importar los datos. En primer lugar, aunque Kibana probablemente detectó automáticamente el campo <code>scalerank</code> como <code>long</code>, percibió erróneamente los campos <code>location</code> y <code>city_location</code> como <code>keyword</code>. Edítalos para que <code>geo_point</code>, y acabes con mapeos que se ven algo así como:</p>{
  "properties": {
    "abbrev":        { "type": "keyword" },
    "city":          { "type": "keyword" },
    "city_location": { "type": "geo_point" },
    "country":       { "type": "keyword" },
    "elevation":     { "type": "double" },
    "location":      { "type": "geo_point" },
    "name":          { "type": "text" },
    "scalerank":     { "type": "long" },
    "type":          { "type": "keyword" }
  }
}<p>Aquí tienes cierta flexibilidad, pero ten en cuenta que el tipo que elijas afectará a cómo se indexa el campo y qué tipo de consultas son posibles. Por ejemplo, si dejas <code>location</code> tal <code>keyword</code> no puedes realizar ninguna consulta de búsqueda geoespacial sobre él. De manera similar, si dejas <code>elevation</code> tal <code>text</code> no puedes realizar consultas numéricas por rango.</p><p>Ahora es el momento de arreglar la pipeline de ingest. Si Kibana detectó automáticamente <code>scalerank</code> como <code>long</code> anterior, también agregó un procesador para convertir el campo en un <code>long</code>. Necesitamos agregar un procesador similar para el campo <code>elevation</code> , esta vez convirtiéndolo a <code>double</code>. Edita la canalización para cerciorarte de que tienes esta conversión en marcha. Antes de almacenar esto, queremos una conversión más, para dividir el campo de <code>type</code> en varios campos. Agregar un procesador <code>split</code> a la canalización, con la siguiente configuración:</p>{
  "split": {
    "field": "type",
    "separator": ":",
    "ignore_missing": true
  }
}<p>La pipeline final de ingesta debería ser la siguiente:</p>{
  "description": "Ingest pipeline created by text structure finder",
  "processors": [
    {
      "csv": {
        "field": "message",
        "target_fields": [
          "abbrev",
          "name",
          "scalerank",
          "type",
          "location",
          "country",
          "city",
          "city_location",
          "elevation"
        ],
        "ignore_missing": false,
        "trim": true
      }
    },
    {
      "convert": {
        "field": "scalerank",
        "type": "long",
        "ignore_missing": true
      }
    },
    {
      "convert": {
        "field": "elevation",
        "type": "double",
        "ignore_missing": true
      }
    },
    {
      "split": {
        "field": "type",
        "separator": ":",
        "ignore_missing": true
      }
    },
    {
      "remove": {
        "field": "message"
      }
    }
  ]
}<p>Ten en cuenta que no agregamos un procesador de conversión para los campos <code>location</code> y <code>city_location</code> . Esto se debe a que el tipo <code>geo_point</code> en el mapeo de campos ya entiende el formato WKT de los datos en estos campos. El tipo <code>geo_point</code> puede comprender una variedad de formatos, incluyendo <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html">WKT, GeoJSON y más</a>. Si tuviéramos, por ejemplo, dos columnas en el archivo CSV para <code>latitude</code> y <code>longitude</code>, tuvimos que agregar un procesador <code>script</code> o un <code>set</code> para combinarlas en un solo campo <code>geo_point</code> (por ejemplo, <code>"set": {"field": "location", "value": "{{lat}},{{lon}}"}</code>).</p><p>Ahora estamos listos para importar el archivo. Haz clic <code>Import</code> y los datos se importarán al índice con los mapeos y la pipeline de ingesta que acabamos de definir. Si hay errores al absorber los datos, Kibana los reportará aquí, así que puedes editar los datos fuente o la pipeline de ingesta y volver a intentarlo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte81621425c7289c5/6a16f70f839dfaf608dcfcad/55dde2940c7aba66ce8257d15007ef797b8a5107-1440x415.png" alt="Kibana Upload - Importación" /><p>Fíjate que se creó una nueva canalización de ingestión. Esto puede comprobar yendo a la sección <code>Stack Management</code> de Kibana y seleccionando <code>Ingest pipelines</code>. Aquí puedes ver la canalización que acabamos de crear y editarla si es necesario. De hecho, la sección <code>Ingest pipelines</code> puede usar para crear y probar canalizaciones de ingest, una función muy útil si planeas hacer ingestas aún más complejas.</p><p>Si quieres explorar estos datos inmediatamente, baja a las secciones posteriores, pero si también quieres importar los límites de la ciudad, sigue leyendo.</p><h3>Importación de los límites de la ciudad</h3><p>El archivo de límites de la ciudad disponible en <a href="https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/main/supporting-blog-content/geospatial-data-ingest/airports/airport_city_boundaries.csv">airport_city_boundaries.csv</a> es un poco más sencillo de importar que el ejemplo anterior. Contiene un campo <code>city_boundary</code> que es una representación WKT del límite de la ciudad como <code>POLYGON</code>y un campo <code>city_location</code> que es una representación <code>geo_point</code> de la ubicación de la ciudad. Podemos importar estos datos de forma similar a los datos del aeropuerto, pero con algunas diferencias:</p><ul><li><p>Tuvimos que seleccionar la opción de anulación <code>Has header row</code> ya que no se detectó automáticamente</p></li><li><p>No fue necesario recortar campos, ya que los datos ya estaban limpios de espacio en blanco extra</p></li><li><p>No tuvimos que editar la tubería de ingesta porque todos los tipos eran de cadena o de tipos espaciales</p></li><li><p>Sin embargo, tuvimos que editar los mapeos de campos para establecer el campo <code>city_boundary</code> a <code>geo_shape</code> y el campo <code>city_location</code> a <code>geo_point</code></p></li></ul><p>Nuestros mapeos finales de campo eran los siguientes:</p>{
  "properties": {
    "abbrev":        { "type": "keyword" },
    "airport":       { "type": "keyword" },
    "city":          { "type": "keyword" },
    "city_boundary": { "type": "geo_shape" },
    "city_location": { "type": "geo_point" },
    "region":        { "type": "text" }
  }
}<p>Como con la importación <code>airports.csv</code> anterior, simplemente haz clic <code>Import</code> para importar los datos al índice. Los datos se importarán junto con los mapeos que editamos y la pipeline de ingesta que Kibana definió.</p><h3>Explorando datos geoespaciales con herramientas de desarrollo</h3><p>En Kibana es habitual explorar los datos indexados con "Descubrir". Sin embargo, si tu intención es crear tu propia app usando ES|Consultas QL, podría ser más interesante intentar acceder a la API en bruto de Elasticsearch. Kibana tiene una consola práctica para experimentar escribiendo consultas. Esto se llama la consola <code>Dev Tools</code> y se puede encontrar en la barra lateral de Kibana. Esta consola se comunica directamente con el clúster Elasticsearch y puede usar para ejecutar consultas, crear índices y más.</p><p>Prueba lo siguiente:</p>POST /_query?error_trace=true&amp;format=txt
{
  "query": """
FROM airports
| EVAL distance = ST_DISTANCE(city_location, TO_GEOPOINT("POINT(12.565 55.673)"))
| WHERE distance &lt; 1000000 AND scalerank &lt; 6 AND distance &gt; 10000
| SORT distance ASC
| KEEP distance, abbrev, name, location, country, city, elevation
| LIMIT 10
  """
}<p>Esto debería proporcionar los siguientes resultados:</p><p>distancia</p><p>Abbrev</p><p>nombre</p><p>Ubicación</p><p>país</p><p>ciudad</p><p>elevación</p><p>273418.05776847183</p><p>JAMÓN</p><p>Hamburgo</p><p>POINT (10.005647830925 53.6320011640866)</p><p>Alemania</p><p>Norderstedt</p><p>17.0</p><p>337534.653466062</p><p>TXL</p><p>Berlin-Tegel Int'l</p><p>POINT (13.2903090925074 52.5544287044101)</p><p>Alemania</p><p>Hohen Neuendorf</p><p>38.0</p><p>483713.15032266214</p><p>OSL</p><p>Oslo Gardermoen</p><p>POINT (11.0991032762581 60.1935783171386)</p><p>Noruega</p><p>Oslo</p><p>208.0</p><p>522538.03148094116</p><p>BMA</p><p>Bromma</p><p>POINT (17.9456175406145 59.3555902065112)</p><p>Suecia</p><p>Estocolmo</p><p>15.0</p><p>522538.03148094116</p><p>ARN</p><p>Arlanda</p><p>POINT (17.9307299016916 59.6511203397372)</p><p>Suecia</p><p>Estocolmo</p><p>38.0</p><p>624274.8274399083</p><p>DUS</p><p>Düsseldorf Int'l</p><p>POINT (6.76494446612174 51.2781820420774)</p><p>Alemania</p><p>Düsseldorf</p><p>45.0</p><p>633388.6966435644</p><p>PRG</p><p>Ruzyn</p><p>POINT (14.2674849854076 50.1076511703671)</p><p>Chequia</p><p>Praga</p><p>381.0</p><p>635911.1873311149</p><p>AMS</p><p>Schiphol</p><p>PUNTO (4.76437693232812 52.3089323889822)</p><p>Países Bajos</p><p>Hoofddorp</p><p>-3.0</p><p>670864.137958866</p><p>FRA</p><p>Nt'l de Frankfurt</p><p>POINT (8.57182286907608 50.0506770895207)</p><p>Alemania</p><p>Fráncfort</p><p>111.0</p><p>683239.2529970079</p><p>WAW</p><p>Okecie Int'l</p><p>POINT (20.9727263383587 52.171026749259)</p><p>Polonia</p><p>Piaseczno</p><p>111.0</p><h2>Visualización de datos geoespaciales con Kibana Maps</h2><p>Kibana Maps es una herramienta poderosa para visualizar datos geoespaciales. Puede emplear para crear mapas con múltiples capas, cada capa representando un conjunto de datos diferente. Los datos pueden filtrar, agregar y estilizar de diversas maneras. En esta sección, te mostraremos cómo crear un mapa en Kibana Maps usando los datos que importamos en la sección anterior.</p><p>En el menú Kibana, navega hasta <code>Analytics</code>-&gt;<code>Maps</code> para abrir una nueva vista del mapa. Haz clic en <code>Add Layer</code> y selecciona <code>Documents</code>, eligiendo el <code>airports</code> de vista de datos y luego editando el estilo de capa para colorear los marcadores usando el campo <code>elevation</code> , para que podamos ver fácilmente la altura de cada aeropuerto.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcc4736d88c1abc9/6a16f7102b835f7353f4afca/9e63726d7c059331e6e20e474f8abee53dc2cbb4-840x388.png" alt="Kibana Maps - Estilo de Capa de Aeropuertos" /><p>Haz clic en 'Conservar cambios' para almacenar el mapa:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ec3cc535b5c21d8/6a16f71375879ec091fe15dc/32ad1dadb5d341a2b66662c58638b781122fc22c-2852x1528.png" alt="Kibana Maps - Aeropuertos" /><p>Ahora agrega una segunda capa, esta vez seleccionando la vista de datos <code>airport_city_boundaries</code> . Esta vez, usaremos el campo <code>city_boundary</code> para estilizar la capa y pondremos el color de relleno en azul claro. Esto mostrará los límites de la ciudad en el mapa. Cerciórate de reordenar las capas para que los marcadores del aeropuerto estén encima.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2932d7ff4a7206c9/6a16f7156f7f0409f09145c7/53dd65026f9a98c13f2cc4f9328a79242f0b70de-2854x1510.png" alt="Kibana Maps - Estilo de capa de límites de ciudades" /><h2>Unirías espaciales</h2><p>ES|QL no soporta comandos <code>JOIN</code>, pero puedes lograr un caso especial de unión usando el <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich">comando</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich"><code>ENRICH</code></a>. Este comando funciona de forma similar a una 'unión por la izquierda' en SQL, permitiéndote enriquecer resultados de un índice con datos de otro índice basar en una relación espacial entre los dos conjuntos de datos.</p><p>Por ejemplo, enriquezcamos los resultados de una tabla de aeropuertos con información adicional sobre la ciudad a la que sirven encontrando el límite de la ciudad que contiene la ubicación del aeropuerto, y luego realicemos algunas estadísticas sobre los resultados:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
| STATS
    centroid = ST_CENTROID_AGG(location),
    count = COUNT(city_location)
    BY region
| SORT count DESC
| LIMIT 10<p>Si ejecutas esta consulta sin preparar primero el índice de enriquecimiento, recibirás un mensaje de error como:</p>cannot find enrich policy [city_boundaries]<p>Esto se debe a que, como mencionamos antes, ES|QL no soporta comandos de <code>JOIN</code> verdadero. Una razón importante para ello es que Elasticsearch es un sistema distribuido, y las uniones son operaciones costosas que pueden ser difíciles de escalar. Sin embargo, el comando <code>ENRICH</code> puede ser bastante eficiente, ya que emplea índices de enriquecimiento especialmente preparados que se duplican a lo largo del clúster, permitiendo realizar uniones locales en cada nodo.</p><p>Para entender mejor esto, centrémonos en el comando <code>ENRICH</code> de la consulta anterior:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary<p>Este comando instruye a Elasticsearch para enriquecer los resultados recuperados del índice de <code>airports</code> y realizar una unión <code>intersects</code> entre el campo <code>city_location</code> del índice original y el campo <code>city_boundary</code> del índice de <code>airport_city_boundaries</code> , que usamos en algunos ejemplos anteriores. Pero parte de esta información no es claramente visible en esta consulta. Lo que sí vemos es el nombre de una política de enriquecimiento <code>city_boundaries</code>, y la información que falta está encapsulada dentro de esa definición de política.</p>{
  "geo_match": {
    "indices": "airport_city_boundaries",
    "match_field": "city_boundary",
    "enrich_fields": ["city", "airport", "region", "city_boundary"]
  }
}<p>Aquí podemos ver que realizará una consulta <code>geo_match</code> (<code>intersects</code> es el valor predeterminado), el campo a emparejar es <code>city_boundary</code>, y los <code>enrich_fields</code> son los campos que queremos agregar al documento original. Uno de esos campos, el <code>region</code> , se usó como clave de agrupación para el comando <code>STATS</code> , algo que no podríamos hacer sin esta capacidad de 'unión izquierda'. Para más información sobre las pólizas de enriquecimiento, consulte la <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/enrich-setup.html">documentación de enriquecimiento</a>.</p><p>Los índices y políticas de enriquecimiento en Elasticsearch fueron diseñados originalmente para enriquecer datos en el momento del índice, empleando datos de otro índice de enriquecimiento preparado. En ES|QL, sin embargo, el comando <code>ENRICH</code> funciona en el momento de la consulta y no requiere el uso de pipelines de ingest. Esto lo hace efectivamente bastante similar a un <code>LEFT JOIN</code>SQL, excepto que no puedes unir dos índices cualquiera, solo un índice normal a la izquierda con un índice enrich especialmente preparado a la derecha.</p><p>En cualquier caso, ya sea para canalizaciones de ingesta o para su uso en ES|QL, es necesario realizar algunos pasos preparatorios para establecer el índice de enriquecimiento y la póliza. Ya importamos el índice de <code>airport_city_boundaries</code> arriba, pero este no se puede usar directamente como índice de enriquecimiento en el comando <code>ENRICH</code> . Primero necesitamos realizar dos pasos:</p><ul><li><p>Crea la política de enriquecimiento descrita anteriormente para definir el índice fuente, el campo en el índice fuente para coincidir y los campos que devolver una vez emparejados.</p></li><li><p>Ejecuta esta política para crear el índice enriquecido. Esto construirá un índice interno especial, leyendo el índice original en una estructura de datos más eficiente que se copia a través del clúster.</p></li></ul><p>La política de enriquecimiento puede crear usando el siguiente comando:</p>PUT /_enrich/policy/city_boundaries
{
  "match": {
    "indices": "airport_city_boundaries",
    "match_field": "city_boundary",
    "enrich_fields": ["city", "airport", "region", "city_boundary"]
  }
}<p>Y la política puede ejecutar usando el siguiente comando:</p>POST /_enrich/policy/city_boundaries/_execute<p>Ten en cuenta que si alguna vez cambias el contenido del índice de <code>airport_city_boundaries</code> , tendrás que volver a ejecutar esta política para ver los cambios reflejados en el índice de enriquecimiento. Ahora vamos a ejecutar el ES| originalConsulta QL de nuevo:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
| STATS
    centroid = ST_CENTROID_AGG(location),
    count = COUNT(city_location)
    BY region
| SORT count DESC
| LIMIT 10<p>Esto devuelve las 5 regiones con más aeropuertos, junto con el centroide de todos los aeropuertos que tienen regiones coincidentes, y el rango en longitud de la representación WKT de los límites de la ciudad dentro de esas regiones:</p><p>centroide</p><p>Recuento</p><p>región</p><p>POINT (-12.139086859300733 31.024386116624648)</p><p>126</p><p>nulo</p><p>POINT (-83.10398317873478 42.300230911932886)</p><p>3</p><p>Detroit</p><p>POINT (39.74537850357592 47.21613017376512)</p><p>3</p><p>городской округ Батайск</p><p>POINT (-156.80986787192523 20.476673701778054)</p><p>3</p><p>Hawai</p><p>POINT (-73.94515332765877 40.70366442203522)</p><p>3</p><p>Ciudad de Nueva York</p><p>POINT (-83.10398317873478 42.300230911932886)</p><p>3</p><p>Detroit</p><p>POINT (-76.66873019188643 24.306286952923983)</p><p>2</p><p>Nueva Providencia</p><p>POINT (-3.0252167768776417 51.39245774131268)</p><p>2</p><p>Cardiff</p><p>POINT (-115.40993484668434 32.73126147687435)</p><p>2</p><p>Municipio de Mexicali</p><p>POINT (41.790108773857355 50.302146775648)</p><p>2</p><p>Центральный район</p><p>POINT (-73.88902732171118 45.57078813901171)</p><p>2</p><p>Montréal</p><p>También puede que notes que la región más común era <code>null</code>. ¿Qué podría implicar esto? Recuerda que comparé este comando con un 'left join' en SQL, lo que significa que si no se encuentra ningún límite de ciudad coincidente para un aeropuerto, el aeropuerto sigue devolver pero con valores <code>null</code> para los campos del índice de <code>airport_city_boundaries</code> . Resulta que hubo 125 aeropuertos que no encontraron <code>city_boundary</code>coincidentes, y un aeropuerto con un  coincidente donde el campo de <code>region</code> estaba <code>null</code>. Esto llevó a un recuento de 126 aeropuertos sin <code>region</code> en los resultados. Si tu caso de uso requiere que todos los aeropuertos puedan coincidir con un límite de ciudad, eso requeriría buscar datos adicionales para cubrir los huecos. Sería necesario determinar dos cosas:</p><ul><li><p>qué registros en el índice de <code>airport_city_boundaries</code> no tienen campos <code>city_boundary</code></p></li><li><p>qué registros en el índice de <code>airports</code> no coinciden usando el comando <code>ENRICH</code> (es decir, no intersectar)</p></li></ul><h2>Usando ES|QL para datos geoespaciales en Kibana Maps</h2><p>Kibana agregó soporte para Spatial ES|QL en la aplicación de Mapas. Esto significa que ahora puedes usar ES|QL para buscar datos geoespaciales en Elasticsearch y visualizar los resultados en un mapa.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91922eb4ef43d290/6a16f7161949f737bfe7a7b5/bd78470bd8a4bc60f0db7006bd804b8fe87e2fea-1440x683.png" alt="Kibana Layers ES|QL" /><p>Hay una nueva opción de capa en el menú de agregar capas, llamada "ES|QL". Como todas las características geoespaciales descritas hasta ahora, esto está en "vista previa técnica". Seleccionar esta opción te permite agregar una capa al mapa basada en los resultados de un ES|Consulta QL. Por ejemplo, podrías agregar una capa al mapa que muestre todos los aeropuertos del mundo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5185ef7461d7e81d/6a16f718839dfa1559dcfcb1/1dd28d3d0509f92d26b0bb5320a2925f7a54c5d9-1440x736.png" alt="Kibana ES|QL - Aeropuertos" /><p>O podrías agregar una capa que muestre los polígonos del índice de <code>airport_city_boundaries</code> , o mejor aún, ¿qué tal esa compleja consulta de <code>ENRICH</code> arriba que genera estadísticas sobre cuántos aeropuertos hay en cada región?</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51ee3543bb811d98/6a16f71a8b73cbbe63189df1/679a0a401faa613c7cedddd07c64f614ac2b7144-1440x727.png" alt="Kibana ES|QL - Estadísticas de la región" /><h2>¿Qué sigue ahora?</h2><p>El anterior blog de <a href="https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one">búsqueda geoespacial</a> se centraba en el uso de funciones como <code>ST_INTERSECTS</code> para realizar búsquedas, disponibles en Elasticsearch desde la versión 8.14. Y este blog te muestra cómo importar los datos que usamos para esas búsquedas. Sin embargo, Elasticsearch 8.15 venía con una función especialmente interesante: <code>ST_DISTANCE</code> que puede usar para realizar búsquedas espaciales eficientes, ¡y este será el tema del próximo blog!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/geospatial-data-ingest-for-esql</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/geospatial-data-ingest-for-esql</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Craig Taverner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc041ebca11476c6/6a16f70560084b31b93c4344/01fde3b1d714f12bf8673140c9f2f940d443de31-1440x823.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 25 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Búsqueda geoespacial de Elasticsearch con ES|QL]]></title>
    <description><![CDATA[Búsqueda geoespacial en el lenguaje de consultas Elasticsearch (ES|QL). Elasticsearch cuenta con poderosas características de búsqueda geoespacial, que ahora están llegando a ES|QL por una facilidad de uso mucho mejorada y familiaridad con el OGC.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch tuvo poderosos <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/geospatial-analysis.html">capacidades de búsqueda y análisis geoespacial</a> durante muchos años, pero la API era bastante diferente de lo que los usuarios típicos de SIG estaban acostumbrados. En el último año hemos <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">agregado el ES|Lenguaje de consulta QL</a>, un lenguaje de consulta por tubes tan fácil, o incluso más sencillo, que SQL. Es especialmente adecuado para los casos de búsqueda y observabilidad en los que Elastic destaca. También estamos agregando soporte para búsqueda y análisis geoespacial dentro de ES|QL, lo que lo hace mucho más fácil de usar, especialmente para usuarios que vienen de comunidades SQL o <a href="https://en.wikipedia.org/wiki/Geographic_information_system">GIS</a> .</p><p>Elasticsearch 8.12 y 8.13 llevaron soporte básico para tipos geoespaciales a ES|QL. Esto se mejoró significativamente con la incorporación de capacidades de búsqueda geoespacial en la versión 8.14. Más importante aún, este soporte fue diseñado para ajustar estrechamente al estándar <a href="https://en.wikipedia.org/wiki/Simple_Features">Simple Feature</a> Access del <a href="https://en.wikipedia.org/wiki/Open_Geospatial_Consortium">Open Geospatial Consortium (OGC)</a> empleado por otras bases de datos espaciales como PostGIS, facilitando mucho su uso para expertos en SIG familiarizados con estos estándares.</p><p>En este blog, te mostraremos cómo usar ES|QL para realizar búsquedas geoespaciales y cómo se compara con los equivalentes SQL y Query DSL. También te mostraremos cómo usar ES|QL para realizar uniones espaciales y cómo visualizar los resultados en Kibana Maps. Ten en cuenta que todas las funciones descritas aquí están en "vista previa técnica", y nos encantaría conocer vuestros comentarios sobre cómo podemos mejorarlas.</p><h2>Búsqueda de datos geoespaciales</h2><p>Empecemos con una consulta de ejemplo:</p>FROM airport_city_boundaries
| WHERE ST_INTERSECTS(
      city_boundary,
      "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
  )
| KEEP abbrev, airport, region, city, city_location
<p>Esto realiza una búsqueda de cualquier polígono de límite de ciudad que intersecte con un polígono rectangular alrededor del Aeropuerto Internacional Sanya Phoenix (SYX).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3897df6bed5d6061/6a17d7c2abe0f29eccdfe861/e48bac8f246c8842f2ea97ddd54910045262aeb1-1440x808.png" alt="Búsqueda Geoespacial ESQL" /><p>En un conjunto de datos de ejemplo de aeropuertos, ciudades y límites urbanos, esta búsqueda encuentra el polígono que se cruza y devuelve los campos deseados del documento correspondiente:</p><p>Abbrev</p><p>aeropuerto</p><p>región</p><p>ciudad</p><p>city_location</p><p>SYX</p><p>Sanya Phoenix Internacional</p><p>天涯区</p><p>Sanya</p><p>POINT(109.5036 18.2533)</p><p>¡Eso fue fácil! Ahora compáralo con el tradicional DSL de Elasticsearch Query para la misma consulta:</p>GET /airport_city_boundaries/_search
{
  "_source": ["abbrev", "airport", "region", "city", "city_location"],
  "query": {
    "geo_shape": {
      "city_boundary": {
        "shape": {
          "type": "polygon",
          "coordinates" : [[
            [109.4, 18.1],
            [109.6, 18.1],
            [109.6, 18.3],
            [109.4, 18.3],
            [109.4, 18.1]
          ]]
        }
      }
    }
  }
}
<p>Ambas consultas son bastante claras en su intención, pero el ES|La consulta QL se parece mucho a SQL. La misma consulta en PostGIS es la siguiente:</p>SELECT abbrev, airport, region, city, city_location
FROM airport_city_boundaries
WHERE ST_INTERSECTS(
    city_boundary,
    'SRID=4326;POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'::geometry
);
<p>Mira atrás en el ES|Ejemplo de QL. Muy parecido, ¿verdad?</p>FROM airport_city_boundaries
| WHERE ST_INTERSECTS(
      city_boundary,
      "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
  )
| KEEP abbrev, airport, region, city, city_location
<p>Comprobamos que los usuarios actuales de la API de Elasticsearch encuentran ES|QL mucho más fácil de usar. Ahora esperamos que los usuarios existentes de SQL, especialmente los de SQL Espacial, descubran que ES|QL se siente muy familiar con lo que están acostumbrados a ver.</p><h4>¿Por qué no SQL?</h4><p>¿Y qué pasa con Elasticsearch SQL? Lleva un tiempo existiendo y tiene algunas características geoespaciales. Sin embargo, Elasticsearch SQL se escribía como un envoltorio sobre la API original de Consultas, lo que significaba que solo se soportaban consultas que podían transpilarse a la API original. ES|QL no tiene esta limitación. Ser una pila completamente nueva permite muchas optimizaciones que no eran posibles en SQL. Nuestros benchmarks muestran ES|QL <a href="https://elasticsearch-benchmarks.elastic.co/#tracks/esql/nightly/default/6M">suele ser muy rápido que la API de Consultas</a>, ¡especialmente con agregaciones!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18bda964c8b24e36/6a17d7c3e3179155d22d568a/b8b6c2b2e45850d832805ed1e71e522f4955f53c-1440x813.png" alt="polígono-intersección-benchmark" /><h2>Diferencias con SQL</h2><p>Claramente, por el ejemplo anterior, ES|QL es algo similar a SQL, pero hay algunas diferencias importantes. Por ejemplo, ES|QL es un lenguaje de consulta por pipes, que comienza con un comando fuente como FROM y luego encadena todos los comandos posteriores junto con el pipe | carácter. Esto facilita mucho entender cómo cada comando recibe una tabla de datos y realiza alguna acción sobre esa tabla, como filtrar con <code>WHERE</code>, agregar columnas con <code>EVAL</code>, o realizar agregaciones con <code>STATS</code>. En lugar de comenzar con <code>SELECT</code> para definir las columnas de salida finales, puede haber uno o más comandos <code>KEEP</code> , siendo el último el que especifica los resultados finales. Esta estructura simplifica el razonamiento sobre la consulta.</p><p>Centrándonos en el comando <code>WHERE</code> del ejemplo anterior, podemos ver que se parece bastante al ejemplo de PostGIS:</p><p><em>ES|QL</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
)
<p><em>PostGIS</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    'SRID=4326;POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'::geometry
)
<p>Aparte de la diferencia en los caracteres de comillas de cadena, la mayor diferencia está en cómo tipificamos la cadena a un tipo espacial. En PostGIS, usamos el sufijo <code>::geometry</code> , mientras que en ES|QL, usamos el sufijo <code>::geo_shape</code> . Esto se debe a que ES|QL se ejecuta dentro de Elasticsearch, y el operador de typecasting <code>::</code> puede usar para convertir una cadena a cualquiera de los <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-limitations.html#_supported_types">ES| soportadosQL tipia</a>, en este caso, un <code>geo_shape</code>. Además, los tipos <code>geo_shape</code> y <code>geo_point</code> en Elasticsearch implican el sistema de coordenadas espaciales conocido como WGS84, más comúnmente conocido con el número SRID 4326. En PostGIS, esto debe ser explícito, de ahí el uso del prefijo <code>SRID=4326;</code> a la cadena WKT. Si se elimina ese prefijo, el SRID se pondrá en 0, que es más parecido a los tipos de Elasticsearch <code>cartesian_point</code> y <code>cartesian_shape</code>, que no están vinculados a ningún sistema de coordenadas específico.</p><p>Ambos ES|QL y PostGIS también proporcionan sintaxis de funciones de conversión de tipos:</p><p><em>ES|QL</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    TO_GEOSHAPE("POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))")
)
<p><em>PostGIS</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    ST_SetSRID(
      ST_GeomFromText('POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'),
      4326
    )
)
<h2>Funciones OGC</h2><p>Elasticsearch 8.14 introduce las siguientes cuatro funciones de búsqueda espacial OGC:</p><p>ES|QL</p><p>PostGIS</p><p>Descripción</p><p>ST_INTERSECTS</p><p>ST_Intersects</p><p>Devuelve verdadero si dos geometrías se cruzan, y falso en caso contrario.</p><p>ST_DISJOINT</p><p>ST_Disjoint</p><p>Devuelve verdadero si dos geometrías no se intersectan, y falso en caso contrario. El inverso de ST_INTERSECTS.</p><p>ST_CONTAINS</p><p>ST_Contains</p><p>Devuelve verdadero si una geometría contiene otra, y falso en caso contrario.</p><p>ST_WITHIN</p><p>ST_Within</p><p>Devuelve verdadero si una geometría está dentro de otra, y falso en caso contrario. El inverso de ST_CONTAINS.</p><p>Estas funciones se comportan de forma similar a sus contrapartes PostGIS y se emplean de la misma manera. Por ejemplo, <code>ST_INTERSECTS</code> devuelve verdadero si dos geometrías se intersectan y falso en caso contrario. Si sigues los enlaces de documentación de la tabla anterior, puede que notes que todos los ES|Los ejemplos de QL están dentro de una cláusula de <code>WHERE</code> luego de una cláusula de <code>FROM</code> , mientras que todos los ejemplos de PostGIS usan geometrías literales. De hecho, ambas plataformas soportan el uso de las funciones en cualquier parte de la consulta donde tengan sentido.</p><p>El primer ejemplo en la documentación de PostGIS para <code>ST_INTERSECTS</code> es:</p>SELECT ST_Intersects(
    'POINT(0 0)'::geometry,
    'LINESTRING ( 2 0, 0 2 )'::geometry
);
<p>El ES|El equivalente en QL de esto sería:</p>ROW ST_INTERSECTS(
    "POINT(0 0)"::geo_point,
    "LINESTRING ( 2 0, 0 2 )"::geo_shape
)
<p>Fíjate en que no especificamos el SRID en el ejemplo de PostGIS. Esto se debe a que en PostGIS, al usar el tipo <code>geometry</code> , todos los cálculos se realizan en un sistema de coordenadas planas, por lo que si ambas geometrías tienen el mismo SRID, no importa cuál sea el SRID. En Elasticsearch, esto también es cierto para la mayoría de las funciones, sin embargo, hay excepciones donde <code>geo_shape</code> y <code>geo_point</code> emplean cálculos esféricos, como veremos en el próximo blog sobre búsqueda por distancia espacial.</p><h2>ES|Versatilidad en QL</h2><p>Vimos ejemplos arriba de usar funciones espaciales en cláusulas <code>WHERE</code> y en comandos <code>ROW</code> . ¿Dónde más tendrían sentido? Un lugar muy útil es en el comando <code>EVAL</code> . Este comando te permite evaluar una expresión y devolver el resultado. Por ejemplo, determinemos si los centroides de todos los aeropuertos agrupados por el nombre de sus países están dentro de un límite que delimita el país:</p>FROM airports
| EVAL in_uk = ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
| EVAL in_iceland = ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON ((-25.4883 65.5312, -23.4668 66.7746, -18.4131 67.4749, -13.0957 66.2669, -12.3926 64.4159, -20.1270 62.7346, -24.7852 63.3718, -25.4883 65.5312))"))
| EVAL within_uk = ST_WITHIN(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
| EVAL within_iceland = ST_WITHIN(location, TO_GEOSHAPE("POLYGON ((-25.4883 65.5312, -23.4668 66.7746, -18.4131 67.4749, -13.0957 66.2669, -12.3926 64.4159, -20.1270 62.7346, -24.7852 63.3718, -25.4883 65.5312))"))
| STATS centroid = ST_CENTROID_AGG(location), count=COUNT() BY in_uk, in_iceland, within_uk, within_iceland
| SORT count ASC
<p>Se esperan los resultados: el centroide de los aeropuertos del Reino Unido está dentro del límite del Reino Unido, y no dentro del límite de Islandia, y viceversa:</p><p>centroide</p><p>Recuento</p><p>in_uk</p><p>in_iceland</p><p>within_uk</p><p>within_iceland</p><p>POINT (-21.946634463965893 64.13187285885215)</p><p>1</p><p>falso</p><p>true</p><p>falso</p><p>true</p><p>POINT (-2.597342072712148 54.33551226578214)</p><p>17</p><p>true</p><p>falso</p><p>true</p><p>falso</p><p>POINT (0.04453958108176276 23.74658354606057)</p><p>873</p><p>falso</p><p>falso</p><p>falso</p><p>falso</p><p>De hecho, estas funciones pueden usar en cualquier parte de la consulta donde su firma tenga sentido. Todos toman dos argumentos, que son o bien un objeto espacial literal o un campo de un tipo espacial, y todos devuelven un valor booleano. Una consideración importante es que el sistema de referencia de coordenadas (CRS) de las geometrías debe coincidir, o se devolverá un error. Esto significa que no puedes mezclar <code>geo_shape</code> y <code>cartesian_shape</code> tipos en la misma llamada de función. Sin embargo, puedes mezclar <code>geo_point</code> y <code>geo_shape</code> tipos, ya que el tipo <code>geo_point</code> es un caso especial del tipo <code>geo_shape</code> , y ambos comparten el mismo sistema de referencia de coordenadas. La documentación de cada una de las funciones definidas anteriormente lista las combinaciones de tipos soportadas.</p><p>Además, cualquiera de los dos argumentos puede ser un literal espacial o un campo, en cualquiera de los dos ordenes. Incluso puedes especificar dos campos, dos literales, un campo y un literal, o un literal y un campo. El único requisito es que los tipos sean compatibles. Por ejemplo, esta consulta compara dos campos en el mismo índice:</p>FROM airport_city_boundaries
| EVAL in_city = ST_INTERSECTS(city_location, city_boundary)
| STATS count=COUNT(*) BY in_city
| SORT count ASC
| EVAL cardinality = CASE(count &lt; 10, "very few", count &lt; 100, "few", "many")
| KEEP cardinality, count, in_city
<p>La consulta básicamente pregunta si la ubicación de la ciudad está dentro del límite de la ciudad, lo cual generalmente debería ser cierto, pero siempre hay excepciones:</p><p>cardinalidad</p><p>Recuento</p><p>in_city</p><p>poco</p><p>29</p><p>falso</p><p>mucho</p><p>740</p><p>true</p><p>Una pregunta mucho más interesante sería si la ubicación del aeropuerto está dentro de los límites de la ciudad a la que sirve el aeropuerto. Sin embargo, la ubicación del aeropuerto se encuentra en un índice diferente al que contiene los límites de la ciudad. Esto requiere un método para consultar y correlacionar eficazmente los datos de estos dos índices separados.</p><h2>Unirías espaciales</h2><p>ES|QL no soporta comandos <code>JOIN</code>, pero puedes lograr un caso especial de unión usando el<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich"> comando</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich"><code>ENRICH</code></a>, que se comporta de forma similar a una 'unión izquierda' en SQL. Este comando funciona de forma similar a una 'unión por la izquierda' en SQL, permitiéndote enriquecer resultados de un índice con datos de otro índice basar en una relación espacial entre los dos conjuntos de datos.</p><p>Por ejemplo, enriquezcamos los resultados de una tabla de aeropuertos con información adicional sobre la ciudad a la que sirven encontrando el límite de la ciudad que contiene la ubicación del aeropuerto, y luego realicemos algunas estadísticas sobre los resultados:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
| MV_EXPAND city_boundary
| EVAL boundary_wkt_length = LENGTH(TO_STRING(city_boundary))
| STATS centroid = ST_CENTROID_AGG(location), count = COUNT(city_location), min_wkt = MIN(boundary_wkt_length), max_wkt = MAX(boundary_wkt_length) BY region
| SORT count DESC
| LIMIT 5
<p>Esto devuelve las 5 regiones con más aeropuertos, junto con el centroide de todos los aeropuertos que tienen regiones coincidentes, y el rango en longitud de la representación WKT de los límites de la ciudad dentro de esas regiones:</p><p>centroide</p><p>Recuento</p><p>min_wkt</p><p>max_wkt</p><p>región</p><p>PUNTO (-32.56093470960719 32.598117914802714)</p><p>90</p><p>207</p><p>207</p><p>nulo</p><p>POINT (-73.94515332765877 40.70366442203522)</p><p>9</p><p>438</p><p>438</p><p>Ciudad de Nueva York</p><p>POINT (-83.10398317873478 42.300230911932886)</p><p>9</p><p>473</p><p>473</p><p>Detroit</p><p>POINT (-156.3020245861262 20.176383580081165)</p><p>5</p><p>307</p><p>803</p><p>Hawai</p><p>POINT (-73.88902732171118 45.57078813901171)</p><p>4</p><p>837</p><p>837</p><p>Montréal</p><p>Entonces, ¿qué pasó realmente aquí? ¿Dónde ocurrió la supuesta <code>JOIN</code> ? El núcleo de la consulta reside en el comando <code>ENRICH</code> :</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
<p>Este comando instruye a Elasticsearch para enriquecer los resultados recuperados del índice de <code>airports</code> y realizar una unión <code>intersects</code> entre el campo <code>city_location</code> del índice original y el campo <code>city_boundary</code> del índice de <code>airport_city_boundaries</code> , que usamos en algunos ejemplos anteriores. Pero parte de esta información no es claramente visible en esta consulta. Lo que sí vemos es el nombre de una política de enriquecimiento <code>city_boundaries</code>, y la información que falta está encapsulada dentro de esa definición de política.</p>{
  "geo_match": {
    "indices": "airport_city_boundaries",
    "match_field": "city_boundary",
    "enrich_fields": ["city", "airport", "region", "city_boundary"]
  }
}
<p>Aquí podemos ver que realizará una consulta <code>geo_match</code> (<code>intersects</code> es el valor predeterminado), el campo a emparejar es <code>city_boundary</code>, y los <code>enrich_fields</code> son los campos que queremos agregar al documento original. Uno de esos campos, el <code>region</code> , se usó como clave de agrupación para el comando <code>STATS</code> , algo que no podríamos hacer sin esta capacidad de 'unión izquierda'. Para más información sobre las pólizas de enriquecimiento, consulte la <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/enrich-setup.html">documentación de enriquecimiento</a>. Al leer esos documentos, notarás que describen el uso de los índices enrich para enriquecer datos en tiempo de indexación, configurando pipelines de ingest. Esto no es necesario para ES|QL, ya que el comando <code>ENRICH</code> funciona en el momento de la consulta. Basta con preparar el índice de enriquecimiento con los datos y la política de enriquecimiento necesarios, y luego usar el comando <code>ENRICH</code> en tu ES|Consultas QL.</p><p>También puede que notes que la región más común era <code>null</code>. ¿Qué podría implicar esto? Recuerda que comparé este comando con un 'left join' en SQL, lo que significa que si no se encuentra ningún límite de ciudad coincidente para un aeropuerto, el aeropuerto sigue devolver pero con valores <code>null</code> para los campos del índice de <code>airport_city_boundaries</code> . Resulta que había 89 aeropuertos que no encontraron <code>city_boundary</code>coincidentes, y un aeropuerto con un <code>region</code> campo de <code>null</code>. Esto llevó a un recuento de 90 aeropuertos sin <code>region</code> en los resultados. Otro detalle interesante es la necesidad del comando <code>MV_EXPAND</code> . Esto es necesario porque el comando <code>ENRICH</code> puede devolver múltiples resultados por cada fila de entrada, y <code>MV_EXPAND</code> ayuda a separar estos resultados en varias filas, una para cada resultado. Esto también aclara por qué "Hawái" muestra resultados <code>min_wkt</code> y <code>max_wkt</code> diferentes: había varias regiones con el mismo nombre pero diferentes límites.</p><h2>Kibana Maps</h2><p>Kibana agregó soporte para Spatial ES|QL en la aplicación de Mapas. Esto significa que ahora puedes usar ES|QL para buscar datos geoespaciales en Elasticsearch y visualizar los resultados en un mapa.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91922eb4ef43d290/6a16f7161949f737bfe7a7b5/bd78470bd8a4bc60f0db7006bd804b8fe87e2fea-1440x683.png" alt="Kibana Layers ES|QL" /><p>Hay una nueva opción de capa en el menú de agregar capas, llamada "ES|QL". Como todas las características geoespaciales descritas hasta ahora, esto está en "vista previa técnica". Seleccionar esta opción te permite agregar una capa al mapa basada en los resultados de un ES|Consulta QL. Por ejemplo, podrías agregar una capa al mapa que muestre todos los aeropuertos del mundo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5185ef7461d7e81d/6a16f718839dfa1559dcfcb1/1dd28d3d0509f92d26b0bb5320a2925f7a54c5d9-1440x736.png" alt="Kibana ES|QL - Aeropuertos" /><p>O podrías agregar una capa que muestre los polígonos del índice de <code>airport_city_boundaries</code> , o mejor aún, ¿qué tal esa compleja consulta de <code>ENRICH</code> arriba que genera estadísticas sobre cuántos aeropuertos hay en cada región?</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51ee3543bb811d98/6a16f71a8b73cbbe63189df1/679a0a401faa613c7cedddd07c64f614ac2b7144-1440x727.png" alt="Kibana ES|QL - Estadísticas de la región" /><h2>¿Qué sigue ahora?</h2><p>Quizá notaste que en dos de los ejemplos anteriores incluimos otra función espacial <code>ST_CENTROID_AGG</code>. Esta es una función agregadora empleada en el comando <code>STATS</code> , y la primera de muchas funciones de análisis espacial que planeamos agregar a ES|QL. ¡Escribiremos sobre ello en el blog cuando tengamos más que mostrar!</p><p>Antes de eso, queremos contarte más sobre una función especialmente emocionante en la que trabajamos: la capacidad de realizar búsquedas espaciales, una de las funciones de búsqueda espacial más empleadas en Elasticsearch. ¿Te imaginas cómo sería la sintaxis de las búsquedas a distancia? ¿Quizá similar a una función OGC? ¡Permanece atento al próximo blog de este serial para descubrirlo!</p><p>Alerta de spoilers: Elasticsearch 8.15 acaba de ser lanzado, y la búsqueda por distancia espacial con ES|¡QL está incluido!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one</guid>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Craig Taverner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd05627be20e89dfb/6a17d7c6414c640256944fdb/de886289dcb56494920875303b622b030b9b810f-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 12 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[De ES|Objetos de QL a PHP]]></title>
    <description><![CDATA[Aprende a ejecutar y gestionar ES|Consultas QL en PHP. Sigue esta guía para mapear ES|QL resulta en un objeto PHP o clase personalizada.]]></description>
    <content:encoded><![CDATA[<p>A partir de <a href="https://github.com/elastic/elasticsearch-php/releases/tag/v8.13.0">elasticsearch-php v8.13.0</a> puedes ejecutar <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> consulta y mapea el resultado a un objeto PHP de <a href="https://www.php.net/manual/en/class.stdclass.php">stdClass</a> o a una clase personalizada.</p><h2>ES|QL</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> es un nuevo lenguaje de consulta Elasticsearch introducido en Elasticsearch 8.11.0. Ahora mismo, está disponible en vista previa técnica. Proporciona una forma poderosa de filtrar, transformar y analizar los datos almacenados en Elasticsearch.</p><p>Emplea "tuberías" (<code>|</code>) para manipular y transformar datos paso a paso. Este enfoque permite a los usuarios componer un serial de operaciones, donde la salida de una operación se convierte en la entrada para la siguiente, permitiendo transformaciones y análisis complejos de datos.</p><p>Por ejemplo, la siguiente consulta devuelve los primeros 3 documentos (filas) del índice <code>sample_data</code> :</p>FROM sample_data
| LIMIT 3
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8b0310cb335872b7/6a17d7eab1e113258679f0df/c23aee777bacdf90c63b717fd9458207dfc0511d-864x284.png" alt="ES|QL produce tablas" /><h2>Caso de uso: ES|Funciones de QL en el cliente oficial de PHP</h2><p>Para ilustrar el ES|Características de QL desarrolladas en el cliente oficial de PHP, almacenamos en Elasticsearch un <a href="https://github.com/elastic/elasticsearch-php-examples/blob/main/examples/ESQL/data/books.csv">archivo CSV</a> de 81.828 libros (54,4 MB) incluyendo la siguiente información:</p>Title;Descrition;Author;Year;Publisher;Ratings
<p>Extrajimos esta lista del <a href="https://www.kaggle.com/datasets/mohamedbakhet/amazon-books-reviews">conjunto de datos de Amazon Books Reviews</a>, disponible al público.</p><p>Creamos un índice <code>books</code> con los siguientes mapeos de Elasticsearch:</p>'mappings' : {
    'properties': {
        'title': {
            'type': 'text'
        },
        'description': {
            'type': 'text'
        },
        'author': {
            'type': 'text'
        },
        'year': {
            'type': 'short'
        },
        'publisher': {
            'type': 'keyword'
        },
        'rating': {
            'type': 'half_float'
        }
    }
}
<p>El valor <code>rating</code> es la media de las reseñas de clasificación tomadas del <a href="https://www.kaggle.com/datasets/mohamedbakhet/amazon-books-reviews?select=Books_rating.csv">archivo Books_rating.csv</a> de 2,9 GB.</p><p><a href="https://github.com/elastic/elasticsearch-php-examples/blob/main/examples/ESQL/bulk.php">Aquí</a> puedes encontrar el script PHP que usamos para importar en masa todos los libros en Elasticsearch. La operación a granel consumía 7 segundos y 28 MB de RAM usando PHP 8.2.17. Con el mapeo propuesto, el tamaño del índice en Elasticsearch es de unos 62 MB.</p><h2>Mapa ES|Resultados QL a un objeto PHP o clase personalizada</h2><p>Podemos ejecutar ES|Consulta QL en PHP usando el punto final <code>esql()-&gt;query()</code> . El resultado de esta consulta es una estructura de datos de tabla. Esto se expresa en JSON usando los campos <code>columns</code> y <code>values</code> . En el campo <code>columns</code> tenemos la definición de <code>name</code> y <code>type</code> .</p><p>Aquí tienes un ejemplo de ES|Consulta QL para recuperar los 10 mejores libros escritos por Stephen King ordenados por las reseñas de ranking de usuarios:</p>$query = &lt;&lt;&lt;EOD
    FROM books
    | WHERE author == "Stephen King"
    | SORT rating DESC
    | LIMIT 10
EOD;

$result = $client-&gt;esql()-&gt;query([
    'body' =&gt; ['query' =&gt; $query]
]);
<p>El resultado JSON de Elasticsearch es el siguiente:</p>{
    "columns": [
        { "name": "author", "type": "text" },
        { "name": "description", "type": "text" },
        { "name": "publisher", "type": "keyword" },
        { "name": "rating", "type": "double" },
        { "name": "title", "type": "text" },
        { "name": "year", "type": "integer" }
    ],
    "values": [
        [
            "Stephen King",
            "The author ...",
            "Turtleback",
            5.0,
            "How writers write",
            2002
        ],
        [
            "Stephen King",
            "In Blockade Billy, a retired coach...",
            "Simon and Schuster",
            5.0,
            "Blockade",
            2010
        ],
        [
            "Stephen King",
            "A chilling collection of twenty horror stories.",
            "Signet Book",
            4.55859375,
            "Night Shift (Signet)",
            1979
        ],
        ...
    ]
}
<p>En este ejemplo tenemos 6 propiedades (autor, descripción, editor, valoración, título, año) relacionadas con un libro y 10 resultados, todos libros de Stephen King.</p><p>Una lista de todos los tipos soportados en ES|Aquí <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-limitations.html#esql-supported-types">se</a> informa de QL.</p><p>El objeto de respuesta <code>$result</code> puede acceder como un array, una cadena o un objeto ( <a href="https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/connecting.html#client-usage">ver aquí</a> para más información).</p><p>Usando la interfaz objeto, podemos acceder a los valores mediante propiedades e índices. Por ejemplo, <code>$result-&gt;values[0][4]</code> devuelve el título (4) del primer libro (0) en la lista, <code>$result-&gt;values[1][3]</code> devuelve el puntaje de rango (3) del segundo libro (1), etc. Recuerda, el índice de un array en PHP empieza desde cero.</p><p>Esta interfaz puede ser suficientemente buena para algunos casos de uso, pero la mayoría de las veces nos gustaría tener una variedad de objetos como resultado.</p><p>Para mapear el resultado en un array de objetos podemos usar la nueva función <a href="https://github.com/elastic/elasticsearch-php/issues/1398">mapTo()</a> de elasticsearch-php.</p><p>Esta función está disponible directamente en el <a href="https://github.com/elastic/elasticsearch-php/blob/main/src/Response/Elasticsearch.php">objeto de respuesta Elasticsearch</a>. Eso significa que puedes acceder a él de la siguiente manera:</p>$books = $result-&gt;mapTo(); // Array of stdClass
foreach ($books as $book) {
    printf(
        "%s, %s, %d, Rating: %.2f\n",
        $book-&gt;author,
        $book-&gt;title,
        $book-&gt;year,
        $book-&gt;rating
    );
}
<p>Si tienes una clase Book personalizada, puedes mapear el resultado usándola de la siguiente manera:</p>class Book
{
    public string $author;
    public string $title;
    public string $description;
    public int $year;
    public float $rating;
}

$books = $result-&gt;mapTo(Book::class); // Array of Book
<p>Si tu clase tiene otras propiedades además de las incluidas en el ES|Resultado QL, esto también funcionará. La función <code>mapTo()</code> usará solo las propiedades devueltas como columnas del ES|Resultado de QL.</p><p>Puedes descargar todos los ejemplos que se mencionan en <a href="https://github.com/elastic/elasticsearch-php-examples/tree/main/examples/ESQL">este artículo aquí</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-php-map-object-class</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-php-map-object-class</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[PHP]]></category>
    <dc:creator><![CDATA[Enrico Zimuel]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99f5ab85c0713977/6a17d7ebfbc5f8072b491918/aea56270f48cb64130d1b515b983434e0960dc2f-500x500.png" length="0" type="image/png"/>
    <pubDate>Mon, 08 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>