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