Elasticsearch Painless獲取當前時間

Elasticsearch Painless獲取當前時間

本文討論下Elasticsearch如何獲取當前時間,通常需要計算時間間隔時使用。日期處理Painless使用標準的Java庫,主要類有:

  • java.time
  • java.time.chrono
  • java.time.format
  • java.time.temporal
  • java.time.zone

官方API地址爲:https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-api-reference-shared-org-elasticsearch-script.html#painless-api-reference-shared-JodaCompatibleZonedDateTime

1. Java 8 獲取當前時間

我們首先介紹幾種Java 8獲取當前日期、時間方式。

1.1. 當前Date

使用java.time.LocalDate 獲取當前系統日期:

LocalDate localDate = LocalDate.now();

獲取帶時區的當前日期:

LocalDate localDate = LocalDate.now(ZoneId.of("GMT+02:30"));
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

當然也可以通過LocalDateTime獲得LocalDate:

LocalDateTime localDateTime = LocalDateTime.now();
LocalDate localDate = localDateTime.toLocalDate();

1.1. 當前Time

通過LocalTime類獲取當前時間:

LocalTime localTime = LocalTime.now();

指定時區獲取:

LocalTime localTime = LocalTime.now(ZoneId.of("GMT+02:30"));
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

也可以通過LocalDateTime類獲取LocalTime:

LocalDateTime localDateTime = LocalDateTime.now();
LocalTime localTime = localDateTime.toLocalTime();

1.3. 當前Timestamp

java.time.Instant 從epoch時間獲取時間戳,即從1970-01-01T00:00:00Z開始計算毫秒正數值:

Instant instant = Instant.now();
long timeStampMillis = instant.toEpochMilli();

當然也可以獲取秒數:

Instant instant = Instant.now();
long timeStampSeconds = instant.getEpochSecond();

2. Elasticsearch Painless獲取當前時間

在大多數Painless上下文中,當前datetime,now都不支持。主要有兩個原因,第一是腳本對每個文檔都運行一次,導致每次now的返回值不同。第二腳本通常運行在分佈式環境,無法獲得一致的now。一般採用的方法是用戶定義參數,字符串類型或數值類型的日期,數值類型更好,無需進行格式解析。

舉例:

long now = params['now'];
ZonedDateTime zdt =ZonedDateTime.ofInstant(doc['event_datetime'].value.toInstant(), ZoneId.of('Z'))
long millisDateTime = zdt.toInstant().toEpochMilli();
long elapsedTime = now - millisDateTime;

我們看到官網示例直接寫ZonedDateTime zdt = doc[‘event_datetime’];,這樣會報異常:即JodaCompatibleZonedDateTime類型不能轉爲ZonedDateTime 。

2.1 計算日期間隔示例

定義映射

PUT /messages
{
    "mappings": {
        "properties": {
            "priority": {
                "type": "integer"
            },
            "datetime": {
                "type": "date"
            },
            "message": {
                "type": "text"
            }
        }
    }
}

載入示例數據

POST /_bulk
{ "index" : { "_index" : "messages", "_id" : "1" } }
{ "priority": 1, "datetime": "2019-07-17T12:13:14Z", "message": "m1" }
{ "index" : { "_index" : "messages", "_id" : "2" } }
{ "priority": 1, "datetime": "2019-07-24T01:14:59Z", "message": "m2" }
{ "index" : { "_index" : "messages", "_id" : "3" } }
{ "priority": 2, "datetime": "1983-10-14T00:36:42Z", "message": "m3" }
{ "index" : { "_index" : "messages", "_id" : "4" } }
{ "priority": 3, "datetime": "1983-10-10T02:15:15Z", "message": "m4" }
{ "index" : { "_index" : "messages", "_id" : "5" } }
{ "priority": 3, "datetime": "1983-10-10T17:18:19Z", "message": "m5" }
{ "index" : { "_index" : "messages", "_id" : "6" } }
{ "priority": 1, "datetime": "2019-08-03T17:19:31Z", "message": "m6" }
{ "index" : { "_index" : "messages", "_id" : "7" } }
{ "priority": 3, "datetime": "2019-08-04T17:20:00Z", "message": "m7" }
{ "index" : { "_index" : "messages", "_id" : "8" } }
{ "priority": 2, "datetime": "2019-08-04T18:01:01Z", "message": "m8" }
{ "index" : { "_index" : "messages", "_id" : "9" } }
{ "priority": 3, "datetime": "1983-10-10T19:00:45Z", "message": "m9" }
{ "index" : { "_index" : "messages", "_id" : "10" } }
{ "priority": 2, "datetime": "2019-07-23T23:39:54Z", "message": "m10" }

