在elasticsearch中,filterby、groupby唯一字段值、sum聚合、orderby查询链



我正试图向elasticsearch发出一个查询,该查询可以进行筛选、分组、总和聚合和排序。我有两个问题:查询应该是什么样子,弹性搜索的性能影响是什么?

让我举一个数据集的例子来支持我的问题。假设我有一套销售:

document type: 'sales' with the following fields and data:
sale_datetime    | sold_product  | sold_at_price
-----------------|---------------|--------------
2015-11-24 12:00 | some product  | 100
2015-11-24 12:30 | some product  | 100
2015-11-24 12:30 | other product | 100
2015-11-24 13:00 | other product | 100
2015-11-24 12:30 | some product  | 200
2015-11-24 13:00 | some product  | 200

我想发出一个查询:

  • 仅考虑2015-11-24 12:15至2015-11-24 13:45期间的销售
  • 按sold_product字段对结果进行分组
  • 计算"每个产品的sold_at_price值总和"
  • 按最大的"每产品sold_at_price值之和"排在第一位、第二位的顺序返回行,依此类推

将其应用于上面的样本数据集,将返回以下结果:

sold_product  | sum of sold_at_price
--------------|--------------
some product  | 300     // takes into account rows 2 and 5
other product | 100     // takes into account row 3

如果可以发出这样的查询,那么弹性搜索的重要性能含义是什么?如果需要考虑:

  • 有许多(数十万,未来可能有数百万)独特的产品
  • 产品名称可能包括多个(几十个)单词/术语(可以生成仅由一个单词组成的唯一产品名称,但这几乎会使数据量增加一倍)
  • 通常有许多(数百万)条记录满足时间范围筛选器(在某些情况下,筛选器可以缩小到一个时间范围内的数万条记录,但不能保证)

提前感谢您的帮助!

这是聚合的典型用例。让我们首先创建一个索引并对数据的映射进行建模。对于sold_datetime,我们有一个普通的date字段,对于sold_at_price,我们有另一个数字字段,并且对于sold_product,我们有字符串类型的多字段。您会注意到,这个多字段有一个名为raw的子字段,它是not_analyzed,将用于在产品名称上创建聚合:

curl -XPUT localhost:9200/sales -d '{
  "mappings": {
    "sale": {
      "properties": {
        "sale_datetime": {
          "type": "date"
        },
        "sold_product": {
          "type": "string",
          "fields": {
            "raw": {
              "type": "string",
              "index": "not_analyzed"
            }
          }
        },
        "sold_at_price": {
          "type": "double"
        }
      }
    }
  }
}'

现在,让我们使用新索引的_bulk端点对样本数据集进行索引:

curl -XPOST localhost:9200/sales/sale/_bulk -d '
{"index": {}}
{"sold_datetime": "2015-11-24T12:00:00.000Z", "sold_product":"some product", "sold_at_price": 100}
{"index": {}}
{"sold_datetime": "2015-11-24T12:30:00.000Z", "sold_product":"some product", "sold_at_price": 100}
{"index": {}}
{"sold_datetime": "2015-11-24T12:30:00.000Z", "sold_product":"other product", "sold_at_price": 100}
{"index": {}}
{"sold_datetime": "2015-11-24T13:00:00.000Z", "sold_product":"other product", "sold_at_price": 100}
{"index": {}}
{"sold_datetime": "2015-11-24T12:30:00.000Z", "sold_product":"some product", "sold_at_price": 200}
{"index": {}}
{"sold_datetime": "2015-11-24T13:00:00.000Z", "sold_product":"some product", "sold_at_price": 200}
'

最后,让我们创建您需要的查询和聚合:

curl -XPOST localhost:9200/sales/sale/_search -d '{
  "size": 0,
  "query": {
    "filtered": {
      "filter": {
        "range": {
          "sold_datetime": {
            "gt": "2015-11-24T12:15:00.000Z",
            "lt": "2015-11-24T12:45:00.000Z"
          }
        }
      }
    }
  },
  "aggs": {
    "sold_products": {
      "terms": {
        "field": "sold_product.raw",
        "order": {
          "total": "desc"
        }
      },
      "aggs": {
        "total": {
          "sum": {
            "field": "sold_at_price"
          }
        }
      }
    }
  }
}'

如您所见,我们正在筛选sold_datetime字段的特定日期间隔(11月24日12:15-12:45)。聚合部分在sold_product.raw字段上定义terms聚合,并且对于每个bucket,我们sum定义sold_at_price字段的值。

请注意,如果您有数百万个可能匹配的文档,为了使其具有性能,您需要首先应用最积极的过滤器,可能是运行查询的企业的id,或者在运行聚合之前排除尽可能多的文档的其他条件。

结果如下:

{
  ...
  "aggregations" : {
    "sold_products" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [ {
        "key" : "some product",
        "doc_count" : 2,
        "total" : {
          "value" : 300.0
        }
      }, {
        "key" : "other product",
        "doc_count" : 1,
        "total" : {
          "value" : 100.0
        }
      } ]
    }
  }
}

最新更新