Problem
# 純小數計算 avg,結果不正確
curl -XGET http://ES_URL:9200/ES_INDEX/ES_TYPE/_search -d '
{
"query": {
"bool": {
"must": [
{
"query_string": {
"query": "xxx",
"analyze_wildcard": true
}
},
{
"range": {
"orig_timestamp": {
"gte": "2018-04-11T10:11:35+08:00",
"lte": "2018-04-11T15:11:35+08:00"
}
}
}
],
"must_not": []
}
},
"sort": [
{
"orig_timestamp": "asc"
}
],
"aggs": {
"time": {
"date_histogram": {
"field": "orig_timestamp",
"interval": "1h",
"time_zone": "Asia/Shanghai",
"min_doc_count": 3
},
"aggs": {
"success_rate": {
"stats": {
"field": "hourly_success_rate"
}
}
}
},
"error_rate": {
"stats": {
"field": "hourly_error_rate"
}
}
},
"timeout": "300s",
"size": 30
}'
然後 aggs 中 sum/avg/stats 對純小數取出的計算結果總是做了取整計算。
問題大概同 aggs的sum 求和後數值只有整數部分,小數部分都沒了
Solution
其實是因爲 mappings,利用如下命令可以看到 mappings 信息。
curl -XGET http://ES_URL:9200/ES_INDEX/_mappings
{
"ES_INDEX": {
"mappings": {
"ES_TYPE": {
"properties": {
"hourly_error_rate": {
"type": "long"
},
"hourly_success_rate": {
"type": "long"
}
}
}
}
}
}
結果,可以看到這個純小數所在的字段 field 的映射類型 type 是 long 長整型。
假設第一個塞進該字段的數是 1,那麼他映射爲 long 長整型,然後即便之後塞進去的數字都是 float 浮點數類型,但是取出來做 avg 的時候,它內部應該是先拿到 mappings,根據 mapping 定義臨時變量,然後計算。那麼,0.5 變成 long 型,就取整成了 0。假設原來需要計算 (0.19+0.51+1)/3,那麼就變成了 (0+0+1)/3=0.33333,悲劇就是這麼產生的。
想要規避這個問題,純靠保持第一個錄入的數據正確還是不太保險。最好是自己寫 create index 之後加入 mappings 信息,或者用 template 也可以。
總之就是最保險的方案就是需要提前定義字段映射信息,保證 index 的映射元數據是正確的。
Reference
Incorrect sum while aggregating in elasticsearch for one particular index