Rails + Elasticsearch 101 (parte 1)

Pedro Cavalheiro
3 min readOct 4, 2016

--

Recentemente participei da implementação de um sistema para análise de comportamento de usuários. A stack escolhida foi Rails + Elastic (antigo Elasticsearch) para coleta/persistência, e posterior análise via Kibana. Como os usos mais comuns do Elastic são Full Text Search e armazenamento de logs (na stack ELK), encontrei pouquíssimos exemplos de uso que se aplicavam a minha necessidade.

Quem trabalha com Rails está acostumado com as facilidades de seu ecossistema, onde com poucas linhas de código (e algumas Gems), é possível fazer muita coisa, sem muito trabalho.

Durante o projeto, percebi que a documentação das Gems principais, voltadas para Elastic não respondia alguns questionamentos básicos, comuns a todo iniciante. Depois de quebrar a cabeça entendendo como elas funcionam, decidi criar uma série de posts com algumas informações que aprendi nesse meio tempo, e que teriam me ajudado muito no início. Vamos lá!

Existem 3 gems principais para se trabalhar com Elastic em aplicações Ruby, para 3 situações distintas:

  • elasticsearch-persistence: permite a integração básica entre uma aplicação Ruby e Elastic, através dos padrões Repository ou ActiveRecord.
  • elasticsearch-model: responsável pela integração com as models do Rails, permitindo trabalhar com callbacks, sincronia automática, etc.
  • elasticsearch-rails: fornece algumas facilidades para aplicações Rails, como gerador de aplicações de busca e integração com ActiveSupport.

O modo mais fácil de integrar o Elastic a sua aplicação, é adicionando as seguintes linhas em suas models:

A linha Elasticsearch::Model é responsável pelo vínculo entre as estruturas do Rails e Elastic. Já a linha Elasticsearch::Model::Callbacks mantém os dados atualizados no Elastic (com um hook after_commit nas ações create, save, update e destroy) de modo automático.

Caso seja necessário ter um tratamento específico, é possível criar estes mesmos callbacks de modo manual (removendo o include dos Callbacks):

O modo como o Elastic persiste os dados funciona em cima dos conceitos de Index e Type, onde um Index é referente a uma estrutura semelhante a uma “tabela”, e um Type é referente a um tipo/agrupamento de dados dentro desta mesma “tabela”. Note as aspas ao redor de tabela: as estruturas internas de persistência (entre Elastic e um banco relacional) são bem diferentes, sendo que a comparação é apenas para facilitar a compreensão.

O Index e Type são definidos automaticamente a partir do nome do seu objeto. No caso da model Article acima, o Index gerado será articles, e o Type article.

Cada coluna/atributo de sua model é mapeado automaticamente para um campo no Elastic, com seus tipos respectivos. A estrutura na qual um documento (registro) é replicado no Elastic é chamado de Mapping, e cada campo possui um tipo distinto. Por padrão, a gem elasticsearch-model “entende” sua estrutura de dados, e cria os mesmos tipos no Elastic. Fácil, fácil :)

Porém, existem casos onde pode ser necessário que Elastic guarde os dados numa estrutura diferente do seu banco relacional, como por exemplo geo_point, geo_mapping (para usar os recursos de busca geográfica do Elastic). Nestes casos, é possível forçar o tipo de dado a ser mapeado pelo Elastic:

Um ponto importante a ser mencionado, é que o Elastic não trabalha bem com dados esparsos e/ou não normalizados (Avoid Sparsity). Algumas boas práticas são:

  • Definir o esquema estrutural previamente, pois alterações em campos são custosas ao Elastic, ainda mais quando a quantidade de dados já for significativa.
  • “Forçar” o Mapping de todos os campos, conforme o exemplo acima. O Elastic não faz o mapeamento de um campo nulo até receber o primeiro registro com ele preenchido. Se seus dados não são consistentes, isso pode gerar perda de performance na indexação.

Caso a sua aplicação já possua dados, você pode forçar o Elastic a importar todos os registros (via console ou na inicialização da sua aplicação):

O metodo import possui diversos parâmetros que auxiliam a rotina de importação. Alguns exemplos:

Neste post, expliquei algumas características básicas de integração, e como o Elastic grava estes dados. Existem vários outros detalhes e recursos disponívels, que explicarei nos próximos capítulos :)

Gostou da leitura? Clique no coração abaixo, assim você me ajuda a transmitir este conhecimento a mais pessoas!

Além disso, dúvidas, críticas e elogios são bem vindos! :D

Obrigado e até a próxima!

--

--