Skip to content

6 ‐ Index Settings: Entendendo Mapping, Analyzers e Aliases

viniciusbktech edited this page May 21, 2024 · 2 revisions

5.1 - Mapping

Importância e Benefícios:

O mapping no Elasticsearch define como os dados são armazenados e indexados. Uma configuração de mapping adequada permite que você otimize o armazenamento, melhore a performance das consultas e controle a análise de texto.

Exemplo de Uso:

PUT /clientes
{
  "mappings": {
    "properties": {
      "nome": { "type": "text" },
      "idade": { "type": "integer" },
      "email": { "type": "keyword" }
    }
  }
}

Este exemplo define um índice clientes com campos específicos e tipos de dados definidos.

Dynamic Mapping:

O Elasticsearch pode inferir os tipos de dados dos campos durante a indexação. No entanto, para um controle mais refinado, é recomendável definir o mapping explicitamente.

Exemplo de Dynamic Mapping:

Se você não definir um mapping e indexar um documento, o Elasticsearch criará um mapping automaticamente.

POST /clientes/_doc/1
{
  "nome": "Maria",
  "idade": 28
}

Neste caso, o Elasticsearch inferirá nome como text e idade como integer.

Dynamic Templates:

Permitem definir regras de mapping para campos com base em padrões de nome, tipo ou outras condições.

Exemplo de Dynamic Template:

PUT /clientes
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

Este template trata todos os novos campos de string como keyword.

POST clientes/_doc/1
{
  "frase": "Boa tarde a todos"  
}

GET clientes/_mapping

Analisando o mapeamento do campo criado, observa-se que o dynamic template reconheceu o novo campo como string e o indexou como keyword.

Field Data Types:

Os tipos de dados de campo incluem text, keyword, date, integer, float, boolean e muitos outros.

Exemplo de Multi-Field:

PUT /clientes
{
  "mappings": {
    "properties": {
      "nome": {
        "type": "text",
        "fields": {
          "raw": { 
            "type":  "keyword"
          }
        }
      }
    }
  }
}

Neste exemplo, nome é indexado como text e como keyword no subcampo raw.

5.2 - Análise de Texto: Analysis, Analyzers, Tokenization e Normalizers

Analysis

Analysis Elastic

A análise de texto no Elasticsearch é um processo fundamental que transforma texto bruto em termos ou tokens que podem ser indexados. Esse processo é composto por várias etapas, incluindo tokenização, filtragem de tokens, e, em alguns casos, normalização. A análise é essencial para realizar buscas eficientes em campos de texto, permitindo que o Elasticsearch entenda e compare os dados de texto.

Analyzers

Analyzers Elastic

Um analyzer no Elasticsearch é uma ferramenta que encapsula o processo de análise de texto, combinando três componentes principais: o tokenizer e, opcionalmente, vários token filters e character filters.

  • Tokenizers: Divide o texto em tokens (geralmente palavras). Por exemplo, o tokenizer standard divide o texto em palavras com base em certos critérios de separação, como espaços e pontuação.

  • Token Filters: Processam a sequência de tokens gerados pelo tokenizer. Eles podem modificar, adicionar ou remover tokens. Exemplos incluem lowercase (converte todos os tokens em letras minúsculas) e stop (remove palavras comuns, também conhecidas como stopwords).

  • Character Filters: Processam o texto antes da tokenização, permitindo a substituição ou remoção de caracteres. Por exemplo, um character filter pode remover HTML ou converter caracteres HTML em seus equivalentes de texto.

Exemplo de Analyzer:

GET /_analyze
{
  "analyzer": "standard",
  "text": "Text for analysis!"
}

Este exemplo demonstra como um texto será analisado usando o analyzer standard.

Tokenization

Tokenization é o processo de dividir o texto em uma série de tokens ou termos. É o primeiro passo crítico na análise de texto, pois determina a granularidade dos dados que serão indexados e pesquisados.

Exemplo de Tokenization:

