<?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[Datos de índice - 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[Datos de índice - 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/index-data</link>
    </image>
    <link>https://www.elastic.co/es/search-labs/blog/category/index-data</link>
    <atom:link href="https://www.elastic.co/es/search-labs/rss/category/index-data.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[es]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 07:34:05 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Cuando TSDS se une a ILM: diseñar flujos de datos temporales que no rechazan los datos tardíos]]></title>
    <description><![CDATA[Cómo los límites de tiempo de TSDS interactúan con las fases de ILM; y cómo diseñar políticas que toleren métricas tardías.]]></description>
    <content:encoded><![CDATA[<p>Recientemente, migré el clúster de métricas de un cliente de “todo en el nivel caliente” a una arquitectura de caliente/frío/congelado. Fue un cambio que había realizado docenas de veces antes. En cuestión de minutos, Logstash dejó de avanzar los datos por completo.</p><p>Elasticsearch rechazaba métricas que llegaban con retraso. Esos rechazos hicieron que el pipeline se retrasara, lo que dio como resultado más datos tardíos, lo que desencadenó aún más rechazos. Finalmente, el pipeline se detuvo por completo.</p><p>Tuvimos que restaurar desde un snapshot, reindexar los datos y rediseñar el pipeline de ingesta para recuperarnos.</p><p>La causa raíz no era la gestión del ciclo de vida de los índices (ILM) en sí. Eran los flujos de datos temporales (TSDS) y cómo imponen índices de respaldo acotados en el tiempo.</p><p>TSDS puede reducir las necesidades de almacenamiento para las métricas en un 40–70 %, pero los cambios de arquitectura que hacen que TSDS sea eficiente también alteran el comportamiento de los índices con el tiempo. Esos cambios importan al diseñar políticas de ILM o cuando tus Pipelines de ingesta pueden generar datos que llegan con retraso.</p><h2>TL;DR</h2><p>Al usar TSDS:</p><ul><li><p>Los índices de respaldo solo aceptan documentos dentro de una ventana de tiempo específica.</p></li><li><p>Si llegan datos tardíos después de que un índice pasa a frío o congelado, Elasticsearch rechaza esos documentos o los envía al almacén de fallas, si está configurado.</p></li></ul><p>Regla de diseño:</p>warm_min_age &gt; rollover_max_age + maximum_expected_lateness<h2>¿Qué es un flujo de datos temporales?</h2><p>Un<em> flujo de datos temporales</em> (TSDS) es un flujo de datos especializado optimizado para datos de métricas. Los datos se distribuyen de manera que los documentos relacionados se encuentren dentro de los mismos fragmentos, lo que optimiza su búsqueda y recuperación. Así es como lo hace Elasticsearch:</p><p>Cada documento contiene:</p><ul><li><p>Una marca de tiempo.</p></li><li><p>Campos de dimensión que identifican las series temporales.</p></li><li><p>Campos métricos que representan valores medidos.</p></li></ul><p>Entre los ejemplos, se incluyen los siguientes:</p><ul><li><p>Uso de CPU por host.</p></li><li><p>Latencia de las solicitudes por servicio.</p></li><li><p>Lecturas de temperatura por sensor.</p></li></ul><p><em>Las dimensiones identifican </em>lo que queremos medir, mientras que <em>las métricas representan </em>valores que cambian con el tiempo.</p><h3>Dimensiones</h3><p>Las dimensiones describen la entidad medida.</p><p>Ejemplos:</p>host.name
service.name
container.id<p>Los definimos en mapeos con:</p>time_series_dimension: true<h3>Métricas</h3><p>Las métricas representan valores numéricos y se definen mediante:</p>time_series_metric<p>Tipos comunes de métricas:</p><ul><li><p>Indicador: valores que suben y bajan.</p></li><li><p>Contador: valores que aumentan hasta que se reinician.</p></li></ul><p>Elastic Agent recopila principalmente métricas y datos de logs, por lo que, incluso si no has habilitado ningún índice TSDS manualmente, puedes tenerlos aún en tu clúster.</p><h3>El campo _tsid</h3><p>Elasticsearch genera internamente un valor <code>_tsid</code> a partir de campos dimensionales. Esto permite que los documentos con dimensiones idénticas se dirijan al mismo shard, lo que mejora:</p><ul><li><p>Compresión.</p></li><li><p>Localidad de búsqueda.</p></li><li><p>Rendimiento de agregación.</p></li></ul><h2>La diferencia clave: índices de respaldo limitados en el tiempo</h2><p>Los flujos de datos tradicionales siempre escriben en el índice de respaldo más reciente, llamado <em>índice de escritura</em>, pero TSDS se comporta de manera diferente.</p><p>Cada índice de respaldo TSDS tiene una ventana de tiempo definida y solo acepta documentos con <code>@timestamp</code> valores que se encuentren en esa ventana:</p>GET _data_stream/my-metrics-data-stream


     "index_mode": "time_series",
     "time_series": {
       "temporal_ranges": [
         {
           "start": "2026-01-15T14:35:50.000Z",
           "end": "2026-03-16T11:34:40.000Z"
         }
       ]
     }<p>Cuando se indexa un documento, Elasticsearch lo dirige al índice de respaldo responsable de esa marca de tiempo, lo que significa que, a diferencia de los índices tradicionales, un TSDS puede escribir en varios índices de respaldo al mismo tiempo.</p><p>Por ejemplo:</p><ul><li><p>Datos en tiempo real → índice más reciente.</p></li><li><p>Datos tardíos → índice anterior que abarca ese intervalo de tiempo.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b001853af30d5f8/6a17dc2cfaa9137a7d93c751/31af2bb3b3dc24db8342e791e1db77a44659ba7a-1589x502.png" alt="Cronología que muestra cómo un documento tardío se enruta a un índice más antiguo, mientras que un documento actual va al índice más reciente." /><h2>Diseño para datos con retraso</h2><p>Las pipelines de ingesta reales rara vez entregan métricas perfectamente a tiempo. Las métricas pueden retrasarse por interrupciones de la red, retrasos en el camino, ingesta por batch y pérdida de dispositivos periféricos, que se vuelven a conectar y comienzan a ponerse al día.</p><p>Los índices tradicionales absorben silenciosamente esos retrasos. TSDS no lo hace.</p><p>Si la marca de tiempo de un documento queda fuera del intervalo de índices de respaldo con capacidad de escritura, Elasticsearch lo rechaza, lo que significa que tu política de ILM debe tener en cuenta los datos que llegan con retraso.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e8eae1b2ddad142/6a17dc2e1d1b8335a793e35c/32a103b95b20e31615c214271e27811a7ee315ae-1999x691.png" alt="Línea de tiempo del ciclo de vida del índice" /><h2>La restricción crítica</h2><p>Los índices de respaldo deben mantenerse con permisos de escritura el tiempo suficiente para aceptar datos tardíos.</p><p>En términos prácticos:</p>time_until_readonly &gt; maximum_expected_lateness<p>Debido a que ILM mide las antigüedades desde el desplazamiento, la regla operativa se convierte en:</p>warm_or_cold_min_age &gt; rollover_max_age + maximum_expected_lateness<p></p><p>Por ejemplo, si las métricas pueden llegar con hasta seis horas de retraso, los índices deben permanecer editables al menos seis horas después de la transferencia.</p><p></p><p>No tener en cuenta esta restricción fue exactamente lo que causó la falla de ingesta descrita anteriormente. Los datos que llegaban tarde se dirigieron a un índice anterior, que ya estaba en el nivel frío y, por tanto, bloqueaba la escritura.</p><p></p><h2>Gestión de documentos rechazados</h2><p>Cuando TSDS rechaza un documento, Elasticsearch devuelve un error, lo que indica que la marca de tiempo no está dentro del rango de índices de escritura. La forma en que tu pipeline de ingesta maneja ese error determina si pierdes datos o si la ingesta se detiene.</p><p>El mecanismo principal para manejar documentos rechazados es el almacén de fallas.</p><h3>Almacén de fallas (recomendado en Elasticsearch 9.1+)</h3><p>Elasticsearch 9.1 introdujo el almacén de fallas, que captura automáticamente los documentos rechazados. En lugar de devolver errores a los clientes, Elasticsearch escribe los documentos rechazados en un índice de fallas dedicado dentro del flujo de datos.</p><p>Puedes inspeccionar las fallas usando:</p>GET metrics-myapp::failures/_search<p>Usar el almacén de fallas evita que los pipelines de ingesta se bloqueen por errores de rechazo, a la vez que conserva los datos fallidos para analizarlos o <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/reindex-tsds">reindexarlos</a>.</p><h2>Monitoreo de problemas de rechazo</h2><p>Los problemas de retraso suelen aparecer primero como anomalías de ingesta. Puedes notarlos primero como:</p><ul><li><p>Caídas repentinas en la tasa de indexación.</p></li><li><p>Aumentos repentinos en el número de documentos rechazados.</p></li><li><p>Una cantidad cada vez mayor de entradas del almacén de fallas.</p></li><li><p>Discrepancias entre el número de entradas y salidas del pipeline.</p></li></ul><p>Alertar sobre estas señales permite a los operadores detectar problemas antes de que los pipelines se detengan. Se pueden utilizar flujos de trabajo, tareas de machine learning y otros mecanismos para automatizar la detección y la notificación.</p><h2>Lista de verificación de migración para TSDS + ILM</h2><p>Si estás migrando un cluster de métricas a TSDS, introduciendo la organización en niveles con ILM o actualizando a una versión de Elasticsearch en la que las métricas son TSDS por defecto, revisa primero estos elementos.</p><h3><strong>1. Mide la latencia de la ingesta</strong></h3><p>Antes de cambiar las políticas de ILM, determina:</p><ul><li><p>Retraso normal de ingesta.</p></li><li><p>Retraso en el peor de los casos durante incidentes.</p></li><li><p>Retrasos causados por pipelines de batch.</p></li></ul><p>Tu diseño de ILM debe contemplar el retraso máximo realista.</p><h3><strong>2. Verifica las ventanas de tiempo de indexación</strong></h3><p>Inspecciona tus índices de respaldo TSDS:</p>GET _data_stream/&lt;your-stream&gt;<p>Busca:</p><ul><li><p><code>time_series.start_time</code></p></li><li><p><code>time_series.end_time</code></p></li></ul><p>Estos límites determinan qué índices pueden aceptar documentos. Entender estas ventanas puede ayudarte a determinar cuán tarde pueden llegar los datos antes de que se rechacen.</p><h3><strong>3. Dimensiona el nivel caliente para llegadas tardías</strong></h3><p>Garantiza que los índices de respaldo permanezcan con permisos de escritura el tiempo suficiente para los datos retrasados.</p><p>Regla operativa:</p><ul><li><p><code>warm_min_age &gt; rollover_max_age + maximum_expected_lateness</code></p></li></ul><p>Recuerda, los índices deben permanecer en modo de escritura durante al menos seis horas si las métricas pueden llegar con seis horas de retraso.</p><h3><strong>4. Decide cómo manejar los documentos rechazados</strong></h3><p>Elige una estrategia antes de habilitar TSDS:</p><ul><li><p>Almacén de fallas (recomendado en Elasticsearch 9.1+).</p></li><li><p>Cola de mensajes no procesados de Logstash.</p></li><li><p>Índice de respaldo para retrasos.</p></li><li><p>Aceptar la pérdida de datos limitada.</p></li></ul><h3><strong>5. Monitorea el estado de la ingesta</strong></h3><p>Agrega alertas para:</p><ul><li><p>La tasa de indexación disminuye.</p></li><li><p>Documentos rechazados.</p></li><li><p>Crecimiento del almacén de fallas.</p></li><li><p>Desajustes de entrada/salida en la pipeline.</p></li></ul><p>Los problemas de datos tardíos a menudo aparecen primero como anomalías de ingesta.</p><h2>Resumen</h2><p>Los flujos de datos temporales ofrecen importantes mejoras de almacenamiento y rendimiento para las cargas de trabajo de métricas, pero introducen un cambio arquitectónico importante: los índices de respaldo están acotados en el tiempo, lo que afecta el comportamiento de ILM.</p><p>Al usar TSDS:</p><ul><li><p>Los índices deben mantenerse con permisos de escritura el tiempo suficiente para aceptar datos tardíos.</p></li><li><p>Los pipelines de ingesta deben gestionar los documentos rechazados de forma segura.</p></li></ul><p>La regla clave a recordar es:</p>warm_min_age &gt; rollover_max_age + maximum_expected_lateness<p>Si diseñas políticas de ILM teniendo en cuenta esa limitación, TSDS funciona muy bien para cargas de trabajo de métricas.</p><p>Sin embargo, si lo ignoras, tu pipeline de ingesta puede descubrir esos límites de tiempo de la manera difícil.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/tsds-ilm-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/tsds-ilm-elasticsearch</guid>
    <category><![CDATA[Datos de índice]]></category>
    <category><![CDATA[Base de datos vectorial]]></category>
    <dc:creator><![CDATA[Bret Wortman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcacf154aeacb29d8/6a17dc30dbb4ffc7ddfb557f/e4c46e4a6f746d9c845857e80de036f5d51cd4e7-1280x720.png" length="0" type="image/png"/>
    <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Rastreador Elástico de Sitio web Abierto como código]]></title>
    <description><![CDATA[Aprende a usar GitHub Actions para gestionar configuraciones de Elastic Open Crawler, de modo que cada vez que enviemos cambios al repositorio, los cambios se apliquen automáticamente a la instancia desplegada del rastreador.]]></description>
    <content:encoded><![CDATA[<p>Con <a href="https://github.com/elastic/crawler">Elastic Open Sitio web Crawler</a> y su arquitectura basada en CLI, tener configuraciones de rastreador versionado y una pipeline CI/CD con pruebas locales ahora es bastante sencillo de lograr.</p><p>Tradicionalmente, gestionar los rastreadores era un proceso manual y propenso a errores. Implicaba editar configuraciones directamente en la interfaz y luchar con clonar configuraciones de rastreo, retrocesos, versionear y más. Tratar las configuraciones de rastreadores como código resuelve esto al proporcionar los mismos beneficios que esperamos en el desarrollo de software: repetibilidad, trazabilidad y automatización.</p><p>Este flujo de trabajo facilita la incorporación del Open Sitio web Crawler a tu pipeline CI/CD para rollbacks, copias de seguridad y migraciones, tareas que eran mucho más complicadas con los Elastic Crawlers anteriores, como el Elastic Sitio web Crawler o el App Search Crawler.</p><p>En este artículo, vamos a aprender cómo:</p><ul><li><p>Gestiona nuestras configuraciones de rastreo usando GitHub</p></li><li><p>Tener una configuración local para probar pipelines antes de desplegar</p></li><li><p>Crea una configuración de producción para ejecutar el rastreador sitio web con nuevos ajustes cada vez que enviemos cambios a nuestra rama principal</p></li></ul><p>Puedes encontrar el repositorio de <a href="https://github.com/llermaly/elastic-open-crawler-as-code"><em><strong>proyectos aquí</strong></em></a><em><strong>. </strong></em><em>Según escribo, estoy usando Elasticsearch 9.1.3 y Open Sitio web Crawler 0.4.2.</em></p><h2>Prerrequisitos</h2><ul><li><p>Escritorio Docker</p></li><li><p>Instancia de Elasticsearch</p></li><li><p>Máquina virtual con acceso SSH (por ejemplo, AWS EC2) y Docker instalados</p></li></ul><h2>Pasos</h2><ol><li><p>Estructura de carpetas</p></li><li><p>Configuración del orugador</p></li><li><p>Docker-compose (entorno local)</p></li><li><p>Acciones en Github</p></li><li><p>Pruebas locales</p></li><li><p>Desplegando a la producción</p></li><li><p>Realización de cambios y re-despliegue</p></li></ol><h2>Estructura de carpetas</h2><p>Para este proyecto, tendremos la siguiente estructura de archivos:</p>├── docker-compose.yml # Local elasticsearch + crawler
├── config/crawler-config.yml # Crawler config
├── .github/workflows/deploy.yml # GH Action to deploy changes
├── local.sh # Script to run our local crawler<h2>Configuración del orugador</h2><p>Bajo <code>crawler-config.yml,</code> pondremos lo siguiente:</p>output_sink: elasticsearch
output_index: web-crawl-index
max_crawl_depth: 1

elasticsearch:
  host: ${ES_HOST}
  api_key: ${ES_API_KEY}
     
domains:
  - url: https://web-scraping.dev
    seed_urls:
      - https://web-scraping.dev/product/1
      - https://web-scraping.dev/product/2
      - https://web-scraping.dev/product/3<p>Esto se rastreará desde <a href="https://web-scraping.dev/products">https://sitio web-scraping.dev/products</a>, un sitio simulado de productos. Solo rastrearemos las tres primeras páginas del producto. La configuración <code>max_crawl_depth</code> evitará que el rastreador descubra más páginas de las definidas como <code>seed_urls</code> al no abrir los enlaces que contienen.</p><p>Elasticsearch <code>host</code> y <code>api_key</code> se llenarán dinámicamente dependiendo del entorno en el que ejecutemos el script.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7d37b966aafbd3c1/6a17ef9842022946e929f6b9/f9831034e1c4ccb554d37bdd188f2824338355a0-890x624.png" alt="La página del producto de &quot;Box of Chocolate Candy&quot; del dominio sitio web-scraping.dev, un sitio simulado para probar sitio web scraping. La página muestra el título del producto, la imagen, la descripción y los elementos HTML para los botones de precio y compra." /><h2>Docker-compose (entorno local)</h2><p>Para la <code>docker-compose.yml,</code> local desplegaremos el rastreador y un único clúster Elasticsearch + Kibana, para poder visualizar fácilmente los resultados del <em><strong>rastreo antes</strong></em> de desplegarlos en producción.</p>services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
    ports:
      - "9200:9200"
    networks: [esnet]
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9200"]
      interval: 5s
      timeout: 5s
      retries: 10

  kibana:
    image: docker.elastic.co/kibana/kibana:9.1.3
    environment:
      - ELASTICSEARCH_HOSTS=http://es01:9200
    ports:
      - "5601:5601"
    networks: [esnet]
    depends_on: [es01]

  crawler:
    image: docker.elastic.co/integrations/crawler:0.4.2
    environment:
      - ES_HOST=http://es01:9200
      - CRAWLER_JRUBY_OPTS=--server
    container_name: crawler
    volumes:
      - ./config:/home/app/config
    networks: [esnet]
    entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
    stdin_open: true
    tty: true

networks:
  esnet:
    driver: bridge<p>Fíjate en cómo el rastreador espera hasta que Elasticsearch esté listo para ejecutar.</p><h2>Acciones en Github</h2><p>Ahora necesitamos crear una acción en GitHub que copie la nueva configuración y ejecute el rastreador en nuestra máquina virtual en cada envío a main. Esto garantiza que siempre tengamos la última configuración desplegada, sin tener que entrar manualmente en la máquina virtual para actualizar archivos y ejecutar el rastreador. Vamos a usar AWS EC2 como proveedor de máquinas virtuales.</p><p>El primer paso es agregar el host (<code>VM_HOST</code>), el usuario de la máquina (<code>VM_USER</code>), la clave SSH RSA (<code>VM_KEY</code>), el host de Elasticsearch (<code>ES_HOST</code>) y la clave API de Elasticsearch (<code>ES_API_KEY</code>) a los secretos de acción de GitHub:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5a0fcfff9b7f997/6a17ef9a6df731d7d40a0fdf/e1075bc54151b4b94eac2a6bd2682e9997e6c709-1106x707.png" alt="Una página de configuración de &quot;acciones, secretos y variables&quot;, que muestra secretos del repositorio como VM_HOST, VM_KEY y VM_USER dentro de una interfaz sitio web." /><p>De este modo, la acción podrá acceder a nuestro servidor para copiar los archivos nuevos y ejecutar el rastreo.</p><p>Ahora, creemos nuestro archivo <code>.github/workflows/deploy.yml</code> :</p>name: Deploy

on:
  push:
    branches: [main]

jobs:
  Deploy:
    name: Deploy to EC2
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5

      - name: Deploy crawler
        env:
          HOSTNAME: ${{ secrets.VM_HOST }}
          USER_NAME: ${{ secrets.VM_USER }}
          PRIVATE_KEY: ${{ secrets.VM_KEY }}
          ES_HOST: ${{ secrets.ES_HOST }}
          ES_API_KEY: ${{ secrets.ES_API_KEY }}
        run: |
          # Save private key
          echo "$PRIVATE_KEY" &gt; private_key
          chmod 600 private_key

          # Generate final config locally
          envsubst &lt; config/crawler-config.yml &gt; config/crawl-config-final.yml

          # Copy the config folder to VM
          scp -o StrictHostKeyChecking=no -i private_key -r config ${USER_NAME}@${HOSTNAME}:~/config

          # SSH into VM and run crawler
          ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} &lt;&lt; EOF
            docker run --rm \
              -v ~/config:/config \
              docker.elastic.co/integrations/crawler:latest jruby \
              bin/crawler crawl /config/crawl-config-final.yml
          EOF<p>Esta acción ejecutará los siguientes pasos cada vez que empujemos cambios en el archivo de configuración del rastreador:</p><ol><li><p>Llenar el host y la clave API de Elasticsearch en la configuración de yml</p></li><li><p>Copia la carpeta config a nuestra máquina virtual</p></li><li><p>Conéctate vía SSH a nuestra máquina virtual</p></li><li><p>Ejecuta el rastreo con la configuración que acabamos de copiar del repositorio</p></li></ol><h2>Pruebas locales</h2><p>Para probar nuestro rastreador localmente, creamos un script bash que llena el host de Elasticsearch con el local de Docker y comienza un rastreo. Puedes ejecutar <code>./local.sh</code> para ejecutarlo.</p>#!/bin/bash

# Exit on any error
set -e

# Load environment variables
export ES_HOST="http://es01:9200"

# Generate final crawler config
envsubst &lt; ./config/crawler-config.yml &gt; ./config/crawl-config-final.yml

# Bring everything up
docker compose up --build<p>Veamos Kibana DevTools para confirmar que el<code> web-crawler-index</code> se rellenó correctamente:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt989660368fe14db8/6a17ef9b9da390c79ce46562/18551635e8265866e389a9632c4e4540958e4468-990x723.png" alt="Kibana DevTools para confirmar que el índice del rastreador sitio web está configurado correctamente." /><h2>Desplegando a la producción</h2><p>Ahora estamos listos para enviar a la rama principal, que desplegará el rastreador en tu máquina virtual y comenzará a enviar registros a tu instancia Serverless Elasticsearch.</p>git add .
git commit -m "First commit"
git push<p>Esto activará la Acción de GitHub, que ejecutará el script de despliegue dentro de la máquina virtual y comenzará a rastrear.</p><p>Puedes confirmar que la acción se ejecutó yendo al repositorio de GitHub y visitando la pestaña "Acciones":</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt986f1a4e4f3288d2/6a17ef9c7f6f1584fdc09c10/67ba3a7164d7a8049fe5661264820826cb18ed64-667x325.png" alt="Las acciones se despliegan en la pestaña EC2 de un repositorio de GitHub." /><h2>Realización de cambios y re-despliegue</h2><p>Algo que quizá notaste es que el <code>price</code> de cada producto forma parte del cuerpo del documento. Lo ideal sería almacenar el precio en un campo aparte para poder aplicar filtros sobre él.</p><p>Vamos a agregar este cambio al archivo <code>crawler.yml</code> para usar <a href="https://github.com/elastic/crawler/blob/main/docs/features/EXTRACTION_RULES.md">reglas de extracción</a> que extraigan el precio de la clase CSS de <code>product-price</code> :</p>output_sink: elasticsearch
output_index: web-crawl-index
max_crawl_depth: 1

elasticsearch:
  host: ${ES_HOST}
  api_key: ${ES_API_KEY}
     
  # Index ingest pipeline to process documents before indexing          
  pipeline_enabled: true
  pipeline: pricing-pipeline

domains:
  - url: https://web-scraping.dev
    seed_urls:
      - https://web-scraping.dev/product/1
      - https://web-scraping.dev/product/2
      - https://web-scraping.dev/product/3
    extraction_rulesets:
      - url_filters:
          - type: ends
            pattern: /product/*
        rules:
          - action: extract
            field_name: price
            selector: .product-price
            join_as: string
            source: html<p>También vemos que el precio incluye un signo de dólar (<code>$</code>), que debemos eliminar si queremos hacer consultas por rango. Podemos usar una canalización de ingesta para eso. Ten en cuenta que lo estamos haciendo referencia en nuestro nuevo archivo de configuración del rastreador arriba:</p>PUT _ingest/pipeline/pricing-pipeline
{
  "processors": [
    {
      "script": {
        "source": """
                ctx['price'] = ctx['price'].replace("$","")
            """
      }
    }
  ]
}<p>Podemos ejecutar ese comando en nuestro clúster de Elasticsearch en producción. Para el desarrollo, al ser efímero, podemos hacer que la creación de pipeline forme parte del archivo <code>docker-compose.yml</code> agregando el siguiente servicio. Ten en cuenta que también agregamos un <code>depends_on</code> al servicio de rastreo para que empiece después de que la tubería se creó con éxito.</p> crawler:
    image: docker.elastic.co/integrations/crawler:0.4.2
    environment:
      - ES_HOST=http://es01:9200
      - CRAWLER_JRUBY_OPTS=--server
    container_name: crawler
    volumes:
      - ./config:/home/app/config
    networks: [esnet]
    entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
    depends_on:
      pipeline-init:
        condition: service_completed_successfully
    stdin_open: true
    tty: true  


  pipeline-init:
    image: curlimages/curl:latest
    depends_on:
      es01:
        condition: service_healthy
    networks: [esnet]
    entrypoint: &gt;
        sh -c "
        echo 'Creating ingest pipeline...';
        curl -s -X PUT http://es01:9200/_ingest/pipeline/pricing-pipeline \\
          -H 'Content-Type: application/json' \\
          -d '{\"processors\":[{\"script\":{\"source\":\"ctx.price = ctx.price.replace(\\\"$\\\", \\\"\\\")\"}}]}';
        echo 'Pipeline created!';
        "<p>Ahora vamos a ejecutar <code>`./local.sh`</code> para ver el cambio localmente:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f390aedf67cb4fe/6a17ef9eaf47b62bd2cde05b/dc1801599344a9f69f072b07ff828c4ba3815d7b-738x473.png" alt="Estoy usando './local.sh' para ver el cambio de precio localmente." /><p>¡Bien! Ahora impulsemos el cambio:</p>git add crawler-config.yml
git commit -m "added price CSS selector"
git push<p>Para confirmar que todo funciona, puedes comprobar tu Kibana de producción, que debería reflejar los cambios y mostrar el precio como un nuevo campo sin el signo del dólar.</p><h2>Conclusión</h2><p>El Elastic Open Sitio web Crawler te permite gestionar tu rastreador como código, lo que significa que puedes automatizar toda la pipeline —desde el desarrollo hasta el despliegue— y agregar entornos locales efímeros y pruebas programáticas contra los datos rastreados, por nombrar algunos ejemplos.</p><p>Se te invita a clonar el repositorio oficial y empezar a indexar tus propios datos usando este flujo de trabajo. También puedes leer <a href="https://www.elastic.co/search-labs/blog/semantic-search-open-crawler">este artículo</a> para aprender a realizar búsqueda semántica en índices producidos por el rastreador.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elastic-open-crawler-config-as-code</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elastic-open-crawler-config-as-code</guid>
    <category><![CDATA[Datos de índice]]></category>
    <category><![CDATA[Operaciones]]></category>
    <dc:creator><![CDATA[Gustavo Llermaly]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt00e7c95012dc38cc/6a17efa0fbc5f80dad491b93/0ac41f55c85ad3f647cb0e0d750ed80bacd397f3-1036x581.png" length="0" type="image/png"/>
    <pubDate>Mon, 22 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cómo mostrar los campos de un índice de Elasticsearch]]></title>
    <description><![CDATA[Aprende a mostrar campos de un índice de Elasticsearch usando las APIs _mapping y _search, subcampos, _source sintéticos y campos de ejecución.]]></description>
    <content:encoded><![CDATA[<p>En este artículo, hablaremos de cómo mostrar los campos de un índice de Elasticsearch. Esto puede ser útil para entender la estructura de tus datos, identificar campos específicos y solucionar problemas. Vamos a tratar los siguientes temas:</p><ol><li><p>Uso de la API <code>_mapping</code> para recuperar información de campos</p></li><li><p>Uso de la API <code>_search</code> para mostrar los valores de los campos</p></li><li><p>Visualización de subcampos</p></li><li><p>_source sintética</p></li><li><p>Campos de tiempo de ejecución</p></li></ol><h2>1. Uso de la API _mapping para recuperar información de campo</h2><p>La API <code>_mapping</code> permite recuperar la definición de mapeo para un índice o varios índices. Esto incluye información sobre los campos, sus tipos de datos y otras propiedades. Para recuperar el mapeo de un índice específico, emplee la siguiente petición:</p>GET /&lt;index_name&gt;/_mapping<p>Por ejemplo, si tienes un índice llamado <code>my_index</code>, puedes recuperar su mapeo con la siguiente petición:</p>GET /my_index/_mapping<p>La respuesta incluirá la definición de mapeo para el índice, que contiene información sobre los campos y sus propiedades.</p><p>También es posible recuperar el mapeo de un campo específico. Esto puede ser útil si tu mapeo es bastante grande y solo quieres centrarte en un campo específico. Para recuperar el mapeo de un campo específico, emplee la siguiente petición:</p>GET /my_index/_mapping/field/my_field<p>También puedes recuperar las asignaciones de varios campos separando sus nombres con comas, como en la siguiente petición:</p>GET /my_index/_mapping/field/my_field_1,my_field_2,my_field_3<h2>2. Uso de la API _search para mostrar los valores de los campos</h2><p>Para mostrar los valores de los campos en un índice de Elasticsearch, puedes usar la API <code>_search</code> . La API <code>_search</code> te ofrece múltiples formas de controlar qué campos se devuelven; Los dos principales son:</p><ol><li><p><strong><code>_source</code></strong>: El campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field"><code>_source</code></a> contiene el cuerpo original del documento JSON exactamente como estaba indexado, incluyendo cualquier cambio realizado por las canalizaciones de ingestión o pasos de preprocesamiento. Para mostrar campos específicos del documento fuente, implementa filtrado de fuentes como veremos a continuación.</p></li><li><p><strong><code>fields</code></strong>: El parámetro <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields"><code>fields</code></a> te permite recuperar campos específicos de tus documentos al realizar una búsqueda, basándote en el mapeo de índice. A diferencia de <code>_source</code>, <code>fields</code> también puede devolver valores de campos almacenados, valores de documentación o campos de ejecución sin referenciar la <code>_source</code>, aunque para campos estándar sin valores de documento ni configuraciones almacenadas, vuelve a <code>_source</code>. Esto puede aportar muchos beneficios como el rendimiento y más, como veremos a continuación.</p></li></ol><h3>Uso del campo _source</h3><p>Por defecto, la API<code> _search</code> devuelve el campo <code>_source</code> , que contiene el documento JSON original que se indexó. Para mostrar campos específicos, puedes agregar filtros en el parámetro <code>_source </code>de la solicitud de búsqueda; Esto se llama filtrado de fuente.</p><p>Aquí tienes un ejemplo de una solicitud de búsqueda que devuelve los valores de los campos <code>title </code>y <code>author</code> para documentos en el índice <code>my_index</code> :</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["title", "author"]
}<p>En este ejemplo, el parámetro <code>_source</code> especifica los campos que se deben devolver.</p><p>Si necesitas aún más control, puedes usar las propiedades de <code>includes</code> y <code>excludes </code>del objeto <code>_source</code>. Por ejemplo, la consulta siguiente devuelve el campo <code>title</code> de nivel superior y todos los subcampos de <code>author</code> excepto <code>author.description</code>.</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": {
     “includes”: [“title”, “author.*],
     “excludes”: [“author.description”]
  }
}<p>En este ejemplo, usamos el patrón <code>author.* </code>para recuperar todos los subcampos directos del objeto <code>author </code>. Luego excluimos explícitamente <code>author.description </code>para que solo se devuelvan los otros campos de autor. Ten en cuenta que esto no tiene mejoras de rendimiento ya que aún tiene que cargar y analizar el JSON de origen, pero puede reducir el tamaño de la respuesta enviada por la red.</p><h3>Uso del parámetro de campos</h3><p>Puedes usar el parámetro <code>fields</code> para filtrar los campos que aparecen en la respuesta de búsqueda. Emplear <code>fields</code> <code>_source</code> ofrece varios beneficios, entre ellos:</p><ul><li><p><strong>Mejora de rendimiento: </strong><code>fields </code>puede devolver valores directamente desde <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-store">campos almacenados</a> o <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/doc-values">valores de documentos</a> sin tener que cargar toda la <code>_source</code>, haciendo que el tamaño de la carga útil de respuesta sea menor.</p></li><li><p><strong>Salida formateada:</strong> Para campos estándar,<code> fields</code> puede recurrir a <code>_source</code> para obtener los valores, pero revisa el mapeo del índice para formatear correctamente la salida, como las fechas formateadas, haciéndolas consistentes con lo que se usa para agregaciones y ordenación.</p></li><li><p><strong>Acceso a campos de tiempo de ejecución:</strong> <code>fields</code> puede devolver campos de tiempo de ejecución, que no existen en el <code>_source</code>original.</p></li><li><p>Aquí <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#search-fields-param">se pueden encontrar</a> más beneficios.</p></li></ul><p>Por ejemplo, para devolver solo los campos <code>title</code> y <code>author</code> en el índice <code>my_index</code> , puedes usar la siguiente solicitud de búsqueda:</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author"],
  "_source": false
}<p>En la consulta anterior, ponemos el campo <code>_source </code>en false para no devolver el documento fuente. Esto puede minimizar significativamente el tamaño de la carga útil para la respuesta, pero recuerda que esto solo funciona porque los campos <code>title</code> y <code>author</code> son del tipo <code>keyword </code>campo, que <code>doc_values</code> habilitaron por defecto. Si el campo no tiene <code>doc_values</code> activado y el <code>_source</code> está configurado como falso, Elasticsearch no tendría forma de recuperarlos y se omitiría en la respuesta.</p><p>Es importante señalar que la respuesta <code>fields</code> siempre devuelve un array de valores para cada campo, incluso si solo hay un único valor. Esto se debe a que Elasticsearch no tiene un tipo de array dedicado, y cualquier campo puede tener varios valores. Para más información sobre los arrays en Elasticsearch, haz <a href="http://elastic.co/docs/reference/elasticsearch/mapping-reference/array">clic aquí</a>.</p><h3>Otras formas de recuperar campos</h3><p>Aunque recuperar campos usando <code>_source</code> o <code>fields</code> son los métodos recomendados, existen diferentes métodos disponibles para casos de uso específicos, como:</p><p><strong>Campos de valor de documentos:</strong> Si quieres evitar <code>_source</code> por completo, puedes buscar usando el parámetro <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#docvalue-fields"><code>docvalue_fields</code></a>. Los valores de documentación almacenan los mismos valores de campo que <code>_source</code> pero en una estructura de datos en disco, optimizada para ordenar y agregar.</p><p>Como está separado de los valores almacenados con <code>_source</code>, puedes aplicar campos específicos sin cargar toda la <code>_source</code>. Esto es útil si consultas documentos grandes pero solo necesitas unos pocos campos pequeños que soporten valores de documentos. Otro caso de uso para usar <code>docvalue_fields </code>es cuando quieres usar formato personalizado en campos <code>date</code> y <code>numeric</code> , como veremos en el ejemplo más abajo.</p><p>Ten en cuenta que esto solo funciona para campos que activas <code>doc_values</code> o para tipos de campos que lo tienen activado por defecto, como <code>keyword</code>, <code>date</code>, tipos numéricos y <code>boolean</code>, no para <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/text"><code>text</code></a> o <a href="https://www.elastic.co/docs/reference/elasticsearch/plugins/mapper-annotated-text-usage"><code>annotated_text</code></a>.</p><p>En este ejemplo, usamos el parámetro <code>docvalue_fields</code> para recuperar los campos <code>title</code>, <code>author</code>, y <code>published</code> sin cargar el documento completo de <code>_source</code> :</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "docvalue_fields": [
    "title",
    "author",
    {
      "field": "published",
      "format": "epoch_millis"
    }
  ],
  "_source": false
}<p>Cuando se ejecuta esta consulta, Elasticsearch toma los valores directamente de su almacenamiento columnar en disco en lugar de referenciar el <code>_source </code>de cada documento. El campo <code>published</code> se devuelve con el formato <code>epoch_millis</code> en lugar del formato por defecto, gracias al parámetro <code>format</code> proporcionado en la consulta.</p><p><strong>Campos almacenados:</strong> Si marcas explícitamente campos específicos como <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-store">almacenados</a> en el mapeo, puedes usar el parámetro <code>stored_fields</code> para filtrar esos campos. Esto es útil si quieres respuestas ligeras solo con esos campos específicos o para campos que almacenaste deliberadamente para recuperarlos después. Se almacena por separado de <code>_source</code>, por lo que este método también es útil para evitar la necesidad de cargar <code>_source</code>.</p><p>Es importante señalar que esta opción está desactivada por defecto y generalmente no se recomienda. Emplea filtrado de fuentes para devolver ciertos subconjuntos del documento fuente original.</p><p>En la consulta de ejemplo a continuación, usamos el parámetro <code>stored_fields</code> para recuperar el campo <code>summary</code> , que tiene la configuración de mapeo de índice "<code>store”: true</code>.</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "stored_fields": ["summary"]
}<p>Cuando se ejecuta esta consulta, Elasticsearch busca si este campo está marcado con <code>”store”: true</code>, si no lo encuentra, se saltará el campo por completo.</p><h2>3. Visualización de subcampos</h2><p>Si tu índice contiene subcampos, puedes usar la notación de puntos para especificar el camino de campo en el parámetro <code>fields</code> . Ten en cuenta que los subcampos son diferentes del <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/nested">tipo de campo anidado</a>. Por ejemplo, si tienes un subcampo llamado <code>address.city</code>, puedes incluirlo en la respuesta de búsqueda así:</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author", "address.city"],
  "_source": false
}<p>En este ejemplo, la respuesta de búsqueda incluirá los valores de los campos <code>title</code>, <code>author</code> <code>address.city</code> .</p><h2>4. _source sintético</h2><p>Si quieres mantener la funcionalidad de usar<code> _source</code> pero también ahorrar espacio en disco, tienes la opción de usar <code>_source</code> sintético en tu mapeo de índice. <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field#synthetic-source">La_source</a> sintética es una función que permite a Elasticsearch reconstruir el <code>_source</code> a partir de datos existentes como campos almacenados y valores de documentos, incluso cuando <code>_source</code> está desactivado. Esto te permite ahorrar mucho espacio de almacenamiento a cambio de velocidades ligeramente menores en el momento de la consulta, ya que la reconstrucción se realiza sobre la marcha. Activa esta función usando los valores que aparecen a continuación en la configuración de tu índice:</p>PUT idx
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  }
}<p>Algunos beneficios de usar <code>_source </code>sintéticos incluyen: visualización completa del documento al usar la API <code>_search</code> , filtrado de código fuente y compatibilidad con otras funciones y herramientas como Kibana que esperan <code>_source</code> estén disponibles, todo ello evitando la necesidad de almacenar el documento completo <code>_source</code> .</p><h2>5. Campos de tiempo de ejecución</h2><p><a href="https://www.elastic.co/docs/manage-data/data-store/mapping/runtime-fields">Los campos de ejecución</a> te permiten definir campos guionizados en el momento de la consulta o en tu mapeo de índice bajo un bloque de ejecución. Estos campos nunca se indexan, por lo que agregar un campo en tiempo de ejecución no aumenta el tamaño del índice pero nunca aparecerá en <code>_source</code>. Los campos de ejecución definidos en el mapeo son persistentes y están disponibles para todas las consultas, mientras que los campos de ejecución definidos en el momento de la consulta son temporales y solo están disponibles en esa solicitud de búsqueda.</p><p>El principal beneficio de usar campos de tiempo de ejecución es la posibilidad de agregar campos a documentos luego de haberlos ingerido, simplificando así tus decisiones de mapeo. Los campos de ejecución también son ideales para enriquecer tus documentos con valores que no existen en el documento original pero que se generan mediante un script, como formatear una cadena o calcular un puntaje.</p><p>También cabe destacar que los campos de ejecución pueden perjudicar el rendimiento, ya que será necesario ejecutar un script para cada documento del conjunto de resultados. Para <a href="https://www.elastic.co/docs/manage-data/data-store/mapping/retrieve-runtime-field">recuperar un campo de ejecución</a>, también puedes usar el parámetro <code>fields</code> en la API <code>_search</code> .</p><h2>Conclusión</h2><p>Mostrar campos de un índice de Elasticsearch puede ir desde simplemente recuperar valores usando el mapeo de índice o el <code>_source</code>, hasta métodos más avanzados usando campos <code>fields</code>, <code>docvalue_fields</code>o en tiempo de ejecución para mayor control y eficiencia. Comprender los compromisos entre diferentes métodos es clave para optimizar tus experiencias de búsqueda. Ya sea que estés optimizando cargas útiles, enriqueciendo documentos o empleando <code>_source</code> sintéticos para ahorrar espacio, Elasticsearch te ofrece múltiples herramientas y funciones para encontrar los datos que necesitas, de la manera que necesitas. Estas técnicas pueden ayudarte a entender la estructura de tus datos, identificar campos específicos y solucionar problemas.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-index-show-fields</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-index-show-fields</guid>
    <category><![CDATA[Datos de índice]]></category>
    <category><![CDATA[Mapeos]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd041e871a8935448/6a17de320b0bedf404dd34ab/23b96aaa1a38b1f4747b4a87695d816f24c0cf70-720x421.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 06 Aug 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Guion Ruby en Logstash]]></title>
    <description><![CDATA[Infórmate sobre el plugin de filtro Ruby Logstash para transformar datos avanzados en tu pipeline Logstash.]]></description>
    <content:encoded><![CDATA[<p>Logstash es una cadena de procesamiento de datos que ingiere datos de múltiples fuentes, los transforma y los envía a los destinos que elijas. Los plugins de filtro son clave para este proceso; Realizan operaciones específicas sobre tus datos a medida que avanzan en la pipeline.</p><p>Logstash incluye varios filtros integrados para tareas comunes como análisis sintáctico, enriquecimiento y modificación de datos. Pero a veces te encontrarás con escenarios que requieren una lógica personalizada que va más allá de lo que estos filtros estándar pueden ofrecer. Aquí es donde entra el <a href="https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-ruby">plugin de filtro Ruby</a> .</p><p><strong>El plugin de filtro Ruby te permite ejecutar código Ruby personalizado directamente dentro de tu pipeline de Logstash.</strong> Cuando los filtros estándar no son suficientes, el filtro Ruby te permite manejar transformaciones complejas de datos, implementar lógica de negocio personalizada o integrarte con sistemas externos.</p><p>En este blog, exploraremos cómo usar los filtros Ruby, desde el uso básico hasta el avanzado.</p><h2>¿Cuándo deberías usar el filtro Ruby?</h2><p>Como arquitecto consultor de Elastic, a menudo veo a clientes que emplean Logstash para la cadena de procesamiento de datos, aunque hoy en día no es un motor de procesamiento de datos de última generación. A menudo tienen dificultades con las limitaciones de los filtros estándar cuando se trata de manipulación compleja de datos o lógica personalizada. En estos casos, el filtro Ruby puede ayudar a superar esos desafíos.</p><p>El filtro Ruby es útil cuando los filtros Logstash estándar no pueden cumplir tus requisitos específicos. Aquí tienes algunos casos de uso comunes:</p><ul><li><p><strong>Manipulación profunda de datos anidados</strong>: Modificar estructuras JSON complejas, arrays dentro de arrays o reestructurar dinámicamente los datos en función del contenido</p></li><li><p><strong>Procesamiento avanzado de cadenas</strong>: Analizar y extraer datos estructurados de texto no estructurado</p></li><li><p><strong>Implementación de lógica de negocio compleja</strong>: Crear transformaciones personalizadas que requieran lógica condicional, bucles o cálculos complejos</p></li></ul><h2>Uso básico</h2><p>Empecemos con un ejemplo sencillo para entender cómo funciona el filtro Ruby.</p><h3>Configuración del filtro Ruby</h3><p>Cuando crees una pipeline de Logstash, deberías colocar el archivo de configuración en el directorio <code>/etc/logstash/conf.d</code> . Alternativamente, puedes usar <code>-f</code> opción para especificar la ruta al archivo de configuración cuando arranques Logstash manualmente, para que puedas experimentar fácilmente con tus pipelines.</p>$ ./bin/logstash -f /path/to/your_pipeline.conf<p>El archivo de configuración debería tener una extensión <code>.conf</code> .</p><p>Para usar el filtro Ruby, define un filtro <code>ruby</code> en la sección de filtros de tu archivo de configuración de la tubería Logstash (*.conf). Aquí tienes un ejemplo básico:</p>filter {
  ruby {
    code =&gt; "
      event.set('new_field', 'Hello from Ruby!')
    "
  }
}<p>Este filtro Ruby en línea define una instancia de filtro Ruby dentro de tu configuración de Logstash. El parámetro <code>code</code> proporciona el script Ruby en línea que Logstash ejecutará para cada evento procesado por este filtro. Dentro de ese script, hay una variable <code>event</code> disponible que representa el propio evento. El objeto evento contiene los datos originales enviados a Logstash y cualquier campo adicional creado durante las etapas de filtro de Logstash. Puedes acceder a esos campos a través de la API de eventos de Logstash como <code>event.get()</code> y <code>event.set()</code>. En este código de ejemplo, <code>event.set('new_field', 'Hello from Ruby!')</code> establecer un nuevo campo llamado <code>new_field</code> al valor de cadena <code>Hello from Ruby!</code>. Puedes agregar cualquier otro código en este bloque de <code>code</code> según lo necesites.</p><p>Ten en cuenta que este objeto <code>event</code> no es un objeto hash de Ruby habitual, aunque actúa como un contenedor de datos clave-valor. Consulta <a href="https://www.elastic.co/docs/reference/logstash/event-api">esta documentación oficial</a> para saber más sobre la API de eventos.</p><h3>Externalizar la escritura Ruby</h3><p>Para transformaciones simples, el código Ruby en línea es cómodo. Pero, para lógica compleja o funciones reutilizables, se recomienda mover el código a un script Ruby externo. Esto mejora la mantenibilidad y mantiene limpia la configuración de tu pipeline de Logstash.</p><p>Primero, crea un script Ruby y almacénalo como <code>my_ruby_script.rb</code>. El script debe definir un método <code>filter</code> que procese el evento. Toma un objeto evento como argumento, que representa el evento actual que se está procesando. El método <code>filter</code> necesita devolver un serial de eventos para emitir. Para eliminar el evento, devuelvo un array vacío.</p><p>Por ejemplo, el siguiente script lee el campo <code>message</code> , calcula su longitud y almacena el resultado en un nuevo campo llamado <code>message_length</code>.</p>def register(params)
  # This method is called when the plugin is loaded.
  # You can use it to initialize any instance variables or perform setup tasks.
end

def filter(event)
  message = event.get('message')

  if message
    event.set('message_length', message.length)
  end

  return [event]
end<p>A continuación, configura la configuración del filtro Ruby para que haga referencia al script usando la opción <code>path</code> . Esto indica a Logstash que cargue y ejecute el script externo. Al usar scripts externos, cerciórate de que el archivo existe y tiene las licencias correctas.</p>filter {
  ruby {
    path =&gt; "/path/to/my_ruby_script.rb"
  }
}<p>Ahora, cada evento se pasa al método <code>filter</code> en <code>my_ruby_script.rb</code> y es procesado por él.</p><p>Este enfoque te ayuda a gestionar la lógica compleja de forma más eficaz, facilitando probar, depurar y reutilizar tu código Ruby.</p><h2>Uso avanzado</h2><p>En esta sección, exploraremos algunos ejemplos avanzados de cómo usar el filtro Ruby en Logstash. Estos ejemplos demostrarán cómo realizar transformaciones de datos, enriquecer eventos e implementar lógica personalizada usando Ruby.</p><h3>Manipulación de estructuras de datos anidadas</h3><p>Un evento Logstash es la estructura de datos central que procesa Logstash. Puede contener varios campos, incluyendo estructuras de datos anidadas como arrays y hashes. El filtro Ruby te permite manipular fácilmente estas estructuras anidadas.</p><p>El filtro Ruby puede manejar estructuras de datos anidadas, como hashes y arrays, permitiéndote modificar o agregar campos dentro de estas estructuras. Esto es útil cuando se trata de formatos de datos complejos como JSON.</p>input {
  generator {
    lines =&gt; [
      '{"nested": {"key1": "value1", "key2": "value2"}}'
    ]
    count =&gt; 1
    codec =&gt; "json"
    ecs_compatibility =&gt; "disabled"
  }
}

filter {
  ruby {
    code =&gt; "
      nested_data = event.get('nested')

      if nested_data.is_a?(Hash)
        nested_data['key3'] = 'value3'
        event.set('nested', nested_data)
      end
    "
  }
}

output {
  stdout { codec =&gt; rubydebug }
}<p>Este ejemplo incluye un objeto JSON anidado en los datos de entrada. El filtro Ruby modifica los datos anidados agregando un nuevo par clave-valor. Este tipo de manipulación para datos anidados no es posible con los filtros Logstash estándar, lo que convierte al filtro Ruby en una opción útil para estructuras de datos complejas.</p><h3>Dividir un solo evento en varios eventos</h3><p>Los filtros Ruby también pueden usar para dividir un solo evento en varios eventos. Esto es útil cuando tienes un solo evento que contiene un array de objetos y quieres crear eventos separados para cada uno.</p><p>Ten en cuenta que ni la tubería de ingesta de Elasticsearch ni los procesadores de Beats/Elastic Agent soportan eventos de división. Este es uno de los casos de uso más estables de Logstash.</p><h4>Con filtro dividido</h4><p>Puedes usar el filtro <code>split</code> para dividir un evento en varios eventos según un campo especificado. Sin embargo, si necesitas realizar transformaciones adicionales o lógica durante la división, puedes usar el filtro Ruby en combinación con el filtro dividido.</p><p>En el siguiente ejemplo, tenemos un feed RSS como una sola línea de texto XML. Contiene múltiples elementos <code>&lt;item&gt;</code> . El filtro Ruby se emplea para extraer los <code>&lt;item&gt;</code> elementos del XML y almacenarlos en un nuevo campo llamado <code>items</code>. El filtro dividido se emplea entonces para dividir el evento en varios eventos según el campo <code>items</code> .</p>input {
  generator {
    lines =&gt; [
      '&lt;rss version="2.0"&gt;&lt;channel&gt;&lt;title&gt;Sample RSS&lt;/title&gt;&lt;item&gt;&lt;title&gt;Article 1&lt;/title&gt;&lt;link&gt;http://example.com/1&lt;/link&gt;&lt;description&gt;Desc 1&lt;/description&gt;&lt;/item&gt;&lt;item&gt;&lt;title&gt;Article 2&lt;/title&gt;&lt;link&gt;http://example.com/2&lt;/link&gt;&lt;description&gt;Desc 2&lt;/description&gt;&lt;/item&gt;&lt;/channel&gt;&lt;/rss&gt;'
    ]
    count =&gt; 1
    codec =&gt; "plain"
    ecs_compatibility =&gt; "disabled"
  }
}

filter {
  xml {
    source =&gt; "message"
    target =&gt; "rss"
    store_xml =&gt; true
    force_array =&gt; false
  }
  ruby {
    code =&gt; "event.set('items', event.get('[rss][channel][item]')) if event.get('[rss][channel][item]')"
  }
  split {
    field =&gt; "items"
  }
  ruby {
    code =&gt; "
      item = event.get('items')
      event.set('title', item['title']) if item['title']
      event.set('link', item['link']) if item['link']
      event.set('description', item['description']) if item['description']
    "
  }
  mutate {
    remove_field =&gt; ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}

output {
  stdout { codec =&gt; rubydebug }
}<p>Esto dará la siguiente manera:</p>{
          "title" =&gt; "Article 1",
           "link" =&gt; "http://example.com/1",
    "description" =&gt; "Desc 1"
}
{
          "title" =&gt; "Article 2",
           "link" =&gt; "http://example.com/2",
    "description" =&gt; "Desc 2"
}<p>Como notaste, el filtro <code>ruby</code> no es esencial en este caso. El filtro <code>split</code> puede usar para dividir el evento en varios eventos basados en el campo <code>items</code> , y el filtro <code>mutate</code> puede usar para eliminar campos innecesarios. Sin embargo, si necesitas realizar transformaciones o lógica adicional durante la división, puedes usar el filtro Ruby.</p><h4>Emplea escritura Ruby en línea</h4><p>También puedes usar un script Ruby en línea para dividir un solo evento en varios eventos usando el método <code>event.clone</code> y el <code>new_event_block variable</code>, como <code>new_event_block.call(new_event)</code>. Esto te permite crear nuevos eventos basados en el evento original mientras se conservan sus datos.</p><p>Aquí tienes un ejemplo de cómo usar el filtro Ruby para dividir un solo evento en varios eventos. La entrada y salida son las mismas que en el ejemplo anterior.</p>filter {
  xml {
    source =&gt; "message"
    target =&gt; "rss"
    store_xml =&gt; true
    force_array =&gt; false
  }
  ruby {
    code =&gt; "
      items = event.get('[rss][channel][item]')
      if items.is_a?(Array)
        items.each do |item|
          new_event = event.clone
          new_event.set('title', item['title'])
          new_event.set('link', item['link'])
          new_event.set('description', item['description'])
          new_event_block.call new_event
        end
        event.cancel
      elsif items.is_a?(Hash)
        event.set('title', items['title'])
        event.set('link', items['link'])
        event.set('description', items['description'])
      end
    "
  }
  mutate {
    remove_field =&gt; ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}<h4>Usa un script Ruby externo</h4><p>También puedes usar un script externo de Ruby para dividir un solo evento en varios eventos.</p><p>Archivo de configuración:</p>filter {
  xml {
    source =&gt; "message"
    target =&gt; "rss"
    store_xml =&gt; true
    force_array =&gt; false
  }
  ruby {
    path =&gt; "path/to/ruby/split_event.rb"
  }
  mutate {
    remove_field =&gt; ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}<p>El sistema Ruby debe externalizar como <code>split_event.rb</code>:</p>def filter(event)
  items = event.get('[rss][channel][item]')
  events = []
  if items.is_a?(Array)
    items.each do |item|
      new_event = event.clone
      new_event.set('title', item['title'])
      new_event.set('link', item['link'])
      new_event.set('description', item['description'])
      events &lt;&lt; new_event
    end
    return events
  elsif items.is_a?(Hash)
    event.set('title', items['title'])
    event.set('link', items['link'])
    event.set('description', items['description'])
    return [event]
  else
    return []
  end
end<p>Recuerda, el método <code>filter</code> debe devolver un serial de eventos. Puedes devolver varios eventos clonando un objeto de evento entrante y agregándolos al array, o puedes devolver un solo evento como un array con un solo elemento.</p>return events
# or
# return [event]<p>Esto te permite dividir un solo evento en varios eventos.</p><h3>Ejecuta comandos externos y analiza su salida</h3><p>El plugin de entrada ejecutiva de Logstash permite ejecutar comandos externos y su salida será un evento de Logstash. La salida del comando se almacenará en el campo <code>message</code> del evento.</p><p>Normalmente, la salida de los comandos del sistema es legible por humanos, pero no está estructurada como JSON u otros formatos que Logstash pueda analizar fácilmente. Para gestionarlo, puedes usar el filtro Ruby para analizar la salida y extraer la información de ella.</p><p>Aquí tienes un ejemplo de cómo se emplea el plugin de entrada <code>exec</code> para ejecutar el comando <code>ps -ef</code> , que lista todos los procesos en ejecución en un sistema tipo Unix. La salida será analizada por el filtro Ruby para extraer información relevante sobre cada proceso.</p>input {
  exec {
    command =&gt; "ps -ef"
    interval =&gt; 60
  }
}

filter {
  ruby {
    code =&gt; '
      processes = []
      lines = event.get("message").split("\n")  
      lines.each_with_index do |line, index|
        # Skip header line and empty lines
        next if index == 0 || line.strip.empty?
        entry = nil
        
        # Use regex to match the ps -ef output format more flexibly
        # This pattern accounts for variable spacing and different time formats
        if line =~ /^\s*(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+([\d:]+\.?\d*)\s+(.+)$/
          uid, pid, ppid, c, stime, tty, time, cmd = $1, $2, $3, $4, $5, $6, $7, $8
          
          entry = {
            "UID" =&gt; uid,
            "PID" =&gt; pid,
            "PPID" =&gt; ppid,
            "C" =&gt; c,
            "STIME" =&gt; stime,
            "TTY" =&gt; tty,
            "TIME" =&gt; time,
            "CMD" =&gt; cmd.strip
          }
        elsif line =~ /^\s*(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+)$/
          # Fallback pattern for lines that might not match the exact format
          # Split the remaining part more carefully
          uid, pid, ppid, c, remainder = $1, $2, $3, $4, $5
          
          # Split remainder into STIME, TTY, TIME, CMD
          parts = remainder.strip.split(/\s+/, 4)
          if parts.length &gt;= 4
            stime, tty, time, cmd = parts[0], parts[1], parts[2], parts[3]
            
            entry = {
              "UID" =&gt; uid,
              "PID" =&gt; pid,
              "PPID" =&gt; ppid,
              "C" =&gt; c,
              "STIME" =&gt; stime,
              "TTY" =&gt; tty,
              "TIME" =&gt; time,
              "CMD" =&gt; cmd
            }
          end
        end
        if entry &amp;&amp; entry["UID"] == "0"
          original_line = line.strip
          entry["original_line"] = original_line if original_line.length &gt; 0
          processes.push(entry)
        end
      end
      event.set("processes", processes)
      event.remove("message")
      event.remove("event")
    '
  }
}

output {
  stdout { codec =&gt; rubydebug }
}<p>Este ejemplo emplea el plugin de entrada <code>exec</code> para ejecutar el comando <code>ps -ef</code> cada 60 segundos. El filtro Ruby procesa la salida, extrayendo campos relevantes como UID, PID, PPID, uso de la CPU (C), hora de inicio (STIME), TTY, tiempo total de CPU (TIME) y el comando (CMD) ejecutado. Funciona bien en mi entorno macOS, pero puede que tengas que ajustar los patrones regex para que coincidan con el formato de salida del comando <code>ps -ef</code> en tu sistema.</p><h3>Emplea librerías integradas</h3><p>El plugin de filtro Ruby permite usar librerías Ruby integradas, que pueden ser muy útiles para diversas tareas. Por ejemplo, puedes usar la librería <code>json</code> para analizar cadenas JSON o la librería <code>date</code> para manipular fechas.</p><p>Aquí tienes un ejemplo de cómo usar la librería <code>json</code> para analizar una cadena JSON almacenada en un campo:</p>require 'json'

def filter(event)
  json_string = event.get('message')
  parsed_json = JSON.parse(json_string)
  event.set('parsed_json', parsed_json)
  return [event]
end<p>Para evitar tener que usar la librería cada vez, deberías externalizar tu código Ruby para poder usar la sentencia <code>require</code> al principio de tu script de filtro Ruby. Esto cargará la biblioteca una vez y la pondrá disponible para tu script.</p><p>Para comprobar qué librerías están disponibles en tu entorno, puedes listar las bibliotecas integradas ejecutando el siguiente código en el filtro Ruby:</p>Gem.loaded_specs.sort_by { |name, _| name }.each do |name, spec|
  puts "#{name}: #{spec.version}"
end<p><strong>Nota: </strong>Las bibliotecas integradas no son soportadas oficialmente por Logstash, y su comportamiento puede cambiar o puede que no estén disponibles en versiones futuras. Úsalos bajo tu propia responsabilidad.</p><h2>Conclusión</h2><p>El filtro Ruby de Logstash te permite personalizar y ampliar las capacidades de tus pipelines de Logstash. En esta publicación, cubrimos lo básico del uso del filtro Ruby y proporcionado ejemplos avanzados de uso.</p><p>Aprovechando el filtro Ruby, puedes manejar tareas complejas de procesamiento de datos que requieren lógica personalizada o manipulación avanzada. Ya sea que trabajes con estructuras de datos anidadas, divisiones de eventos o analizes y conviertas textos complejos/no estructurados en JSON estructurado, el filtro Ruby ofrece flexibilidad para satisfacer tus necesidades específicas.</p><p>Esperamos que esta guía te proporcionó el conocimiento e inspiración para explorar todo el potencial del filtro Ruby de Logstash. ¡Feliz guion!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ruby-scripting-logstash</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ruby-scripting-logstash</guid>
    <category><![CDATA[Datos de índice]]></category>
    <category><![CDATA[Ruby]]></category>
    <dc:creator><![CDATA[Dai Sugimori]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt503d18396642e73e/6a17f62aaf47b68527cde121/b1bcd63c033ccbde102c20ba3085f165f9289a71-1600x1000.png" length="0" type="image/png"/>
    <pubDate>Tue, 24 Jun 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Visualización de campos en un índice de Elasticsearch]]></title>
    <description><![CDATA[Explorando técnicas para mostrar campos en un índice de Elasticsearch.
]]></description>
    <content:encoded><![CDATA[<p>En este artículo, hablaremos de cómo mostrar campos en un índice de Elasticsearch. Esto puede ser útil para entender la estructura de tus datos, identificar campos específicos y solucionar problemas. Vamos a tratar los siguientes temas:</p><ol><li><p><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#1.-using-the--mapping-api-to-retrieve-field-information">Uso de la </a>API<a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#1.-using-the--mapping-api-to-retrieve-field-information"><code>_mapping</code></a><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#1.-using-the--mapping-api-to-retrieve-field-information"> para recuperar información de campo</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#2.-using-the--search-api-to-display-field-values">Uso de la </a>API<a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#2.-using-the--search-api-to-display-field-values"><code>_search</code></a><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#2.-using-the--search-api-to-display-field-values"> para mostrar los valores de los campos</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#3.-filtering-fields-using-the-fields-parameter">Filtrado de campos usando el </a><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#3.-filtering-fields-using-the-fields-parameter"> parámetrofields</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#4.-displaying-nested-fields">Visualización de campos anidados</a></p></li></ol><h2>1. Uso de la API _mapping para recuperar información de campo</h2><p>La API <code>_mapping</code> permite recuperar la definición de mapeo para un índice o varios <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-index/">índices</a>. Esto incluye información sobre los campos, sus tipos de datos y otras propiedades. Para recuperar el mapeo de un índice específico, emplee la siguiente petición:</p>GET /&lt;index_name&gt;/_mapping<p>Por ejemplo, si tienes un índice llamado <code>my_index</code>, puedes recuperar su mapeo con la siguiente petición:</p>GET /my_index/_mapping<p>La respuesta incluirá la definición de mapeo para el índice, que contiene información sobre los campos y sus propiedades.</p><p>También es posible recuperar el mapeo de un campo específico. Esto puede ser útil si tu mapeo es bastante grande y solo quieres centrarte en un campo específico. Para recuperar el mapeo de un campo específico, emplee la siguiente petición:</p>GET /my_index/_mapping/field/my_field<p>También puedes recuperar los mapeos de varios campos separando sus nombres con comas, como en la siguiente petición:</p>GET /my_index/_mapping/field/my_field_1,my_field_2,my_field_3<h2>2. Uso de la API _search para mostrar los valores de los campos</h2><p>Para mostrar los valores de los campos en un índice de Elasticsearch, puedes usar la API <code>_search</code> . Por defecto, la API <code>_search</code> devuelve el campo <code>_source</code> , que contiene el documento JSON original que se indexó. Para mostrar solo campos específicos, puedes usar el parámetro <code>_source</code> en la solicitud de búsqueda.</p><p>Aquí tienes un ejemplo de una solicitud de búsqueda que devuelve los valores de los campos <code>title</code> y <code>author</code> para documentos en el índice <code>my_index</code> :</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["title", "author"]
}<p>En este ejemplo, el parámetro <code>_source</code> especifica los campos que se deben devolver.</p><h2>3. Filtrado de campos usando el parámetro de campos</h2><p>También puedes usar el parámetro <code>fields</code> para filtrar los campos que aparecen en la respuesta de búsqueda. Esto puede ser útil si solo necesitas campos específicos y quieres reducir el tamaño de la respuesta. El parámetro <code>fields</code> acepta una matriz de nombres de campos o patrones comodines.</p><p>Por ejemplo, para devolver solo los campos <code>title</code> y <code>author</code> de los documentos en el índice de <code>my_index</code> , puedes usar la siguiente solicitud de búsqueda:</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author"],
  "_source": false
}<p>Ten en cuenta que el parámetro <code>_source</code> está configurado como falso para no devolver el documento fuente.</p><p>Para devolver todos los campos con un <code>text</code> tipo de dato, puedes usar un patrón comodín como este:</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["*.text"],
  "_source": false
}<h2>4. Visualización de campos anidados</h2><p>Si tu índice contiene campos anidados, puedes usar la notación de puntos para especificar el camino de campo anidado en el parámetro <code>fields</code> . Por ejemplo, si tienes un campo anidado llamado <code>address.city</code>, puedes incluirlo en la respuesta de búsqueda así:</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author", "address.city"],
  "_source": false
}<p>En este ejemplo, la respuesta de búsqueda incluirá los valores de los campos <code>title</code>, <code>author</code> <code>address.city</code> .</p><h2>Conclusión</h2><p>En conclusión, se puede lograr mostrar campos en un índice de Elasticsearch empleando la API <code>_mapping</code> para recuperar información de campos y la API <code>_search</code> para mostrar los valores de campo. Puedes filtrar los campos que aparecen en la respuesta de búsqueda usando los parámetros de <code>_source</code> o <code>fields</code> y mostrar los campos anidados usando la notación de puntos. Estas técnicas pueden ayudarte a entender la estructura de tus datos, identificar campos específicos y solucionar problemas.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index</guid>
    <category><![CDATA[Datos de índice]]></category>
    <dc:creator><![CDATA[Kofi Bartlett]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3a1fcc771a2504e5/6a17f817abe0f23038dfebca/fa386d7bbaeab6855e62897ace8d7dca91a060b4-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 26 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Excluyendo campos de Elasticsearch de la indexación]]></title>
    <description><![CDATA[Aprende a configurar Elasticsearch para excluir campos, las principales razones para excluir campos de indexación y las mejores prácticas a seguir.]]></description>
    <content:encoded><![CDATA[<p>En Elasticsearch, la indexación se refiere al proceso de almacenar y organizar los datos de una manera que los hace fácilmente consultables. Aunque indexar todos los campos de un documento puede ser útil en algunos casos, hay situaciones en las que podrías querer excluir ciertos campos de la indexación. Esto puede ayudar a mejorar el rendimiento, reducir los costos de almacenamiento y minimizar el tamaño total de tu índice de Elasticsearch.</p><p>En este artículo, hablaremos de las razones por las que excluye campos de la indexación, cómo configurar Elasticsearch para excluir campos específicos y algunas buenas prácticas a seguir al hacerlo.</p><h2>Razones para excluir campos de la indexación</h2><ol><li><p><strong>Rendimiento: </strong>Indexar todos los campos de un documento puede aumentar el tiempo de indexación y ralentizar el rendimiento de búsqueda. Excluyendo campos que no son necesarios para búsqueda o agregación, puedes mejorar el rendimiento general de tu clúster de Elasticsearch.</p></li><li><p><strong>Almacenamiento: </strong>Los campos de indexación consumen espacio de almacenamiento. Excluir campos que no son necesarios para búsqueda o agregación puede ayudar a reducir los requisitos de almacenamiento de tu clúster de Elasticsearch.</p></li><li><p><strong>Tamaño del índice: </strong>El tamaño de un índice de Elasticsearch está directamente relacionado con el número de campos indexados. Al excluir campos innecesarios, puedes minimizar el tamaño de tu índice, lo que puede llevar a un rendimiento de búsqueda e indexación más rápido.</p></li></ol><h2>Configuración de Elasticsearch para excluir campos</h2><p>Para excluir un campo de la indexación en Elasticsearch, puedes usar la propiedad "index" en el mapeo del campo. Al poner la propiedad "index" en "false", Elasticsearch no indexará el campo, y no será buscable ni estará disponible para agregaciones.</p><p>Aquí tienes un ejemplo de cómo excluir un campo de la indexación usando el mapeo Elasticsearch:</p>PUT /my_index
{
  "mappings": {
    "properties": {
      "field_to_exclude": {
        "type": "text",
        "index": false
      }
    }
  }
}<p>En este ejemplo, estamos creando un nuevo índice llamado "my_index" con un solo campo llamado "field_to_exclude". Al poner la propiedad "index" en "false", le decimos a Elasticsearch que no indexe este campo. Sin embargo, el campo seguirá estando disponible en el documento fuente.</p><h2>Mejores prácticas para excluir campos de la indexación</h2><ol><li><p><strong>Analiza tus datos: </strong>Antes de excluir campos de la indexación, es esencial analizar tus datos y entender qué campos son necesarios para la búsqueda y agregación. Esto te ayudará a tomar decisiones informadas sobre qué campos excluir.</p></li><li><p><strong>Prueba tus cambios: </strong>Al excluir campos de la indexación, es fundamental probar tus cambios para cerciorarte de que la funcionalidad de búsqueda y agregación sigue funcionando como se espera. Esto puede ayudarte a evitar problemas inesperados o de rendimiento.</p></li><li><p><strong>Rendimiento del monitor:</strong> Luego de excluir campos de la indexación, monitoriza el rendimiento de tu clúster de Elasticsearch para cerciorarte de que los cambios tuvieron el efecto deseado. Esto puede ayudarte a identificar posibles optimizaciones adicionales que puedan ser necesarias.</p></li><li><p><strong>Emplea filtrado de fuente:</strong> Si necesitas almacenar un campo en Elasticsearch pero no quieres que sea buscable ni disponible para agregaciones, considera usar filtrado de fuente. Esto te permite almacenar el campo en el campo _source pero excluirlo del índice.</p></li></ol><h2>Conclusión</h2><p>Excluir campos de la indexación en Elasticsearch puede ayudar a mejorar el rendimiento, reducir los costos de almacenamiento y minimizar el tamaño total de tu índice. Analizando cuidadosamente tus datos y entendiendo qué campos son necesarios para la búsqueda y agregación, puedes tomar decisiones informadas sobre cuáles excluir. Prueba siempre tus cambios y monitoriza el rendimiento de tu clúster de Elasticsearch para cerciorarte de que tus optimizaciones tienen el efecto deseado.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/excluding-elasticsearch-fields-from-indexing</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/excluding-elasticsearch-fields-from-indexing</guid>
    <category><![CDATA[Datos de índice]]></category>
    <dc:creator><![CDATA[Kofi Bartlett]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt399bcc5a2e55bfe0/6a1708b45091684557e1ba3c/3aa0b481994d2445ba979d3c79fff64c5ee6676a-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 12 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Plantillas de índice en Elasticsearch: Cómo usar plantillas componibles]]></title>
    <description><![CDATA[Explora cómo crear plantillas de índice componibles y de componentes en Elasticsearch para garantizar mappings consistentes y automatizar la configuración de la indexación.]]></description>
    <content:encoded><![CDATA[<p>Un índice de Elasticsearch puede configurar mediante mapeo, ajustes y alias: </p><ul><li><p>Las definiciones de mapeo especifican el esquema de datos.</p></li><li><p>Los ajustes ajustan el tamaño del fragmento y las frecuencias de refresco. </p></li><li><p>Se emplean alias para dar nombres alternativos al índice.</p></li></ul><p>Cuando indexamos un documento por primera vez o creamos un índice vacío usando la API Create Index, el índice se creará con la configuración predeterminada, sin esquema de datos y sin alias. Estos valores por defecto funcionan bastante bien en entornos de desarrollo y pruebas, pero puede que necesitemos personalizar nuestros índices para entornos de producción.</p><p>Trabajar con los mapeos y ajustes predeterminados en producción puede resultar en un índice y un rendimiento de búsqueda deficientes. Instanciar índices manualmente es un proceso tedioso y que consume mucho tiempo. Recrear tales índices en cualquier entorno es especialmente poco práctico si disponemos de un esquema de mapeo elaborado, así como de configuraciones y alias personalizados.</p><p>Por suerte, Elasticsearch nos proporciona una herramienta para aplicar automáticamente una configuración predefinida al crear índices en forma de plantillas <em>de índices</em> <em>.</em></p><h2>Plantillas de índice</h2><p>Las plantillas de índice nos permiten crear índices con una configuración definida por el usuario. Un índice puede extraer la configuración de estas plantillas, por ejemplo un número determinado de fragmentos y réplicas o mapeos de campos, durante su instanciación. Se definirá una plantilla con un patrón de nombre y alguna configuración en él. Si el nombre del índice coincide con el patrón de nombres de la plantilla, el nuevo índice se creará con la configuración definida en la plantilla.</p><p>Elasticsearch mejoró su funcionalidad de plantillas en la versión 7.8 con plantillas componibles. Esta versión más reciente ofrece plantillas de índice mucho más reutilizables, como se demuestra en este artículo.</p><h3>Tipos de plantillas de indexación</h3><p>Las plantillas de índice pueden clasificar en dos categorías:</p><ul><li><p><strong>Plantillas de índice (o plantillas de índice composable):</strong> Las plantillas de índice componibles pueden existir por sí solas o estar compuestas por no tener o más plantillas componentes (ver la segunda categoría).</p></li><li><p><strong>Plantillas de componentes:</strong> La plantilla de componentes es una plantilla <em>reutilizable</em> por sí sola que define la configuración requerida. Normalmente se espera que la plantilla de componentes esté asociada a una plantilla de índice. Cada una de las plantillas de componentes puede anexar con una o varias plantillas de índice. </p></li></ul><p>Como puedes ver en la imagen de abajo, las plantillas índice A y B comparten entre sí las plantillas de componentes (en este caso solo una, la Plantilla 3). Una plantilla de índice puede consistir en ninguna o muchas plantillas de componentes y cada una de las plantillas de componentes puede asociar a ninguna o a muchas plantillas de índice. Ambos tipos de plantillas pueden existir por separado, sin embargo, las plantillas de componentes no sirven a menos que estén adjuntas a una plantilla de índice.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7fca132e0eb86c50/6a17f5b7ec0f8982a65a678c/96c0aac29d3992e54a79be34e14cf909e0ca2ea9-1202x556.png" alt="Plantillas de índice en Elasticsearch y sus componentes." /><p>La idea general es desarrollar un catálogo de plantillas de componentes para que una organización las emplee para diversas necesidades (por ejemplo, especificar las distintas plantillas de componentes para entornos individuales) y anexarlas a varios índices mediante las plantillas de índices componibles.</p><h2>Cómo crear plantillas componibles (indexadas)</h2><p>Elasticsearch proporciona un punto final _index_template para gestionar plantillas de índice. El usuario proporciona todos los mapeos, ajustes y alias necesarios junto con un patrón de nombres de índice en esta plantilla. Vamos a repasar un ejemplo de cómo crear una plantilla para una aplicación de microservicios <em>client-order-service</em> que es responsable de la lógica de generación de pedidos. </p><p>Supongamos que nuestro requisito es crear una plantilla para pedidos de clientes, representada con un patrón que incluye comodines: *pedidos. Se espera que esta plantilla tenga ciertos mapeos y configuraciones, como el campo order_date, así como fragmentos y números de réplica.</p><p>Cualquier índice que se empareje con esta plantilla durante su creación hereda las configuraciones definidas en esta plantilla. Por ejemplo, un índice de black_friday_orders tendrá el campo order_date, los fragmentos se pondrán en 5 y las réplicas en 2. Además, <em>todos</em> los índices creados a partir de esta plantilla heredan también un único <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-alias/">nombre de alias</a> . Creemos este orders_template con un patrón de índice definido como *orders y con un esquema de mapeo que consiste en un solo campo de oder_date con un formato de fecha predefinido dd-MM-yyyy. El código que aparece a continuación muestra cómo crear esta plantilla de índice.</p>PUT _index_template/orders_template
{
  "index_patterns": ["*orders"],
  "priority": 300,
  "template": {
    "mappings": {
      "properties": {
        "order_date": {
          "type": "date",
          "format":"dd-MM-yyyy"
        }
      }
    },
    "settings":{
      "number_of_shards":5,
      "number_of_replicas":2
    },
    "aliases":{
      "all_orders":{}
    }
  }
}<p>Cuando ejecutas esta consulta en DevTools de Kibana, la plantilla se crea con el patrón índice *orders junto con el mapeo predefinido, los ajustes y un alias. El index_patterns es una variedad de patrones de coincidencia; cualquier índice que coincida con este patrón derivará la configuración de la plantilla. Puedes ejecutar lo siguiente para recuperar la plantilla persistente que debería reiterar lo que hicimos:</p>GET _index_template/orders_template <p>También hay una prioridad, un número positivo, definido al crear el atributo de plantilla definido en la plantilla: cada plantilla se define con una prioridad para que cualquier cambio conflictivo de diferentes plantillas se resuelva usando este valor con precedencia dada al valor de mayor prioridad. A continuación profundizaremos en la prioridad de las plantillas.</p><h2>Crear un índice con la plantilla</h2><p>Ahora que tenemos una plantilla – un plano para crear índices – el siguiente paso es crear un índice. Cuando el nombre del índice coincide con el patrón dado, las configuraciones con plantilla se aplican automáticamente. Para demostrar el punto, como muestra el código de abajo, creemos un índice completamente nuevo llamado: blackfriday_orders:</p>PUT blackfriday_orders<p>Como el nombre del índice (blackfriday_orders) coincide con el patrón de nombres definido en la plantilla (es decir, *órdenes), el índice debería obtener todas las configuraciones derivadas de la plantilla. Recuperemos este índice recién creado y compruebemos si esto es realmente cierto ejecutando el siguiente código:</p>GET blackfriday_orders<p>Esto debería volver:</p>{
  "blackfriday_orders" : {
    "aliases" : {
      "all_orders" : { }
    },
    "mappings" : {
      "properties" : {
        "order_date" : {
          "type" : "date",
          "format" : "dd-MM-yyyy"
        }
      }
    },
    "settings" : {
      "index" : {
         ...
        "number_of_shards" : "5",
        "number_of_replicas" : "2"
      }
    }
  }
}<p>Como indica la respuesta, la configuración del blackfriday_orders fue heredada de la plantilla. Podemos probar con varias combinaciones de los índices que hereden con éxito la configuración plantillada:</p>PUT blackfriday_orders
PUT americaorders
PUT cancelled--orders
PUT undefined101orders<p>Sin embargo, los siguientes índices no heredarán la configuración ya que el nombre no coincidirá con el patrón:</p>PUT blackfriday_orders2
PUT open_orders_
PUT allorders_total<p>Algo importante a recordar es que todos los índices derivados de una plantilla tienen el mismo alias – all_orders – en este caso. Existe un beneficio en tener este alias: podemos consultar simplemente con este único alias en lugar de en múltiples índices.</p>GET blackfriday_orders,americaorders,undefined101orders/_search
GET all_orders/_search 
{
  "query": {
    "range": {
      "order_date": {
        "gte": "01-12-2021",
        "lte": "31-12-2021"
      }
    }
  }
}<p>Aunque creamos una plantilla para *pedidos, se espera que cualquier índice coincidente adopte la configuración de la plantilla. Normalmente, consciente o inconscientemente, los equipos pueden crear algunas plantillas más por diversas razones. Esto significa que a veces el nombre del índice puede coincidir con dos patrones de plantilla diferentes. Elasticsearch tiene que decidir cuál de las configuraciones de esas plantillas debe aplicar. Afortunadamente, este dilema puede resolver usando la prioridad de la plantilla.</p><h2>Cómo crear plantillas de componentes</h2><p>Aprendimos sobre las plantillas de índice en la parte anterior de este artículo. Hay un par de desventajas al crear las plantillas con la configuración incorporada; una de ellas es que la configuración no es exportable para otras plantillas. Si queremos tener una configuración similar, por ejemplo para plantillas relacionadas con clientes (*clientes), puede que tengamos que recrear toda la plantilla. Eso significa que podemos estar creando docenas de ellos en una organización típica (además puede que tengas algunos más según los entornos).</p><p>Como siempre esperamos la reutilización, Elasticsearch rediseñó las plantillas teniendo en cuenta la reutilización. Las plantillas de componentes cumplen con ese requisito. Si vienes de un entorno DevOps, lo más probable es que tengas que crear índices con una configuración preestablecida para cada uno de los entornos. En lugar de aplicar manualmente cada una de estas configuraciones, puedes crear una plantilla de componentes para cada uno de los entornos.</p><p>Una plantilla de componentes no es más que un bloque reutilizable de configuraciones que podemos usar para crear más plantillas de índice. Ten en cuenta que las plantillas de componentes no tienen valor a menos que estén agrupadas con plantillas de índice. Se exponen a través de un punto final _component_template. Veamos cómo encaja todo esto.</p><h3>Configuraciones en una plantilla de índice</h3><p>Vamos a extraer los ajustes que definimos en nuestra plantilla de índice antes y crear una plantilla de componente a partir de ella. Se espera que el settings_component_template tenga cinco fragmentos principales con dos réplicas por fragmento principal. El primer paso, como muestra la lista de código a continuación, es declarar y ejecutar una plantilla de componente con esta configuración.</p>PUT _component_template/settings_component_template
{
  "template":{
    "settings":{
      "number_of_shards":5,
      "number_of_replicas":2
    }
  }
}<p>Como muestra el código anterior, usamos el punto final _component_template para crear una plantilla de componentes. El cuerpo de la solicitud contiene la información de la plantilla en un objeto plantilla. El settings_component_template ya está disponible para su uso en otras partes de las plantillas del índice. Una diferencia notable es que esta plantilla no define ningún patrón de índice; Simplemente es un bloque de código que configura algunas propiedades para nosotros.</p><h3>Plantilla de mapeo</h3><p>De la misma manera, creemos otra plantilla. Esta vez, extraigamos el esquema de mapeo que definimos antes en las plantillas de índice independientes. El código siguiente muestra el guion:</p>PUT _component_template/mappings_component_template
{
  "template": {
    "mappings": {
      "properties": {
        "order_date": {
          "type": "date",
          "format":"dd-MM-yyyy"
        }
      }
    }
  }
}<h3>Plantilla de alias</h3><p>Siguiendo el mismo flujo, también podemos tener una plantilla de componentes con los alias – dos alias (all_orders y sales_orders):</p>PUT _component_template/aliases_component_template
{
  "template": {
    "aliases": {
      "all_orders": {},
      "sales_orders":{}
    }
  }
}<h3>Plantilla de índice componible</h3><p>Ahora que tenemos estas tres plantillas de componentes, el siguiente paso es ponerlas en práctica. Podemos hacerlo dejando que una plantilla de índice para, por ejemplo, christmas_orders, la emplee:</p>PUT _index_template/composed_orders_template
{
  "index_patterns": [
    "*orders"
  ],
  "priority": 500,
  "composed_of": [
    "settings_component_template",
    "mappings_component_template",
    "aliases_component_template"
  ]
}<p>La etiqueta composed_of es una colección de todas las plantillas de componentes que conforman esta plantilla. En este caso, elegimos las plantillas de componentes de configuración, mapeo y alias. También estamos subiendo la prioridad, así que esta plantilla supera a cualquier otra. Una vez que la plantilla está lista, cualquier índice que coincida con el patrón *orders heredará la configuración de estas tres plantillas componentes.</p><p>Dicho esto, si deseamos crear una nueva plantilla, por ejemplo clientes, con solo una de las plantillas existentes (settings_component_template) y una nueva (aliases_component_template – ver más abajo), podemos hacerlo con:</p>PUT _component_template/aliases_component_template2
{
  "template": {
    "aliases": {
      "all_customers": {}
    }
  }
}<p>La plantilla del índice es la siguiente:</p>PUT _index_template/composed_customers_template
{
  "index_patterns": [
    "*customers*"
  ],
  "priority": 200,
  "composed_of": [
    "settings_component_template",
    "aliases_component_template2"
  ]
}<p>¿Viste que el settings_component_template se ha (re)empleado en dos plantillas diferentes? Ese es el poder de las plantillas de componentes.</p><h2>Prioridad de la plantilla de indexación</h2><p>Existe la posibilidad de que los desarrolladores creen múltiples plantillas de índice sin mirar el stock existente. Es importante establecer una prioridad en cada una de estas plantillas para que se emplee la de mayor prioridad. Por ejemplo, el my_orders_template_1 anula la my_orders_template_2 en el siguiente fragmento de código:</p>PUT _index_template/my_orders_template_1
{
  "index_patterns": ["*orders"],
  "priority": 1000,
  "template": { ... }
}
PUT _index_template/my_orders_template2
{
  "index_patterns": ["*orders"],
  "priority": 300,
  "template": { ... }
}<p>Cuando tienes varias plantillas que coinciden con los índices que se están creando, Elasticsearch aplica todas las configuraciones de todas las plantillas coincidentes pero anula cualquier cosa que tenga mayor prioridad.</p><h2>Precedencia de plantillas</h2><p>Por último, puede que te preguntes por la precedencia de las plantillas: ¿la configuración definida en la plantilla de componentes anula la que aparece en la plantilla principal del índice? ¿O al revés? Bueno, hay algunas reglas:</p><ul><li><p>Un índice creado con configuraciones tiene prioridad explícita sobre todo; esto significa que si creas un índice con configuración explícita, no esperes que las plantillas las sobreescriban.</p></li><li><p>Las plantillas heredadas (plantillas creadas antes de la versión 7.8) tienen una prioridad inferior a las plantillas componibles.</p></li></ul><h2>Resumen</h2><ul><li><p>Un índice contiene mapeos, configuraciones y alias: los mapeos definen el esquema de campos, los ajustes establecen los parámetros del índice como el número de fragmentos y réplicas, y los alias dan nombres alternativos al índice.</p></li><li><p>Las plantillas nos permiten crear índices con configuraciones predefinidas. Nombrar un índice con un nombre que coincida con el patrón de índice definido en una plantilla específica configurará automáticamente ese índice según la plantilla.</p></li><li><p>Elasticsearch introdujo plantillas de índice componibles en la versión 7.8. Las plantillas de índice componibles permiten modularidad y versionado de las plantillas.</p></li><li><p>Las plantillas componibles consisten en no tener o más plantillas de componentes.</p></li><li><p>Una plantilla de índice también puede tener su propia configuración definida.</p></li><li><p>Una plantilla de componente es una plantilla reutilizable con configuración predefinida, igual que una plantilla de índice componible.</p></li><li><p>Sin embargo, se espera que las plantillas de componentes formen parte de una plantilla de índice; No sirven de nada si no están "compuestas" en una plantilla de índice.</p></li><li><p>Las plantillas de componentes no tienen un patrón de índice definido, lo que es otra razón por la que se "espera" que formen parte de una plantilla de índice.</p></li><li><p>Cada una de las plantillas tiene una prioridad: un número positivo. Cuanto mayor sea el número, mayor es la precedencia para que se aplique esa plantilla.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/index-composable-templates</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/index-composable-templates</guid>
    <category><![CDATA[Datos de índice]]></category>
    <dc:creator><![CDATA[Kofi Bartlett]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt98737a72caa74fe4/6a17f5b84b055d278d43236a/510750708df50bf79463586a1bbf35bf94acfa30-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cómo ingerir datos a través de Airbyte en Elasticsearch]]></title>
    <description><![CDATA[Usar Airbyte para ingir datos en Elasticsearch. Cubriremos los requisitos previos, la configuración de Airbyte y la integración paso a paso.]]></description>
    <content:encoded><![CDATA[<p>Airbyte es una herramienta de integración de datos que permite mover información de diversas fuentes a diferentes destinos de forma automatizada y escalable. Te permite extraer datos de APIs, bases de datos y otros sistemas y cargarlos en plataformas como Elasticsearch, que ofrece búsqueda avanzada y análisis eficiente.</p><p>En este artículo, explicaremos cómo configurar Airbyte para que insienta datos en Elasticsearch, cubriendo conceptos clave, requisitos previos y la integración paso a paso.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99eabff95c587c00/6a17e300e8fbce2d303a18a9/ea7af907dfd4c0b7e8673164467ee236623282d2-1360x802.png" alt="Configura Airbyte para que ingira datos en Elasticsearch" /><h2>Conceptos fundamentales de Airbyte</h2><p>Airbyte tiene varios conceptos esenciales para su uso. A continuación, destacamos los principales:</p><ul><li><p>Fuentes: Define el origen de los datos que se extraerá.</p></li><li><p>Destinos: Define dónde se enviarán y almacenarán los datos.</p></li><li><p>Conexiones: Configura la relación entre la fuente y el destino, incluyendo la frecuencia de sincronización.</p></li></ul><h2>Integración de Airbyte con Elasticsearch</h2><p>En esta demostración, realizaremos una integración en la que los datos almacenados en un cubo S3 serán migrados a un índice Elasticsearch. Mostraremos cómo configurar el código fuente (S3) y el destino (Elasticsearch) en Airbyte.</p><h3>Prerrequisitos</h3><p>Para seguir a esta demostración, deben cumplir los siguientes requisitos:</p><ol><li><p>Crea un cubo en AWS, donde se almacenarán los archivos JSON que contienen los datos.</p></li><li><p><a href="https://docs.airbyte.com/using-airbyte/getting-started/oss-quickstart">Instala Airbyte localmente</a> usando Docker.</p></li><li><p>Crea un clúster de Elasticsearch en Elastic Cloud para almacenar los datos ingeridos.</p></li></ol><p>A continuación, detallaremos cada uno de estos pasos.</p><h4>Instalación de Airbyte</h4><p>Airbyte puede ejecutar localmente usando Docker o en la nube, donde existen costos asociados al uso. Para esta demostración, usaremos la versión local con Docker.</p><p>La instalación puede tardar unos minutos. Tras seguir las instrucciones de instalación, Airbyte estará disponible en: http://localhost:8000.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f9149be887b5500/6a17e302abe0f2b1c6dfe956/66147b5121413ad9baecb10c6886288e917f7c09-1600x1102.png" alt="Instalación de Airbyte" /><p></p><p>Luego de iniciar sesión, podemos empezar a configurar la integración.</p><h4>Creación del cubo</h4><p>En este paso, necesitarás una cuenta de AWS para crear un bucket S3. Además, es esencial establecer las licencias correctas creando una política y un usuario IAM para permitir el acceso al cubo.</p><p>En el bucket, subiremos archivos JSON que contengan diferentes registros de log, que luego serán migrados a Elasticsearch. Los registros de archivos contienen este contenido:</p>{
   "timestamp": "2025-02-15T14:00:12Z",
   "level": "INFO",
   "service": "data_pipeline",
   "message": "Pipeline execution started",
   "details": {
       "pipeline_id": "abc123",
       "source": "MySQL",
       "destination": "Elasticsearch"
   }
}<p>A continuación están los archivos cargados en el cubo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbf769c5e9bdb5dfe/6a17e3043e9e45a360ba13f7/f3e7f5889002e3a804121a880d97f1d93044e2f7-1600x680.png" alt="Archivos cargados en el cubo en Airbyte" /><h4>Configuración de la nube elástica</h4><p>Para facilitar la demostración, usaremos Elastic Cloud. Si aún no tienes cuenta, puedes crear una cuenta de prueba gratis aquí: <a href="https://cloud.elastic.co/registration">Elastic Cloud Registration</a>.</p><p>Luego de configurar el despliegue en Elastic Cloud, necesitarás obtener:</p><ul><li><p>La URL del servidor Elasticsearch.</p></li><li><p>Un usuario para acceder a Elasticsearch.</p></li></ul><p>Para obtener la URL, ve a Despliegues &gt; Mi despliegue, en la aplicación, busca Elasticsearch y haz clic en 'Copiar el punto final'.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltddfaaf863168e2bb/6a17e305faa913e29793c7e4/2e38c0bfb2cea83d9ef90dba0e673559fe358199-1368x1056.png" alt="Configuración de la nube elástica" /><p>Para crear el usuario, sigue los pasos a continuación:</p><ol><li><p>Accede a Kibana &gt; Stack Management &gt; usuarios.</p></li><li><p>Crea un nuevo usuario con el rol de superusuario.</p></li><li><p>Diligencia el espacio para crear al usuario.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca15b0d3084f4c67/6a17e3073e9e4507e2ba13fb/54d098805087a2d60772475cbb31784083a38c25-1600x1026.png" alt="Creación de usuarios en Elastic Cloud" /><p>Ahora que lo tenemos todo listo, podemos empezar a configurar los conectores en Airbyte.</p><h3>Configuración del conector fuente</h3><p>En este paso, crearemos el conector fuente para S3. Para ello, accederemos a la interfaz de Airbyte y seleccionaremos la opción Fuente en el menú. Luego, buscaremos el conector S3. A continuación, detallamos los pasos necesarios para configurar el conector:</p><ol><li><p>Accede a Airbyte y ve al menú de Fuentes.</p></li><li><p>Busca y selecciona el conector S3.</p></li><li><p>Configura los siguientes parámetros:</p><ol><li><p>Nombre de la fuente: Define un nombre para la fuente de datos.</p></li><li><p>Método de entrega: Seleccionar Réplica de Registros (recomendado para datos estructurados).</p></li><li><p>Formato de datos: Elige formato JSON.</p></li><li><p>Nombre del flujo: Define el nombre del índice en Elasticsearch.</p></li><li><p>Nombre del cubo: Introduce el nombre del cubo en AWS.</p></li><li><p>Clave de acceso de AWS y clave secreta de AWS: Introduce las credenciales de acceso.</p></li></ol></li></ol><p>Haz clic en Configurar código fuente y espera la validación.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdca1fb8d8f5d034f/6a17e30963baff75bc741bb2/f83566ab5ebad07ad9fd61dc20353ca5a95c8c92-1600x1099.png" alt="Esperar la validación para la ingestión de datos de Airbyte y Elasticsearch" /><h3>Conector de destino de configuración</h3><p>En este paso, configuraremos el conector de destino, que será Elasticsearch. Para ello, accederemos al menú y seleccionaremos la opción Destino. Luego, buscaremos Elasticsearch y haremos clic en el resultado que devolvemos. Ahora, procederemos con la configuración de esta conexión:</p><ol><li><p>Accede a Airbyte y ve al menú de Destinos.</p></li><li><p>Busca y selecciona el conector Elasticsearch.</p></li><li><p>Configura los siguientes parámetros:</p><ol><li><p>Método de autenticación: Elige nombre de usuario/contraseña.</p></li><li><p>Nombre de usuario y contraseña: Emplea las credenciales creadas en Kibana.</p></li><li><p>Endpoint del servidor: Pega la URL copiada de Elastic Cloud.</p></li></ol></li></ol><p>Haz clic en <strong>Configurar destino</strong> y espera la validación.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72056179d048e027/6a17e30b033c8d5d9d6bb115/f9246b54cc77e0589c0bf658ffc274fd28f766d5-1600x941.png" alt="Crear un destino en Elastic Cloud para la ingesta de datos de Airbyte" /><h3>Creación de la conexión de origen y destino</h3><p>Una vez creada la Fuente y el Destino, se creará la conexión entre ellos, completando así la creación de la integración. </p><p>A continuación se muestran las instrucciones para crear la conexión:</p><p>1. En el menú, ve a Conexiones y haz clic en Crear Primera Conexión.</p><p>2. En la siguiente pantalla, podrás seleccionar una Fuente existente o crear una nueva. Como ya tenemos una Fuente creada, seleccionaremos la Fuente S3.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7f1e01215a7a328/6a17e30c63baff53d7741bb6/14937ccb2686bbde7cb99ffe7e13f7f4e35d7a31-1600x393.png" alt="Selecciona una fuente existente o crea una nueva en Airbyte" /><p>3. El siguiente paso será seleccionar el destino. Como ya creamos el conector Elasticsearch, se seleccionará para finalizar la configuración.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltece5c720f28eee00/6a17e30d505ac3dc68ad8aa3/d10962bc175f9ce0b2745ea913d739d3c40ba3b6-1600x431.png" alt="Selecciona el destino en Airbyte" /><p>En el siguiente paso, será necesario definir el Modo de Sincronización y qué esquema se empleará. Como solo se creó el esquema de registro, será la única opción disponible para la selección.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7cbcccad9cfd5a8d/6a17e30f414c64d32d9450e9/d5d3a2e20038d17ef701822fe7ccc38d6e175c50-1600x805.png" alt="Define el modo de sincronización en Airbyte" /><p>4. Pasaremos al paso Configurar Conexión. Aquí podemos definir el nombre de la conexión y la frecuencia de ejecución de la integración. La frecuencia puede configurar de tres maneras:</p><ul><li><p><strong>Cron</strong>: Ejecuta las sincronizaciones basar en la expresión cron definida por el usuario (por ejemplo, 0 0 15 * * ?, a las 15:00 todos los días);</p></li><li><p><strong>Programado</strong>: Ejecuta las sincronizaciones en el intervalo de tiempo especificado (por ejemplo, cada 24 horas, cada 2 horas);</p></li><li><p><strong>Manual</strong>: Ejecuta las sincronizaciones manualmente.</p></li></ul><p>Para esta demostración, seleccionaremos la opción Manual.</p><p>Finalmente, al hacer clic en <strong>Configurar conexión</strong>, se establecerá la conexión entre la Fuente y el Destino.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f5fc0e51035b733/6a17e311af47b67ac8cddeff/1a0470f6dff341cb90e6ecfd8ae87a7d2243cbf4-1600x626.png" alt="Haciendo clic en Configurar conexión en Airbyte" /><h3>Sincronización de datos de S3 a Elasticsearch</h3><p>Cuando vuelves a la pantalla de Conexiones, puedes ver la conexión que se creó. Para ejecutar el proceso, simplemente haz clic en Sincronizar. A partir de ese momento, comenzará la migración de datos de S3 a Elasticsearch.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b0ad7a7446f9d58/6a17e3121d1b83b4c593e3c3/c1ce54b129eafb2533b638e4df2b96f3a266ad62-1600x347.png" alt="Sincronización de datos de S3 con Elasticsearch en Airbyte" /><p>Si todo va bien, obtendrás el estado sincronizado.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt857cdad5bd7fe03f/6a17e313be608646e00046b9/6fe9d5826787066bd8bf0f5e17905df9f8d698e6-1600x361.png" alt="Estado sincronizado de S3 a Elasticsearch en Airbyte" /><h3>Visualización de datos en Kibana</h3><p>Ahora, iremos a Kibana para analizar los datos y comprobar si están indexados correctamente. En la sección Kibana Discovery crearemos una Vista de Datos llamada logs. Con esto, podremos explorar los datos que solo existen en el índice de registros, que se creó tras la sincronización.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1bc137f7c04e50ee/6a17e3151480094f2eb486cf/6b6909647ac3187d0fee477cfd732741314f82cf-1600x566.png" alt="Visualización de datos en Kibana: Airbyte y Elastic" /><p>Ahora podemos visualizar los datos indexados y realizar análisis sobre ellos. De este modo, validamos todo el flujo de migración usando Airbyte, donde cargamos los datos presentes en el bucket e los indexamos en Elasticsearch.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8c0a36a83ee4bb1/6a17e317b1e1135ea679f20e/ca8e9b3d7f7112291af58acf514f4573b44037e8-1600x806.png" alt="Airbyte y Elastic: visualiza los datos indexados y realiza análisis sobre ellos en Kibana" /><h2>Conclusión: Integración de Airbyte y Elasticsearch</h2><p>Airbyte demostró ser una herramienta eficiente para la integración de datos, permitiéndonos conectar varias fuentes y destinos de forma automatizada. En este tutorial, demostramos cómo ingirir datos de un cubo S3 a un índice de Elasticsearch, destacando los pasos principales del proceso.</p><p>Este enfoque facilita la ingestión de grandes volúmenes de datos y permite análisis dentro de Elasticsearch, como búsquedas complejas, agregaciones y visualizaciones de datos.</p><h2>Referencias</h2><p><strong>Inicio rápido de Airbyte:</strong></p><p><a href="https://docs.airbyte.com/using-airbyte/getting-started/oss-quickstart#part-1-install-abctl">https://docs.airbyte.com/using-airbyte/getting-started/oss-quickstart#part-1-install-abctl</a></p><p><strong>Conceptos básicos:</strong></p><p><a href="https://docs.airbyte.com/using-airbyte/core-concepts/">https://docs.airbyte.com/using-airbyte/core-concepts/</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/airbyte-elasticsearch-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/airbyte-elasticsearch-ingest-data</guid>
    <category><![CDATA[Datos de índice]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5defe5e12935b233/6a17e318505ac3ed7fad8aa7/dce2bad9949006163af95ed05b5a1eacf5393dc7-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 14 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cómo ingerir datos en Elasticsearch a través de LlamaIndex]]></title>
    <description><![CDATA[Un paso a paso sobre cómo ingerir datos y buscar usando RAG con LlamaIndex.]]></description>
    <content:encoded><![CDATA[<p>En este artículo, implementaremos un motor de búsqueda para las preguntas frecuentes empleando LlamaIndex para indexar los datos. Elasticsearch servirá como nuestra base de datos vectorial, permitiendo la búsqueda vectorial, mientras que RAG (Generación Aumentada por Recuperación) enriquecerá el contexto, proporcionando respuestas más precisas.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5895fbc057ffc1b/6a17f4cf3e9e45288bba15ef/7ac65a686bdd76c145e903f5c3110c62875a525f-972x501.png" alt="LlamaIndex y Elasticsearch: Ingestiendo documentos y construyendo búsqueda de preguntas frecuentes" /><h2>¿Qué es LlamaIndex?</h2><p>LlamaIndex es un marco que facilita la creación de agentes y flujos de trabajo impulsados por Grandes Modelos de Lenguaje (LLMs) para interactuar con datos específicos o privados. Permite la integración de datos de diversas fuentes (APIs, PDFs, bases de datos) con LLMs, facilitando tareas como investigación, extracción de información y generación de respuestas contextualizadas.</p><p><strong>Conceptos clave:</strong></p><ul><li><p>Agentes: Asistentes inteligentes que emplean LLMs para realizar tareas, desde respuestas simples hasta acciones complejas.</p></li><li><p>Flujos de trabajo: Procesos de varios pasos que combinan agentes, conectores de datos y herramientas para tareas avanzadas.</p></li><li><p>Aumento de contexto: Una técnica que enriquece el LLM con datos externos, superando sus limitaciones de entrenamiento.</p></li></ul><p><strong>Integración de LlamaIndex</strong> <strong>con Elasticsearch:</strong></p><p>Elasticsearch puede usar de varias formas con LlamaIndex:</p><ul><li><p>Fuente de datos: Emplea el Elasticsearch Reader para extraer documentos.</p></li><li><p>Modelo de incrustaciones: Codificar datos en vectores para búsquedas semánticas.</p></li><li><p>Almacenamiento vectorial: Emplea Elasticsearch como repositorio para buscar documentos vectorizados.</p></li><li><p>Almacenamiento avanzado: Configurar estructuras como resúmenes de documentos o grafos de conocimiento.</p></li></ul><h2>Uso de LlamaIndex y Elasticsearch para crear una búsqueda de preguntas frecuentes </h2><h3>Preparación de datos</h3><p>Usaremos como ejemplo las <a href="https://www.elastic.co/guide/en/cloud/current/ec-faq-getting-started.html">preguntas frecuentes de Elasticsearch Service</a> . Cada pregunta se extraía de la web y se almacenaba en un archivo de texto individual. Puedes usar cualquier método para organizar los datos; En este ejemplo, elegimos almacenar los archivos localmente.</p><p>Archivo de ejemplo:</p>File Name: what-is-elasticsearch-service.txt
Content: Elasticsearch Service is hosted and managed Elasticsearch and Kibana brought to you by the creators of Elasticsearch. Elasticsearch Service is part of Elastic Cloud and ships with features that you can only get from the company behind Elasticsearch, Kibana, Beats, and Logstash. Elasticsearch is a full text search engine that suits a range of uses, from search on websites to big data analytics and more.<p>Luego de almacenar todas las preguntas, el directorio se verá así:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt43467eb9c5579103/6a17f4d02f4a5cc60bfa8a62/1f367d57f2e650334671a2c03156ef4412c0c615-962x704.png" alt="" /><h3>Instalación de dependencias</h3><p>Implementaremos la ingestión y búsqueda usando el lenguaje Python, la versión que usé fue la 3.9. Como requisito previo, será necesario instalar las siguientes dependencias:</p>llama-index-vector-stores-elasticsearch
llama-index
openai<p>Elasticsearch y Kibana se crearán con Docker, configurados mediante docker-compose.yml para ejecutar la versión 8.16.2. Esto facilita la creación del entorno local.</p>version: '3.8'
services:

 elasticsearch:
   image: docker.elastic.co/elasticsearch/elasticsearch:8.16.2
   container_name: elasticsearch-8.16.2
   environment:
     - node.name=elasticsearch
     - xpack.security.enabled=false
     - discovery.type=single-node
     - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
   ports:
     - 9200:9200
   networks:
     - shared_network

 kibana:
   image: docker.elastic.co/kibana/kibana:8.16.2
   container_name: kibana-8.16.2
   restart: always
   environment:
     - ELASTICSEARCH_URL=http://elasticsearch:9200
   ports:
     - 5601:5601
   depends_on:
     - elasticsearch
   networks:
     - shared_network

networks:
 shared_network:<h3>Ingesta de documentos usando LlamaIndex</h3><p>Los documentos se indexarán en Elasticsearch usando LlamaIndex. Primero, cargamos los archivos con <strong>SimpleDirectoryReader</strong>, que permite cargar archivos desde un directorio local. Luego de cargar los documentos, los indexaremos usando el <strong>VectorStoreIndex</strong>.</p>documents = SimpleDirectoryReader("./faq").load_data()

storage_context = StorageContext.from_defaults(vector_store=es)
index = VectorStoreIndex(documents, storage_context=storage_context, embed_model=embed_model)<p>Los Vector Stores en LlamaIndex son responsables de almacenar y gestionar las incrustaciones de documentos. LlamaIndex soporta diferentes tipos de Vector Stores, y en este caso, usaremos Elasticsearch. En el StorageContext, configuramos la instancia de Elasticsearch. Dado que el contexto es local, no se requerían parámetros adicionales. Para configuraciones en otros entornos, consulte la documentación para comprobar los parámetros necesarios: <a href="https://docs.llamaindex.ai/en/stable/examples/vector_stores/ElasticsearchIndexDemo/#configuring-elasticsearchstore">ElasticsearchStore Configuration</a>.</p><p>Por defecto, LlamaIndex emplea el modelo <strong>OpenAI text-embedding-ada-002</strong> para generar embeddings. Sin embargo, en este ejemplo, usaremos el modelo <strong>text-embedding-3-small</strong> . Es importante señalar que se requerirá una clave API de OpenAI para usar el modelo.</p><p>A continuación se muestra el código completo para la ingestión de documentos.</p>import openai
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore

openai.api_key = os.environ["OPENAI_API_KEY"]

es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200"
)

def format_title(filename):
   filename_without_ext = filename.replace('.txt', '')
   text_with_spaces = filename_without_ext.replace('-', ' ')
   formatted_text = text_with_spaces.title()

   return formatted_text


embed_model = OpenAIEmbedding(model="text-embedding-3-small")

documents = SimpleDirectoryReader("./faq").load_data()

for doc in documents:
   doc.metadata['title'] = format_title(doc.metadata['file_name'])

storage_context = StorageContext.from_defaults(vector_store=es)
index = VectorStoreIndex(documents, storage_context=storage_context, embed_model=embed_model)<p>Tras la ejecución, los documentos se indexarán en el índice <strong>de preguntas frecuentes</strong> como se muestra a continuación:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt391ae1bfc1daefbf/6a17f4d22f4a5c9887fa8a66/d59b85ed1f57bf80e84d6cb0d6d722a2d12ae4c0-1600x745.png" alt="" /><h3>Búsqueda con RAG</h3><p>Para realizar búsquedas, configuramos el cliente <strong>ElasticsearchStore</strong> , configurando los campos <strong>index_name</strong> y <strong>es_url</strong> con la URL de Elasticsearch. En <strong>retrieval_strategy</strong>, definimos la <strong>AsyncDenseVectorStrategy</strong> para búsquedas vectoriales. También están disponibles otras estrategias, como <strong>AsyncBM25Strategy</strong> (búsqueda por palabras clave) y <strong>AsyncSparseVectorStrategy</strong> (vectores dispersos). Se pueden encontrar más detalles en la <a href="https://docs.llamaindex.ai/en/stable/api_reference/storage/vector_store/elasticsearch/">documentación oficial</a>.</p>es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200",
   retrieval_strategy=AsyncDenseVectorStrategy(
   )
)<p>A continuación, se creará un objeto <strong>VectorStoreIndex</strong> , donde configuramos el <strong>vector_store</strong> usando el objeto ElasticsearchStore. Con el método <strong>as_retriever</strong> , realizamos la búsqueda de los documentos más relevantes para una consulta, estableciendo el número de resultados devueltos a 5 mediante el <strong>parámetro similarity_top_k</strong> .</p>   index = VectorStoreIndex.from_vector_store(vector_store=es)
   retriever = index.as_retriever(similarity_top_k=5)
   results = retriever.retrieve(query)<p>El siguiente paso es RAG. Los resultados de la búsqueda vectorial se incorporan en un prompt formateado para el LLM, permitiendo una respuesta contextualizada basada en la información recuperada.</p><p>En la PromptPlantilla, definimos el formato del prompt, que incluye:</p><ul><li><p>Contexto ({context_str}): documentos recuperados por el recuperador.</p></li><li><p>Consulta ({query_str}): la pregunta del usuario.</p></li><li><p>Instrucciones: pautas para que el modelo responda según el contexto, sin depender de conocimientos externos.</p></li></ul>qa_prompt = PromptTemplate(
   "You are a helpful and knowledgeable assistant."
   "Your task is to answer the user's query based solely on the context provided below."
   "Do not use any prior knowledge or external information.\n"
   "---------------------\n"
   "Context:\n"
   "{context_str}\n"
   "---------------------\n"
   "Query: {query_str}\n"
   "Instructions:\n"
   "1. Carefully read and understand the context provided.\n"
   "2. If the context contains enough information to answer the query, provide a clear and concise answer.\n"
   "3. Do not make up or guess any information.\n"
   "Answer: "
)<p>Finalmente, el LLM procesa el prompt y devuelve una respuesta precisa y contextual.</p>llm = OpenAI(model="gpt-4o")
context_str = "\n\n".join([n.node.get_content() for n in results])
response = llm.complete(
   qa_prompt.format(context_str=context_str, query_str=query)
)

print("Answer:")
print(response)<p>El código completo se encuentra a continuación:</p>es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200",
   retrieval_strategy=AsyncDenseVectorStrategy(
   )
)


def print_results(results):
   for rank, result in enumerate(results, start=1):
       title = result.metadata.get("title")
       score = result.get_score()
       text = result.get_text()
       print(f"{rank}. title={title} \nscore={score} \ncontent={text}")


def search(query: str):
   index = VectorStoreIndex.from_vector_store(vector_store=es)

   retriever = index.as_retriever(similarity_top_k=10)
   results = retriever.retrieve(QueryBundle(query_str=query))
   print_results(results)

   qa_prompt = PromptTemplate(
       "You are a helpful and knowledgeable assistant."
       "Your task is to answer the user's query based solely on the context provided below."
       "Do not use any prior knowledge or external information.\n"
       "---------------------\n"
       "Context:\n"
       "{context_str}\n"
       "---------------------\n"
       "Query: {query_str}\n"
       "Instructions:\n"
       "1. Carefully read and understand the context provided.\n"
       "2. If the context contains enough information to answer the query, provide a clear and concise answer.\n"
       "3. Do not make up or guess any information.\n"
       "Answer: "
   )

   llm = OpenAI(model="gpt-4o")
   context_str = "\n\n".join([n.node.get_content() for n in results])
   response = llm.complete(
       qa_prompt.format(context_str=context_str, query_str=query)
   )

   print("Answer:")
   print(response)


question = "Elastic services are free?"
print(f"Question: {question}")
search(question)<p>Ahora podemos realizar nuestra búsqueda, por ejemplo, "¿Los servicios de Elastic son gratis?" y obtener una respuesta contextualizada basada en los datos de las preguntas frecuentes en sí.</p>Question: Elastic services are free?
Answer:
Elastic services are not entirely free. However, there is a 14-day free trial available for exploring Elastic solutions. After the trial, access to features and services depends on the subscription level.<p>Para generar esta respuesta, se emplearon los siguientes documentos:</p>1. title=Can I Try Elasticsearch Service For Free 
score=1.0 
content=Yes, sign up for a 14-day free trial. The trial starts the moment a cluster is created.
During the free trial period get access to a deployment to explore Elastic solutions for Enterprise Search, Observability, Security, or the latest version of the Elastic Stack.

2. title=Do You Offer Elastic S Commercial Products 
score=0.9941274512218439 
content=Yes, all Elasticsearch Service customers have access to basic authentication, role-based access control, and monitoring.
Elasticsearch Service Gold, Platinum and Enterprise customers get complete access to all the capabilities in X-Pack: Security, Alerting, Monitoring, Reporting, Graph Analysis &amp; Visualization. Contact us to learn more.

3. title=What Is Elasticsearch Service 
score=0.9896776845746571 
content=Elasticsearch Service is hosted and managed Elasticsearch and Kibana brought to you by the creators of Elasticsearch. Elasticsearch Service is part of Elastic Cloud and ships with features that you can only get from the company behind Elasticsearch, Kibana, Beats, and Logstash. Elasticsearch is a full text search engine that suits a range of uses, from search on websites to big data analytics and more.

4. title=Can I Run The Full Elastic Stack In Elasticsearch Service 
score=0.9880631561979476 
content=Many of the products that are part of the Elastic Stack are readily available in Elasticsearch Service, including Elasticsearch, Kibana, plugins, and features such as monitoring and security. Use other Elastic Stack products directly with Elasticsearch Service. For example, both Logstash and Beats can send their data to Elasticsearch Service. What is run is determined by the subscription level.

5. title=What Is The Difference Between Elasticsearch Service And The Amazon Elasticsearch Service 
score=0.9835054890793161 
content=Elasticsearch Service is the only hosted and managed Elasticsearch service built, managed, and supported by the company behind Elasticsearch, Kibana, Beats, and Logstash. With Elasticsearch Service, you always get the latest versions of the software. Our service is built on best practices and years of experience hosting and managing thousands of Elasticsearch clusters in the Cloud and on premise. For more information, check the following Amazon and Elastic Elasticsearch Service comparison page.
Please note that there is no formal partnership between Elastic and Amazon Web Services (AWS), and Elastic does not provide any support on the AWS Elasticsearch Service.<h2>Conclusión</h2><p>Empleando LlamaIndex, demostramos cómo crear un sistema eficiente de búsqueda de preguntas frecuentes con soporte para Elasticsearch como base de datos vectorial. Los documentos se ingieren e indexan mediante incrustaciones, lo que permite búsquedas vectoriales. A través de una PromptPlantilla, los resultados de la búsqueda se incorporan al contexto y se envían al LLM, que genera respuestas precisas y contextualizadas basadas en los documentos recuperados.</p><p>Este flujo de trabajo integra la recuperación de información con la generación contextualizada de respuestas para ofrecer resultados precisos y relevantes.</p><h2>Referencias</h2><p><a href="https://www.elastic.co/guide/en/cloud/current/ec-faq-getting-started.html">https://www.elastic.co/guide/en/cloud/current/ec-faq-getting-started.html</a></p><p><a href="https://docs.llamaindex.ai/en/stable/api_reference/readers/elasticsearch/">https://docs.llamaindex.ai/en/stable/api_reference/readers/elasticsearch/</a></p><p><a href="https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_index/">https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_index/</a></p><p><a href="https://docs.llamaindex.ai/en/stable/examples/query_engine/custom_query_engine/">https://docs.llamaindex.ai/en/stable/examples/query_engine/custom_query_engine/</a></p><p><a href="https://www.elastic.co/search-labs/integrations/llama-index">https://www.elastic.co/search-labs/integrations/llama-index</a></p><p></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-llamaindex-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-llamaindex-ingest-data</guid>
    <category><![CDATA[Datos de índice]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf9f88afa92e90390/6a17f4d33e03d7987d4f2dd0/b8b760bfd8694df43fd74ba90ae5fc1edbe4ce76-1150x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 28 Feb 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cómo ingestar datos en Elasticsearch a través de Apache Airflow]]></title>
    <description><![CDATA[Aprende cómo ingerir datos en Elasticsearch a través de Apache Airflow.]]></description>
    <content:encoded><![CDATA[<h2>¿Qué es el Apache Airflow?</h2><p>Apache Airflow es una plataforma diseñada para crear, programar y monitorizar flujos de trabajo. Se emplea para orquestar procesos ETL, pipelines de datos y otros flujos de trabajo complejos, ofreciendo flexibilidad y escalabilidad. Su interfaz visual y capacidades de monitorización en tiempo real hacen que la gestión de tuberías sea más accesible y eficiente, permitiéndote seguir el progreso y los resultados de tus ejecuciones. A continuación se presentan sus cuatro pilares principales:</p><ul><li><p><strong>Dinámico: </strong>Las canalizaciones están definidas en Python, lo que permite una generación dinámica y flexible de flujos de trabajo.</p></li><li><p><strong>Extensible:</strong> Airflow puede integrar con una variedad de entornos, crear operadores personalizados y ejecutar código específico según sea necesario.</p></li><li><p><strong>Elegante:</strong> Los pipelines se escriben de manera limpia y explícita.</p></li><li><p><strong>Escalable:</strong> Su arquitectura modular emplea una cola de mensajes para orquestar un número arbitrario de trabajadores.</p></li></ul><p>En la práctica, el flujo de aire puede emplear en escenarios como:</p><ul><li><p><strong>Importación de datos: </strong>Orquestar la ingestión diaria de datos en una base de datos como Elasticsearch.</p></li><li><p><strong>Monitorización de registros:</strong> Gestionar la recopilación y procesamiento de archivos de registro, que luego se analizan en Elasticsearch para identificar errores o anomalías.</p></li><li><p><strong>Integración de múltiples fuentes de datos:</strong> Combina información de diferentes sistemas (APIs, bases de datos, archivos) en una sola capa en Elasticsearch, simplificando la búsqueda y la elaboración de reportes.</p></li></ul><h2>Comprendiendo los DAG (Grafos Acíclicos Dirigidos) en el flujo de aire</h2><p>En Airflow, los flujos de trabajo se representan mediante DAGs (Grafos Acíclicos Dirigidos). Un DAG es una estructura que define la secuencia en la que se ejecutarán las tareas. Las principales características de los DAG son:</p><ul><li><p><strong>Composición por tareas independientes:</strong> Cada tarea representa una unidad de trabajo y está diseñada para ejecutar de forma independiente.</p></li><li><p><strong>Secuenciación: </strong>La secuencia en la que se ejecutan las tareas está definida explícitamente en el DAG.</p></li><li><p><strong>Reusabilidad:</strong> Los DAG están diseñados para ejecutar repetidamente, facilitando la automatización de procesos.</p></li></ul><h2>Componentes de flujo de aire</h2><p>El ecosistema Airflow está compuesto por varios componentes que trabajan juntos para orquestar tareas:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt25a23489b8725b3e/6a17dfcfe8fbceba103a1846/bacd83aff625026d62f023e0434baa5782a2761a-1046x628.png" alt="Componentes principales del flujo de aire" /><ul><li><p><strong>Programador:</strong> Responsable de programar los DAGs y enviar tareas para su ejecución por parte de los trabajadores.</p></li><li><p><strong>Ejecutor:</strong> Gestiona la ejecución de las tareas, delegándolas a los trabajadores.</p></li><li><p><strong>Servidor sitio web:</strong> Proporciona una interfaz gráfica para interactuar con DAGs y tareas.</p></li><li><p><strong>Carpeta Dags:</strong> Carpeta donde almacenamos los DAGs escritos en Python.</p></li><li><p><strong>Metadatos:</strong> Base de datos que sirve como repositorio para la herramienta, empleada por el planificador y el ejecutor para almacenar el estado de ejecución.</p></li></ul><h2>Apache Airflow y Elasticsearch</h2><p>Demostraremos el uso de Apache Airflow y Elasticsearch para orquestar tareas e indexar resultados en Elasticsearch. El objetivo de esta demostración es crear una cadena de tareas para actualizar registros en un índice de Elasticsearch. Este índice contiene una base de datos de películas, donde los usuarios pueden valorar y asignar valoraciones. Imaginando un escenario con cientos de audiencias diarias, es necesario mantener actualizado el registro de audiencias. Para ello, se desarrollará un DAG que se ejecutará diariamente, responsable de recuperar las nuevas calificaciones consolidadas y actualizar los registros en el índice.</p><p>En el flujo del DAG, tendremos una tarea para obtener las valoraciones, seguida de otra tarea para validar los resultados. Si los datos no existen, el DAG será dirigido a una tarea de fallo. De lo contrario, los datos se indexarán en Elasticsearch. El objetivo es actualizar el campo de calificación de las películas en un índice recuperando las calificaciones mediante un método con el mecanismo responsable de calcular los puntajes.</p><h2>Usando Apache Airflow y Elasticsearch con Docker</h2><p>Para crear un entorno contenedorizado, usaremos Apache Airflow con Docker. Sigue las instrucciones de la <a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html">guía "Running Airflow en Docker"</a> para configurar Airflow de forma práctica.</p><p>En cuanto a Elasticsearch, usaré un clúster en Elastic Cloud, pero si prefieres, también puedes configurar Elasticsearch con Docker. Ya se creó un índice que contiene un catálogo de películas, con los datos de las películas indexados. El campo de 'calificación' de estas películas se actualizará.</p><h2>Creación del DAG</h2><p>Tras instalarla mediante Docker, se creará una estructura de carpetas, incluyendo la carpeta dags, donde debemos colocar nuestros archivos DAG para que Airflow los reconozca.</p><p>Antes de eso, debemos cerciorarnos de que las dependencias necesarias estén instaladas. Estas son las dependencias de este proyecto:</p>pip install apache-airflow apache-airflow-providers-elasticsearch<p>Crearemos el archivo <code>update_ratings_movies.py</code> y empezaremos a programar las tareas.</p><p>Ahora, importemos las bibliotecas necesarias:</p>from airflow import DAG
from airflow.operators.python import PythonOperator, BranchPythonOperator
from airflow.providers.elasticsearch.hooks.elasticsearch import ElasticsearchPythonHook<p>Emplearemos <a href="https://airflow.apache.org/docs/apache-airflow-providers-elasticsearch/stable/hooks/elasticsearch_python_hook.html"><strong>el ElasticsearchPythonHook</strong></a>, un componente que simplifica la integración entre Airflow y un clúster de Elasticsearch al abstraer la conexión y el uso de APIs externas.</p><p>A continuación, definimos el DAG, especificando sus argumentos principales:</p><ul><li><p><strong><code>dag_id</code></strong>: el nombre del DAG.</p></li><li><p><strong><code>start_date</code></strong>: cuando empezará el DAG.</p></li><li><p><strong><code>schedule</code></strong>: define la periodicidad (diaria en nuestro caso).</p></li><li><p><strong><code>doc_md</code></strong>: documentación que se importará y mostrará en la interfaz Airflow.</p></li></ul><h2>Definición de las tareas</h2><p>Ahora, definamos las tareas del DAG. La primera tarea será la de recuperar los datos de valoración de la película. Usaremos el <strong>PythonOperator</strong> con la <code>task_id</code> configurada en <code>'get_movie_ratings'</code>. El parámetro <code>python_callable</code> llamará a la función responsable de obtener las valoraciones.</p>get_ratings_operator = PythonOperator(
   task_id='get_movie_ratings',
   python_callable=get_movie_ratings_task
)<p>A continuación, necesitamos validar si los resultados son válidos. Para esto, usaremos un condicional con <strong>un operador BranchPython</strong>. La <code>task_id</code> será <code>'validate_result'</code>, y la <code>python_callable</code> llamará a la función de validación. El parámetro <code>op_args</code> se empleará para pasar el resultado de la tarea anterior, <code>'get_movie_ratings'</code>, a la función de validación.</p>validate_result = BranchPythonOperator(
   task_id='validate_result',
   python_callable=validate_result,
   op_args=["{{ task_instance.xcom_pull(task_ids='get_movie_ratings') }}"]
)<p>Si la validación tiene éxito, tomaremos los datos de la tarea <code>'get_movie_ratings'</code> e los indexaremos en Elasticsearch. Para lograrlo, crearemos una nueva tarea, <code>'index_movie_ratings'</code>, que empleará el <strong>PythonOperator</strong>. El parámetro <code>op_args</code> pasará los resultados de la tarea <code>'get_movie_ratings'</code> a la función de indexación.</p>index_ratings_operator = PythonOperator(
   task_id='index_movie_ratings',
   python_callable=index_movie_ratings_task,
   op_args=["{{ task_instance.xcom_pull(task_ids='get_movie_ratings') }}"]
)<p>Si la validación indica un fallo, el DAG procederá a una tarea de notificación de fallo. En este ejemplo, simplemente imprimimos un mensaje, pero en un escenario real podríamos configurar alertas para notificar sobre los fallos.</p>failed_get_rating_operator = PythonOperator(
   task_id='failed_get_rating_operator',
   python_callable=lambda: print('Ratings were False, skipping indexing.')
)<p>Finalmente, definimos las dependencias de las tareas, cerciorándonos de que se ejecuten en el orden correcto:</p>get_ratings_operator &gt;&gt; validate_result &gt;&gt; [index_ratings_operator, failed_get_rating_operator]<p>Ahora sigue el código completo de nuestro DAG:</p>"""
DAG update Rating Movies
"""
import ast
import random

from airflow import DAG
from datetime import datetime

from airflow.operators.python import PythonOperator, BranchPythonOperator
from airflow.providers.elasticsearch.hooks.elasticsearch import ElasticsearchPythonHook


def index_movie_ratings_task(movies):
   es_hook = ElasticsearchPythonHook(hosts=None,
                                     es_conn_args={
                                         "cloud_id": "cloud_id"
                                         "api_key": "api-key"
                                     })
   es_client = es_hook.get_conn
   actions = []
   for movie in ast.literal_eval(movies):
       actions.append(
           {
               "update": {
                   "_id": movie["id"],
                   "_index": "movies"
               }
           }
       )
       actions.append(
           {
               "doc": {
                   "rating": movie["rating"]
               },
               "doc_as_upsert": True
           }
       )
   result = es_client.bulk(operations=actions)
   print(f"Ingestion completed.")
   print(result)
   return True


def get_movie_ratings_task():
   movies = [
       {"id": i, "rating": round(random.uniform(1, 10), 1)}
       for i in range(1, 100)
   ]
   return movies

def validate_result(result):
   if not result:
       return 'failed_get_rating_operator'
   else:
       return 'index_movie_ratings'


with DAG(
       dag_id="update_ratings_movies_2024",
       start_date=datetime(2024, 12, 29),
       schedule="@daily",
       doc_md=__doc__,
):
   get_ratings_operator = PythonOperator(
       task_id='get_movie_ratings',
       python_callable=get_movie_ratings_task
   )

   validate_result = BranchPythonOperator(
       task_id='validate_result',
       python_callable=validate_result,
       op_args=["{{ task_instance.xcom_pull(task_ids='get_movie_ratings') }}"],
       provide_context=True
   )

   index_ratings_operator = PythonOperator(
       task_id='index_movie_ratings',
       python_callable=index_movie_ratings_task,
       op_args=["{{ task_instance.xcom_pull(task_ids='get_movie_ratings') }}"]
   )

   failed_get_rating_operator = PythonOperator(
       task_id='failed_get_rating_operator',
       python_callable=lambda: print('Ratings were False, skipping indexing.')
   )

get_ratings_operator &gt;&gt; validate_result &gt;&gt; [index_ratings_operator, failed_get_rating_operator]<h2>Visualización de la ejecución del DAG</h2><p>En la interfaz Apache Airflow, podemos visualizar la ejecución de los DAGs. Simplemente ve a la pestaña "DAGs" y localiza el DAG que creaste.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9c5de210a5a264ad/6a17dfd00b0bed0290dd34cf/905b9191c4e3191e8b5608174d4c555370bf25eb-1600x760.png" alt="Visualiza la ejecución de los DAGs en la interfaz Apache Airflow con Elasticsearch" /><p>A continuación, podemos visualizar las ejecuciones de las tareas y sus respectivos estados. Al seleccionar una ejecución para una fecha específica, podemos acceder a los registros de cada tarea. Ten en cuenta que en la tarea <strong><code>index_movie_ratings</code></strong> podemos ver los resultados de indexación en el índice y que se completó con éxito.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0beddf311a808d24/6a17dfd2033c8d76de6bb0ba/73c3f738d27500cf153377bedf1aa2b67a94a8c8-1600x648.png" alt="visualiza las ejecuciones de las tareas y sus estados en Apache Airflow con Elasticsearch" /><p>En las otras pestañas, es posible acceder a información adicional sobre las tareas y el DAG, ayudando en el análisis y resolución de posibles problemas.</p><h2>Conclusión</h2><p>En este artículo, demostramos cómo integrar Apache Airflow con Elasticsearch para crear una solución de ingestión de datos. Mostramos cómo configurar el DAG, definir las tareas responsables de recuperar, validar e indexar datos de video, así como monitorizar y visualizar la ejecución de estas tareas en la interfaz Airflow.</p><p>Este enfoque puede adaptar fácilmente a diferentes tipos de datos y flujos de trabajo, haciendo de Airflow una herramienta útil para orquestar pipelines de datos en diversos escenarios.</p><h2>Referencias</h2><p>Apache AirFlow</p><p><a href="https://airflow.apache.org/">https://airflow.apache.org/</a></p><p>Instalar Apache Airflow con Docker</p><p><a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html">https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html</a></p><p>Elasticsearch Python Hook</p><p><a href="https://airflow.apache.org/docs/apache-airflow-providers-elasticsearch/stable/hooks/elasticsearch_python_hook.html">https://airflow.apache.org/docs/apache-airflow-providers-elasticsearch/stable/hooks/elasticsearch_python_hook.html</a></p><p>Operador Python</p><p><a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/operator/python.html">https://airflow.apache.org/docs/apache-airflow/stable/howto/operator/python.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/apache-airflow-elasticsearch-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/apache-airflow-elasticsearch-ingest-data</guid>
    <category><![CDATA[Datos de índice]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt28475ace97d1d989/6a1704c9286714d6dd93e1ec/5d4b47ac5d2ba453fc19dcc15efa2aed5f55d88b-1440x1355.png" length="0" type="image/png"/>
    <pubDate>Fri, 17 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cómo ingerir datos en Elasticsearch a través de Kafka]]></title>
    <description><![CDATA[Una guía paso a paso para integrar Apache Kafka con Elasticsearch para una ingestión, indexación y visualización eficiente de datos usando Python, Docker Compose y Kafka Connect.]]></description>
    <content:encoded><![CDATA[<p>En este artículo, mostramos cómo integrar Apache Kafka con Elasticsearch para la ingestión e indexación de datos. Proporcionaremos una visión general de Kafka, su concepto de productores y consumidores, y crearemos un índice de registros donde los mensajes serán recibidos e indexados a través de Apache Kafka. El proyecto está implementado en Python y el código está disponible en <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/elasticsearch-through-apache-kafka">GitHub</a>.</p><h3><strong>Prerrequisitos</strong></h3><ul><li><p>Docker y Docker Compose: Cerciórate de tener Docker y Docker Compose instalados en tu máquina.</p></li><li><p>Python 3.x: Para ejecutar los scripts de productor y consumidor.</p></li></ul><h3><strong>Introducción a Apache Kafka</strong></h3><p>Apache Kafka es una plataforma de streaming distribuida que permite una alta escalabilidad y disponibilidad, así como tolerancia a fallos. En Kafka, la gestión de datos se realiza a través de los componentes principales:</p><ul><li><p><strong>Intermediario</strong>: responsable de almacenar y distribuir mensajes entre productores y consumidores.</p></li><li><p><strong>Zookeeper</strong>: gestiona y coordina los corredores Kafka, controlando el estado del clúster, los líderes de partición y la información del consumidor.</p></li><li><p><strong>Temas</strong>: canales donde se publican y almacenan datos para su consumo.</p></li><li><p><strong>Consumidores y productores</strong>: mientras los productores envían datos a los temas, los consumidores recuperan esos datos.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4aae32304d7417f6/6a17f7be577262b47c1bcdac/89a37243baec48bbdfa85e3298fc91082322ed4e-1600x868.png" alt="Diagram Apache Kafka" /><p>Estos componentes trabajan juntos para formar el ecosistema Kafka, proporcionando un marco robusto para el flujo de datos.</p><h3><strong>Estructura del proyecto</strong></h3><p>Para entender el proceso de ingesta de datos, lo dividimos en etapas:</p><ul><li><p><strong>Aprovisionamiento de infraestructura</strong>: configurar el entorno Docker para soportar Kafka, Elasticsearch y Kibana.</p></li><li><p><strong>Creación de productores</strong>: implementando el Productor Kafka, que envía datos al tema de registros.</p></li><li><p><strong>Creación de Consumidores</strong>: desarrollar el Consumidor Kafka para leer e indexar mensajes en Elasticsearch.</p></li><li><p><strong>Validación de ingestión</strong>: verificar y validar los datos enviados y consumidos.</p></li></ul><h3><strong>Configuración de infraestructura con Docker Compose</strong></h3><p>Empleamos Docker Compose para configurar y gestionar los servicios necesarios. A continuación, encontrarás el código Docker Compose que configura cada servicio necesario para la integración de Apache Kafka, Elasticsearch y Kibana, cerciorando un proceso de ingestión de datos.</p>version: "3"

services:

  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    container_name: zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181

  kafka:
    image: confluentinc/cp-kafka:latest
    container_name: kafka
    depends_on:
      - zookeeper
    ports:
      - "9092:9092"
      - "9094:9094"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST:${HOST_IP}:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.1
    container_name: elasticsearch-8.15.1
    environment:
      - node.name=elasticsearch
      - xpack.security.enabled=false
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - ./elasticsearch:/usr/share/elasticsearch/data
    ports:
      - 9200:9200

  kibana:
    image: docker.elastic.co/kibana/kibana:8.15.1
    container_name: kibana-8.15.1
    ports:
      - 5601:5601
    environment:
      ELASTICSEARCH_URL: http://elasticsearch:9200
      ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'<p>Puedes acceder al archivo directamente desde el repositorio <a href="https://github.com/andreluiz1987/elasticsearch-labs/tree/supporting-blog/elasticsearch-apache-kafka/supporting-blog-content/elasticsearch-through-apache-kafka">de GitHub</a> de Elasticsearch Labs.</p><h3><strong>Envío de datos con el productor Kafka</strong></h3><p>El productor es responsable de enviar mensajes al tema de los logs. Al enviar mensajes en lotes, aumenta la eficiencia del uso de la red, permitiendo optimizaciones con los ajustes de <code>batch_size</code> y <code>linger_ms</code> , que controlan la cantidad y latencia de los lotes, respectivamente. La <code>acks='all'</code> de configuración garantiza que los mensajes se almacenen de forma duradera, lo cual es esencial para los datos de registro importantes.</p>producer = KafkaProducer(
   bootstrap_servers=['localhost:9092'],  # Specifies the Kafka server to connect
   value_serializer=lambda x: json.dumps(x).encode('utf-8'),  # Serializes data as JSON and encodes it to UTF-8 before sending
   batch_size=16384,     # Sets the maximum batch size in bytes (here, 16 KB) for buffered messages before sending
   linger_ms=10,         # Sets the maximum delay (in milliseconds) before sending the batch
   acks='all'            # Specifies acknowledgment level; 'all' ensures message durability by waiting for all replicas to acknowledge
)


def generate_log_message():
   levels = ["INFO", "WARNING", "ERROR", "DEBUG"]
   messages = [
       "User login successful",
       "User login failed",
       "Database connection established",
       "Database connection failed",
       "Service started",
       "Service stopped",
       "Payment processed",
       "Payment failed"
   ]
   log_entry = {
       "level": random.choice(levels),
       "message": random.choice(messages),
       "timestamp": time.time()
   }
   return log_entry

def send_log_batches(topic, num_batches=5, batch_size=10):
   for i in range(num_batches):
       logger.info(f"Sending batch {i + 1}/{num_batches}")
       for  in range(batch_size):
           log_message = generate_log_message()
           producer.send(topic, value=log_message)
       producer.flush()


if __name__ == "__main__":
   topic = "logs"
   send_log_batches(topic)
   producer.close()<p>Al iniciar el productor, los mensajes se envían en lotes sobre el tema, como se muestra a continuación:</p>INFO:kafka.conn:Set configuration …
INFO:log_producer:Sending batch 1/5 
INFO:log_producer:Sending batch 2/5
INFO:log_producer:Sending batch 3/5
INFO:log_producer:Sending batch 4/5<h3><strong>Consumo e indexación de datos con el Kafka Consumer</strong></h3><p>El consumidor está diseñado para procesar mensajes de forma eficiente, consumiendo lotes del tema de logs e indexándolos en Elasticsearch. Con <code>auto_offset_reset='latest'</code>, se cerciora que el consumidor empiece a procesar los mensajes más recientes, ignorando los antiguos, y <code>max_poll_records=10</code> limita el lote a 10 mensajes. Con <code>fetch_max_wait_ms=2000</code>, el consumidor espera hasta 2 segundos para acumular suficientes mensajes antes de procesar el lote.</p><p>En su bucle principal, el consumidor consume mensajes de registro, procesa e indexa cada lote en Elasticsearch, cerciorando la ingestión continua de datos.</p>consumer = KafkaConsumer(
   'logs',                               
   bootstrap_servers=['localhost:9092'],
   auto_offset_reset='latest',            # Ensures reading from the latest offset if the group has no offset stored
   enable_auto_commit=True,               # Automatically commits the offset after processing
   group_id='log_consumer_group',         # Specifies the consumer group to manage offset tracking
   max_poll_records=10,                   # Maximum number of messages per batch
   fetch_max_wait_ms=2000                 # Maximum wait time to form a batch (in ms)
)

def create_bulk_actions(logs):
   for log in logs:
       yield {
           "_index": "logs",
           "_source": {
               'level': log['level'],
               'message': log['message'],
               'timestamp': log['timestamp']
           }
       }

if __name__ == "__main__":
   try:
       print("Starting message processing…")
       while True:

           messages = consumer.poll(timeout_ms=1000)  # Poll receive messages

           # process each batch messages
           for _, records in messages.items():
               logs = [json.loads(record.value) for record in records]
               bulk_actions = create_bulk_actions(logs)
               response = helpers.bulk(es, bulk_actions)
               print(f"Indexed {response[0]} logs.")
   except Exception as e:
       print(f"Erro: {e}")
   finally:
       consumer.close()
       print(f"Finish")<h3><strong>Visualización de datos en Kibana</strong></h3><p>Con Kibana, podemos explorar y validar los datos ingeridos de Kafka e indexados en Elasticsearch. Accediendo <strong>a Dev Tools</strong> en Kibana, puedes ver los mensajes indexados y confirmar que los datos son los esperados. Por ejemplo, si nuestro productor Kafka enviara 5 lotes de 10 mensajes cada uno, deberíamos ver un total de 50 registros en el índice.</p><p>Para verificar los datos, puedes usar la siguiente consulta en la sección <strong>de Herramientas de desarrollo</strong> :</p>GET /logs/_search
{
  "query": {
    "match_all": {}
  }
}<p>Respuesta:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc15fb278fe6f984e/6a17f7c0b1e1131ac279f404/f44f95fc27bba50991412d5c7e7728519b9bdec4-688x1024.png" alt="Respuesta verifica los datos - Kafka &amp; Elasticsearch​" /><p>Además, Kibana ofrece la capacidad de crear visualizaciones y paneles que pueden ayudar a que el análisis sea más intuitivo e interactivo. A continuación, podéis ver algunos ejemplos de los paneles y visualizaciones que creamos, que ilustran los datos en varios formatos, mejorando nuestra comprensión de la información procesada.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltabc2856c9feedc47/6a17f7c13e9e4522bfba1651/a18e0ebb543e929136d4786651bc6cee32fa69bc-1600x470.png" alt="Visualización de Kibana - Kafka &amp; Elasticsearch​" /><h3><strong>Ingesta de datos con Kafka Connect</strong></h3><p>Kafka Connect es un servicio diseñado para facilitar la integración entre fuentes de datos y destinos (sumideros), como bases de datos o sistemas de archivos. Opera con conectores predefinidos que gestionan el movimiento de datos automáticamente. En nuestro caso, Elasticsearch funciona como el sumidero de datos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53e3acaf62e7dbed/6a17f7c3577262c5151bcdb0/52a6982c864fdc04cb7a8a5fb02e67dca0ba8226-1600x819.png" alt="Ingesta de datos con Kafka Connect" /><p>Con Kafka Connect, podemos simplificar el proceso de ingesta de datos, eliminando la necesidad de implementar manualmente el flujo de trabajo de ingestión de datos en Elasticsearch. Con el conector adecuado, Kafka Connect permite que los datos enviados a un tema Kafka se indexen directamente en Elasticsearch con una configuración mínima y sin necesidad de codificación adicional.</p><h4><strong>Trabajando con Kafka Connect</strong></h4><p>Para implementar Kafka Connect, agregaremos el<a href="https://github.com/andreluiz1987/es-apache-kafka/blob/main/docker-compose.yml#L31"> servicio kafka-connect </a>a nuestra configuración de Docker Compose. Una parte clave de esta configuración es la instalación del conector Elasticsearch, que gestionará la indexación de datos.</p><p>Tras configurar el servicio y crear el contenedor Kafka Connect, será necesario un archivo de configuración para el conector Elasticsearch. Este archivo define parámetros esenciales como:</p><ul><li><p><code>connection.url</code>: URL de conexión para Elasticsearch.</p></li><li><p><code>topics</code>: El tema de Kafka que el conector monitorizará (en este caso, "logs").</p></li><li><p><code>type.name</code>: Tipo de documento en Elasticsearch (normalmente _doc).</p></li><li><p><code>value.converter</code>: Convierte los mensajes Kafka a formato JSON.</p></li><li><p><code>value.converter.schemas.enable</code>: Especifica si el esquema debe incluir.</p></li><li><p><code>schema.ignore</code> y <code>key.ignore</code>: Ajustes para ignorar esquemas y claves de Kafka durante la indexación.</p></li></ul><p>A continuación se muestra el comando <code>curl</code> para crear el conector Elasticsearch en Kafka Connect:</p>curl --location '{{url}}/connectors' \
--header 'Content-Type: application/json' \
--data '{
    "name": "elasticsearch-sink-connector",
    "config": {
        "connector.class": "io.confluent.connect.elasticsearch.ElasticsearchSinkConnector",
        "topics": "logs",
        "connection.url": "http://elasticsearch:9200",
        "type.name": "_doc",
        "value.converter": "org.apache.kafka.connect.json.JsonConverter",
        "value.converter.schemas.enable": "false",
        "schema.ignore": "true",
        "key.ignore": "true"
    }
}'<p>Con esta configuración, Kafka Connect comenzará automáticamente a ingerir los datos enviados al tema de "logs" e indexarlos en Elasticsearch. Este enfoque permite la ingesta e indexación de datos totalmente automatizada sin necesidad de codificación adicional, simplificando así todo el proceso de integración.</p><h3><strong>Conclusión</strong></h3><p>Integrar Kafka y Elasticsearch crea una poderosa cadena de procesamiento y análisis de datos en tiempo real. Esta guía proporciona un enfoque fundamental para construir una arquitectura robusta de ingestión de datos, con visualización y análisis fluidos en Kibana, lista para adaptar a requisitos más complejos en el futuro.</p><p>Además, usar Kafka Connect hace que la integración entre Kafka y Elasticsearch sea aún más ágil, eliminando la necesidad de código adicional para procesar e indexar datos. Kafka Connect permite que los datos enviados a un tema específico se indexen automáticamente en Elasticsearch con una configuración mínima.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-apache-kafka-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-apache-kafka-ingest-data</guid>
    <category><![CDATA[Datos de índice]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53e3acaf62e7dbed/6a17f7c3577262c5151bcdb0/52a6982c864fdc04cb7a8a5fb02e67dca0ba8226-1600x819.png" length="0" type="image/png"/>
    <pubDate>Tue, 24 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cómo ingerir datos en Elasticsearch a través de Apache Camel]]></title>
    <description><![CDATA[Aprende a ingir datos en Elasticsearch a través de Apache Camel con un ejemplo práctico.]]></description>
    <content:encoded><![CDATA[<p>La ingestión de datos en Elasticsearch usando Apache Camel es un proceso que combina la robustez de un motor de búsqueda con la flexibilidad de un marco de integración. En este artículo, exploraremos cómo Apache Camel puede simplificar y optimizar la ingestión de datos en Elasticsearch. Para ilustrar esta funcionalidad, implementaremos una aplicación introductoria que demuestra, paso a paso, cómo configurar y usar Apache Camel para enviar datos a Elasticsearch.</p><h2>¿Qué es el camello apache?</h2><p>Apache Camel es un framework de integración de código abierto que simplifica la conexión de sistemas diversos, permitiendo a los desarrolladores centrar en la lógica de negocio sin preocupar por las complejidades de la comunicación del sistema. El concepto central en Camel son las "rutas", que definen el camino que sigue un mensaje desde el origen hasta el destino, incluyendo potencialmente pasos intermedios como transformaciones, validaciones y filtrado.</p><h3>Arquitectura del camello apache</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt05652327efd4d5d1/6a17e6dafbc5f8a588491a8b/bef8145623a8fa80f929f9faa57ce0c460be2d0b-884x458.png" alt="Arquitectura del camello apache" /><p>Camel emplea "componentes" para conectarse a diferentes sistemas y protocolos, como bases de datos y servicios de mensajería, y "endpoints" para representar los puntos de entrada y salida de los mensajes. Estos conceptos proporcionan un diseño modular y flexible, facilitando la configuración y gestión de integraciones complejas de forma eficiente y escalable.</p><h2>Uso de Elasticsearch y Apache Camel</h2><p>Demostraremos cómo configurar una aplicación Java sencilla que emplea Apache Camel para ingirir datos en un clúster de Elasticsearch. También se cubrirán los procesos de creación, actualización y eliminación de datos en Elasticsearch usando rutas definidas en Apache Camel (Apache Camel).</p><h3>1. Agregar dependencias</h3><p>El primer paso para configurar esta integración es agregar las dependencias necesarias al archivo <code>pom.xml</code> de tu proyecto. Esto incluirá las bibliotecas Apache Camel y Elasticsearch. Vamos a usar la nueva biblioteca cliente de Java API, así que debemos importar el componente <code>camel-elasticsearch</code> y la versión debe ser la misma que la de la biblioteca <code>camel-core</code> .</p><p>Si quieres usar el cliente de descanso de bajo nivel de Java, debes emplear el componente de cliente de descanso de bajo nivel de Elasticsearch.</p>&lt;dependency&gt;
   &lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
   &lt;artifactId&gt;camel-core&lt;/artifactId&gt;
   &lt;version&gt;4.7.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
   &lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
   &lt;artifactId&gt;camel-elasticsearch&lt;/artifactId&gt;
   &lt;version&gt;4.7.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
   &lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
   &lt;artifactId&gt;camel-jackson&lt;/artifactId&gt;
   &lt;version&gt;4.7.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
   &lt;groupId&gt;co.elastic.clients&lt;/groupId&gt;
   &lt;artifactId&gt;elasticsearch-java&lt;/artifactId&gt;
   &lt;version&gt;8.14.3&lt;/version&gt;
&lt;/dependency&gt;
<h3>2. Configuración y ejecución del Camel Context</h3><p>La configuración comienza creando un nuevo contexto Camel usando la clase <code>DefaultCamelContext</code> , que sirve como base para definir y ejecutar rutas. A continuación, configuramos el componente Elasticsearch, que permitirá a Apache Camel interactuar con un clúster de Elasticsearch. La instancia <code>ESlasticsearchComponent</code> está configurada para conectarse a la dirección <code>localhost:9200</code>, que es la dirección predeterminada para un clúster local de Elasticsearch. Para una configuración de entorno que requiera autenticación, deberías leer la documentación sobre cómo configurar el componente y habilitar la autenticación básica, conocida como <strong>"Configurar el componente y habilitar la autenticación básica".</strong></p>public class ESComponent {

    public static ElasticsearchComponent getInstance() {
        var elasticsearch = new ElasticsearchComponent();
        elasticsearch.setHostAddresses("localhost:9200");
        return elasticsearch;
    }

    public static String getName() {
        return "elasticsearch";
    }
}
<p>Este componente se agrega al contexto de Camel, permitiendo que las rutas definidas empleen este componente para realizar operaciones en Elasticsearch.</p>try (var context = new DefaultCamelContext()) {
   context.addComponent(ESComponent.getName(), ESComponent.getInstance());
   context.addRoutes(new OperationBulkRoute());
   context.start();
}
<p>Después, las rutas se agregan al contexto. Crearemos rutas para la indexación masiva, actualización y eliminación de documentos.</p><h3>3. Configuración de rutas Camel</h3><h4>Indexación de datos</h4><p>La primera ruta que configuraremos es para la indexación de datos. Emplearemos un archivo JSON que contiene un catálogo de películas. La ruta se configurará para leer el archivo ubicado en <a href="https://gist.github.com/andreluiz1987/40756874b5fbea0a29586f9376d7f1f4"><code>src/main/resources/movies.json</code></a>, deserializar el contenido JSON en objetos Java y luego aplicar una estrategia de agregación para combinar múltiples mensajes en uno solo, permitiendo operaciones por lotes en Elasticsearch. Se configuró el tamaño de 500 elementos por mensaje, es decir, el volumen indexará 500 películas a la vez.</p><p>Operación de búsqueda elástica de ruta en masa</p>String URI_BULK_OPERATION = String
       .format("elasticsearch://elasticsearch?operation=%s&amp;indexName=%s",
               IndexOperationConfig.BULK_OPERATION,
               INDEX_NAME);
public class OperationBulkRoute extends RouteBuilder {
   private static final Log log = LogFactory.getLog(OperationBulkRoute.class);
   private static final int BULK_SIZE = 500;

   @Override
   public void configure() {
       from("file:src/main/resources?fileName=movies.json&amp;noop=true")
               .routeId("route-bulk-ingest")
               .unmarshal().json()
               .split(body())
               .aggregate(constant(true), new BulkAggregationStrategy())
               .completionSize(BULK_SIZE)
               .to(URI_BULK_OPERATION)
               .process(exchange -&gt; {
                   var body = exchange.getIn().getBody(String.class);
                   log.info(String.format("Response: %s", body));
               })
               .end();
   }
}
<p>El lote de documentos se enviará al endpoint de operaciones masivas de Elasticsearch. Este enfoque garantiza eficiencia y rapidez al manejar grandes volúmenes de datos.</p><h4>Actualización de datos</h4><p>La siguiente opción será actualizar los documentos. Indexamos algunas películas en el paso anterior y ahora crearemos nuevas rutas para buscar un documento por código de referencia y luego actualizar el campo de calificación.</p><p>Configuramos un contexto Camel <code>(DefaultCamelContext)</code>, donde se registra un componente de Elasticsearch y se agrega una ruta personalizada llamada IngestionRoute. La operación comienza enviando el código del documento a través de ProducerTemplate, que inicia la ruta desde el punto final direct:update-ingestión.</p>try (var context = new DefaultCamelContext()) {
    context.addComponent(ESComponent.getName(), ESComponent.getInstance());
    context.addRoutes(new IngestionRoute());
    context.start();
    ProducerTemplate producerTemplate = context.createProducerTemplate();
    producerTemplate.sendBody("direct:update-ingestion", documentCode);
    Thread.sleep(5000);
}
<p>A continuación, tenemos el IngestionRoute, que es el punto final de entrada para este flujo. La ruta realiza varias operaciones por oleoductos. Primero, se realiza una búsqueda en Elasticsearch para localizar el documento por código <code>(direct:search-by-id)</code>, donde el SearchByCodeProcessor ensambla la consulta a partir del código. Después, el documento recuperado es procesado por el UpdateRatingProcessor, que convierte el resultado en objetos Película, actualiza la calificación de la película a un valor específico y prepara el documento actualizado para ser enviado de nuevo a Elasticsearch para su actualización.</p>public class IngestionRoute extends RouteBuilder {
    private static final Log log = LogFactory.getLog(IngestionRoute.class);

    @Override
    public void configure() throws Exception {

        from("direct:update-ingestion")
                .pipeline()
                .to("direct:search-by-id")
                .to(URI_SEARCH_OPERATION)
                .to("direct:update-rating")
                .to(URI_UPDATE_OPERATION)
                .process(exchange -&gt; {
                    var body = exchange.getIn().getBody(String.class);
                    log.info(String.format("Response: %s", body));
                })
                .end();

        from("direct:search-by-id")
                .process(new SearchByCodeProcessor());

        from("direct:update-rating")
                .process(new UpdateRatingProcessor());
    }
}
<p>El procesador <code>SearchByCodeProcessor</code> estaba configurado solo para ejecutar la consulta de búsqueda:</p>public class SearchByCodeProcessor implements Processor {
    @Override
    public void process(Exchange exchange) throws Exception {
        var code = exchange.getIn().getBody();

        String query = "{\n" +
                "  \"query\": {\n" +
                "   \"term\": {\n" +
                "     \"code\": {\n" +
                "       \"value\":" + code + "\n" +
                "     }\n" +
                "   }\n" +
                "  }\n" +
                "}";
        exchange.setProperty("document_code", code);
        exchange.getIn().setBody(query);
    }
}
<p>El procesador <code>UpdateRatingProcessor</code> es responsable de actualizar el campo de calificación.</p>public class UpdateRatingProcessor implements Processor {

    private final ObjectMapper objectMapper;

    public UpdateRatingProcessor() {
        this.objectMapper = new ObjectMapper();
        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Override
    public void process(Exchange exchange) throws Exception {

        HitsMetadata response = exchange.getIn().getBody(HitsMetadata.class);
        var code = Long.parseLong(exchange.getProperty("document_code").toString());

        if (response != null &amp;&amp; response.hits() != null) {

            var documents = parseToMovies(response);

            var optionalMovie = documents.stream()
                    .filter(document -&gt; code == (document.getSource().getCode())).findAny();

            optionalMovie.ifPresent(document -&gt; {
                document.getSource().setRating(13.0);
                Map&lt;String, Object&gt; updateMap = new HashMap&lt;&gt;();
                updateMap.put("doc", document.getSource());
                exchange.getIn().setHeader("indexId", document.getId());
                exchange.getIn().setBody(updateMap);
            });
        }
    }
<h4>Eliminación de datos</h4><p>Finalmente, la ruta para eliminar documentos está configurada. Aquí, eliminaremos un documento usando su ID. En Elasticsearch, para eliminar un documento necesitamos conocer el identificador del documento, el índice donde se almacena el documento y ejecutar una solicitud de borrado. En Apache Camel realizaremos esta operación creando una nueva ruta como se muestra a continuación.</p><p>La ruta comienza desde el punto final direct:op-delete, que sirve como punto de entrada. Cuando un documento necesita ser eliminado, su identificador <code>(_id)</code> se recibe en el cuerpo del mensaje. La ruta entonces establece el encabezado indexId con el valor de este identificador usando<code>("${body}")</code>simple , que extrae el _id del cuerpo del mensaje.</p>public class OperationDeleteRoute extends RouteBuilder {
   private static final Log log = LogFactory.getLog(OperationDeleteRoute.class);

   @Override
   public void configure() {
       from("direct:op-delete")
               .routeId("route-delete")
               .setHeader("indexId", simple("${body}"))
               .to(URI_DELETE_OPERATION)
               .process(exchange -&gt; {
                   var body = exchange.getIn().getBody(String.class);
                   log.info(String.format("Response: %s", body));
               })
               .end();
       ;
   }
}
String URI_DELETE_OPERATION = String
       .format("elasticsearch://elasticsearch?operation=%s&amp;indexName=%s",
               IndexOperationConfig.DELETE_OPERATION,
               INDEX_NAME);
<p>Finalmente, el mensaje se dirige al punto final especificado por URI_DELETE_OPERATION, que se conecta con Elasticsearch para realizar la operación de eliminación de documentos en el índice correspondiente.
Ahora que creamos la ruta, podemos crear un contexto Camel <code>(DefaultCamelContext)</code>, que está configurado para incluir el componente Elasticsearch.</p>try (var context = new DefaultCamelContext()) {
   context.addComponent(ESComponent.getName(), ESComponent.getInstance());
   context.addRoutes(new OperationDeleteRoute());
   context.start();
   ProducerTemplate producerTemplate = context.createProducerTemplate();
   producerTemplate.sendBody("direct:op-delete", documentId);
}
<p>A continuación, se agrega la ruta de borrado, definida por la clase <code>OperationDeleteRoute</code> , al contexto. Con el contexto inicializado, se emplea un <code>ProducerTemplate</code> para pasar el identificador del documento que debe eliminar al punto final <code>direct:op-delete</code> , lo que activa la ruta de eliminación.</p><h2>Conclusión</h2><p>La integración entre Apache Camel y Elasticsearch permite una ingesta de datos robusta y eficiente, aprovechando la flexibilidad de Camel para definir rutas que pueden manejar diferentes escenarios de manipulación de datos, como indexación, actualización y eliminación. Con esta configuración, puedes orquestar y automatizar procesos complejos de forma escalable, cerciorando que tus datos se gestionen de forma eficiente en Elasticsearch. Este ejemplo demostró cómo estas herramientas pueden emplear juntas para crear una solución eficiente y adaptable para la ingestión de datos.</p><h2>Referencias</h2><ul><li><p><a href="https://camel.apache.org/manual/">Camello apache</a></p></li><li><p><a href="https://camel.apache.org/manual/architecture.html">Arquitectura del camello apache</a></p></li><li><p><a href="https://camel.apache.org/components/4.4.x/eips/aggregate-eip.html">Camello apache agregado</a></p></li><li><p><a href="https://camel.apache.org/components/4.4.x/file-component.html">Componente de archivo</a></p></li><li><p><a href="https://camel.apache.org/components/4.4.x/elasticsearch-component.html">Componente Elasticsearch</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-apache-camel-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-apache-camel-ingest-data</guid>
    <category><![CDATA[Datos de índice]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt05652327efd4d5d1/6a17e6dafbc5f8a588491a8b/bef8145623a8fa80f929f9faa57ce0c460be2d0b-884x458.png" length="0" type="image/png"/>
    <pubDate>Mon, 09 Sep 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>