ElasticSearch-一意の値を返す


122

languagesレコードからすべての値を取得して、それらを一意にする方法を教えてください。

記録

PUT items/1
{ "language" : 10 }

PUT items/2
{ "language" : 11 }

PUT items/3
{ "language" : 10 }

クエリ

GET items/_search
{ ... }

# => Expected Response
[10, 11]

どんな助けでも素晴らしいでしょう。


1
fields: [languages]与えられたフィールドの値のみを与えますが、それらを一意にすることはおそらくコードで行う方が簡単です。あなたのためにそれを行うことができる便利な集約があるかもしれませんが。
Ashalynd 2014

1
このトピックを研究している人のために、ここでも役立つディスカッションがあります:
elasticsearch

回答:


165

集計という用語を使用できます。

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

検索は次のようなものを返します:

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

size集計内のパラメータは、集計結果に含める用語の最大数を指定します。すべての結果が必要な場合は、これをデータ内の一意の用語の数よりも大きい値に設定します。


2
"fields" : ["language"]同じ結果をもたらします。回答を拡張して、集計フレームワークが言語値のみを返すことができるかどうかを確認できますか?#=> [10, 11, 10]
ChuckJHardy 2014

1
@CharlesJHardy、同じ結果にはなりません。探しているデータは「集約」キーの下にあります。結果の例を使用して回答を編集しました。また、「サイズ」を0に設定することもできます。0にすると、ドキュメントを含めずに、必要な集計結果のみを含めることができます。
アントン

1
多くの可能な値があるlanguage場合は、size=0およびを追加してshard_size=0すべての値を確実に取得できるようにしてください。elasticsearch.org/guide/en/elasticsearch/reference/current/…を
Dror

3
私はこの答えはOPを扱っていないと思います。元の質問では、カウントではなく明確なが必要です。何か不足していますか?
blowlow、2016年

4
@BHBH、答えは明確な値を提供します。これらは「キー」の値、つまり「10」、「11」、「12」です。(集計>言語>バケット>キー...)
アントン

9

Elasticsearch 1.1+には、一意のカウントを提供するCardinality Aggregationがあります。

これは実際には近似値であり、精度が高いカーディナリティデータセットでは低下する可能性がありますが、テストでは一般にかなり正確です。

precision_thresholdパラメータを使用して精度を調整することもできます。トレードオフ、またはコースは、メモリ使用量です。

docsからのこのグラフは、高いほどprecision_threshold、はるかに正確な結果につながることを示しています。


相対誤差としきい値


2
Cardinality Aggregationは、用語が存在する場合、それが結果に表示されることを保証しますか(カウント> = 1)?または、大規模なデータセットに一度だけ現れるいくつかの用語を見逃す可能性がありますか?
マーク

2
@マークは、設定した精度しきい値によって異なります。しきい値が高いほど、見逃す可能性が低くなります。精度しきい値の設定には40,000の制限があることに注意してください。つまり、それよりも高いデータセットは推定値が存在するため、単一の値が失われる可能性があります
Sundar

12
この答えは間違っていると思います。カーディナリティー集約は優れたツールです。ただし、タスクは用語自体を取得することであり、用語の数を見積もることではありませんでした。
アントン

4

languageフィールドの一意の値の最初のドキュメントを取得する場合は、これを行うことができます:

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

3

私自身もこのような解決策を探しています。用語集で参照が見つかりました。

したがって、それによると、以下は適切な解決策です。

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

しかし、次のエラーが発生した場合:

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

その場合、次のようにリクエストに「KEYWORD」を追加する必要があります。

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

1

近似値やマジックナンバー(size: 500)を設定せずにすべての一意の値を取得する場合はCOMPOSITE AGGREGATION(ES 6.5+)を使用します。

公式ドキュメントから:

「ネストされた用語の集計ですべての用語または用語のすべての組み合わせを取得する場合は、COMPOSITE AGGREGATION使用する必要があります。これにより、用語集計のフィールドのカーディナリティよりも大きいサイズを設定するのではなく、可能なすべての用語にページ番号を付けることができます。用語の集約は、上位の用語を返すことを意図しており、ページ分割を許可していません。」

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);

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.