POST /_analyze
{
  "tokenizer": "standard",
  "text": "Quick brown fox."
}

Este exemplo mostra como o texto será dividido em tokens usando o tokenizer standard.

Normalizers

Os normalizers são usados para campos do tipo keyword e aplicam uma transformação de normalização de texto. Diferentemente dos analyzers, que podem alterar o número de termos, os normalizers sempre produzem exatamente um termo por termo de entrada, sendo úteis para normalizar tokens para buscas consistentes, como converter textos para minúsculas.

Exemplo de Normalizer:

Suponha que você tenha definido um normalizer no seu mapping que converte texto para minúsculas:

PUT /meu_index
{
  "settings": {
    "analysis": {
      "normalizer": {
        "meu_normalizer": {
          "type": "custom",
          "char_filter": [],
          "filter": ["lowercase"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "meu_campo_keyword": {
        "type": "keyword",
        "normalizer": "meu_normalizer"
      }
    }
  }
}

Este normalizer pode ser usado para garantir que todos os dados no campo meu_campo_keyword sejam armazenados em letras minúsculas.

POST meu_index/_doc/
{
  "meu_campo_keyword": "BOA TARDE"
}
GET meu_index/_search
{
  "size": 0, 
  "aggs": {
    "my_aggs": {
      "terms": {
        "field": "meu_campo_keyword"
      }
    }
  }
}

5.3 - Alias

Aliases e sua Aplicabilidade:

Um alias é um nome alternativo para um índice que pode ser usado para ocultar detalhes de implementação aos usuários ou aplicações.

Exemplo de Criação de Alias:

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "clientes",
        "alias": "clientes_atuais"
      }
    }
  ]
}

Agora, clientes_atuais pode ser usado no lugar do nome do índice clientes.

Aliases com Filtros:

Exemplo de Alias com Filtro:

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "clientes",
        "alias": "clientes_adultos",
        "filter": { "range": { "idade": { "gte": 18 } } }
      }
    }
  ]
}

Este alias clientes_adultos só expõe documentos de clientes com 18 anos ou mais.

5.4 - Laboratório 1: Criando um Índice com Mapeamento Específico no Elasticsearch

Neste laboratório, vamos criar um índice no Elasticsearch chamado clientes com um mapeamento específico para os campos codigo, nome, sexo, estado_civil, data_nascimento e caracteristicas. Além disso, configuraremos um mapeamento dinâmico para campos com o prefixo logradouro para serem tratados como keyword.

Etapa 1: Criar o Índice com Mapeamento

  1. Definir o Índice e o Mapeamento:
