首页 > 解决方案 > Elasticsearch中日期数据类型的平均聚合?

问题描述

我使用存储的脚本在我的索引中存储了 3 个日期数据类型变量(date1、date2、date3)。我计算了两个日期变量(date2 - date1)之间的持续时间并存储在第三个日期变量(date3)中。这是我使用过的完整映射、脚本和更新请求。

PUT /myindex

POST /myindex/_mappings
{
  "properties":{
    "date1":{
      "type":"date"
    },
    "date2":{
      "type":"date"
    },
    "date3":{
      "type":"date"
    }
  }
}

POST _scripts/myindexscript/
{
  "script":{
    "source" :"""

          if (ctx._source.date1==null) {
            ctx._source.date1 = new Date().getTime();
          }
          ctx._source.status.add(params.status);
          if (!(params.status).equalsIgnoreCase('Info')) {
              ctx._source.date2 = new Date().getTime();
              ctx._source.date3=ctx._source.date2 - ctx._source.date1;
          }

    """,
    "lang": "painless"
  }
}

POST /myindex/_update/1
{
  "script":{
    "id":"myindexscript",
    "params":{
      "status": "Infoa"
    }
  }
  , "upsert": {
    "date1":null,
    "date2":null,
    "date3":null,
    "status": []
  },
  "scripted_upsert": true
}

如您在此处看到的,我已插入 1 个文档。为了在文档中插入,我首先使用字段 status='Info' 然后运行字段 status!='Infoa' 的 _update 请求。在这里你可以看到 GET /myindex/_search 的输出

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "myindex",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "date3" : 5589,
          "date2" : 1590062272146,
          "date1" : 1590062266557,
          "status" : [
            "Info",
            "Infoa"
          ]
        }
      }
    ]
  }
}

现在我运行这个特定的聚合请求

GET /myindex/_search
{
  "query": {
    "term": {
      "status.keyword": {
        "value": "Infoc"
      }
    }
  }, 
  "aggs": {
    "NAME": {
      "avg": {
        "field": "date3"
      }
    }
  },
  "size": 20
}

这给出了这个奇怪的输出:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.8025915,
    "hits" : [
      {
        "_index" : "myindex",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.8025915,
        "_source" : {
          "date3" : 5589,
          "date2" : 1590062272146,
          "date1" : 1590062266557,
          "status" : [
            "Info",
            "Infoa"
          ]
        }
      }
    ]
  },
  "aggregations" : {
    "NAME" : {
      "value" : 1.142046432E14,
      "value_as_string" : "5589-01-01T00:00:00.000Z"
    }
  }
}

为什么聚合结果中的值不是 5589 而是 "value" : 1.142046432E14 。我无法理解如何处理 date3 字段以进行平均计算。请帮忙?

标签: dateelasticsearchelasticsearch-aggregationelasticsearch-painless

解决方案


两个毫秒时间戳日期的差是一个整数。在你的情况下

1590062272146 - 1590062266557 = 5589

由于您的映射规定date3为 type date,因此 ES 尝试解析它。它猜测它是一年,所以结果日期是Jan 1st, year 5589,以毫秒为单位确实是1.142046432E14,即114204643200000

所以数学是对的。


现在,

date2在UTC是Thu May 21 2020 11:57:52

date1在UTC是Thu May 21 2020 11:57:46

所以两者的差异,四舍五入,是 6 秒。

您现在基本上有两种选择:

  • 调用其他类似的date3东西milli_diff并使其成为integer而不是date. 那么您的avgagg 结果也将以毫秒为单位
  • date3默认设置为formatofepoch_millis并在其上执行您的日期魔法(例如在此示例scripted_metric中的聚合中漂亮地打印平均经过的差异)

推荐阅读