Elasticsearch日期直方图桶的平均值

10

我在ElasticSearch中索引了许多文档,现在需要获取以下数据:

对于每个月份,获取每个工作日平均文档数量(如果不可能,则默认使用20天)。

我已经使用日期直方图聚合将数据汇总为月份桶。我尝试嵌套一个统计桶,但这种聚合使用的是从文档字段中提取出的数据,而不是父桶中的数据。

以下是我的查询:

{
    "query": {
        "match_all": {}
    },
    "aggs": {
        "docs_per_month": {
            "date_histogram": {
                "field": "created_date",
                "interval": "month",
                "min_doc_count": 0
            }
            "aggs": {
                '???': '???'
            }
        }
    }
}

编辑

为了让我的问题更加清晰,我需要:

  • 获取本月创建的文档总数(这已经通过date_histogram聚合完成)
  • 获取本月的工作日数
  • 将第一个数字除以第二个数字。


肯定需要更新我的个人资料... - Thibault J
4个回答

11

对于仍然感兴趣的人,您现在可以使用avg_bucket聚合完成此操作。这仍然有点棘手,因为您不能简单地在date_historgram聚合结果上运行avg_bucket,但是通过具有某些唯一值的辅助value_count聚合,它可以正常工作 :)

{
  "size": 0,
  "aggs": {
    "orders_per_day": {
      "date_histogram": {
        "field": "orderedDate",
        "interval": "day"
      },
      "aggs": {
        "amount": {
          "value_count": {
            "field": "dateCreated"
          }
        }
      }
    },
    "avg_daily_order": {
      "avg_bucket": {
        "buckets_path": "orders_per_day>amount"
      }
    }
  }
}

这个答案应该被接受。它考虑了最近版本中添加的内容,因此现在比其他答案更准确。 - SantiStSupery
不错的解决方法,谢谢。 - sleepyhead

4

有一种相当复杂且不太高效的解决方案,使用以下的scripted_metric聚合

{
  "size": 0,
  "query": {
    "match_all": {}
  },
  "aggs": {
    "docs_per_month": {
      "date_histogram": {
        "field": "created_date",
        "interval": "month",
        "min_doc_count": 0
      },
      "aggs": {
        "avg_doc_per_biz_day": {
          "scripted_metric": {
            "init_script": "_agg.bizdays = []; _agg.allbizdays = [:]; start = new DateTime(1970, 1, 1, 0, 0); now = new DateTime(); while (start < now) { def end = start.plusMonths(1); _agg.allbizdays[start.year + '_' + start.monthOfYear] = (start.toDate()..<end.toDate()).sum {(it.day != 6 && it.day != 0) ? 1 : 0 }; start = end; }",
            "map_script": "_agg.bizdays << _agg.allbizdays[doc. created_date.date.year+'_'+doc. created_date.date.monthOfYear]",
            "combine_script": "_agg.allbizdays = null; doc_count = 0; for (d in _agg.bizdays){ doc_count++ }; return doc_count / _agg.bizdays[0]",
            "reduce_script": "res = 0; for (a in _aggs) { res += a }; return res"
          }
        }
      }
    }
  }
}

接下来我们详细说明每个脚本。

init_script中我正在创建从1970年开始,每个月的工作日数量的一个映射,并将其存储在_agg.allbizdays 映射中。

_agg.bizdays = [];
_agg.allbizdays = [:]; 
start = new DateTime(1970, 1, 1, 0, 0);
now = new DateTime();
while (start < now) { 
    def end = start.plusMonths(1);     
    _agg.allbizdays[start.year + '_' + start.monthOfYear] = (start.toDate()..<end.toDate()).sum {(it.day != 6 && it.day != 0) ? 1 : 0 }; 
    start = end; 
}

map_script中,我只是获取每个文档所在月份的工作日数量;
_agg.bizdays << _agg.allbizdays[doc.created_date.date.year + '_' + doc. created_date.date.monthOfYear];

combine_script中,我正在计算每个分片的平均文档数量。
_agg.allbizdays = null;
doc_count = 0; 
for (d in _agg.bizdays){ doc_count++ }; 
return doc_count / _agg.bizdays[0];

最后,在reduce_script中,我正在对每个节点的平均文档计数求和:

res = 0; 
for (a in _aggs) { res += a }; 
return res

我认为这很复杂,正如Andrei所说,等到2.0版本发布后可能会更好,但是如果您需要的话,现在有这个解决方案。


2
你需要的基本上是这样的东西(但它不可用,因为这不是一项可用功能):
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "docs_per_month": {
      "date_histogram": {
        "field": "date",
        "interval": "month",
        "min_doc_count": 0
      },
      "aggs": {
        "average": {
          "avg": {
            "script": "doc_count / 20"
          }
        }
      }
    }
  }
}

它无法工作,因为没有办法从“父”聚合中访问doc_count。但是,在Elasticsearch的2.x分支中将有可能实现这一点,目前正在积极开发中:https://github.com/elastic/elasticsearch/issues/8110。这个新功能将在一个聚合结果(桶)之上添加第二层操作,并且不仅仅是你的用例,还有很多其他用例。除非你想尝试一些想法或在你的应用程序中执行自己的计算,否则你需要等待这个功能。

1
你想排除周六和周日的时间戳文档,因此可以使用脚本在查询中排除这些文档。
{
  "query": {
    "filtered": {
      "filter": {
        "script": {
          "script": "doc['@timestamp'].date.dayOfWeek != 7 && doc['@timestamp'].date.dayOfWeek != 6"
        }
      }
    }
  },
  "aggs": {
    "docs_per_month": {
      "date_histogram": {
        "field": "created_date",
        "interval": "month",
        "min_doc_count": 0
      },
      "aggs": {
        "docs_per_day": {
          "date_histogram": {
            "field": "created_date",
            "interval": "day",
            "min_doc_count": 0
          }
        },
        "aggs": {
          "docs_count": {
            "avg": {
              "field": ""
            }
          }
        }
      }
    }
  }
}

你可能不需要按月份进行第一次聚合,因为你已经使用每天的间隔来获取此信息。
另外,你需要确保启用动态脚本,通过将下面的内容添加到你的elasticsearch.yml配置文件中。
script.disable_dynamic: false

或者在 /config/scripts 下添加一个 Groovy 脚本,并使用过滤器中的脚本进行过滤查询。


谢谢您的回答。然而,我不想仅计算在工作日创建的文档,我需要计算整个月份的所有文档(这已经完成),然后除以工作日的数量。我不知道的是如何计算该数字(月份中的工作日数)。 - Thibault J
我会编辑我的问题,因为我意识到它可能会误导。 - Thibault J
虽然它可能不能准确地帮助到原帖作者,但它确实帮助我找到了在聚合操作中使用直方图的正确语法。谢谢! - physicalattraction

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接