O tema de busca por texto completo tem se tornado cada dia mais comentado na comunidade de desenvolvedores conforme a necessidade de trabalhar com dados volumosos tem se tornado cada vez mais comum. Encontrar dados importantes em um grande emaranhado de documentos acabou se tornando uma necessidade recorrente, por muito tempo, sem uma solução comumente aceita. Até que surge o Elasticsearch.


Elasticsearch:

O Elasticsearch é uma poderosa ferramenta para realização de buscas e análise de dados construída para trabalhar com grandes volumes de dados, permitindo indexar documentos e realizar buscas nesses documentos em (quase) tempo real.

Porque usar Elasticsearch

O Elasticsearch realiza buscas por Índice Invertido e esse princípio trabalha da seguinte forma:

  • No momento em que um documento é indexado, o Elasticsearch separa todos os seus termos em Tokens.
  • Em seguida ele faz uma medição para definir quais tokens são relevantes, eliminando assim artigos, preposições, etc.
  • O próximo passo do Elasticsearch é organizar os tokens em um índice e informar em cada token quais documentos contém esse token.
  • Quando uma busca for feita ela agirá sobre esse índice invertido ao invés de vasculhar cada documento individualmente, procurando pelos termos buscados.

Esse processo de indexação é o que torna o Elasticsearch um motor de busca em semi-tempo-real.

Benefícios do Elasticsearch

O Elasticsearch suporta um grande volume de dados sem perder performance. Ele pode ser implementado em qualquer sistema independentemente da plataforma, por fornecer uma API REST. Além disso, o Elasticsearch é altamente escalável, podendo ir de um servidor a muitos servidores simultâneos.

Quando usar o Elasticsearch

O Elasticsearch é necessário sempre que for interessante encontrar informações em grandes volumes de dados buscando por texto. Ele também fornece uma API para realizar análises sobre os dados recuperados como resultado da busca.

Onde usar o Elasticsearch

Em qualquer projeto que deseje realizar buscas por texto completo e analisar dos dados recuperados por essa busca, seja esta realizada em um banco de dados, em arquivos de log ou qualquer outra forma de armazenamento de documentos de texto.

Como instalar o Elasticsearch

O Elasticsearch pode ser instalado em diversos sistemas operacionais do próprio site oficial. Para Windows há um instalador executável que, por padrão, deixa o Elasticsearch pronto para funcionamento, como ilustrado na Figura 1.

Download para Windows
Figura 1. Download para Windows
Nota: Para instalar o Elasticsearch é preciso já ter o Java instalado em seu computador.

Após executar basta clicar em avançar em todas as telas até que o Elasticsearch tenha terminado de ser instalado, como mostram as Figuras 2 a 6.

Download para windows
Figura 1. Download para Windows

Nota: Nesse projeto usamos a versão 6.4.2 do Elasticsearch.

Após executar basta clicar em avançar em todas as telas até que o Elasticsearch tenha terminado de ser instalado, como vemos nas Figuras 2 a 6.

Não é preciso alterar os diretórios
Figura 2. Não é preciso alterar os diretórios

Instalar como serviço
Figura 3. Instalar como serviço

Configurações avançadas
Figura 4. Configurações avançadas

Plugins opcionais
Figura 5. Plugins opcionais

Tela de conclusão
Figura 6. Tela de conclusão

o Elasticsearch provê uma interface por meio de uma API REST para realizar suas consultas, indexar documentos etc. que, por padrão, é disponibilizada no localhost do computador em que foi instalado pela porta 9200. Para verificar se o Elasticsearch está rodando adequadamente abra o navegador e entre em http://localhost:9200 para ver uma resposta parecida com a seguinte:

{
    "name" : "DESKTOP-1AILDTS",
    "cluster_name" : "Elasticsearch",
    "cluster_uuid" : "6wUOuGmnQHqRXAeVOXwBwg",
    "version" : {
    "number" : "6.4.1",
    "build_flavor" : "unknown",
    "build_type" : "unknown",
    "build_hash" : "e36acdb",
    "build_date" : "2018-09-13T22:18:07.696808Z",
    "build_snapshot" : false,
    "lucene_version" : "7.4.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
    },
    "tagline" : "You Know, for Search"
}
Nota: Esse artigo é feito com base no Elasticsearch de versão 6.4.

