Je veux obtenir le nombre de groupes qui remplissent une certaine condition. En termes SQL, je veux faire ce qui suit dans Elasticsearch.
SELECT COUNT(*) FROM
(
SELECT
senderResellerId,
SUM(requestAmountValue) AS t_amount
FROM
transactions
GROUP BY
senderResellerId
HAVING
t_amount > 10000 ) AS dum;
Jusqu'à présent, je pouvais regrouper par senderResellerId par agrégation de termes. Mais lorsque j'applique des filtres, cela ne fonctionne pas comme prévu.
Demande élastique
{
"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
}
Réponse réelle
{
"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
}
}
}
}
}
]
}
}
}
Comme vous pouvez le voir dans la réponse ci-dessus, il renvoie des revendeurs mais l'agrégation reseller_sale est nulle dans les résultats.
Plus de détails sont ici .
Vous pouvez utiliser l'un des pipeline aggregations
, à savoir agrégation du sélecteur de compartiment . La requête ressemblerait à ceci:
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
}
Après avoir mis les documents suivants dans l'index:
"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"
}
}
]
Le résultat de la requête est:
"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
}
}
]
}
}
C'est à dire. uniquement ceux senderResellerId
dont les ventes cumulées sont >10000
.
Pour implémenter un équivalent de SELECT COUNT(*) FROM (... HAVING)
on peut utiliser une combinaison de agrégation de script de bucket avec agrégation de bucket sum . Bien qu'il ne semble y avoir aucun moyen direct de compter le nombre de compartiments que bucket_selector
A réellement sélectionnés, nous pouvons définir un bucket_script
Qui produit 0
Ou 1
Selon une condition, et sum_bucket
qui produit son sum
:
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
}
La sortie sera:
"aggregations": {
"reseller_sale_sum": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
...
]
},
"max_sales_stats": {
"value": 1
}
}
Le nombre de compartiments souhaité se trouve dans max_sales_stats.value
.
Je dois souligner 2 choses:
Les agrégations de pipeline fonctionnent sur les sorties produites à partir d'autres agrégations plutôt qu'à partir de jeux de documents, ajoutant des informations à l'arborescence de sortie.
Cela signifie que l'agrégation bucket_selector
Sera appliquée après et sur le résultat de l'agrégation terms
sur senderResellerId
. Par exemple, s'il existe plus de senderResellerId
que size
de terms
définitions d'agrégation, vous n'obtiendrez pas tout les identifiants de la collection avec sum(sales) > 10000
, mais uniquement ceux qui apparaissent dans la sortie de l'agrégation terms
. Envisagez d'utiliser le tri et/ou définissez un paramètre size
suffisant.
Cela s'applique également au deuxième cas, COUNT() (... HAVING)
, qui ne comptera que les compartiments réellement présents dans la sortie de l'agrégation.
Dans le cas où cette requête est trop lourde ou le nombre de compartiments trop important, pensez à dénormalisation vos données ou stockez cette somme directement dans le document, afin que vous puissiez utiliser ordinaire range
requête pour atteindre votre objectif.
J'espère que cela pourra aider!