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如何獲取當前時間,並計算時間間隔。