【问题标题】:Elastica not grouping aggregation before setScriptElastica 在 setScript 之前没有对聚合进行分组
【发布时间】:2018-08-06 09:06:06
【问题描述】:

我在 elasticsearch 中有一个类型产品,其中包含一个包含多个 id 的列,其中一些是相同的。 还有包含当前价格和数量的列。我希望得到每个唯一 ID 的价格 * qte 的总和。

id    price    qte
__    _____    _________

1    25        4
1    25        4
1    25        4
2    38        2
2    38        2
3    12        3
3    12        3
3    12        3
3    12        3
4    33        6
5    64        8
5    64        8

(如果您想知道为什么会这样,这是因为其他列的值不同,还要注意每个唯一的 id 都有唯一的价格和唯一的数量)

所以我创建了我的聚合:

$id = new \Elastica\Aggregation\Terms('id');
$id->setField('id')->setSize(0);

$qte_price = new \Elastica\Aggregation\Sum('qte_price');
$qte_price->setScript('doc["price"].value * doc["qte"].value');

$id->addAggregation($qte_price);

这里的问题是qte_price 在执行setScript() 之前没有使用id 上的第一个聚合,因此对所有id 甚至重复的id 求和price * qte 的总数。

换句话说,我想计算id=1 => 25*4(而不是(25*4)*3id=2 => 38*2(而不是(38*2)*2) ..等)

我发现解决这个问题的技巧是将答案除以 doc_count,但我正在寻找使用 Elastica 的官方方法。

【问题讨论】:

    标签: php elasticsearch elastica


    【解决方案1】:

    我将对priceqte 字段使用avg 聚合,然后使用bucket_script pipeline aggregation 将平均价格乘以平均数量,这将达到您的预期。

    3*25 的平均值是 25,3*4 的平均值是 4,然后你可以将 25 乘以 4,得到 id = 1 的答案...其他 id 也是如此。

    在纯 DSL 中,它看起来像这样:

    {
      "size": 0,
      "aggs": {
        "by_id": {
          "terms": {
            "field": "id"
          },
          "aggs": {
            "avg_price": {
              "avg": {
                "field": "price"
              }
            },
            "avg_qte": {
              "avg": {
                "field": "qte"
              }
            },
            "price_by_qte": {
              "bucket_script": {
                "buckets_path": {
                  "avgPrice": "avg_price",
                  "avgQte": "avg_qte"
                },
                "script": "params.avgPrice * params.avgQte"
              }
            }
          }
        }
      }
    }
    

    用 Elastica 表示,它会是这样的:

    $id = new \Elastica\Aggregation\Terms('id');
    $id->setField('id')->setSize(0);
    
    $avg_price = new \Elastica\Aggregation\Avg('avg_price');
    $avg_price->setField('price');
    $id->addAggregation($avg_price);
    
    $avg_qte = new \Elastica\Aggregation\Avg('avg_qte');
    $avg_qte->setField('qte');
    $id->addAggregation($avg_qte);
    
    $bucketScriptAggregation = new BucketScript(
        'price_by_qte',
        [
            'avgPrice' => 'avg_price',
            'avgQte' => 'avg_qte',
        ],
        'params.avgPrice * params.avgQte'
    );
    $id->addAggregation($bucketScriptAggregation);
    

    【讨论】:

    • 这是一个很好的解决方法,但我的在性能方面更好(将每个唯一 id 的答案除以 doc_count,从而得到正确的结果),因为我只使用 2 个聚合,而你的使用 3 + BucketScript 聚合。难道没有一种不那么饥饿的方法吗?
    • 不同之处在于,需要在您的应用程序中完成按 doc_count 的划分,而我的解决方案完全利用 ES,以便为您提供立即使用的准确答案。我不会太在意存储桶脚本 agg,它只是对已经聚合的数据执行计算的一种方式,它并不重。
    • 管道聚合已正式创建,用于在 ES 中对聚合结果执行计算并减轻客户端应用程序的负担。此外,在谈论性能时,必须始终对两种解决方案进行衡量,然后才能得出过早的结论;-)
    • 在我的情况下,我更喜欢使用我的技巧,因为它使用较少的聚合,但你的答案值得奖励,如果我只需要使用 elasticsearch 这样做,我会保留它以供参考。谢谢! :)
    • 我已经按照你的要求提供了一个“官方方式” ;-) 此外,你有没有测量过这两种解决方案的性能?
    猜你喜欢
    • 2020-11-09
    • 2018-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-14
    • 2020-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多