Buscar Em um array json elastic search

Galera seguinte comprei o livro para sanar um problema além de obter conhecimentos.

Gostaria de saber como fazer a busca em um array de json no meu documento.

Boa noite, Jorge, tudo bom? Não sei se entendi exatamente a sua dúvida, vamos ver se consigo lhe ajudar, você gostaria de ter em seu documento do Elastic um campo que fosse um array de textos e que o Elastic fizesse a busca nesse array? Se for isso, é bem fácil de utilizar arrays no Elasticsearch, basta indexar os documentos como um array e todas as consultas do Elasticsearch farão a consulta no array. Vamos a um exemplo:

Vamos supor que eu tenho um índice com dados de produtos e que um desses campos é um array de categorias do produto. Um exemplo de mapeamento que podemos fazer para esse índice é o abaixo (estarei utilizando o curl do linux/unix para os comandos):

curl -XPOST 'localhost:9200/loja' curl -XPUT 'localhost:9200/loja/produto/_mapping' -d '{ “produto” : { "properties" : { “descricao” : {"type" : "string"}, “preco” : {"type" : “double”}, “categorias” : {"type" : “string”} } } }'

A seguir, vamos inserir alguns documentos para podermos testar consultas:
curl -XPOST 'localhost:9200/loja/produto' -d '{"descricao":"esta e a descricao 1 ['teste1','teste2','teste3'] do produto","preco":23.56,"categorias":["eletrônicos","som","carro"] }' curl -XPOST 'localhost:9200/loja/produto' -d '{"descricao":"esta e a descricao 2 ['teste1','teste4','teste6'] do produto","preco":73.56,"categorias":["eletrônicos","som","casa"] }' curl -XPOST 'localhost:9200/loja/produto' -d '{"descricao":"esta e a descricao 3 ['teste6','teste2','teste8'] do produto","preco":23.56,"categorias":["eletrônicos","tv","smart"] }'
Uma vez criado o índice e passando os dados nesse formato de array, é muito fácil fazermos consultas no array, basta consultarmos o campo como faríamos com qualquer outro campo. Por exemplo, se dentro dessa massa de produtos, quisermos consultar produtos que, dentro do array de categorias estão classificados como “smart”, basta consultarmos:

curl -XGET 'http://localhost:9200/loja/_search?pretty' -d '{ "query": { "query_string" : { "query": "categorias:smart" } } }'
Conforme podemos ver pelo resultado abaixo, a query retornou o documento correto:

{ "took" : 12, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 0.5, "hits" : [ { "_index" : "loja", "_type" : "produto", "_id" : "AVOXEKbyi8CnjCo0EpJz", "_score" : 0.5, "_source" : { "descricao" : "esta e a descricao 3 [teste6,teste2,teste8] do produto", "preco" : 23.56, "categorias" : [ "eletrônicos", "tv", "smart" ] } } ] } }

A consulta também permite que possamos buscar por mais de um valor do array, como na query abaixo:

curl -XGET 'http://localhost:9200/loja/_search?pretty' -d '{ "query": { "query_string" : { "query": "categorias:smart AND categorias:tv" } } }'

Por fim, você deve ter reparado que eu coloquei um array json no meio do texto das descrições, certo? Isso é apenas para demonstrar que, se for o seu caso de que você tem um array json no meio do texto do seu documento, também é possível pesquisar pelos valores dentro desses arrays, por exemplo se você pesquisar por:

curl -XGET 'http://localhost:9200/loja/_search?pretty' -d '{ "query": { "query_string" : { "query": "descricao:teste8" } } }'

Você verá pelo resultado, que a pesquisa foi feita corretamente:

{ "took" : 2, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 0.3125, "hits" : [ { "_index" : "loja", "_type" : "produto", "_id" : "AVOXEKbyi8CnjCo0EpJz", "_score" : 0.3125, "_source" : { "descricao" : "esta e a descricao 3 [teste6,teste2,teste8] do produto", "preco" : 23.56, "categorias" : [ "eletrônicos", "tv", "smart" ] } } ] } }

Espero que tenha conseguido responder a sua dúvida, qualquer coisa não hesite em responder, abs!

Olá alexandreesl então seria buscar em um array de json dentro do campo musics

tipo:
{
“title” : “John”,
“musics” : [
{“name” : “dear marie”},
{“name” : “perfect lonely”}
]
}

Opa Jorge, então vamos a sua dúvida, vamos fazer outro exemplo, tenhamos então uma massa de testes no formato do seu problema:

curl -XPOST 'localhost:9200/loja/music' -d '{"title":"James Retfield","musics":[ {"name" : "One"}, {"name" : "Sad but true"}, {"name" : "Unforgiven"}] }' curl -XPOST 'localhost:9200/loja/music' -d '{"title":"John Lennon","musics":[ {"name" : "She loves you"}, {"name" : "Here comes the sun"}, {"name" : "Imagine"}] }' curl -XPOST 'localhost:9200/loja/music' -d '{"title":"Bruce Dickson","musics":[ {"name" : "Aces High"}, {"name" : "Iron Maiden"}, {"name" : "Wasted Years"}] }'

