如何解决Painless脚本遍历嵌套locations字段的映射缺失错误?
问题场景
我的文档结构如下:
"_source": { "id": 60, "price": 60, "locations": [ { "id": 1, "price": 70 }, { "id": 5, "price": 80 } ] }
price字段同时存在于根节点和嵌套的locations数组中,需求是:
- 当查询参数传入
locationId时,过滤对应locations元素中的price值 - 未传入
locationId时,过滤根节点的price值
我编写了如下Painless脚本:
if(params['locationId'] != null){ for (def location : doc['locations']) { if (location.id == params['locationId']) { if ((params['lte'] != null || location.price <= params['lte']) && (params['gte'] != null || location.price >= params['gte'])) { return true; } else { return false; } } } } return (params['lte'] != null || params._source.price <= params['lte']) && (params['gte'] != null || params._source.price >= params['gte']);
使用方式如下:
"query": { "bool": { "filter": [ { "script": { "script": { "source": "my_script", "params": { "locationId": "2" } } } } ] } }
但运行时出现错误:No field found for [locations] in mapping with types [],尝试用params._source.locations访问嵌套数组,但filter查询中无法直接访问_source,该如何解决?
错误原因
doc['locations']无法访问嵌套结构:如果locations是嵌套类型(nested),doc上下文只能访问顶级字段,不能直接遍历嵌套数组;如果是普通对象数组,会被Elasticsearch扁平化存储,同样无法通过doc['locations']获取完整对象结构。- Filter查询默认不加载
_source:Filter阶段属于查询上下文,默认不会加载原始文档的_source数据,直接访问params._source会报错。
解决方法
方法一:用nested查询实现(推荐)
如果locations是nested类型(这是存储对象数组的标准方式,避免扁平化问题),可以结合bool+nested查询实现需求,性能远优于脚本:
"query": { "bool": { "filter": [ { "bool": { "should": [ // 传入locationId时,匹配对应嵌套元素的price { "bool": { "must": [ { "term": { "locations.id": "2" } }, { "range": { "locations.price": { "gte": 70, "lte": 80 } } } ] } }, // 未传入locationId时,匹配根节点price { "bool": { "must_not": [ { "exists": { "field": "locationId" } } ], "filter": { "range": { "price": { "gte": 70, "lte": 80 } } } } } ], "minimum_should_match": 1 } } ] } }
方法二:修改脚本并启用_source访问
如果必须用脚本,需要显式启用_source访问,同时调整脚本逻辑:
修改后的脚本
def source = params._source; if(params.locationId != null){ for (def location : source.locations) { if (location.id == params.locationId) { def match = true; if(params.lte != null && location.price > params.lte) match = false; if(params.gte != null && location.price < params.gte) match = false; return match; } } // 找不到匹配的locationId时返回false return false; } // 处理根节点price的情况 def match = true; if(params.lte != null && source.price > params.lte) match = false; if(params.gte != null && source.price < params.gte) match = false; return match;
使用方式(需开启_source访问)
"query": { "bool": { "filter": [ { "script": { "script": { "source": "my_script", "params": { "locationId": "2", "gte": 70, "lte": 80 }, "lang": "painless", "fetch_source": true // 启用_source访问 } } } ] } }
注意:启用
fetch_source会加载完整文档数据,大数据量场景下性能较差,优先推荐方法一。
额外提示
如果你的locations字段还不是nested类型,建议先修改映射:
"mappings": { "properties": { "id": {"type": "integer"}, "price": {"type": "integer"}, "locations": { "type": "nested", "properties": { "id": {"type": "integer"}, "price": {"type": "integer"} } } } }
内容的提问来源于stack exchange,提问作者Davidaa_WoW
相关产品推荐
相关产品推荐

