特定の条件を満たすグループの数を取得したい。 SQLの用語では、Elasticsearchで次のことを行います。
SELECT COUNT(*) FROM
(
SELECT
senderResellerId,
SUM(requestAmountValue) AS t_amount
FROM
transactions
GROUP BY
senderResellerId
HAVING
t_amount > 10000 ) AS dum;
これまでのところ、用語集約によってsenderResellerIdでグループ化できました。しかし、フィルターを適用すると、期待どおりに機能しません。
弾性リクエスト
{
"aggregations": {
"reseller_sale_sum": {
"aggs": {
"sales": {
"aggregations": {
"reseller_sale": {
"sum": {
"field": "requestAmountValue"
}
}
},
"filter": {
"range": {
"reseller_sale": {
"gte": 10000
}
}
}
}
},
"terms": {
"field": "senderResellerId",
"order": {
"sales>reseller_sale": "desc"
},
"size": 5
}
}
},
"ext": {},
"query": { "match_all": {} },
"size": 0
}
実際の応答
{
"took" : 21,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"hits" : {
"total" : 150824,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
"reseller_sale_sum" : {
"doc_count_error_upper_bound" : -1,
"sum_other_doc_count" : 149609,
"buckets" : [
{
"key" : "RES0000000004",
"doc_count" : 8,
"sales" : {
"doc_count" : 0,
"reseller_sale" : {
"value" : 0.0
}
}
},
{
"key" : "RES0000000005",
"doc_count" : 39,
"sales" : {
"doc_count" : 0,
"reseller_sale" : {
"value" : 0.0
}
}
},
{
"key" : "RES0000000006",
"doc_count" : 57,
"sales" : {
"doc_count" : 0,
"reseller_sale" : {
"value" : 0.0
}
}
},
{
"key" : "RES0000000007",
"doc_count" : 134,
"sales" : {
"doc_count" : 0,
"reseller_sale" : {
"value" : 0.0
}
}
}
}
}
]
}
}
}
上記の応答からわかるように、リセラーを返していますが、結果ではreseller_sale集計はゼロです。
詳細は こちら です。
_pipeline aggregations
_ のいずれか、つまり バケットセレクター集約 を使用できます。クエリは次のようになります。
_POST my_index/tdrs/_search
{
"aggregations": {
"reseller_sale_sum": {
"aggregations": {
"sales": {
"sum": {
"field": "requestAmountValue"
}
},
"max_sales": {
"bucket_selector": {
"buckets_path": {
"var1": "sales"
},
"script": "params.var1 > 10000"
}
}
},
"terms": {
"field": "senderResellerId",
"order": {
"sales": "desc"
},
"size": 5
}
}
},
"size": 0
}
_
次のドキュメントをインデックスに追加した後:
_ "hits": [
{
"_index": "my_index",
"_type": "tdrs",
"_id": "AV9Yh5F-dSw48Z0DWDys",
"_score": 1,
"_source": {
"requestAmountValue": 7000,
"senderResellerId": "ID_1"
}
},
{
"_index": "my_index",
"_type": "tdrs",
"_id": "AV9Yh684dSw48Z0DWDyt",
"_score": 1,
"_source": {
"requestAmountValue": 5000,
"senderResellerId": "ID_1"
}
},
{
"_index": "my_index",
"_type": "tdrs",
"_id": "AV9Yh8TBdSw48Z0DWDyu",
"_score": 1,
"_source": {
"requestAmountValue": 1000,
"senderResellerId": "ID_2"
}
}
]
_
クエリの結果は次のとおりです。
_"aggregations": {
"reseller_sale_sum": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "ID_1",
"doc_count": 2,
"sales": {
"value": 12000
}
}
]
}
}
_
つまり累積売上が_>10000
_であるsenderResellerId
のみ。
SELECT COUNT(*) FROM (... HAVING)
に相当するものを実装するには、- バケットスクリプトの集約 と バケットの集約の合計 の組み合わせを使用します。 _bucket_selector
_が実際に選択したバケットの数をカウントする直接的な方法はないようですが、条件に応じて_bucket_script
_または_0
_を生成する_1
_を定義できます。 sum
を生成する_sum_bucket
_:
_POST my_index/tdrs/_search
{
"aggregations": {
"reseller_sale_sum": {
"aggregations": {
"sales": {
"sum": {
"field": "requestAmountValue"
}
},
"max_sales": {
"bucket_script": {
"buckets_path": {
"var1": "sales"
},
"script": "if (params.var1 > 10000) { 1 } else { 0 }"
}
}
},
"terms": {
"field": "senderResellerId",
"order": {
"sales": "desc"
}
}
},
"max_sales_stats": {
"sum_bucket": {
"buckets_path": "reseller_sale_sum>max_sales"
}
}
},
"size": 0
}
_
出力は次のようになります。
_ "aggregations": {
"reseller_sale_sum": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
...
]
},
"max_sales_stats": {
"value": 1
}
}
_
目的のバケットカウントは_max_sales_stats.value
_にあります。
私は2つのことを指摘しなければなりません:
パイプライン集約は、ドキュメントセットからではなく、他の集約から生成された出力に対して機能し、出力ツリーに情報を追加します。
これは、terms
でのsenderResellerId
集約の結果の後に_bucket_selector
_集約が適用されることを意味します。たとえば、senderResellerId
集合のsize
よりもterms
が多い場合、IDはallを取得しません。 sum(sales) > 10000
を持つコレクション。ただし、terms
集約の出力に表示されるもののみ。ソートの使用を検討するか、十分なsize
パラメーターを設定してください。
これは、2番目のケースCOUNT() (... HAVING)
にも適用されます。これは、集計の出力に実際に存在するバケットのみをカウントします。
このクエリが重すぎる場合やバケットの数が多すぎる場合は、 非正規化 データを考慮するか、この合計をドキュメントに直接保存します。したがって、プレーン range
目標を達成するためのクエリ。
お役に立てば幸いです!