Skip to main content
 首页 » 编程设计

Elasticsearch Painless获取当前时间

2022年07月19日146kevingrace

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如何获取当前时间,并计算时间间隔。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/105186817
阅读延展