Desmistificando o Elasticsearch: Mapping & Analyzers

Thiago Barradas
thiagobarradas
Published in
6 min readMay 10, 2018
Elasticsearch é uma ferramenta para buscas “incrivelmente incríveis”.

Na era da informação, vemos cada vez mais uma quantidade expressiva de conteúdos espalhados pela Internet. Diversos sites, aplicativos e ferramentas em geral oferecem em abundância os mais variados tipos de informações, porém seus motores de busca nem sempre são eficazes. Muitas vezes, apesar da qualidade do conteúdo, localizar a informação pode ser muito difícil pelo formato da busca e pelas limitações na utilização de bancos relacionais, causando uma experiência ruim ao usuário, incluindo o fato de que, em grandes volumes de dados, o tempo de processamento pode ser alto. Para tornar a busca do seu conteúdo fácil, com diversos recursos para informações que são aparentemente subjetivas e extremamente rápidas, podemos utilizar o Elasticsearch.

Elasticsearch

O Elasticsearch é um servidor de buscas distribuído desenvolvido em Java, baseado no Apache Lucene e possui código aberto para toda a comunidade. Ele é utilizado principalmente para buscas full-text, possuindo recursos muitos interessantes.

A comunicação com o serviço é feita através do protocolo HTTP utilizando Json, sendo ele uma API Rest. Sua documentação é bem completa e na Internet é possível achar bastante conteúdo de alta qualidade sobre o assunto. O Elasticsearch é mantido pela elastic.

Dentre alguns de seus recursos, além de diversas formas de buscas full-text e outros tipos de dados, é possível citar agregações, highlighting, autocomplete, geo-localização, entre outros. A utilização de todas essas funcionalidades é simples, mas em alguns casos podemos ter problemas relacionados à estrutura que criamos no momento de indexar nossos documentos. Neste artigo, abordaremos alguns conceitos para que fique mais claro como funciona a indexação, incluindo o mapeamento e os analisadores.

Mapping

É o processo de definição de como um documento e seus campos são armazenados e indexados no Elasticsearch. Nesta etapa, podemos definir os tipos de dados para cada campo (text, keyword, integer, date, geolocation etc.). Campos do tipo string se dividem em keyword e text, e, se for text, podemos definir qual analisador será utilizado.

Para os diversos tipos de dados, os índices têm um comportamento comum, exceto para strings (tipo text e keyword), que será o nosso foco.

É importante ressaltar que, após criado, o mapeamento não pode ser alterado e por isso devemos ter muita atenção ao criar o esquema de nossos documentos. Se for necessário alterar o esquema, é preciso reindexar todos os documentos.

Para strings, pode-se definir o tipo:

  • text — O dado é tratado e quebrado em diversos índices a partir do analisador definido;
  • keyword — O dado não é analisado, e o índice possui exatamente o valor recebido;

Você pode ler mais sobre o mapeamento na documentação oficial.

Analyzers

Os analisadores são responsáveis por tratar os dados e gerar os índices associados a eles. O texto recebido é quebrado em diversos tokens, seja por espaços em branco ou caracteres especiais, e cada token é analisado e normalizado. Apenas campos definidos no seu mapeamento com o tipo text passam por esse processo.

Os analisadores também atuam quando é efetuada uma query em cima desses dados, usando sempre o analisador definido no mapeamento, para que os dados enviados para a consulta também sejam normalizados da mesma maneira que são normalizados quando inseridos. Existem diversos tipos de analisadores, para diversas necessidades, idiomas específicos e com possibilidade de customização.

Vamos supor que estamos indexando a informação: “Apenas um teste”. Se o tipo do campo for keyword, o seu índice será exatamente seu valor. Mas se o tipo do campo for text, seu índice seria composto pelos seguintes tokens: “apenas”, “um”, “teste”.

Você pode ler mais sobre o analisadores na documentação oficial.

Mas e aí?

