<?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[Ruby - 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[Ruby - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/pt/search-labs/blog/category/ruby-programming</link>
    </image>
    <link>https://www.elastic.co/pt/search-labs/blog/category/ruby-programming</link>
    <atom:link href="https://www.elastic.co/pt/search-labs/rss/category/ruby-programming.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[pt]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 15:07:25 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Apresentamos o construtor de consultas ES|QL para o cliente Ruby do Elasticsearch.]]></title>
    <description><![CDATA[Aprenda a usar o construtor de consultas ES|QL recém-lançado para o cliente Ruby do Elasticsearch. Uma ferramenta para criar consultas ES|QL mais facilmente com código Ruby.]]></description>
    <content:encoded><![CDATA[<p>Recentemente lançamos <a href="https://github.com/elastic/esql-ruby/"><code>elastic-esql</code></a>, uma gem Ruby publicada sob a licença Apache 2. Esta gem permite que você crie consultas <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES|QL</a> da Elastic em Ruby idiomático, que você pode então usar com a API de consulta ES|QL. O ES|QL permite que os desenvolvedores filtrem, transformem e analisem dados armazenados no Elasticsearch por meio de consultas. Ele usa "pipes" ( <code>|</code> ) para trabalhar com os dados passo a passo. A gem usa funções Ruby, que você pode encadear ao objeto original para construir consultas mais complexas:</p><p><strong>ESQL:</strong></p><p><strong>Rubi:</strong></p>Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending<h2>Instalação</h2><p>A gem pode ser instalada a partir do RubyGems com o seguinte comando:</p>gem install elastic-esql<p>Ou pode ser adicionado ao Gemfile de um projeto:</p>gem 'elastic-esql'<h2>Uso</h2><p>Você pode construir uma consulta completa de uma só vez ou criar um objeto de consulta com um comando de origem como <code>from</code> ou <code>row</code> e, em seguida, encadear métodos ES|QL para construí-lo.</p>query = Elastic::ESQL.from('sample_data')
query.limit(2).sort('@timestamp')<p>A gem traduz o código para ES|QL no método <code>to_s</code> , portanto, retorna a consulta ES|QL quando é impressa ou convertida em uma String:</p>query = Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending
query.to_s
# =&gt; "FROM sample_data | LIMIT 2 | SORT @timestamp DESC"<p>Você pode instanciar um objeto de consulta e modificar seu estado inicial usando os equivalentes <code>!</code> de cada função:</p>query = Elastic::ESQL.from('sample_data')
query.to_s
# =&gt; "FROM sample_data"
query.limit!(2).sort!('@timestamp')
query.to_s
# =&gt; "FROM sample_data | LIMIT 2 | SORT @timestamp"<p>A ferramenta fornece maneiras convenientes de encadear etapas extras a uma função ES|QL, como <code>enrich</code> e <code>sort</code>. Depois de chamar <code>enrich</code> em um objeto <code>Elastic::ESQL</code> , você pode encadear <code>on</code> e <code>with</code> a ele:</p>esql.enrich!('policy').on('a').with({ name: 'language_name' })<p>Você também pode encadear <code>desc</code>, <code>asc</code>, <code>nulls_first</code> e <code>nulls_last</code> à sua consulta após usar <code>sort</code>:</p>Elastic::ESQL.from('sample_data').sort('@timestamp').asc.to_s
# =&gt; 'FROM sample_data | SORT @timestamp ASC'

Elastic::ESQL.from('sample_data').sort('@timestamp').desc.nulls_first.to_s
# =&gt; 'FROM sample_data | SORT @timestamp DESC NULLS FIRST'<p>Também oferece suporte a strings personalizadas, caso você queira escrever a consulta ES|QL por conta própria ou usar um recurso que ainda não foi adicionado à biblioteca. <code>custom</code> irá unir as strings no final da consulta. Isso os adicionará conforme forem enviados para a função, sem adicionar nenhum caractere de barra vertical. Eles serão combinados ao restante da consulta por um caractere de espaço.</p>esql = Elastic::ESQL.from('sample_data')
esql.custom('| MY_VALUE = "test value"').to_s
# =&gt; 'FROM sample_data | MY_VALUE = "test value"'<p>Você também pode encadear funções <code>custom</code> :</p>esql.custom('| MY_VALUE = "test value"').custom('| ANOTHER, VALUE')
'FROM sample_data | MY_VALUE = "test value" | ANOTHER, VALUE'<h2>Utilizando o Construtor de Consultas ES|QL com o cliente Ruby</h2><p>Você pode usar o construtor de consultas diretamente com <a href="https://github.com/elastic/elasticsearch-ruby">elasticsearch-ruby</a> e a API <code>esql.query</code> enviando o objeto de consulta:</p>require 'elasticsearch'
require 'elastic/esql'

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

query = Elastic::ESQL.from(index)
                     .sort('@timestamp')
                     .desc
                     .where('event_duration &gt; 5000000')
                     .limit(3)
                     .eval({ duration_ms: 'ROUND(event_duration/1000000.0, 1)' })
client.esql.query(body: { query: query })<p>Você também pode usá-lo com o auxiliar ES|QL do cliente Ruby do Elasticsearch. <a href="https://www.elastic.co/search-labs/blog/esql-ruby-helper-elasticsearch">Saiba mais</a>:</p>require 'elasticsearch/helpers/esql_helper'

Elasticsearch::Helpers::ESQLHelper.query(client, query)<h2>Como uma ferramenta independente</h2><p>A gem foi projetada como uma ferramenta independente para construir consultas ES|QL de forma idiomática. Não possui dependências de tempo de execução; você pode usá-lo com o cliente oficial do Elasticsearch para Ruby ou de forma independente.</p><p>A consulta gerada pode ser usada com a API <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-esql-query"><code>esql.query</code></a> de qualquer forma que um aplicativo interaja com a API do Elasticsearch (Ruby ou não). Uma vez que uma consulta é construída com <code>elastic-esql</code>, a String gerada pode ser enviada para a API como o parâmetro <code>query</code> no corpo da solicitação. </p><p>Anteriormente, escrevi sobre <a href="https://www.elastic.co/search-labs/blog/elasticsearch-ruby-tools">como usar o Elasticsearch com ferramentas populares do Ruby</a>. Esta gem pode ser usada com qualquer uma das ferramentas populares do Ruby para consultar o Elasticsearch com ES|QL.</p><h2>Conclusão</h2><p>Esta biblioteca está em desenvolvimento ativo e a API final ainda não foi concluída. Atualmente, está disponível em versão de pré-visualização técnica. Se você tiver algum comentário sobre a API atual ou sobre o uso em geral, não hesite em <a href="https://github.com/elastic/esql-ruby/issues">abrir uma nova solicitação</a>. Consulte <a href="https://github.com/elastic/esql-ruby/?tab=readme-ov-file#ruby-esql-query-builder">o arquivo README</a> para saber mais sobre o construtor de consultas Ruby ES|QL.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-query-builder-elasticsearch-ruby-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-query-builder-elasticsearch-ruby-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Ruby]]></category>
    <dc:creator><![CDATA[Fernando Briano]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt85d112ccca541b9e/6a17dccb4b055d6bfd4320cc/f8e1263ab53d356824a4fc539084151be80899db-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Scripting em Ruby no Logstash]]></title>
    <description><![CDATA[Saiba mais sobre o plugin de filtro Ruby para Logstash, que permite a transformação avançada de dados em seu pipeline Logstash.]]></description>
    <content:encoded><![CDATA[<p>O Logstash é um pipeline de processamento de dados que ingere dados de múltiplas fontes, os transforma e os envia para os destinos escolhidos. Os plugins de filtro são essenciais para esse processo; eles executam operações específicas nos seus dados à medida que estes percorrem o pipeline.</p><p>O Logstash inclui diversos filtros integrados para tarefas comuns, como analisar, enriquecer e modificar dados. Mas, às vezes, você encontrará cenários que exigem lógica personalizada que vai além do que esses filtros padrão podem fornecer. É aí que entra o <a href="https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-ruby">plugin de filtro Ruby</a> .</p><p><strong>O plugin de filtro Ruby permite executar código Ruby personalizado diretamente no seu pipeline Logstash.</strong> Quando os filtros padrão não são suficientes, o filtro Ruby permite lidar com transformações de dados complexas, implementar lógica de negócios personalizada ou integrar-se a sistemas externos.</p><p>Neste blog, vamos explorar como usar filtros em Ruby, desde o uso básico até o avançado.</p><h2>Quando você deve usar o filtro Ruby?</h2><p>Como arquiteto consultor da Elastic, vejo frequentemente clientes usando o Logstash para o pipeline de processamento de dados, embora atualmente ele não seja um mecanismo de processamento de dados de última geração. Eles frequentemente enfrentam dificuldades com as limitações dos filtros padrão quando se trata de manipulação complexa de dados ou lógica personalizada. Nesses casos, o filtro Ruby pode ajudar a superar esses desafios.</p><p>O filtro Ruby é útil quando os filtros padrão do Logstash não atendem às suas necessidades específicas. Aqui estão alguns casos de uso comuns:</p><ul><li><p><strong>Manipulação de dados aninhados em profundidade</strong>: Modifique estruturas JSON complexas, arrays dentro de arrays ou reestruture dados dinamicamente com base no conteúdo.</p></li><li><p><strong>Processamento avançado de strings</strong>: Analise e extraia dados estruturados de textos não estruturados.</p></li><li><p><strong>Implementação de lógica de negócios complexa</strong>: Crie transformações personalizadas que exigem lógica condicional, loops ou cálculos complexos.</p></li></ul><h2>Uso básico</h2><p>Vamos começar com um exemplo simples para entender como funciona o filtro Ruby.</p><h3>Configurando o filtro Ruby</h3><p>Ao criar um pipeline Logstash, você deve colocar o arquivo de configuração no diretório <code>/etc/logstash/conf.d</code> . Alternativamente, você pode usar a opção <code>-f</code> para especificar o caminho para o arquivo de configuração ao iniciar o Logstash manualmente, para que você possa experimentar seus pipelines facilmente.</p>$ ./bin/logstash -f /path/to/your_pipeline.conf<p>O arquivo de configuração deve ter a extensão <code>.conf</code> .</p><p>Para usar o filtro Ruby, defina um filtro <code>ruby</code> na seção de filtro do seu arquivo de configuração do pipeline Logstash (*.conf). Eis um exemplo básico:</p>filter {
  ruby {
    code =&gt; "
      event.set('new_field', 'Hello from Ruby!')
    "
  }
}<p>Este filtro Ruby embutido define uma instância de filtro Ruby dentro da sua configuração do Logstash. O parâmetro <code>code</code> fornece o script Ruby embutido que o Logstash executará para cada evento processado por este filtro. Dentro desse script, há uma variável <code>event</code> disponível que representa o próprio evento. O objeto de evento contém os dados originais enviados ao Logstash e quaisquer campos adicionais criados durante os estágios de filtragem do Logstash. Você pode acessar esses campos por meio da API de Eventos do Logstash, como <code>event.get()</code> e <code>event.set()</code>. Neste exemplo de código, <code>event.set('new_field', 'Hello from Ruby!')</code> definiu um novo campo chamado <code>new_field</code> com o valor da string <code>Hello from Ruby!</code>. Você pode adicionar qualquer outro código neste bloco <code>code</code> conforme necessário.</p><p>Note que este objeto <code>event</code> não é um objeto hash comum do Ruby, embora funcione como um contêiner de dados do tipo chave-valor. Consulte <a href="https://www.elastic.co/docs/reference/logstash/event-api">esta documentação oficial</a> para saber mais sobre a API de Eventos.</p><h3>Externalizar script Ruby</h3><p>Para transformações simples, o código Ruby embutido é conveniente. Porém, para lógica complexa ou funções reutilizáveis, recomenda-se mover o código para um script Ruby externo. Isso melhora a capacidade de manutenção e mantém a configuração do seu pipeline Logstash organizada.</p><p>Primeiro, crie um script Ruby e salve-o como <code>my_ruby_script.rb</code>. O script deve definir um método <code>filter</code> que processa o evento. Ela recebe um objeto de evento como argumento, que representa o evento atual que está sendo processado. O método <code>filter</code> precisa retornar uma matriz de eventos para emitir. Para descartar o evento, retorne um array vazio.</p><p>Por exemplo, o seguinte script lê o campo <code>message</code> , calcula seu comprimento e armazena o resultado em um novo campo chamado <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>Em seguida, defina a configuração do filtro Ruby para referenciar o script usando a opção <code>path</code> . Isso instrui o Logstash a carregar e executar o script externo. Ao usar scripts externos, certifique-se de que o arquivo existe e possui as permissões corretas.</p>filter {
  ruby {
    path =&gt; "/path/to/my_ruby_script.rb"
  }
}<p>Agora, cada evento é passado para o método <code>filter</code> em <code>my_ruby_script.rb</code> e é processado por ele.</p><p>Essa abordagem ajuda você a gerenciar lógicas complexas com mais eficiência, facilitando o teste, a depuração e a reutilização do seu código Ruby.</p><h2>Uso avançado</h2><p>Nesta seção, exploraremos alguns exemplos avançados de uso do filtro Ruby no Logstash. Estes exemplos demonstrarão como realizar transformações de dados, enriquecer eventos e implementar lógica personalizada usando Ruby.</p><h3>Manipulando estruturas de dados aninhadas</h3><p>Um evento do Logstash é a estrutura de dados principal que o Logstash processa. Pode conter diversos campos, incluindo estruturas de dados aninhadas, como arrays e hashes. O filtro Ruby permite manipular essas estruturas aninhadas com facilidade.</p><p>O filtro Ruby consegue lidar com estruturas de dados aninhadas, como hashes e arrays, permitindo que você modifique ou adicione campos dentro dessas estruturas. Isso é útil ao lidar com formatos de dados complexos 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 exemplo inclui um objeto JSON aninhado nos dados de entrada. O filtro Ruby modifica os dados aninhados adicionando um novo par chave-valor. Esse tipo de manipulação de dados aninhados não é possível com os filtros padrão do Logstash, o que torna o filtro Ruby uma opção prática para estruturas de dados complexas.</p><h3>Dividir um único evento em vários eventos.</h3><p>Os filtros Ruby também podem ser usados para dividir um único evento em vários eventos. Isso é útil quando você tem um único evento contendo uma matriz de itens e deseja criar eventos separados para cada item.</p><p>Note que nem o pipeline de ingestão do Elasticsearch nem os processadores do Beats/Elastic Agent suportam a divisão de eventos. Este é um dos casos de uso mais fortes para o Logstash.</p><h4>Com filtro dividido</h4><p>Você pode usar o filtro <code>split</code> para dividir um evento em vários eventos com base em um campo especificado. No entanto, se precisar realizar transformações ou lógicas adicionais durante a divisão, você pode usar o filtro Ruby em combinação com o filtro de divisão.</p><p>No exemplo a seguir, temos um feed RSS como uma única linha de texto XML. Contém múltiplos elementos <code>&lt;item&gt;</code> . O filtro Ruby é usado para extrair os elementos <code>&lt;item&gt;</code> do XML e armazená-los em um novo campo chamado <code>items</code>. O filtro de divisão é então usado para dividir o evento em vários eventos com base no 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>Isso resultará no seguinte:</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 você deve ter percebido, o filtro <code>ruby</code> não é essencial neste caso. O filtro <code>split</code> pode ser usado para dividir o evento em vários eventos com base no campo <code>items</code> e o filtro <code>mutate</code> pode ser usado para remover campos desnecessários. No entanto, se você precisar realizar transformações ou lógicas adicionais durante a divisão, poderá usar o filtro Ruby.</p><h4>Use script Ruby embutido</h4><p>Você também pode usar um script Ruby embutido para dividir um único evento em vários eventos usando o método <code>event.clone</code> e o <code>new_event_block variable</code>, como <code>new_event_block.call(new_event)</code>. Isso permite criar novos eventos com base no evento original, preservando seus dados.</p><p>Aqui está um exemplo de como usar o filtro Ruby para dividir um único evento em vários eventos. A entrada e a saída são as mesmas do exemplo 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>Utilizar script Ruby externo</h4><p>Você também pode usar um script Ruby externo para dividir um único evento em vários eventos.</p><p>Arquivo de configuração:</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>O script Ruby precisa ser externalizado 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>Lembre-se, o método <code>filter</code> deve retornar uma matriz de eventos. Você pode retornar vários eventos clonando um objeto de evento recebido e adicionando-os à matriz, ou pode retornar um único evento como uma matriz com um único elemento.</p>return events
# or
# return [event]<p>Isso permite dividir um único evento em vários eventos.</p><h3>Executar comandos externos e analisar seus resultados.</h3><p>O plugin Logstash exec input permite executar comandos externos, e a saída desses comandos será um evento do Logstash. O resultado do comando será armazenado no campo <code>message</code> do evento.</p><p>Normalmente, a saída dos comandos do sistema é legível para humanos, mas não está estruturada em JSON ou outros formatos que o Logstash possa analisar facilmente. Para lidar com isso, você pode usar o filtro Ruby para analisar a saída e extrair as informações dela.</p><p>Aqui está um exemplo de uso do plugin de entrada <code>exec</code> para executar o comando <code>ps -ef</code> , que lista todos os processos em execução em um sistema do tipo Unix. A saída será analisada pelo filtro Ruby para extrair informações relevantes sobre cada processo.</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 exemplo usa o plugin de entrada <code>exec</code> para executar o comando <code>ps -ef</code> a cada 60 segundos. O filtro Ruby processa a saída, extraindo campos relevantes como UID, PID, PPID, uso da CPU (C), hora de início (STIME), TTY, tempo total da CPU (TIME) e o comando (CMD) executado. Funciona bem no meu ambiente macOS, mas você pode precisar ajustar os padrões regex para corresponder ao formato de saída do comando <code>ps -ef</code> no seu sistema.</p><h3>Utilize bibliotecas integradas</h3><p>O plugin de filtro Ruby permite usar bibliotecas Ruby integradas, o que pode ser muito útil para diversas tarefas. Por exemplo, você pode usar a biblioteca <code>json</code> para analisar strings JSON ou a biblioteca <code>date</code> para manipular datas.</p><p>Aqui está um exemplo de como usar a biblioteca <code>json</code> para analisar uma string JSON armazenada em um 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 exigir a biblioteca sempre, você deve externalizar seu código Ruby para que possa usar a instrução <code>require</code> no início do seu script de filtro Ruby. Isso carregará a biblioteca uma única vez e a tornará disponível para uso em seu script.</p><p>Para verificar quais bibliotecas estão disponíveis em seu ambiente, você pode listar as bibliotecas integradas executando o seguinte código no filtro Ruby:</p>Gem.loaded_specs.sort_by { |name, _| name }.each do |name, spec|
  puts "#{name}: #{spec.version}"
end<p><strong>Observação: </strong>as bibliotecas integradas não são oficialmente suportadas pelo Logstash e seu comportamento pode mudar ou elas podem não estar disponíveis em versões futuras. Use-os por sua conta e risco.</p><h2>Conclusão</h2><p>O filtro Logstash Ruby permite personalizar e ampliar as funcionalidades dos seus pipelines Logstash. Neste post, abordamos os conceitos básicos do uso do filtro Ruby e fornecemos exemplos de uso avançado.</p><p>Ao utilizar o filtro Ruby, você pode lidar com tarefas complexas de processamento de dados que exigem lógica personalizada ou manipulação avançada. Seja trabalhando com estruturas de dados aninhadas, dividindo eventos ou analisando e convertendo texto complexo/não estruturado em JSON estruturado, o filtro Ruby oferece a flexibilidade necessária para atender às suas necessidades específicas.</p><p>Esperamos que este guia tenha lhe fornecido o conhecimento e a inspiração necessários para explorar todo o potencial do filtro Logstash Ruby. Boa programação!</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[Dados de indexação]]></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[Como migrar seu aplicativo Ruby do OpenSearch para o Elasticsearch]]></title>
    <description><![CDATA[Um guia para migrar um código-fonte Ruby do cliente OpenSearch para o cliente Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>O cliente Ruby do OpenSearch foi derivado do cliente Ruby do Elasticsearch na versão <code>7.x</code>, portanto, as bases de código são relativamente semelhantes. Isso significa que, ao migrar um código-fonte Ruby do OpenSearch para o Elasticsearch, o código das respectivas bibliotecas de cliente parecerá muito familiar. Neste post do blog, vou mostrar um exemplo de aplicativo Ruby que usa o OpenSearch e os passos para migrar esse código para o Elasticsearch.</p><p>Ambos os clientes são distribuídos sob a popular Licença Apache 2.0, sendo, portanto, software livre e de código aberto. A licença do Elasticsearch foi atualizada recentemente e o núcleo do Elasticsearch e do Kibana são publicados sob a licença de código aberto AGPL, aprovada pela OSI, desde a versão 8.16.</p><h2>Considerando a versão do Elasticsearch ao migrar um aplicativo Ruby.</h2><p>Um aspecto a considerar na migração é qual versão do Elasticsearch será utilizada. Recomendamos usar a versão estável mais recente, que no momento em que este texto foi escrito é <code>8.17.0</code>. As versões secundárias do cliente Ruby do Elasticsearch seguem as versões secundárias do Elasticsearch. Então, para o Elasticsearch <code>8.17.x</code>, você pode usar a versão <code>8.17.x</code> da gem Ruby.</p><p>O OpenSearch foi derivado do Elasticsearch 7.10.2. Assim, as APIs podem ter mudado e recursos diferentes podem ser usados em qualquer uma delas. Mas isso está fora do escopo desta postagem, e vou analisar apenas as operações mais comuns em um aplicativo de exemplo.</p><p>Para Ruby on Rails, você pode usar o cliente oficial do Elasticsearch ou as <a href="https://github.com/elastic/elasticsearch-rails/">bibliotecas de integração do Rails</a>. Recomendamos a migração para a versão estável mais recente do Elasticsearch e do cliente, respectivamente. A versão <code>elasticsearch-rails</code> da gem <code>8.0.0</code> suporta Rails <code>6.1</code>, <code>7.0</code> e <code>7.1</code> e Elasticsearch <code>8.x</code>.</p><h2>O código</h2><p>Neste exemplo, segui os passos para <a href="https://opensearch.org/docs/latest/install-and-configure/install-opensearch/tar/">instalar o OpenSearch a partir de um arquivo tar</a>. Após baixar e extrair o arquivo tar, precisei definir uma senha de administrador inicial, que usarei posteriormente para instanciar o cliente.</p><p>Criei um diretório com um <code>Gemfile</code> que se parece com isto:</p>source 'https://rubygems.org'

gem 'opensearch-ruby'<p>Após executar <code>bundle install</code>, a gem é instalada para o meu projeto. Esta versão do opensearch-ruby instalada é <code>3.4.0</code> e a versão do OpenSearch que estou executando é <code>2.18.0</code>. Eu escrevi o código em um arquivo <code>example_code.rb</code> no mesmo diretório. O código inicial neste arquivo é a instanciação de um cliente OpenSearch:</p>require 'opensearch'

client = OpenSearch::Client.new(
  host: 'https://localhost:9200',
  user: 'admin',
  password: ENV['OPENSEARCH_INITIAL_ADMIN_PASSWORD'],
  transport_options: { ssl: { verify: false } }
)<p>O parâmetro de opção de transporte <code>ssl: { verify: false}</code> está sendo passado conforme o guia do usuário para facilitar os testes. Em produção, isso deve ser configurado dependendo da implantação do OpenSearch.</p><p>A partir da versão 2.12.0 do OpenSearch, a variável de ambiente <code>OPENSEARCH_INITIAL_ADMIN_PASSWORD</code> deve ser definida com uma senha forte ao executar o script de instalação. Seguindo os passos para instalar o OpenSearch a partir de um arquivo tar.gz, exportei a variável no meu console e agora ela está disponível para o meu script Ruby.</p><p>Uma API simples para garantir que o cliente esteja se conectando ao OpenSearch é usar a API <code>cluster.health</code> :</p>puts 'HEALTH:'
pp client.cluster.health<p>E de fato funciona:</p>$ be ruby example_code.rb
HEALTH:
{"cluster_name"=&gt;"opensearch",
"status"=&gt;"yellow",
 "timed_out"=&gt;false,
 "number_of_nodes"=&gt;1,
 "number_of_data_nodes"=&gt;1,<p>Testei alguns dos exemplos comuns que temos na documentação do cliente Ruby do Elasticsearch e eles funcionaram conforme o esperado:</p>index = 'books'
puts 'Creating index'
response = client.indices.create(index: index)
puts response
# Creating index
# {"acknowledged"=&gt;true, "shards_acknowledged"=&gt;true, "index"=&gt;"books"}

puts 'Indexing a document'
document = { title: 'The Time Machine', author: 'H. G. Wells', year: 1895 }
response = client.index(index: index, body: document, refresh: true)
puts response
# Indexing document
# {"_index"=&gt;"books", "_id"=&gt;"esalT5MB4vnuJz5TtqOc", "_version"=&gt;1, "result"=&gt;"created", "forced_refresh"=&gt;true, "_shards"=&gt;{"total"=&gt;2, "successful"=&gt;1, "failed"=&gt;0}, "_seq_no"=&gt;0, "_primary_term"=&gt;1}

id = response['_id']
puts 'Getting document'
response = client.get(index: index, id: id)
puts response
# Getting document
# {"_index"=&gt;"books", "_id"=&gt;"esalT5MB4vnuJz5TtqOc", "_version"=&gt;1, "_seq_no"=&gt;0, "_primary_term"=&gt;1, "found"=&gt;true, "_source"=&gt;{"title"= &gt;"The Time Machine", "author"=&gt;"H. G. Wells", "year"=&gt;1895}}

puts "Does an index exist?"
puts client.indices.exists(index: 'imaginary_index')
# Does an index exist?
# false

puts 'Processing Bulk request'
body = [
  { index: { _index: 'books', data: { name: 'Leviathan Wakes', author: 'James S.A. Corey', release_date: '2011-06-02', page_count: 561 } } },
  { index: { _index: 'books', data: { name: 'Hyperion', author: 'Dan Simmons', release_date: '1989-05-26', page_count: 482 } } },
  { index: { _index: 'books', data: { name: 'Dune', author: 'Frank Herbert', release_date: '1965-06-01', page_count: 604 } } },
  { index: { _index: 'books', data: { name: 'Dune Messiah', author: 'Frank Herbert', release_date: '1969-10-15', page_count: 331 } } },
  { index: { _index: 'books', data: { name: 'Children of Dune', author: 'Frank Herbert', release_date: '1976-04-21', page_count: 408 } } },
  { index: { _index: 'books', data: { name: 'God Emperor of Dune', author: 'Frank Herbert', release_date: '1981-05-28', page_count: 454 } } },
  { index: { _index: 'books', data: { name: 'Consider Phlebas', author: 'Iain M. Banks', release_date: '1987-04-23', page_count: 471 } } },
  { index: { _index: 'books', data: { name: 'Pandora\'s Star', author: 'Peter F. Hamilton', release_date: '2004-03-02', page_count: 768 } } },
  { index: { _index: 'books', data: { name: 'Revelation Space', author: 'Alastair Reynolds', release_date: '2000-03-15', page_count: 585 } } },
  { index: { _index: 'books', data: { name: 'A Fire Upon the Deep', author: 'Vernor Vinge', release_date: '1992-06-01', page_count: 613 } } },
  { index: { _index: 'books', data: { name: 'Ender\'s Game', author: 'Orson Scott Card', release_date: '1985-06-01', page_count: 324 } } },
  { index: { _index: 'books', data: { name: '1984', author: 'George Orwell', release_date: '1985-06-01', page_count: 328 } } },
  { index: { _index: 'books', data: { name: 'Fahrenheit 451', author: 'Ray Bradbury', release_date: '1953-10-15', page_count: 227 } } },
  { index: { _index: 'books', data: { name: 'Brave New World', author: 'Aldous Huxley', release_date: '1932-06-01', page_count: 268 } } },
  { index: { _index: 'books', data: { name: 'Foundation', author: 'Isaac Asimov', release_date: '1951-06-01', page_count: 224 } } },
  { index: { _index: 'books', data: { name: 'The Giver', author: 'Lois Lowry', release_date: '1993-04-26', page_count: 208 } } },
  { index: { _index: 'books', data: { name: 'Slaughterhouse-Five', author: 'Kurt Vonnegut', release_date: '1969-06-01', page_count: 275 } } },
  { index: { _index: 'books', data: { name: 'The Hitchhiker\'s Guide to the Galaxy', author: 'Douglas Adams', release_date: '1979-10-12', page_count: 180 } } },
  { index: { _index: 'books', data: { name: 'Snow Crash', author: 'Neal Stephenson', release_date: '1992-06-01', page_count: 470 } } },
  { index: { _index: 'books', data: { name: 'Neuromancer', author: 'William Gibson', release_date: '1984-07-01', page_count: 271 } } },
  { index: { _index: 'books', data: { name: 'The Handmaid\'s Tale', author: 'Margaret Atwood', release_date: '1985-06-01', page_count: 311 } } },
  { index: { _index: 'books', data: { name: 'Starship Troopers', author: 'Robert A. Heinlein', release_date: '1959-12-01', page_count: 335 } } },
  { index: { _index: 'books', data: { name: 'The Left Hand of Darkness', author: 'Ursula K. Le Guin', release_date: '1969-06-01', page_count: 304 } } },
  { index: { _index: 'books', data: { name: 'The Moon is a Harsh Mistress', author: 'Robert A. Heinlein', release_date: '1966-04-01', page_count: 288 } } }
]
puts client.bulk(body: body, refresh: true)
# Processing Bulk request
# {"took"=&gt;38, "errors"=&gt;false, "items"=&gt;[{"index"=&gt;{"_index"=&gt;"books", "_id"=&gt;" ...

query = { query: { multi_match: { query: 'dune', fields: ['name'] } } }
puts 'Search results'
response = client.search(index: index, body: query)
puts response
# Search results
# {"_index"=&gt;"books", "_id"=&gt;"oEawT5MBOXHuGXdEu5Wu", "_score"=&gt;2.2886353, "_source"=&gt;{"name"=&gt;"Dune", "author"=&gt;"Frank Herbert", "release_date"=&gt;"1965-06-01", "page_count"=&gt;604}}
# {"_index"=&gt;"books", "_id"=&gt;"oUawT5MBOXHuGXdEu5Wu", "_score"=&gt;1.8893257, "_source"=&gt;{"name"=&gt;"Dune Messiah", "author"=&gt;"Frank Herbert", "release_date"=&gt;"1969-10-15", "page_count"=&gt;331}}
# {"_index"=&gt;"books", "_id"=&gt;"okawT5MBOXHuGXdEu5Wu", "_score"=&gt;1.6086557, "_source"=&gt;{"name"=&gt;"Children of Dune", "author"=&gt;"Frank Herbert", "release_date"=&gt;"1976-04-21", "page_count"=&gt;408}}
# {"_index"=&gt;"books", "_id"=&gt;"o0awT5MBOXHuGXdEu5Wu", "_score"=&gt;1.40059, "_source"=&gt;{"name"=&gt;"God Emperor of Dune", "author"=&gt;"Frank Herbert", "release_date"=&gt;"1981-05-28", "page_count"=&gt;454}}

puts 'Updating document'
document = { title: 'Walkaway', author: 'Cory Doctorow', release_date: '2017' }
response = client.index(index: index, body: document, refresh: true)
id = response['_id']
response = client.update(index: index, id: id, body: { doc: { release_date: '2017-04-26' } })
puts response
# Updating document
# {"_index"=&gt;"books", "_id"=&gt;"degnZJMBIGr4X0Yim55L", "_version"=&gt;2, "result"=&gt;"updated", "_shards"=&gt;{"total"=&gt;2, "successful"=&gt;1, "failed"=&gt;0}, "_seq_no"=&gt;26, "_primary_term"=&gt;1}

puts 'Retrieveing multiple documents'
response = client.search(index: index, body: { query: { match_all: {} }, size: 3, stored_fields: '_id' })
ids = response['hits']['hits']
ids.map { |a| a.delete('_score') }
response = client.mget(body: { docs: [{ _index: index, _id: ids }] })
puts response
# Retrieveing multiple documents
# {"docs"=&gt;[{"_index"=&gt;"books", "_id"=&gt;"qeg2ZJMBIGr4X0YiiqD2", "_version"=&gt;1, "_seq_no"=&gt;0, "_primary_term"=&gt;1, "found"=&gt;true, "_source"=&gt;{"title"=&gt;"The Time Machine", "author"=&gt;"H. G. Wells", "year"=&gt;1895}}, {"_index"=&gt;"books", "_id"=&gt;"q-g2ZJMBIGr4X0Yii6Ah", "_version"=&gt;1, "_seq_no"=&gt;1, "_primary_term"=&gt;1, "found"=&gt;true, "_source"=&gt;{"name"=&gt;"Leviathan Wakes", "author"=&gt;"James S.A. Corey", "release_date"=&gt;"2011-06-02", "page_count"=&gt;561}}, {"_index"=&gt;"books", "_id"=&gt;"rOg2ZJMBIGr4X0Yii6Ah", "_version"=&gt;1, "_seq_no"=&gt;2, "_primary_term"=&gt;1, "found"=&gt;true, "_source"=&gt;{"name"=&gt;"Hyperion", "author"=&gt;"Dan Simmons", "release_date"=&gt;"1989-05-26", "page_count"=&gt;482}}]}

puts "Count #{client.count(index: index)['count']}"
puts 'Deleting by query'
response = client.delete_by_query(index: index, body: { query: { match: { author: 'Robert A. Heinlein' } } }, refresh: true)
puts response
puts "Count #{client.count(index: index)['count']}"
# Count 26
# Deleting by query
# {"took"=&gt;16, "timed_out"=&gt;false, "total"=&gt;2, "deleted"=&gt;2, "batches"=&gt;1, "version_conflicts"=&gt;0, "noops"=&gt;0, "retries"=&gt;{"bulk"=&gt;0, "search"=&gt;0}, "throttled_millis"=&gt;0, "requests_per_second"=&gt;-1.0, "throttled_until_millis"=&gt;0, "failures"=&gt;[]}
# Count 24

puts 'Deleting document'
response = client.delete(index: index, id: id)
puts response
# Deleting document
# {"_index"=&gt;"books", "_id"=&gt;"nEawT5MBOXHuGXdEu5WA", "_version"=&gt;2, "result"=&gt;"deleted", "_shards"=&gt;{"total"=&gt;2, "successful"=&gt;1, "failed"=&gt;0}, "_seq_no"=&gt;25, "_primary_term"=&gt;1}

puts 'Deleting index'
response = client.indices.delete(index: index)
puts response
# Deleting index
# {"acknowledged"=&gt;true}<h2>Migrando um aplicativo Ruby para o Elasticsearch</h2><p>O primeiro passo é adicionar <code>elasticsearch-ruby</code> no Gemfile. Após executar <code>bundle install</code>, a gem do cliente Ruby do Elasticsearch será instalada. Se você quiser testar seu código antes de migrar completamente, você pode inicialmente deixar a gem <code>opensearch-ruby</code> lá.</p><p>O próximo passo importante será a instanciação do cliente. Isso vai depender de como você está executando o Elasticsearch. Para manter uma abordagem semelhante nestes exemplos, estou seguindo os passos descritos em <a href="https://www.elastic.co/downloads/elasticsearch">"Baixar o Elasticsearch</a> e executá-lo localmente".</p><p>Ao executar <code>bin/elasticsearch</code>, o Elasticsearch será iniciado com recursos de segurança configurados automaticamente. Certifique-se de copiar a senha do usuário elastic (mas você pode redefini-la executando <code>bin/elasticsearch-reset-password -u elastic</code>). Se você estiver seguindo este exemplo, certifique-se de parar o OpenSearch antes de iniciar o Elasticsearch, pois ambos são executados na mesma porta.</p><p>No início de <code>example_code.rb</code>, comentei a instanciação do cliente OpenSearch e adicionei a instanciação para um cliente Elasticsearch:</p># require 'opensearch'

# client = OpenSearch::Client.new(
#   host: 'https://localhost:9200',
#   user: 'admin',
#   password: ENV['OPENSEARCH_INITIAL_ADMIN_PASSWORD']
#   transport_options: { ssl: { verify: false } }
# )

require 'elasticsearch'

client = Elasticsearch::Client.new(
  host: 'https://localhost:9200',
  user: ENV['ELASTICSEARCH_USER'],
  password: ENV['ELASTICSEARCH_PASSWORD'],
  transport_options: { ssl: { verify: false } }
)<p>Como você pode ver, o código é quase idêntico neste cenário de teste. Isso varia de acordo com a implementação do Elasticsearch e a forma como você decide se conectar e autenticar com ele. O mesmo se aplica aqui ao OpenSearch em relação à segurança: a opção de não verificar o SSL destina-se apenas a fins de teste e não deve ser usada em produção.</p><p>Depois que o cliente estiver configurado, eu executo o código novamente com: <code>bundle exec ruby example_code.rb</code>. E tudo simplesmente funciona!</p><h2>Depurando problemas de migração</h2><p>Dependendo das APIs que seu aplicativo utiliza, existe a possibilidade de você receber um erro ao executar seu código no Elasticsearch se as APIs do OpenSearch divergirem. A <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html">documentação das APIs REST</a> é uma referência essencial para informações detalhadas sobre como usar as APIs. Certifique-se de consultar a documentação da versão do Elasticsearch que você está usando. Você também pode consultar a referência <a href="https://rubydoc.info/gems/elasticsearch-api"><code>Elasticsearch::API</code></a> .</p><p>Alguns erros que você pode encontrar no Elasticsearch podem ser:</p><ul><li><p><code>ArgumentError: Required argument '&lt;ARGUMENT&gt;' missing</code> - Este é um erro do cliente e será gerado quando uma solicitação não contiver um parâmetro obrigatório.</p></li><li><p><code>Elastic::Transport::Transport::Errors::BadRequest: [400] {"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"request [/example/_doc] contains unrecognized parameter: [test]"}]...</code> Esse erro vem do Elasticsearch e significa que o código do cliente está usando um parâmetro que o Elasticsearch não reconhece para a API que está sendo usada.</p></li></ul><p>O cliente Elasticsearch irá gerar erros do Elasticsearch com a mensagem de erro detalhada enviada pelo servidor. Assim, mesmo para parâmetros ou endpoints não suportados, o erro deve informar qual é a diferença.</p><h2>Conclusão</h2><p>Como demonstramos com este exemplo de código, a migração de um aplicativo Ruby do OpenSearch para o Elasticsearch não é muito complexa do ponto de vista do Ruby. É preciso estar ciente das diferenças de versão e de quaisquer APIs potencialmente divergentes entre os mecanismos de busca. Mas, para as ações mais comuns, a principal mudança na migração de clientes está na instanciação. Nesse aspecto, ambos são semelhantes, mas a forma como o host e as credenciais são definidos varia de acordo com a maneira como o Stack está sendo implantado. Após configurar o cliente e verificar se ele está se conectando ao Elasticsearch, você pode substituir o cliente OpenSearch pelo cliente Elasticsearch sem problemas.
</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ruby-opensearch-elasticsearch-migration</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ruby-opensearch-elasticsearch-migration</guid>
    <category><![CDATA[Ruby]]></category>
    <dc:creator><![CDATA[Fernando Briano]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f2ce1fb5cbf7d70/6a17e1dd148009bc02b486af/47756f629737d47c4430860ea23366a8d24c90d9-1280x720.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 13 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>