ElasticSearch - Zwróć unikalne wartości

122

Jak uzyskać wartości wszystkich languagesrekordów i uczynić je wyjątkowymi.

Dokumentacja

PUT items/1
{ "language" : 10 }

PUT items/2
{ "language" : 11 }

PUT items/3
{ "language" : 10 }

Pytanie

GET items/_search
{ ... }

# => Expected Response
[10, 11]

Każda pomoc byłaby świetna.

ChuckJHardy
źródło
1
fields: [languages]poda tylko wartości danego pola, ale uczynienie ich unikalnymi jest prawdopodobnie łatwiejsze w kodzie. Chociaż może istnieć przydatna agregacja, która może zrobić to za Ciebie.
Ashalynd
1
Dla tych, którzy
badają

Odpowiedzi:

165

Możesz użyć terminów agregacja .

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

Wyszukiwanie zwróci coś takiego:

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

sizeParametr ciągu Określa agregacji maksymalną liczbę terminów do uwzględnienia w wyniku agregacji. Jeśli potrzebujesz wszystkich wyników, ustaw to na wartość większą niż liczba unikalnych terminów w Twoich danych.

Anton
źródło
2
"fields" : ["language"]przynosi ten sam wynik. Czy możesz rozszerzyć swoją odpowiedź, aby sprawdzić, czy struktura agregacji może zwracać tylko wartości języka? #=> [10, 11, 10]
ChuckJHardy
1
@CharlesJHardy, to nie daje tego samego wyniku. Dane, których szukasz, znajdują się pod kluczem „agregacje”. Zredagowałem odpowiedź, podając przykładowy wynik. Możesz / powinieneś również ustawić "rozmiar": 0, aby nie uwzględniać żadnego z dokumentów, a jedynie żądane zagregowane wyniki.
Anton
1
Zauważ, że jeśli masz wiele możliwych wartości, languagemożesz chcieć dodać size=0i shard_size=0, aby upewnić się, że otrzymasz wszystkie wartości. Zobacz flexiblesearch.org/guide/en/elasticsearch/reference/current/…
Dror
3
Myślę, że ta odpowiedź nie dotyczy PO. Oryginalne pytanie chce, aby różne wartości się nie liczyły. Czy coś mi brakuje?
bhurlow
4
@BHBH, odpowiedź zawiera różne wartości. Są to wartości „klucza”, czyli „10”, „11” i „12”. (agregacje> langs> Buckets> key ...)
Anton
9

Elasticsearch 1.1+ ma agregację liczebności, która daje unikalną liczbę

Zauważ, że jest to w rzeczywistości przybliżenie, a dokładność może spaść w przypadku zestawów danych o wysokiej kardynalności, ale generalnie jest dość dokładna w moich testach.

Możesz także dostroić dokładność za pomocą precision_thresholdparametru. Kompromisem jest oczywiście użycie pamięci.

Ten wykres z dokumentacji pokazuje, jak wyższy precision_thresholdprowadzi do znacznie dokładniejszych wyników.


Względny błąd a próg

bradvido
źródło
2
Czy agregacja liczności gwarantuje, że jeśli termin istnieje, to pojawi się w wynikach (z liczbą> = 1)? Czy może przeoczyć niektóre terminy, które pojawiają się tylko raz w dużym zbiorze danych?
znak
2
@mark zależy od ustawionego progu dokładności. Im wyższy próg, tym mniejsza szansa, że ​​przegapi. Zauważ, że istnieje limit 40 000 w ustawianiu progu dokładności. Co oznacza, że ​​zestaw danych jest wyższy niż ten, będzie oszacowanie, a zatem pojedyncza wartość może zostać pominięta
Sundar
12
Uważam, że ta odpowiedź jest błędna. Agregacja liczności jest doskonałym narzędziem. Jednak zadaniem było pobranie samych terminów, a nie oszacowanie liczby różnych terminów.
Anton
4

Ja też szukam tego typu rozwiązania dla siebie. Znalazłem odniesienie w kategoriach agregacji .

Tak więc, zgodnie z tym, właściwe rozwiązanie jest następujące.

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

Ale jeśli napotkasz następujący błąd:

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

W takim przypadku musisz dodać „ KEYWORD ” w żądaniu, na przykład:

   {
    "aggs" : {
        "langs" : {
            "terms" : { "field" : "language.keyword",  
                        "size" : 500 }
        }
    }}
MAULIK MODI
źródło
4

jeśli chcesz uzyskać pierwszy dokument dla każdej languageunikalnej wartości pola, możesz to zrobić:

{
 "query": {
    "match_all": {
    }
  },
  "collapse": {
    "field": "language.keyword",
    "inner_hits": {
    "name": "latest",
      "size": 1
    }
  }
}
MajidJafari
źródło
1

Jeśli chcesz uzyskać wszystkie unikalne wartości bez żadnego przybliżenia lub ustawiania magicznej liczby ( size: 500), użyj AGREGACJI KOMPOZYTU (ES 6.5+) .

Z oficjalnej dokumentacji :

„Jeśli chcesz pobrać wszystkie terminy lub wszystkie kombinacje terminów w zagnieżdżonej agregacji terminów , powinieneś użyć AGREGACJI ZŁOŻONEJ, która umożliwia stronicowanie wszystkich możliwych terminów zamiast ustawiania rozmiaru większego niż liczność pola w agregacji terminów. agregacja terminów ma na celu zwrócenie najpopularniejszych terminów i nie pozwala na podział na strony ”.

Przykład implementacji w 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
źródło