Principais comandos no Elasticsearch

O Elasticsearch fornece uma API REST, por padrão, que recebe requisições dos quatro verbos HTTP: GET, POST, PUT e DELETE. Todo processo de utilização ocorre por meio de requisições HTTP à API do Elasticsearch por meio do envio de objetos JSON no corpo destas.

A URI de uma requisição HTTP segue a seguinte estrutura:

endereco_da_api:porta/indice/tipo_do_documento

Requisição GET para uri/_search

Retorna todos os documentos do índice indicado que sejam do tipo definido. Permite um objeto JSON no corpo da requisição, através do qual são definidas as especificações da busca.

Requisição POST para uri

Realiza a indexação de um documento cujos dados devem ser passados como um objeto JSON no corpo da requisição.

Requisição PUT para uri/_id

_id deve ser o valor do código de identificação do documento. Caso o _id seja de um documento existente, substitui os campos do documento pelo corpo JSON da requisição. Caso o _id não seja de um documento existente cadastra um documento com os campos sendo o corpo JSON da requisição.

Requisição DELETE para uri/_id

_id deve ser o valor do código de identificação do documento. Caso o _id seja de um documento existente, deleta fisicamente o documento do índice em que ele está indexado.

Como usar o Elasticsearch

Para indexar documentos podemos enviar uma requisição do tipo POST para a API seguindo o seguinte padrão:

POST http://localhost:9200/futebol/jogador 
{
    "nome": "Édson Arantes do Nascimento",
    "apelido": "Pelé",
    "titulos_mundiais": [
        "1958",
        "1962",
        "1970"
    ]
}
  • Linha 1: Representamos o envio da requisição HTTP do verbo POST ao endpoint da API local do Elasticsearch. Esse tipo de requisição pode ser feita através de um aplicativo como o Postman ou através de qualquer sistema que faça requisições HTTP. Após o endereço e porta, informamos que desejamos indexar o documento do tipo jogador no índice futebol.
  • Linhas 2 a 10: Objeto JSON no corpo da requisição que informa os dados que serão indexados como um documento no Elasticsearch. Após a indexação esse documento, bem como seus termos, poderá ser encontrado por meio de uma busca ao Elasticsearch direcionada ao índice futebol.

Exemplo de consulta em Elasticsearch

A forma mais comum de realizar buscas no Elasticsearch são as queries que, além de encontrar todos os documentos que possuam o termo buscado, ordena-os por relevância com base em um cálculo interno:

GET http://localhost:9200/futebol/jogador/_search 
{
    "query": {
        "match": {
            "apelido": {
            "query": "Pelé"
            }
        }
    }
}
  • Linha 1: Representamos o envio da requisição HTTP do verbo GET ao endpoint da API local do Elasticsearch. Após o endereço e porta, informamos que desejamos realizar uma busca através da declaração _search após o índice e o tipo.
  • Linha 3: iniciamos a abertura da query em um objeto JSON no corpo da requisição.
  • Linha 4: Definimos que a query realizará uma combinação (match) entre o termo buscado e os termos dos documentos.
  • Linhas 5 e 6: Definimos que estamos buscando por um documento que tenha o seu campo apelido com o valor igual a "Pelé".

Nota: Quando um termo é convertido em token ele é automaticamente convertido para caixa baixa caso possua alguma letra maiúscula.

Combinar consultas no Elasticsearch

O Elasticsearch permite combinar o resultado de múltiplas queries através do recurso de query booleana, que combina tipos de query diferentes através das combinações booleanas (e, ou, etc.):

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "star"
          }
        }
      ],
      "should": [
        {
          "match": {
            "title": "wars"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "title": "trek"
          }
        }
      ]
    }
  }
}

