Elasticsearch -如何通过同时使用来自两个字段的信息来创建桶?

3wabscal  于 2022-12-03  发布在  ElasticSearch
关注(0)|答案(1)|浏览(148)

我的文档是这样的:

{'start': 0, 'stop': 3, 'val': 3}
{'start': 2, 'stop': 4, 'val': 1}
{'start': 5, 'stop': 6, 'val': 4}

我们可以想象,每个文档占据从'start''stop'的x坐标,并且具有一定的值'val''start' < 'stop'是保证的)。

目标是绘制一条线,显示占据x坐标的所有文档中这些值'val'的总和:

this graph online
在现实中,有许多文档具有许多不同的'start''stop'坐标。速度很重要,因此:

最多几个ElasticSearch请求就可以实现这一点吗?如何实现?

我尝试过的

通过一个ElasticSearch请求,我们可以得到min_startmax_stop坐标,这些将是x的边界。
然后,我们将x坐标划分为N个区间,并在每个区间的循环中,我们发出一个ElasticSearch请求:以过滤掉完全位于该间隔之外的所有文档,并进行'val'的总和聚合。
由于存在N+1请求,因此这种方法花费的时间太多,如果我们希望得到精度更高的行,则时间将线性增加。
编码:

N = 300  # number of intervals along x

x = []
y = []

data = es.search(index='index_name',
                 body={
                    'aggs': {
                        'min_start': {'min': {'field': 'start'}},
                        'max_stop': {'max': {'field': 'stop'}}
                    }
                 })
min_x = data['aggregations']['min_start']['value']
max_x = data['aggregations']['max_stop']['value']

x_from = min_x
x_step = (max_x - min_x) / N
for _ in range(N):
    x_to = x_from + x_step
    
    data = es.search(
        index='index_name', 
        body= {
            'size': 0,  # to not return any actual documents

            'query': {
                'bool': {
                    'should': [
                        # start is in the current x-interval:
                        {'bool': {'must': [
                            {'range': {'start': {'gte': x_from}}},
                            {'range': {'start': {'lte': x_to}}}
                        ]}},

                        # stop is in the current x-interval:
                        {'bool': {'must': [
                            {'range': {'stop': {'gte': x_from}}},
                            {'range': {'stop': {'lte': x_to}}}
                        ]}},

                        # current x-interval is inside start--stop
                        {'bool': {'must': [
                            {'range': {'start': {'lte': x_from}}},
                            {'range': {'stop': {'gte': x_to}}}
                        ]}}
                    ],
                    'minimum_should_match': 1  # at least 1 of these 3 conditions should match
                }
            },

            'aggs': {
                'vals_sum': {'sum': {'field': 'val'}}
            }
        }
    )
    
    # Append info to the lists:
    x.append(x_from)    
    y.append(data['aggregations']['vals_sum']['value'])
    
    # Next x-interval:
    x_from = x_to

from matplotlib import pyplot as plt
plt.plot(x, y)
vsdwdz23

vsdwdz231#

正确的方法是使用range field typeavailable since 5.2),而不是使用两个字段startstop,然后重新实现相同的逻辑,如下所示:

PUT test 
{
  "mappings": {
    "properties": {
      "range": {
        "type": "integer_range"
      },
      "val": {
        "type":"integer"
      }
    }
  }
}

您的文档将如下所示:

{
      "range" : {
        "gte" : 0,
        "lt" : 3
      },
      "val" : 3
    }

然后,查询将简单地利用histogram聚合,如下所示:

POST test/_search 
{
  "size": 0,
  "aggs": {
    "histo": {
      "histogram": {
        "field": "range",
        "interval": 1
      },
      "aggs": {
        "total": {
          "sum": {
            "field": "val"
          }
        }
      }
    }
  }
}

结果如预期:三三四一零四

相关问题