Tendo essa massa, podemos perfeitamente fazer buscas como no exemplo abaixo, onde buscamos documentos que contenham as palavras ‘loves’ e ‘sad’ nos nomes das músicas:

curl -XGET 'http://localhost:9200/loja/_search?pretty' -d '{ "query": { "query_string" : { "query": "musics.name:loves OR musics.name:sad" } } }'

Que gera um resultado correto como o abaixo:

{ "took" : 15, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 0.019691018, "hits" : [ { "_index" : "loja", "_type" : "music", "_id" : "AVObTCovylhGHQI8Ukfz", "_score" : 0.019691018, "_source" : { "title" : "James Retfield", "musics" : [ { "name" : "One" }, { "name" : "Sad but true" }, { "name" : "Unforgiven" } ] } }, { "_index" : "loja", "_type" : "music", "_id" : "AVObT_5mylhGHQI8Ukf0", "_score" : 0.014065012, "_source" : { "title" : "John Lennon", "musics" : [ { "name" : "She loves you" }, { "name" : "Here comes the sun" }, { "name" : "Imagine" } ] } } ] } }

Respondi sua dúvida agora?

abs

Sim. tenho mais 2 dúvidas hehe.

Como posso está passando essa consulta para a library do elastic search em php.

e a outra dúvida é:

è mais seguro utilizar a library php para “encaspular” o ip de onde é feito request utilizando a library php dele na minha aplicação junto com shield do que utilizar o ip mesmo via ajax para fazer o request nesse ip com shield ?

Olá, Jorge! Cara, infelizmente não tenho familiaridade com essa library php então não sei dizer a sua primeira pergunta, mas acredito que seja análoga a um módulo Python que eu tenho utilizado bastante, nesse módulo existe um método de consulta genérica que recebe como parâmetro o body da consulta em formato JSON, então acredito que a library php deve ter algo nesse mesmo estilo. Quanto à segurança, não vejo problemas em você usar a library para efetuar a chamada ao invés de chamar o elasticsearch na mão, as libraries sempre são boas para facilitar a nossa vida.

Abs

Boa noite Alexandre, Te perguntar eu consigo usar no filter -> bool -> should o termo exato ?

Pois por exemplo:

tenho um post de Alvorada de Minas, mas se eu buscar Antônio prado de Minas ele aparece. pois estou usando match.

Bom dia, Jorge! Se entendi corretamente sua dúvida, você gostaria que na sua query viesse apenas documentos de alvorada de minas, correto? Bom, conforme informado na documentação da elastic (referência: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html) se você não possuir cláusulas de must ou filter na bool query, os filtros na cláusula should passaram a filtrar na sua pesquisa - mas você precisará definir o atributo minimum_should_match para a mesma quantidade dos seus filtros de should para definir que você só quer documentos que tenham os dois termos.

Uma outra alternativa que eu acho mais simples é que você utilize uma query string e filtre no seu campo pelas duas palavras, com um AND, por exemplo como eu fiz nos exemplos do outro Jorge aí acima, aí você poderia fazer algo assim:

{
"query": {
"query_string" : {
"query": "texto:alvorada AND texto:minas"
}
}
}

O ponto principal é que, devido ao Elasticsearch ser uma engine de busca, o mesmo irá trazer todos os documentos que ele considerar relevantes - a partir de um algoritmo de relevância que você pode encontrar mais detalhes no livro - para os filtros informados, sendo assim, se desejarmos que tenhamos resultados mais específicos, de fato temos que informar mais filtros.

Dois últimos pontos que gostaria de destacar, caso lhe ajude, é que vamos supor que essa parte da informação você gostaria que sempre fosse uma consulta exata, por exemplo alvorada de minas eu entendo que seja que você gostaria de pesquisar por documentos originados dessa cidade, correto? Se assim for e os detalhes da sua implementação permitirem, você poderia criar um campo específico somente com o munícipio do documento, especificamente para as consultas por munícipio.

Nesse caso, como essas consultas sempre seriam por termos exatos, você pode definir que nesse campo o elasticsearch não fará indexação, desse modo a consulta sempre será pelos termos exatos, através do atributo “index”: “not_analyzed” quer pode ser definido quando você cria os tipos de documentos do índice através da interface de mapeamento.

A última dica é para não esquecer de definir o analisador de português brasileiro, pois ele tratará questões como acentuação etc. Outro ponto importante é colocar o filtro de stopwords, esse filtro irá fazer com que palavras como “de”, “para”, “a”, “as”, “os” etc sejam excluídas da indexação - elas continuarão aparecendo nos resultados! - assim você evita gastar recursos do indexador desnecessariamente com a indexação de palavras que não tem relevância alguma numa consulta. A configuração também é feita no momento da criação do índice, como no exemplo abaixo:

"settings": {
                    "analysis": {
                        "filter": {
                            "my_stop": {
                                "type": "stop",
                                "stopwords": "_brazilian_"
                            }
                        }
                    }
                }

Espero ter podido lhe ajudar, por favor, se quiser avançar mais na discussão, pode por favor abrir outro tópico? Apenas para que não misturemos os assuntos no mesmo tópico, assim facilita para que os outros leitores possam achar mais rapidamente a informação.

obrigado