elasticsearch 聚合之 date_histogram 聚合
- 2022 年 11 月 30 日
- 筆記
- elasticsearch, ELK
1、背景
此處來簡單學習一下 elasticsearch
的 date_histogram
直方圖聚合。它和普通的直方圖histogram
聚合差不多,但是date_histogram
只可於 日期或日期範圍
類型的值一起使用。
2、bucket_key如何計算
- 假設我們存在如下時間
2022-11-29 23:59:59
。 - 在
es
中時間為2022-11-29 23:59:59 +0000
,因為上方的時間沒有時區,所以會自動加上0時區
,對應的時間戳為1669766399000
- 此處假設以
1d
為單位來聚合 - 聚合統計中
time_zone
的值為+0800
- bucket_key計算公式為
bucket_key = localToUtc(Math.floor(utcToLocal(value) / interval) * interval))
計算步驟如下:(此處是我自己的理解,如果不對歡迎指出)
utcToLocal(value)
= 1669766399000(utc的值
)+
8*60*60*1000(time_zone +8的值
) = 1669795199000Math.floor(utcToLocal(value) / interval) * interval)
= Math.floor(1669795199000 / (24*60*60*1000)) * (24*60*60*1000) = 1669766400000localToUtc(...)
=1669766400000-
86060*1000=1669737600000key_as_string
=utc時間1669737600000轉換成東八區時間展示為=2022/11/30 00:00:00
3、前置知識
- 日期(
date
)類型的字段在es
中是以long
類型的值保存的。 es
中默認 默認的時區是0時區
。- 如果我們有一個東八區的時間,那麼在es中是如何存儲的呢?
- 假設存在如下mapping
"invoked_time": {
"type": "date",
"format": ["yyyy-MM-dd HH:mm:ss"]
}
- 如果我們此時存在 如下
東八區
時間2022-11-29 12:12:12
,那麼在 es 會存儲為2022-11-29 12:12:12 +0000
對應的時間戳,為什麼會加上+0000
,因為我們自己的時間字符串中沒有時區,就會加上默認的0時區。
4、日曆和固定時間間隔
既然我們是根據時間來進行聚合,那麼必然就會涉及到這麼一個問題。假設以天為單位來聚合,那麼1天
到底是固定
的24小時
呢,還是可變
的呢? 因為存在時區
的關係,在有的國家,在某些時區下,一天就不一定是24個小時。因此在es
中提供了calendar-aware time intervals
, 和 fixed time intervals.
兩種類型。
4.1 Calendar intervals 日曆間隔
日曆感知間隔使用calendar_interval參數配置。
它可以自動感應到日曆中的時區變化。它的單位只能是單數,不可是複數,比如2d
就是錯誤的。
日曆間隔 可用的單位為:分鐘 (1m
)、小時 (1h
)、天 (1d
)、星期 (1w
)、月 (1M
)、季度 (1q
)、年 (1y
)
舉個例子:1m
是從何時開始的,何時結束的?.
所有的分鐘都從00
秒開始。一分鐘是指定時區中第一分鐘的00秒和下一分鐘的00秒之間的時間間隔,用於補償任何介於其間的閏秒,因此整點後的分鐘數和秒數在開始和結束時是相同的。
4.2 Fixed intervals 固定間隔
固定間隔使用fixed_interval參數進行配置。
與日曆感知間隔相比,固定間隔是固定數量的SI
單位,無論它們落在日曆的哪個位置,都不會偏離。一秒總是由1000ms組成
。這允許以支持的單位的任意倍數指定固定間隔。但是,這意味着固定間隔不能表示其他單位,例如月,因為一個月的持續時間不是固定的數量。嘗試指定月或季度等日曆間隔將引發異常。
固定間隔 可用的單位為:
毫秒 (ms
)
秒 (s
)
定義為每個1000毫秒
分鐘 (m
)
所有分鐘都從00秒開始。 定義為每個60秒(60,000毫秒)
小時 (h
)
所有小時都從00分00秒開始。 定義為每60分鐘(3,600,000毫秒)
天 (d
)
所有天都在儘可能早的時間開始,通常是00:00:00(午夜)。 定義為24小時(86,400,000毫秒)
5、數據準備
5.1 準備mapping
PUT /index_api_invoked_time
{
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"api": {
"type": "keyword"
},
"invoked_time": {
"type": "date",
"format": ["yyyy-MM-dd HH:mm:ss"]
}
}
}
}
5.2 準備數據
PUT /index_api_invoked_time/_bulk
{"index":{"_id":1}}
{"api":"/user/infos","invoked_time": "2022-11-26 00:00:00"}
{"index":{"_id":2}}
{"api":"/user/add"}
{"index":{"_id":3}}
{"api":"/user/update","invoked_time": "2022-11-26 23:59:59"}
{"index":{"_id":4}}
{"api":"/user/list","invoked_time": "2022-11-27 00:00:00"}
{"index":{"_id":5}}
{"api":"/user/export","invoked_time": "2022-11-29 23:59:59"}
{"index":{"_id":6}}
{"api":"/user/detail","invoked_time": "2022-12-01 01:00:00"}
6、聚合案例
6.1 dsl
POST /index_api_invoked_time/_search
{
"size": 0,
"aggregations": {
"agg_01": {
"date_histogram": {
"field": "invoked_time",
"calendar_interval": "1d",
"min_doc_count": 0,
"missing": "2022-11-27 23:59:59",
"time_zone": "+08:00",
"offset":"+10h",
"extended_bounds": {
"min": "2022-11-26 10:00:00",
"max": "2022-12-03 10:00:00"
}
}
}
}
}
6.2 java代碼
@Test
@DisplayName("日期直方圖聚合")
public void test01() throws IOException {
SearchRequest request = SearchRequest.of(searchRequest ->
searchRequest.index("index_api_invoked_time")
.size(0)
.aggregations("agg_01", agg ->
agg.dateHistogram(dateAgg ->
// 聚合的字段
dateAgg.field("invoked_time")
// 聚合的單位,日曆感知 單位為天,此時的一天不一定為24小時,因為夏令時時,有些國家一天可能只有23個小時
.calendarInterval(CalendarInterval.Day)
// 固定間隔, 此處可以指定 1天就是24小時
// .fixedInterval()
// 如果聚合的桶中,沒有文檔也返回
.minDocCount(0)
// 對於文檔中,聚合字段缺失,此處給一個默認值,默認情況是此文檔不參與聚合
.missing(DateTime.of("2022-11-27 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
// 時區
.timeZone("+08:00")
// 偏移,偏移是在時間在對應的時區調整之後,再去偏移
.offset(time -> time.time("+10h"))
// 如果返回的桶數據不在這個邊界中,則給默認值,不會對數據進行過濾。
.extendedBounds(bounds ->
bounds.min(FieldDateMath.of(f -> f.expr("2022-11-26 10:00:00")))
.max(FieldDateMath.of(f -> f.expr("2022-12-03 10:00:00")))
)
)
)
);
System.out.println("request: " + request);
SearchResponse<String> response = client.search(request, String.class);
System.out.println("response: " + response);
}
6.3 聚合結果
7、完整代碼
8、參考文檔
- //www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-datehistogram-aggregation.html#date-histogram-missing-value
- //www.pipiho.com/es/7.7/cn/search-aggregations-bucket-datehistogram-aggregation.html