圖解:Elasticsearch 8.X 如何求解環(huán)比上升比例?
1、企業(yè)級Elasticsearch 8.X 實戰(zhàn)問題
問題描述:有個聚合的需求,問下大家,一個索引中有時間字段 要求 計算本月和上月相比的環(huán)比上升比例?——來自GPVIP群
2、問題釋義
2.1 啥叫環(huán)比?
環(huán)比是統(tǒng)計學(xué)術(shù)語,表示連續(xù)2個統(tǒng)計周期內(nèi)的量的變化比。
2.2 Elasticsearch 怎么做計算問題?
其實這個問題比較大,從大的角度講:Elasticsearch 更適合做檢索,能做腳本計算處理,但會有性能問題。
官方明確強調(diào):
Avoid script——If possible, avoid using script-based sorting, scripts in aggregations, and the script_score query.
通俗點說,避免使用腳本,除非特殊情況必須使用。
Elasticsearch 能支持的計算問題如下幾種方式:

- 腳本檢索(script query) 腳本檢索參見:
- 腳本聚合(script aggregation) 參見:

- 腳本預(yù)處理(ingest pipeline 之 script pipeline)。
3、問題拆解
回歸我們的問題,分兩個維度拆解。
- 維度1:從數(shù)據(jù)到結(jié)果。原始的數(shù)據(jù)至少包含兩個字段:日期字段和數(shù)據(jù)字段,并沒有基于日期的匯總數(shù)據(jù)。
也就是說,匯總結(jié)果數(shù)據(jù),需要我們借助聚合實現(xiàn)。
- 維度2:從結(jié)果到數(shù)據(jù)。
最終結(jié)果需要臨近的兩個月份的匯總結(jié)果計算求得,需要借助:bucket_script 子聚合實現(xiàn)。而bucket_script 需要兩重聚合,且嵌套到內(nèi)層實現(xiàn)。
可以通過如下三個步驟實現(xiàn),如下腦圖梳理。
- 步驟1:創(chuàng)建索引。
- 步驟2:導(dǎo)入數(shù)據(jù)(自己構(gòu)造)。
- 步驟3:聚合實現(xiàn)(最核心)。
聚合的實現(xiàn)是問題求解的關(guān)鍵。
-
最外層聚合:時間范圍聚合,借助Date Range篩選近兩個月的數(shù)據(jù)。
-
內(nèi)層聚合:分別求解出本月和前一個月的數(shù)據(jù)。其實又需要拆解為兩層聚合。
-
第一層 :過濾當(dāng)月和前一個月的時間范圍。借助:filter aggs 實現(xiàn)。
-
第二層 :指標(biāo) sum aggs 聚合實現(xiàn)結(jié)果求和統(tǒng)計。
-
-
與上內(nèi)層同級實現(xiàn) bucket_script 結(jié)果求解,計算環(huán)比!

4、問題求解
按照上面腦圖拆解的三個步驟搞定實現(xiàn)。視頻如下:
4.1:step1 創(chuàng)建索引且指定Mapping!
DELETE?test-20221109
PUT?test-20221109
{
??"mappings":?{
????"properties":?{
??????"insert_date":?{
????????"type":?"date"
??????},
??????"count":?{
????????"type":?"integer"
??????}
????}
??}
}
4.2 step2 :寫入數(shù)據(jù)
POST?test-20221109/_bulk
{"index":{"_id":1}}
{"insert_date":"2022-11-09T12:00:00Z","count":5}
{"index":{"_id":2}}
{"insert_date":"2022-11-08T12:00:00Z","count":150}
{"index":{"_id":3}}
{"insert_date":"2022-12-09T12:00:00Z","count":33}
{"index":{"_id":4}}
{"insert_date":"2022-12-08T12:00:00Z","count":44}
{"index":{"_id":5}}
{"insert_date":"2022-12-09T12:00:00Z","count":55}
{"index":{"_id":6}}
{"insert_date":"2022-12-08T12:00:00Z","count":66}
4.3 step3:聚合求解環(huán)比
POST?test-20221109/_search
{
??"size":?0,
??"aggs":?{
????"range_aggs":?{
??????"range":?{
????????"field":?"insert_date",
????????"format":?"yyyy-MM-dd",
????????"ranges":?[
??????????{
????????????"from":?"2022-11-01",
????????????"to":?"2022-12-31"
??????????}
????????]
??????},
??????"aggs":?{
????????"11month_count":?{
??????????"filter":?{
????????????"range":?{
??????????????"insert_date":?{
????????????????"gte":?"2022-11-01",
????????????????"lte":?"2022-11-30"
??????????????}
????????????}
??????????},
??????????"aggs":?{
????????????"sum_aggs":?{
??????????????"sum":?{
????????????????"field":?"count"
??????????????}
????????????}
??????????}
????????},
????????"12month_count":?{
??????????"filter":?{
????????????"range":?{
??????????????"insert_date":?{
????????????????"gte":?"2022-12-01",
????????????????"lte":?"2022-12-31"
??????????????}
????????????}
??????????},
??????????"aggs":?{
????????????"sum_aggs":?{
??????????????"sum":?{
????????????????"field":?"count"
??????????????}
????????????}
??????????}
????????},
????????"bucket_division":?{
??????????"bucket_script":?{
????????????"buckets_path":?{
??????????????"pre_month_count":?"11month_count?>?sum_aggs",
??????????????"cur_month_count":?"12month_count?>?sum_aggs"
????????????},
????????????"script":?"(params.cur_month_count?-?params.pre_month_count)?/?params.pre_month_count"
??????????}
????????}
??????}
????}
??}
}
求解結(jié)果如下:


5、小結(jié)
其實這個聚合實現(xiàn)相當(dāng)復(fù)雜,且 不夠靈活 ,可擴展性不強。
業(yè)務(wù)選型層面,如果非實時求解的場景,真的不建議這么做。
我們可以定時離線計算結(jié)果統(tǒng)計,借助 Java 或者 python 等代碼實現(xiàn)更為順暢和“絲滑”。
你的業(yè)務(wù)層面有沒有遇到類似問題?歡迎留言說一下你的方案。
6、推薦
1 、 全網(wǎng)首發(fā)!從 0 到 1 Elasticsearch 8.X 通關(guān)視頻 2 、 Elasticsearch 8.X 如何實現(xiàn)更精準(zhǔn)的檢索? 3、Elasticsearch 檢索性能優(yōu)化實戰(zhàn)指南 4、Elasticsearch 預(yù)處理沒有奇技淫巧,請先用好這一招! 更短時間更快習(xí)得更多干貨!
中國50%+Elastic認(rèn)證專家出自于此! 在不確定的時代,尋求確定性!
比同事
搶先
一步學(xué)習(xí)進階干貨
!