統計星期

GET /messages/_search?pretty=true
{
  "size": 0, 
    "aggs": {
        "day-of-week-count": {
            "terms": {
                "script": "return doc[\"datetime\"].value.getDayOfWeekEnum();"
            }
        }
    }
}

返回結果如下:

...
  "aggregations" : {
    "day-of-week-count" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "MONDAY",
          "doc_count" : 3
        },
        {
          "key" : "SUNDAY",
          "doc_count" : 2
        },
        {
          "key" : "WEDNESDAY",
          "doc_count" : 2
        },
        {
          "key" : "FRIDAY",
          "doc_count" : 1
        },
        {
          "key" : "SATURDAY",
          "doc_count" : 1
        },
        {
          "key" : "TUESDAY",
          "doc_count" : 1
        }
      ]
    }
  }

統計上午和下午

GET /messages/_search?pretty=true
{
  "size": 0, 
    "aggs": {
        "am-pm-count": {
            "terms": {
                "script": "return doc[\"datetime\"].value.getHour() < 12 ? \"AM\" : \"PM\";"
            }
        }
    }
}

返回響應:

  "aggregations" : {
    "am-pm-count" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "PM",
          "doc_count" : 7
        },
        {
          "key" : "AM",
          "doc_count" : 3
        }
      ]
    }
  }

統計年

GET /messages/_search?pretty=true
{
  "size": 3, 
    "script_fields" : {
        "message_age" : {
            "script" : {
                "source": "ZonedDateTime now = ZonedDateTime.ofInstant(Instant.ofEpochMilli(params['now']), ZoneId.of('Z')); ZonedDateTime mdt=ZonedDateTime.ofInstant(doc['datetime'].value.toInstant(), ZoneId.of('Z'));return mdt.until(now, ChronoUnit.YEARS);",
                "params": {
                    "now": 1674005645830
                }
            }
        }
    }
}

可讀形式腳本:

ZonedDateTime now = ZonedDateTime.ofInstant(Instant.ofEpochMilli(params['now']), ZoneId.of('Z')); 
ZonedDateTime mdt=ZonedDateTime.ofInstant(doc['datetime'].value.toInstant(), ZoneId.of('Z'));
return mdt.until(now, ChronoUnit.YEARS);

第一步獲取ZonedDateTime類型now對象。然後轉換文檔的datetime字段爲ZonedDateTime mdt對象,最後 mdt.until(now, ChronoUnit.YEARS)求兩個時間間隔年份。

這裏需要注意的是doc[‘datetime’].value類型爲JodaCompatibleZonedDateTime,需要通過ZonedDateTime.ofInstant(doc['datetime'].value.toInstant(), ZoneId.of('Z'))轉換爲ZonedDateTime。

響應如下:

 "hits" : [
      {
        "_index" : "messages",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "fields" : {
          "message_age" : [
            3
          ]
        }
      },
      {
        "_index" : "messages",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "fields" : {
          "message_age" : [
            3
          ]
        }
      },
      {
        "_index" : "messages",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 1.0,
        "fields" : {
          "message_age" : [
            39
          ]
        }
      }
    ]

3. 總結

本文介紹Painless如何獲取當前時間,並計算時間間隔。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章