Essa faz uso do atributo bool, que define o escopo da query booleana. Dentro dela podemos declarar diversas queries que terão seus resultados combinados. O atributo bool aceita os seguintes atributos:

  • filter: Realiza uma busca pelos documentos que contenham o termo buscado, porém não classifica nenhuma ordem de relevância aos resultados encontrados.
  • must: Realiza uma busca pelos documentos que contenham o termo buscado e classifica uma ordem de relevância aos resultados encontrados por um cálculo de pontuação.
  • must_not: Realiza uma busca pelos documentos que NÃO contenham o termo definido e não classifica nenhuma ordem de relevância aos resultados encontrados.
  • should: Realiza uma busca pelos documentos que contenham o termo buscado e classifica uma ordem de relevância aos resultados encontrados por um cálculo de pontuação. Porém, se a query booleana possuir um must ou filter, então o documento combinará com a query booleana mesmo que nenhuma query de should combine. Nesse caso, should apenas elevará a pontuação de seus resultados como sendo mais relevantes do que os documentos que não lhes pertencem.

Elasticsearch PHP

O Elasticsearch dispõe de uma biblioteca oficial para seu uso em aplicações na linguagem PHP. Essa biblioteca pode ser adicionada ao seu projeto através do Composer, o gerenciador de dependências do PHP. Para fazer isso basta rodar o seguinte comando em seu projeto:

composer require Elasticsearch/Elasticsearch:6.0

A biblioteca pode ser utilizada da seguinte forma:

<?php
use Elasticsearch\ClientBuilder;

$hosts = [
    'http:\\localhost:9200'
];

$client = ClientBuilder::create()
            ->setHosts($hosts)
            ->build();

Primeiramente fazemos importação da classe ClientBuilder no escopo em que vamos utilizá-la. Em seguida, criamos um array com o endereço e porta da API do Elasticsearch a qual desejamos nos conectar.

Por fim, executamos o método create(), passando $hosts como parâmetro do método setHosts() e encerrando a expressão com o método build(). Com o objeto $client podemos realizar nossas consultas ao servidor Elasticsearch que foi informado em $hosts.

Indexando um documento

<?php
$params = [
    'index' => 'locadora',
    'type' => 'filmes',
    'body' => [
        'title' => 'Matrix',
        'tagline' => 'todos acham que é real.',
        'generos' => [
            [
                'id' => 28,
                'genero' => 'ficção científica'
            ],
            [
                'id' => 878,
                'genero' => 'ação'
            ]
        ]
    ]
];

$resposta = $client->index($params);
print_r($resposta);

Criamos um array com as especificações do documento que queremos indexar ao Elasticsearch. Nesse array definimos o índice e o tipo do documento indexado nos atributos index e type, respectivamente.

No atributo body definimos os dados do documento que estamos indexando e em seguida eles serão convertidos em um objeto JSON quando o documento for de fato armazenado no banco do Elasticsearch.

Por fim, executamos o método index do objeto $client que criamos previamente e passamos o array criado como parâmetro do método e armazenamos a resposta do Elasticsearch. Caso a indexação tenha tido êxito a resposta deverá ter o seguinte formato:

Array
    (
        [_index] => locadora
        [_type] => filmes
        [_id] => _id-gerado-pelo-Elasticsearch
        [_version] => 1
        [created] => 1
    )

Realizando buscas

A busca no Elasticsearch através da biblioteca PHP pode ser feita de forma muito semelhante à indexação de um documento:

<?php
$params = [
    'index' => 'locadora',
    'type' => 'filmes',
    'body' => [
        'query' => [
            'match' => [
                'title' => 'matrix'
            ]
        ]
    ]
];

$response = $client->search($params);

Primeiramente criamos um array com as especificações da query a partir da qual realizaremos a busca ao Elasticsearch. No campo body declaramos a query que segue a mesma sintaxe das queries JSON do Elasticsearch.

Em seguida executamos o método search do objeto client do Elasticsearch que foi previamente criado e passamos o array com as especificações da query como parâmetro desse método, armazenando seu retorno em uma variável.