ElasticSearch - Retornar valores exclusivos

122

Como obter os valores de todos languagesos registros e torná-los únicos.

Registros

PUT items/1
{ "language" : 10 }

PUT items/2
{ "language" : 11 }

PUT items/3
{ "language" : 10 }

Inquerir

GET items/_search
{ ... }

# => Expected Response
[10, 11]

Qualquer ajuda seria ótimo.

ChuckJHardy
fonte
1
fields: [languages]fornecerá apenas os valores do campo especificado, mas torná-los únicos é provavelmente mais fácil de fazer no código. Embora possa haver, existe uma agregação útil que pode fazer isso por você.
Ashalynd 23/08/14
1
Para aqueles investigar este assunto, há também discussão útil aqui: Procurar valores distintos, contagens não distintas em ElasticSearch
BLONG

Respostas:

165

Você pode usar os termos de agregação .

{
"size": 0,
"aggs" : {
    "langs" : {
        "terms" : { "field" : "language",  "size" : 500 }
    }
}}

Uma pesquisa retornará algo como:

{
"took" : 16,
"timed_out" : false,
"_shards" : {
  "total" : 2,
  "successful" : 2,
  "failed" : 0
},
"hits" : {
"total" : 1000000,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
  "langs" : {
    "buckets" : [ {
      "key" : "10",
      "doc_count" : 244812
    }, {
      "key" : "11",
      "doc_count" : 136794

    }, {
      "key" : "12",
      "doc_count" : 32312
       } ]
    }
  }
}

O sizeparâmetro na agregação especifica o número máximo de termos a serem incluídos no resultado da agregação. Se você precisar de todos os resultados, defina-o como um valor maior que o número de termos exclusivos em seus dados.

Anton
fonte
2
"fields" : ["language"]traz de volta o mesmo resultado. Você pode expandir sua resposta para ver se a estrutura de agregação pode retornar apenas os valores de idioma? #=> [10, 11, 10]
precisa saber é o seguinte
1
@CharlesJHardy, não tem o mesmo resultado. Os dados que você está procurando estão na chave "agregações". Editei minha resposta com um exemplo de resultado. Você também pode / deve definir "tamanho": 0, para não incluir nenhum dos documentos, apenas os resultados agregados desejados.
Anton
1
Observe que, se você tiver muitos valores possíveis, languagepoderá adicionar size=0e shard_size=0, para obter todos os valores. Veja elasticsearch.org/guide/en/elasticsearch/reference/current/…
Dror
3
Eu acho que essa resposta não aborda o OP. A pergunta original quer que valores distintos não sejam importantes. Estou esquecendo de algo?
bhurlow
4
@BHBH, a resposta fornece os valores distintos. Eles são os valores "chave", ou seja, "10", "11" e "12". (agregações> langs> buckets> key ...)
Anton
9

O Elasticsearch 1.1+ possui a agregação de cardinalidade, que fornece uma contagem única

Observe que na verdade é uma aproximação e a precisão pode diminuir com os conjuntos de dados de alta cardinalidade, mas geralmente é bastante preciso nos meus testes.

Você também pode ajustar a precisão com o precision_thresholdparâmetro O trade-off, ou claro, é o uso de memória.

Este gráfico dos documentos mostra como um aumento precision_thresholdleva a resultados muito mais precisos.


Erro relativo vs limite

bradvido
fonte
2
A agregação de cardinalidade garante que, se um termo existir, ele aparecerá nos resultados (com uma contagem> = 1)? Ou poderia perder alguns termos que aparecem apenas uma vez em um grande conjunto de dados?
marca
2
@mark depende do limite de precisão que você definiu. Quanto maior o limite, menor a chance de que ele perca. Observe que há um limite de 40.000 na configuração de limite de precisão. O que significa que, um conjunto de dados maior que isso, haverá uma estimativa e, portanto, o valor único poderá ser perdido
Sundar
12
Eu acredito que esta resposta está errada. A agregação de cardinalidade é uma excelente ferramenta. No entanto, a tarefa era recuperar os próprios termos, não estimar quantos termos diferentes existem.
Anton
4

se você deseja obter o primeiro documento para cada language valor exclusivo de campo, pode fazer o seguinte:

{
 "query": {
    "match_all": {
    }
  },
  "collapse": {
    "field": "language.keyword",
    "inner_hits": {
    "name": "latest",
      "size": 1
    }
  }
}
MajidJafari
fonte
3

Também estou procurando esse tipo de solução para mim. Encontrei referência em termos de agregação .

Portanto, de acordo com o que segue, é a solução adequada.

{
"aggs" : {
    "langs" : {
        "terms" : { "field" : "language",  
                    "size" : 500 }
    }
}}

Mas se você encontrou o seguinte erro:

"error": {
        "root_cause": [
            {
                "type": "illegal_argument_exception",
                "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [fastest_method] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
            }
        ]}

Nesse caso, você deve adicionar " KEYWORD " na solicitação, como a seguir:

   {
    "aggs" : {
        "langs" : {
            "terms" : { "field" : "language.keyword",  
                        "size" : 500 }
        }
    }}
MAULIK MODI
fonte
1

Se você deseja obter todos os valores exclusivos sem aproximações ou definir um número mágico ( size: 500), use COMPOSITE AGGREGATION (ES 6.5+) .

Da documentação oficial :

"Se você deseja recuperar todos os termos ou todas as combinações de termos em uma agregação de termos aninhados, use a COMPOSITE AGGREGATION que permite paginar todos os termos possíveis em vez de definir um tamanho maior que a cardinalidade do campo na agregação de termos. A agregação de termos deve retornar os principais termos e não permite a paginação ".

Exemplo de implementação em JavaScript:

const ITEMS_PER_PAGE = 1000;

const body =  {
    "size": 0, // Returning only aggregation results: https://www.elastic.co/guide/en/elasticsearch/reference/current/returning-only-agg-results.html
    "aggs" : {
        "langs": {
            "composite" : {
                "size": ITEMS_PER_PAGE,
                "sources" : [
                    { "language": { "terms" : { "field": "language" } } }
                ]
            }
        }
     }
};

const uniqueLanguages = [];

while (true) {
  const result = await es.search(body);

  const currentUniqueLangs = result.aggregations.langs.buckets.map(bucket => bucket.key);

  uniqueLanguages.push(...currentUniqueLangs);

  const after = result.aggregations.langs.after_key;

  if (after) {
      // continue paginating unique items
      body.aggs.langs.composite.after = after;
  } else {
      break;
  }
}

console.log(uniqueLanguages);

Ilarion Halushka
fonte