PUT /clientes
{
  "mappings": {
    "dynamic_templates": [
      {
        "logradouros_as_keywords": {
          "match_mapping_type": "string",
          "match_pattern": "regex",
          "match": "^logradouro.*",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ],
    "properties": {
      "codigo": {
        "type": "integer"
      },
      "nome": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      },
      "sexo": {
        "type": "keyword"
      },
      "estado_civil": {
        "type": "keyword"
      },
      "data_nascimento": {
        "type": "date"
      },
      "caracteristicas": {
        "type": "text"
      }
    }
  }
}

Explicação:

  • codigo: Campo do tipo integer.
  • nome: Campo do tipo text com um subcampo keyword para permitir buscas exatas e baseadas em texto.
  • sexo e estado_civil: Campos do tipo keyword para buscas exatas.
  • data_nascimento: Campo do tipo date para armazenar datas.
  • caracteristicas: Campo do tipo text para armazenar texto livre.
  • dynamic_templates: Template dinâmico que aplica o tipo keyword a qualquer campo que comece com logradouro.

Etapa 2: Indexando Documentos de Exemplo

Agora, vamos indexar um documento de exemplo para ver o mapeamento em ação.

POST /clientes/_doc/1
{
  "codigo": 123,
  "nome": "João Silva",
  "sexo": "masculino",
  "estado_civil": "casado",
  "data_nascimento": "1980-05-15",
  "caracteristicas": "Amigável, extrovertido, atencioso",
  "logradouro_principal": "Rua das Flores",
  "logradouro_secundario": "Avenida das Árvores"
}

Neste exemplo, os campos logradouro_principal e logradouro_secundario são tratados como keyword devido ao nosso mapeamento dinâmico.

Etapa 3: Validando o Mapeamento

Você pode verificar o mapeamento efetivo usando a seguinte chamada API:

GET /clientes/_mapping

Essa chamada retornará o mapeamento atual do índice clientes, permitindo que você confirme se todos os campos estão configurados conforme desejado.

Etapa 4: Acrescentando Parâmetro Dynamic no Mapeamento

O parâmetro dynamic no mapeamento do Elasticsearch controla como os campos não especificados no mapeamento são tratados quando novos documentos são indexados. Ele pode ter três valores: true, false e strict.

  1. Dynamic: true

    Este é o valor padrão. Quando dynamic está configurado como true, o Elasticsearch detecta automaticamente os tipos de campo dos campos não mapeados e os adiciona ao mapeamento.

    Exemplo:

    Se indexarmos um novo documento com um campo não definido no mapeamento, como novo_campo, o Elasticsearch adicionará automaticamente novo_campo ao mapeamento com um tipo inferido.

  2. Dynamic: false

    Quando configurado como false, o Elasticsearch não adicionará novos campos ao mapeamento. Os campos não mapeados serão aceitos e indexados, mas não serão pesquisáveis.

    Exemplo de Mapeamento com Dynamic False:

    PUT /clientes
    {
      "mappings": {
        "dynamic": "false",
        "dynamic_templates": [
          {
            "logradouros_as_keywords": {
              "match_mapping_type": "string",
              "match_pattern": "regex",
              "match": "^logradouro.*",
              "mapping": {
                "type": "keyword"
              }
            }
          }
        ],
        "properties": {
          "codigo": {
            "type": "integer"
          }
          // outros campos mapeados
        }
      }
    }

    Se indexarmos um documento com um campo que não esteja no mapeamento, esse campo será armazenado, mas não será adicionado ao mapeamento.

  3. Dynamic: strict

    Se dynamic for configurado como strict, o Elasticsearch rejeitará documentos que contêm campos não mapeados, retornando um erro.

    Exemplo de Mapeamento com Dynamic Strict:

    PUT /clientes
    {
      "mappings": {
        "dynamic": "strict",
        "dynamic_templates": [
          {
            "logradouros_as_keywords": {
              "match_mapping_type": "string",
              "match_pattern": "regex",
              "match": "^logradouro.*",
              "mapping": {
              "type": "keyword"
              }
            }
          }
        ],
        "properties": {
          "codigo": {
            "type": "integer"
          }
          // outros campos mapeados
        }
      }
    }

    Nesse caso, se tentarmos indexar um documento com um campo que não esteja no mapeamento, receberemos um erro.

Ao definir dynamic como strict, o Elasticsearch garantirá que somente os campos definidos no mapeamento possam ser indexados, oferecendo um controle estrito sobre a estrutura dos dados no índice.

5.5 - Laboratório 2 - Customizando Analysis e Analyzers

Neste laboratório, vamos aprender a customizar um analyzer no Elasticsearch usando o tokenizer edge_ngram. Vamos aplicar isso no contexto de um índice chamado fornecedor, focando na tokenização do campo nome_fornecedor para aprimorar as buscas.

Passo 1: Criando o Índice com Analyzer Customizado

  1. Defina o índice fornecedor com o analyzer customizado:

    PUT /fornecedor
    {
      "settings": {
        "analysis": {
          "analyzer": {
            "my_analyzer_ngram_3": {
              "tokenizer": "split_3_ngram"
            }
          },
          "tokenizer": {
            "split_3_ngram": {
              "type": "edge_ngram",
              "min_gram": 3,
              "max_gram": 15,
              "token_chars": [
                "letter",
                "digit"
              ]
            }
          }
        }
      },
      "mappings": {
        "properties": {
          "codigo": { "type": "integer" },
          "nome_fornecedor": { "type": "text", "fields": { "keyword": { "type": "keyword" } } },
          "nome_fornecedor_engram": {
            "type": "text",
            "analyzer": "my_analyzer_ngram_3",
            "search_analyzer": "my_analyzer_ngram_3",
            "fields": { "keyword": { "type": "keyword" } }
          },
          "cnpj": { "type": "keyword" },
          "endereco": { "type": "keyword" },
          "bairro": { "type": "keyword" },
          "cidade": { "type": "keyword" },
          "uf": { "type": "keyword" }
        }
      }
    }

    Aqui, criamos um tokenizer edge_ngram chamado split_3_ngram, que gera tokens com um comprimento de 3 a 15 caracteres. Este tokenizer é usado no analyzer my_analyzer_ngram_3, que é aplicado ao campo nome_fornecedor_engram.

Passo 2: Testando o Tokenizer

Para entender como o texto será tokenizado, você pode usar o seguinte comando:

POST fornecedor/_analyze
{
  "tokenizer": "split_3_ngram",
  "text": "GOLLINHASAEREAS"
}

Esse comando irá demonstrar como o texto "GOLLINHASAEREAS" será dividido em tokens pelo tokenizer customizado my_analyzer_ngram_3.

Passo 3: Inserindo Documentos para Teste

  1. Insira o primeiro documento:

    POST /fornecedor/_doc/1
    {
      "codigo": 124,
      "nome_fornecedor": "GOL Linhas Aereas",
      "nome_fornecedor_engram": "GOL Linhas Aereas",
      "cnpj": "12222334000102",
      "endereco": "Rua tal e etc",
      "bairro": "Fulano",
      "cidade": "Caxias",
      "uf": "RJ"
    }
  2. Insira o segundo documento:

    POST /fornecedor/_doc/1
    {
      "codigo": 124,
      "nome_fornecedor": "TAM Linhas Aereas",
      "nome_fornecedor_engram": "TAM Linhas Aereas",
      "cnpj": "12222334000102",
      "endereco": "Rua tal e etc",
      "bairro": "Fulano",
      "cidade": "Caxias",
      "uf": "RJ"
    }

Passo 4: Realizando uma Busca

Agora, vamos realizar uma busca para verificar a eficácia do nosso analyzer:

GET fornecedor/_search
{
  "query": {
    "match": {
      "nome_fornecedor_engram": "TAMLINHASAEREAS"
    }
  }
}

Nessa busca, estamos procurando por "TAMLINHASAEREAS" no campo nome_fornecedor_engram. Graças ao nosso analyzer customizado, mesmo que não haja uma correspondência exata, a busca será capaz de encontrar "TAM Linhas Aereas" graças à tokenização feita pelo edge_ngram.

Conclusão

Neste laboratório, você aprendeu a criar um analyzer customizado usando edge_ngram no Elasticsearch, aplicá-lo a um campo específico e testar sua funcionalidade. Além disso, você viu como inserir dados e realizar uma busca efetiva utilizando o analyzer customizado para melhorar a relevância dos resultados.

5.6 - Laboratório 3 - Criando Aliases de Índices

Neste laboratório, aprenderemos a unificar e organizar o acesso a múltiplos índices no Elasticsearch usando aliases. Primeiramente, criaremos dois índices, fornecedor_matriz e fornecedor_filial, e depois configuraremos aliases para unificá-los e criar uma visão filtrada.

Passo 1: Criação dos Índices

  1. Crie o índice fornecedor_matriz:

    PUT /fornecedor_matriz
    {
      "mappings": {
        "properties": {
          "codigo": { "type": "integer" },
          "nome_fornecedor": {
            "type": "text",
            "fields": { "keyword": { "type": "keyword" } }
          },
          "cnpj": { "type": "keyword" },
          "endereco": { "type": "keyword" },
          "bairro": { "type": "keyword" },
          "cidade": { "type": "keyword" },
          "uf": { "type": "keyword" }
        }
      }
    }
  2. Crie o índice fornecedor_filial:

    PUT /fornecedor_filial
    {
      "mappings": {
        "properties": {
          "codigo": { "type": "integer" },
          "nome_fornecedor": {
            "type": "text",
            "fields": { "keyword": { "type": "keyword" } }
          },
          "cnpj": { "type": "keyword" },
          "endereco": { "type": "keyword" },
          "bairro": { "type": "keyword" },
          "cidade": { "type": "keyword" },
          "uf": { "type": "keyword" }
        }
      }
    }

Passo 2: Inserindo Documentos nos Índices

  1. Insira um documento no fornecedor_matriz:

    POST /fornecedor_matriz/_doc/1
    {
      "codigo": 124,
      "nome_fornecedor": "TAM Linhas Aereas",
      "cnpj": "12222334000102",
      "endereco": "Rua tal e etc",
      "bairro": "Fulano",
      "cidade": "Caxias",
      "uf": "RJ"
    }
  2. Insira um documento no fornecedor_filial:

    POST /fornecedor_filial/_doc/1
    {
      "codigo": 124,
      "nome_fornecedor": "GOL Linhas Aereas",
      "cnpj": "12222334000102",
      "endereco": "Rua tal e etc",
      "bairro": "Fulano",
      "cidade": "Santos",
      "uf": "SP"
    }

Passo 3: Criando o Alias "corporacao"

  1. Configure o alias corporacao para unificar os dois índices:

    POST _aliases
    {
      "actions": [
        {
          "add": {
            "index": "fornecedor_matriz",
            "alias": "corporacao"
          }
        },
        {
          "add": {
            "index": "fornecedor_filial",
            "alias": "corporacao"
          }
        }
      ]
    }
  2. Teste a busca pelo alias corporacao:

    GET corporacao/_search

    Essa busca retornará resultados de ambos os índices, fornecedor_matriz e fornecedor_filial.

Passo 4: Criando um Alias Filtrado "fornecedores_sp"

  1. Crie um alias fornecedores_sp com um filtro para a UF "SP":

    POST _aliases
    {
      "actions": [
        {
          "add": {
            "index": "fornecedor_*",
            "alias": "fornecedores_sp",
            "filter": {
              "term": {
                "uf": "SP"
              }
            }
          }
        }
      ]
    }
  2. Realize uma consulta pelo alias fornecedores_sp:

    GET fornecedores_sp/_search

    A consulta retornará apenas os documentos do estado de São Paulo.

Conclusão

Você aprendeu a criar e configurar aliases para unificar o acesso a múltiplos índices no Elasticsearch, além de poder restringir o acesso a informações especificas de determinados índices. Com o alias, também é possível aplicar o versionamento de um índice sem que o seu nome original seja alterado.

5.7 - Laboratório 4 - Aplicando todas as configurações de índice de uma só vez

Neste laboratório, aprenderemos a configurar todos os parâmetros vistos nessa aula de uma só vez. Adicionalmente, vamos atrelar a essas configurações uma política de ILM para entendermos que também podemos definir tudo isso no momento da criação do índice. Primeiro, vamos criar a política que queremos atrelar aos índices em questão; depois, criaremos o que chamamos de componente template, além de atrelar ele a um index template. Por fim, faremos um bulk em dois índices para verificar as configurações feitas.

Passo 1: Criação da política de ILM

  1. Definir a política policy_pessoas:

    PUT _ilm/policy/policy_pessoas
    {
      "policy": {
        "phases": {
          "hot": {
            "actions": {
           }
          },
          "warm": {
            "min_age": "1d",
            "actions": {
              "forcemerge": {
                "max_num_segments": 1
              }
            }
          },
          "cold": {
            "min_age": "7d",
            "actions": {
              "freeze": {}
            }
          },
          "delete": {
            "min_age": "30d",
            "actions": {
              "delete": {}
            }
          }
        }
      }
    }
    
    

Passo 2: Criação do component template

Vamos criar o component template a atrelar às novas configurações a ILM Policy.

  1. Definir o component template ct_pessoas:

    PUT _component_template/ct_pessoas
    {
      "template": {
        "settings": {
          "number_of_shards": 1,
          "number_of_replicas": 1,
          "index.lifecycle.name": "policy_pessoas"
        },
        "mappings": {
          "properties": {
            "nome": {
              "type": "text"
            },
            "idade": {
              "type": "integer"
            },
            "sexo": {
              "type": "keyword"
            },
            "uf": {
              "type": "keyword"
            }
          }
        },
        "aliases": {
          "pessoas": {}
        }
      }
    }

Passo 3: Criação do index template

Nesta etapa vamos criar o template do índice contendo todas as informações que criamos e vamos também atrelar um padrão de nome para os índices se associarem a esse template.

  1. Definir o index template it_pessoas:

    PUT _index_template/it_pessoas
    {
      "index_patterns": ["pessoas_*"],
      "composed_of": ["ct_pessoas"]
    }
    
    

Passo 4: Indexar documentos de exemplo

  1. Indexar documentos no índice pessoas_rj:

    POST /_bulk
    { "index" : { "_index" : "pessoas_rj", "_id" : "1" } }
    { "nome" : "João da Silva", "sexo" : "masculino", "idade" : 19, "uf" : "RJ" }
    { "index" : { "_index" : "pessoas_rj", "_id" : "2" } }
    { "nome" : "Maria Oliveira", "sexo" : "feminino", "idade" : 28, "uf" : "RJ" }
    { "index" : { "_index" : "pessoas_rj", "_id" : "3" } }
    { "nome" : "Roberto Carlos", "sexo" : "masculino", "idade" : 35, "uf" : "RJ" }
    { "index" : { "_index" : "pessoas_rj", "_id" : "4" } }
    { "nome" : "Ana Maria", "sexo" : "feminino", "idade" : 14, "uf" : "RJ" }
    { "index" : { "_index" : "pessoas_rj", "_id" : "5" } }
    { "nome" : "Paulo Souza", "sexo" : "masculino", "idade" : 15, "uf" : "RJ" }
  2. Indexar documentos no índice pessoas_sp:

    POST /_bulk
    { "index" : { "_index" : "pessoas_sp", "_id" : "1" } }
    { "nome" : "Bruno Garcia", "sexo" : "masculino", "idade" : 22, "uf" : "SP" }
    { "index" : { "_index" : "pessoas_sp", "_id" : "2" } }
    { "nome" : "Renata Santos", "sexo" : "feminino", "idade" : 30, "uf" : "SP" }
    { "index" : { "_index" : "pessoas_sp", "_id" : "3" } }
    { "nome" : "Gilberto Ferreira", "sexo" : "masculino", "idade" : 48, "uf" : "SP" }
    { "index" : { "_index" : "pessoas_sp", "_id" : "4" } }
    { "nome" : "Maria Silva", "sexo" : "feminino", "idade" : 12, "uf" : "SP" }
    { "index" : { "_index" : "pessoas_sp", "_id" : "5" } }
    { "nome" : "Pedro Borges", "sexo" : "masculino", "idade" : 15, "uf" : "SP" }

Passo 5: Verificar indexação e aplicação das configurações

GET pessoas_rj
GET pessoas_sp
GET pessoas/_search

Conclusão

Você aprendeu a aplicar todas as configurações vistas nessa página em poucas operações. Configuramos uma política de ILM, atrelamos essa política a um component template onde adicionamos configurações do índice e mapeamento dos campos esperados; após isso, associamos essas criações a um template de índice, que ficou responsável por encontrar novos índices que atendesse aos critérios passados e aplicasse neles as configurações criadas.

Clone this wiki locally