ElasticSearch 7中忽略空值字段的聚合查询适配问题
问题:Elasticsearch 7+聚合统计含空值字段文档的解决方案
Elasticsearch 7起引入了破坏性变更:访问文档的缺失字段时会抛出异常,即调用doc['field'].value时如果字段不存在会触发错误。
现有业务需要执行如下聚合查询,统计user_id和campaign_id拼接后的基数,但要包含存在空值字段的文档:
{ "query":{ "bool":{ "must":[ { "terms":{ "state":[ "pending", "queued", "deferred" ] } }, { "terms":{ "tenant_tag":[ "prod" ] } } ] } }, "aggs":{ "count":{ "cardinality":{ "script":"doc['user_id'].value + '_' + doc['campaign_id'].value" } } }, "size":0 }
之前尝试添加exists查询块,虽然能执行,但直接排除了含空值字段的文档,导致统计结果不准确:
{ "exists":{ "field":"user_id" } }, { "exists":{ "field":"campaign_id" } }
解决方案:在聚合脚本中安全处理缺失字段
核心是在Painless脚本中提前判断字段是否存在,给缺失字段分配默认值,避免抛出异常的同时保留这类文档的统计。
方式1:用containsKey判断字段存在性
修改聚合脚本如下,给缺失字段赋值为空字符串(可根据业务需求换成_missing_等标记):
{ "query":{ "bool":{ "must":[ { "terms":{ "state":[ "pending", "queued", "deferred" ] } }, { "terms":{ "tenant_tag":[ "prod" ] } } ] } }, "aggs":{ "count":{ "cardinality":{ "script": """ def userId = doc.containsKey('user_id') ? doc['user_id'].value : ''; def campaignId = doc.containsKey('campaign_id') ? doc['campaign_id'].value : ''; return userId + '_' + campaignId; """ } } }, "size":0 }
方式2:用doc.get()简化默认值赋值
Painless脚本支持doc.get(fieldName, defaultValue)的写法,直接给缺失字段设置默认值,代码更简洁:
{ "query":{ "bool":{ "must":[ { "terms":{ "state":[ "pending", "queued", "deferred" ] } }, { "terms":{ "tenant_tag":[ "prod" ] } } ] } }, "aggs":{ "count":{ "cardinality":{ "script": "doc.get('user_id', '').value + '_' + doc.get('campaign_id', '').value" } } }, "size":0 }
注意事项
- 如果字段是数值类型(而非字符串),默认值要匹配字段类型,比如用
0代替空字符串:"script": "doc.get('user_id', 0).value + '_' + doc.get('campaign_id', 0).value" - 不要添加
exists查询条件,否则会过滤掉缺失字段的文档,不符合业务需求。
内容的提问来源于stack exchange,提问作者Anon
相关产品推荐
相关产品推荐

