ElasticSearch - Restituisci valori univoci


122

Come ottenere i valori di tutti languagesi record e renderli unici.

Records

PUT items/1
{ "language" : 10 }

PUT items/2
{ "language" : 11 }

PUT items/3
{ "language" : 10 }

domanda

GET items/_search
{ ... }

# => Expected Response
[10, 11]

Qualsiasi aiuto sarebbe grande.


1
fields: [languages]fornirà solo i valori del campo dato, ma renderli univoci è probabilmente più facile da fare nel codice. Anche se potrebbe esserci una comoda aggregazione che può farlo per te.
Ashalynd

1
Per coloro che ricercano questo argomento, c'è anche una discussione utile qui: Trova valori distinti, non conteggi distinti in elasticsearch
blong

Risposte:


165

Puoi utilizzare i termini aggregazione .

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

Una ricerca restituirà qualcosa come:

{
"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
       } ]
    }
  }
}

Il sizeparametro all'interno dell'aggregazione specifica il numero massimo di termini da includere nel risultato dell'aggregazione. Se hai bisogno di tutti i risultati, impostalo su un valore maggiore del numero di termini univoci nei tuoi dati.


2
"fields" : ["language"]riporta lo stesso risultato. Puoi espandere la tua risposta per vedere se il framework di aggregazione può restituire solo i valori della lingua? #=> [10, 11, 10]
ChuckJHardy

1
@CharlesJHardy, non ha lo stesso risultato. I dati che stai cercando si trovano nella chiave "aggregazioni". Ho modificato la mia risposta con un risultato di esempio. Puoi / dovresti anche impostare "size": 0, per non includere nessuno dei documenti, solo i risultati aggregati che desideri.
Anton

1
Nota che se hai molti valori possibili per languagepotresti voler aggiungere size=0e shard_size=0, per assicurarti di ottenere tutti i valori. Vedi elasticsearch.org/guide/en/elasticsearch/reference/current/…
Dror

3
Penso che questa risposta non si rivolga all'OP. La domanda originale vuole che i valori distinti non contino. Mi sto perdendo qualcosa?
bhurlow

4
@BHBH, La risposta fornisce i valori distinti. Sono i valori "chiave", ovvero "10", "11" e "12". (aggregazioni> lang> bucket> chiave ...)
Anton

9

Elasticsearch 1.1+ ha la Cardinality Aggregation che ti darà un conteggio unico

Si noti che in realtà è un'approssimazione e l'accuratezza può diminuire con set di dati ad alta cardinalità, ma in genere è piuttosto accurato nei miei test.

È inoltre possibile regolare la precisione con il precision_thresholdparametro. Il compromesso, o corso, è l'utilizzo della memoria.

Questo grafico dalla documentazione mostra come un valore più alto precision_thresholdporta a risultati molto più accurati.


Errore relativo vs soglia


2
Ha cardinalità aggregazione garanzia che se esiste un termine, allora apparirà nei risultati (con un conteggio> = 1)? O potrebbe mancare alcuni termini che compaiono solo una volta in un set di dati di grandi dimensioni?
segna il

2
@mark dipende dalla soglia di precisione impostata. Più alta è la soglia, minori saranno le possibilità che manchi. Notare che esiste un limite di 40.000 nell'impostazione della soglia di precisione. Ciò significa che, un set di dati superiore a quello, ci sarà una stima e quindi il singolo valore potrebbe essere perso
Sundar

12
Credo che questa risposta sia sbagliata. L'aggregazione della cardinalità è uno strumento eccellente. Tuttavia, il compito era recuperare i termini stessi, non stimare quanti termini diversi ci sono.
Anton

4

se vuoi ottenere il primo documento per ogni languagevalore univoco del campo, puoi farlo:

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

3

Sto cercando questo tipo di soluzione anche per me stesso. Ho trovato riferimento in termini di aggregazione .

Quindi, secondo quanto segue è la soluzione corretta.

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

Ma se hai riscontrato il seguente errore:

"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."
            }
        ]}

In tal caso, devi aggiungere " KEYWORD " nella richiesta, come segue:

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

1

Se vuoi ottenere tutti i valori univoci senza alcuna approssimazione o senza impostare un numero magico ( size: 500), usa l' AGGREGAZIONE COMPOSITA (ES 6.5+) .

Dalla documentazione ufficiale :

"Se si desidera recuperare tutti i termini o tutte le combinazioni di termini in un'aggregazione di termini nidificati, è necessario utilizzare l'AGGREGAZIONE COMPOSITA che consente di impaginare tutti i termini possibili anziché impostare una dimensione maggiore della cardinalità del campo nell'aggregazione dei termini. l'aggregazione dei termini ha lo scopo di restituire i primi termini e non consente l'impaginazione. "

Esempio di implementazione in 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);

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.