Fala pessoal! Tudo certo?
Espero que sim! 🙂
Vamos continuar avançando com os conceitos do Elasticsearch.
Caso não tenha acompanhado a primeira postagem, você pode ir no link abaixo, pois ela servirá de base para esta postagem. 😉
https://thedataengineer.com.br/2021/05/14/instalacao-e-utilizacao-elasticsearch-basico/
Os pontos que quero abordar aqui são:
- Criação de mapping
- Analyzers
- Id do index (documento)
- Bulk
Bora começar então!
Criação de Mapping
Para fazer um paralelo com o mundo relacional e facilitar o entendimento do Mapping, podemos dizer que o Mapping é o “schema” de uma tabela, onde definimos os campos, tipos e regras aplicadas.
No post anterior, deixamos que o próprio Elasticsearch definisse nosso Mapping, isso é legal do ponto de vista de facilidade, mas, pode ser um problema quando um tipo indesejado é atribuído ao campo.
Para verificarmos nosso Mapping atual, podemos utilizar o comando abaixo:
GET /meu-primeiro-index/_mapping
O nosso mapping é retornado como reposta:
{
"meu-primeiro-index" : {
"mappings" : {
"properties" : {
"avaliacoes" : {
"type" : "long"
},
"direcao" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"elenco" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"link_adorocinema" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"nota" : {
"type" : "float"
},
"roteiro" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"titulo" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
Agora, para definirmos nosso próprio mapping, devemos utilizar a maneira explicita:
PUT /meu-segundo-index
{
"mappings": {
"properties": {
"titulo" : {"type" : "text"},
"direcao" : {"type" : "text"},
"roteiro" : {"type" : "text"},
"elenco" : {"type" : "text"},
"nota" : {"type" : "float"},
"avaliacoes" : {"type" : "integer"},
"link_adorocinema" : {"type": "text"}
}
}
}
Como retorno temos a seguinte saída no Kibana:
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "meu-segundo-index"
}
Um ponto importante para destacar aqui. Temos alguns campos que tem o tipo “text” que receberão listas no documento, o Elasticsearch não tem um tipo “list” ou “array”, mas todos os campos podem conter mais de um valor por definição, desde que tenham o mesmo tipo.
Para saber mais, veja a documentação: https://www.elastic.co/guide/en/elasticsearch/reference/current/array.html
Analyzers
Analyzers permitem que o Elasticseach realize a indexação do texto e a busca dos mesmos, utilizando analise de texto. Esse recurso é útil para resolver um problema da busca não exata.
Da forma que realizamos a busca na primeira parte, tivemos que incluir a correspondência exata do nome do titulo, caso contrario não teríamos obtido resultado.
Com o analizer adequado, podemos transformar aquele texto em um token que melhor se adequá à nossa indexação e então realizar uma busca não exata pelo termo. Isso é útil quando temos acentos, pontos ou erros de digitação na busca.
Esse é um dos motivos para o Elasticsearch retornar o score de uma busca.
Antes de iniciarmos a pratica, cabe aqui ressaltar que já estamos utilizando um analyzer, ele é o default. Não tivemos que nos preocupar com isso na criação do nosso mapping. Esse analyzer padrão apenas quebra as palavras de um texto por espaço e remove pontos. Digamos então tenhamos os seguintes textos para inserir:
- Olá, mundo (doc 1)
- Olá, Elasticsearch (doc 2)
- Esse mundo é grande (doc 3)
O analyzer padrão irá criar o que se chama de índice invertido, associando a palavra ao documento. O índice gerado do exemplo acima ficaria da seguinte forma:
"olá" : {1, 2}
"mundo" : {1, 3}
"elasticsearch" : {2}
"é" : {3}
"grande" : {3}
Como puderam ver, os acentos são armazenados no índice invertido que é onde o Elasticsearch realiza a busca, logo, se não houver uma correspondência exata na pesquisa não localizaremos o documento.
Os tipos mais comuns de analyzers são:
- Padrão (deixa o texto em caixa baixa e retira virgulas e pontos).
- Simples (deixa o texto em caixa baixa e remove algarismos).
- Idioma (Portuguese, English e etc…).
- Esse é um dos mais importantes, onde podemos remover acentuação, deixar as palavras no singular e permite que adicionemos sinônimos dentro da nossa pesquisa em recursos mais avançados.
Podemos utilizar o analisador do Elasticsearch para verificar como a analise de um certo texto é feita, vejamos no exemplo abaixo:
GET /meu-segundo-index/_analyze
{
"text" : "O Poderoso Chefão"
}
Resposta:
{
"tokens" : [
{
"token" : "o",
"start_offset" : 0,
"end_offset" : 1,
"type" : "",
"position" : 0
},
{
"token" : "poderoso",
"start_offset" : 2,
"end_offset" : 10,
"type" : "",
"position" : 1
},
{
"token" : "chefão",
"start_offset" : 11,
"end_offset" : 17,
"type" : "",
"position" : 2
}
]
}
Agora, vejamos como o token seria criado caso utilizássemos o analyzer portuguese:
GET /meu-segundo-index/_analyze
{
"analyzer": "portuguese",
"text" : "O Poderoso Chefão"
}
Resposta:
{
"tokens" : [
{
"token" : "poderos",
"start_offset" : 2,
"end_offset" : 10,
"type" : "",
"position" : 1
},
{
"token" : "chefa",
"start_offset" : 11,
"end_offset" : 17,
"type" : "",
"position" : 2
}
]
}
Como podem ver, foram retirados os acentos e realizado um ajuste no texto para evitarmos “poderoso, poderosa, poderosos, etc.”. Isso ajuda o Elasticsearch na busca não exata.
Vamos adicionar o analyzer portuguese em nossos títulos, no momento da criação do índice.
PUT /meu-terceiro-index
{
"mappings": {
"properties": {
"titulo" : {
"type" : "text"
,"analyzer": "portuguese"
},
"direcao" : {"type" : "text"},
"roteiro" : {"type" : "text"},
"elenco" : {"type" : "text"},
"nota" : {"type" : "float"},
"avaliacoes" : {"type" : "integer"},
"link_adorocinema" : {"type": "text"}
}
}
}
Depois de adicionar o documento abaixo nos dois índices, podemos tentar realizar a busca não exata.
# Documentos
POST meu-segundo-index/_doc/
{
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : ["Francis Ford Coppola","Mario Puzo"],
"elenco" : ["Marlon Brando", "Al Pacino", "James Caan"],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
POST meu-terceiro-index/_doc/
{
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : ["Francis Ford Coppola","Mario Puzo"],
"elenco" : ["Marlon Brando", "Al Pacino", "James Caan"],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
Vamos buscar a palavra “chefao” (sem acento) no “meu-segundo-index”.
GET /meu-segundo-index/_search?q=chefao
# Resultado:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
Agora, veja a mesma busca no índice “meu-terceiro-index”.
GET /meu-terceiro-index/_search?q=chefao
# Resultado:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.2876821,
"hits" : [
{
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "8_s1KXoBlL3R1jOB_LjG",
"_score" : 0.2876821,
"_source" : {
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : [
"Francis Ford Coppola",
"Mario Puzo"
],
"elenco" : [
"Marlon Brando",
"Al Pacino",
"James Caan"
],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
}
]
}
}
Bem legal, né?
Ainda poderíamos adicionar novos elementos dentro do analisador, como por exemplo, sinônimos de palavras, associando uma palavra especifica à outras. Com por exemplo:
Caso buscássemos NoSQL, poderíamos associar essa palavra à outros termos como por exemplo: MongoDB, Redis, Elasticsearch e outros.
Abordarei essa prática em uma outra postagem, creio que o analyzer portuguese já serve de inicio para buscas um pouco mais avançadas que a padrão.
Id do index
Note que quando inserimos um novo documento o Elasticsearch cria o campo “_id” automaticamente. Isso não é muito bom, pois, imagine que eu queira atualizar o mesmo documento?
Se eu tentasse reinserir um documento que já existe, o Elasticsearch criaria dois ids diferentes para cada um deles.
Podemos corrigir esse comportamento de maneira bem simples. Vamos lá? 🙂
Estamos realizando o POST do documento da seguinte forma:
POST meu-terceiro-index/_doc/
{
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : ["Francis Ford Coppola","Mario Puzo"],
"elenco" : ["Marlon Brando", "Al Pacino", "James Caan"],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
Devemos passar nesse POST, também o valor “/1” (id do documento que queremos atualizar) e apenas isso já é o suficiente para atribuirmos o valor que quisermos no campo “_id”
POST meu-terceiro-index/_doc/1
{
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : ["Francis Ford Coppola","Mario Puzo"],
"elenco" : ["Marlon Brando", "Al Pacino", "James Caan"],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
Agora podemos realizar o GET desse documento, conforme a sintaxe abaixo:
GET meu-terceiro-index/_doc/1
#Resultado:
{
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 1,
"_primary_term" : 2,
"found" : true,
"_source" : {
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : [
"Francis Ford Coppola",
"Mario Puzo"
],
"elenco" : [
"Marlon Brando",
"Al Pacino",
"James Caan"
],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
}
Podemos notar que além do valor “_id” estar com “1” (valor que atribuímos), ele também tem a propriedade “_version“, com o valor 1.
Esse campo corresponde a versão do documento, caso eu realize um novo POST esse numero mudará, veja:
POST meu-terceiro-index/_doc/1
{
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : ["Francis Ford Coppola","Mario Puzo"],
"elenco" : ["Marlon Brando", "Al Pacino", "James Caan"],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
GET meu-terceiro-index/_doc/1
# Resultado:
{
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"_seq_no" : 2,
"_primary_term" : 2,
"found" : true,
"_source" : {
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : [
"Francis Ford Coppola",
"Mario Puzo"
],
"elenco" : [
"Marlon Brando",
"Al Pacino",
"James Caan"
],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
}
Não é possível recuperar versões anteriores do documento, caso você queira realizar esse tipo de ação, terá que implementar você mesmo. Esse campo serve apenas para verificarmos quantas vezes o documento foi sobrescrito.
Bulk
O Bulk é utilizado quando queremos realizar uma tarefa massiva dentro do Elasticsearch, para essa postagem, irei demostrar como inserir dados de forma massiva.
Podemos executar muitas outras ações, como create, update e delete.
Para mais informações veja a documentação: https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
Agora, vamos inserir 3 documentos de uma só vez utilizando o bulk:
POST meu-terceiro-index/_bulk
{"index":{"_id":2}}
{"titulo":"O Auto da Compadecida","direcao":"Guel Arraes","roteiro":["Guel Arraes","João Falcão"],"elenco":["Matheus Nachtergaele","Selton Mello","Denise Fraga"],"nota":4.7,"avaliacoes":3026,"link_adorocinema":"https://www.adorocinema.com/filmes/filme-120824/"}
{"index":{"_id":3}}
{"titulo":"A Lista de Schindler","direcao":"Steven Spielberg","roteiro":"Steven Spielberg","elenco":["Liam Neeson","Ben Kingsley","Ralph Fiennes"],"nota":4.8,"avaliacoes":4744,"link_adorocinema":"https://www.adorocinema.com/filmes/filme-9393/"}
{"index":{"_id":4}}
{"titulo":"O Senhor dos Anéis - O Retorno do Rei","direcao":"Peter Jackson","roteiro":["Peter Jackson","Philippa Boyens"],"elenco":["Sean Astin","Elijah Wood","Viggo Mortensen"],"nota":4.7,"avaliacoes":7726,"link_adorocinema":"https://www.adorocinema.com/filmes/filme-39187/"}
# Resultado:
{
"took" : 27,
"errors" : false,
"items" : [
{
"index" : {
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 2,
"status" : 201
}
},
{
"index" : {
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "3",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 4,
"_primary_term" : 2,
"status" : 201
}
},
{
"index" : {
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "4",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 5,
"_primary_term" : 2,
"status" : 201
}
}
]
}
Uma coisa que deve-se notar, é que a indentação importa no caso da inserção. Cada dado deve estar na sua linha.
Depois, podemos realizar um GET para verificar os resultados.
GET meu-terceiro-index/_search
{
"query": {
"match_all": {}
}
}
# Resultado:
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "8_s1KXoBlL3R1jOB_LjG",
"_score" : 1.0,
"_source" : {
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : [
"Francis Ford Coppola",
"Mario Puzo"
],
"elenco" : [
"Marlon Brando",
"Al Pacino",
"James Caan"
],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
},
{
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"titulo" : "O Poderoso Chefão",
"direcao" : "Francis Ford Coppola",
"roteiro" : [
"Francis Ford Coppola",
"Mario Puzo"
],
"elenco" : [
"Marlon Brando",
"Al Pacino",
"James Caan"
],
"nota" : 4.8,
"avaliacoes" : 6112,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-1628/"
}
},
{
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"titulo" : "O Auto da Compadecida",
"direcao" : "Guel Arraes",
"roteiro" : [
"Guel Arraes",
"João Falcão"
],
"elenco" : [
"Matheus Nachtergaele",
"Selton Mello",
"Denise Fraga"
],
"nota" : 4.7,
"avaliacoes" : 3026,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-120824/"
}
},
{
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"titulo" : "A Lista de Schindler",
"direcao" : "Steven Spielberg",
"roteiro" : "Steven Spielberg",
"elenco" : [
"Liam Neeson",
"Ben Kingsley",
"Ralph Fiennes"
],
"nota" : 4.8,
"avaliacoes" : 4744,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-9393/"
}
},
{
"_index" : "meu-terceiro-index",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"titulo" : "O Senhor dos Anéis - O Retorno do Rei",
"direcao" : "Peter Jackson",
"roteiro" : [
"Peter Jackson",
"Philippa Boyens"
],
"elenco" : [
"Sean Astin",
"Elijah Wood",
"Viggo Mortensen"
],
"nota" : 4.7,
"avaliacoes" : 7726,
"link_adorocinema" : "https://www.adorocinema.com/filmes/filme-39187/"
}
}
]
}
}
Notem que até o primeiro índice inserido sem passar o _id está entre os resultados. 😀
Essa é uma ótima maneira de realizar operações massivas, recomento a leitura da documentação, lá temos todas as informações necessárias para performar esse tipo de ação.
Conclusão
Bom, espero ter contribuído nem que seja um pouquinho com a melhora do conhecimento sobre o Elasticsearch.
Eu gosto bastante da ferramenta, ela pode se encaixar em diversos cenários, e como puderam ver, ela é de simples instalação.
Eu ainda me aprofundarei em como implementa-lá em um cluster, visto que uma das vantagens é a escalabilidade.
Também trarei em uma outra postagem o aprofundamento dos analyzers que é um recurso fundamental da tecnologia.
Espero que tenham gostado! Até logo! 🙂