Agora que compreendemos esse conceito, podemos começar a nos questionar sobre algum dos impactos — por que usar e como usar o tipo text ou keyword. Um campo do tipo keyword é usado normalmente para expressões exatas, únicas. Podemos ter como exemplos possíveis enumeradores, unique identifier (esse tipo deve ser usado como string, pois não existe nativo), siglas, enumeradores, etc. Se executado uma query full-text em cima desse dado, ele não seria encontrado mesmo que a palavra-chave fosse exatamente o valor que está armazenado, porque o texto buscado seria quebrado em diversos tokens e o índice do dado armazenado é o valor completo, logo, não teríamos um “match”. O inverso também não funcionaria. Executar uma query por valor exato em um campo do tipo text não traria resultados, uma vez que a palavra-chave seria o valor completo, buscado nos tokens processados, e também não retornariam resultados. Logo, fica claro que devemos pensar e planejar bem ao definir o que são dados exatos e subjetivos na hora de mapear nossos campos, e usar as queries corretas ao executar uma consulta.

Existe um grande problema quando falamos de campos do tipo text e quando precisamos que essa informação possa ser utilizada também com seu valor exato. Dois exemplos clássicos ocorrem com ordenação e agregações.

Vamos supor que temos os seguintes dados armazenados em nosso servidor:

Exemplo de dado armazenado e tokens gerados para sua referencia — índices.

Se fosse solicitado para ordenar por esse campo, a ordenação seria executada no conjunto dos tokens referentes a cada valor, e o primeiro valor seria para ordenar. Para o valor “Apenas um teste”, uma ordenação crescente se basearia no tokenapenas”. Uma ordenação decrescente se basearia no tokenum”, causando na verdade uma desordenação dos dados.

Exemplo de ordenação ascendente executada primeiramente nos tokens, para depois ordenar os dados.
Exemplo de ordenação descendente executada primeiramente nos tokens, para depois ordenar os dados.

Se fôssemos agregar todos os valores distintos desse campo, também teríamos problemas. Em vez de obtermos os três valores distintos (“Apenas um teste”, “Estou aqui ainda”, “Boa tarde”), iríamos obter todos os tokens separadamente. O resultado seria o conjunto: “apenas”, “um”, “teste”, “estou”, “aqui”, “ainda”, “boa”, “tarde”.

Para campos que não serão ordenados e nem agregados como conteúdos muito extensos, isso não seria um problema. Mas para campos curtos e analisáveis, como título de um artigo, nome de pessoas, nome de bairros etc., cujas possíveis ordenações e agregações sejam necessárias, seria bem complicado lidar com isso.

A melhor maneira para contornar esse problema é ter o mesmo campo indexado das duas formas, sendo um do tipo text para o uso de buscas full-text e outro do tipo keyword para ordenações e agregações.

Como resolver?

Para implementar essa estratégia, não é necessário criar dois campos com nomes diferentes. Dentro do mapeamento de um campo, é possível declarar subcampos (multifields, veja a documentação) e especificar como eles serão indexados. Por convenção, usamos um subcampo chamado raw do tipo keyword dentro do campo do tipo text, permitindo que essas operações possam ser executadas. Um campo title teria também um subcampo title.raw.

Por padrão, ao criar um documento com mapeamento automático o próprio Elasticsearch já cria para todas os campos identificados como string, um mapeamento do tipo text com um sub campo keyword (com o nome keyword). Sendo assim, um campo chamado name mapeado automaticamente, poderia ser acessado como keyword através do campo name.keyword.

Importante ressaltar que, criar sub campos aumenta a quantidade de armazenamento utilizado, já que armazemos tokens referentes ao campo de duas maneiras. Por isso, vale entender se um campo precisar ser do tipo text, keyword, ou, os dois ao mesmo tempo.

Novamente, pense bastante antes de definir o mapeamento para os campos dos seu documentos.

Para finalizar, um exemplo de quando adotar cada um dos três casos:

  • text O conteúdo de um artigo de um blog, por natureza, extenso. Esse campo precisa ter buscas por seu conteúdo, mas não agrega valor ordenar os documentos por esse valor ou fazer uma agregação pelo conteúdo do artigo;
  • keyword — O status da publicação desse artigo. Normalmente, conhecemos todos os possíveis status de um artigo de um blog, que seria um enumerador com valores como “Rascunho” ou “Publicado” ;
  • text e keyword— O nome do autor da publicação. Além de querer fazer buscas por palavras chaves, etc, posso ter a necessidade de fazer uma ordenação por esse campo, ou, simplesmente agregar os documentos por seu autor.;

--

--

Thiago Barradas
thiagobarradas

Microsoft MVP, Elastic Contributor. Entusiasta de novas tecnologias e arquiteto de software na Stone. Cultuador do hábito de formar cabeças pensantes.