如何修复Elasticsearch查询以返回嵌套_source中的指定字段(Java)
问题描述
我正在用Java的ElasticSearchClient查询Elasticsearch,返回的数据量太大,想只获取需要的字段,但_source里的variables是嵌套数组结构。
示例索引响应(多个索引结构一致):
[ { "_index": "kn-tas-20200630", "_type": "_doc", "_id": "1122334455", "_score": null, "_source": { "variables": [ { "rawValue": "DEFH", "name": "MANAGER" }, { "rawValue": "ABCD", "name": "EMPLOYEE" }, { "rawValue": "[{\"rowId\":102030,\"rowType\":\"SIM\"}]", "name": "extData" } ] }, "sort": [ 1665735632119 ] } ]
需要实现的查询目标:
- 根据
name获取对应的rawValue(比如指定MANAGER,返回DEFH) - 获取
extData中的rowType值(指定extData+rowType,返回SIM)
当前的查询语句:
{ "from": 0, "size": 100, "query": { "bool": { "must": [ { "terms": { "prcKey": [ "K-112" ], "boost": 1.0 } } ], "must_not": [ { "exists": { "field": "endDate", "boost": 1.0 } }, { "term": { "personInCharge": { "value": "ABC", "boost": 1.0 } } } ], "adjust_pure_negative": true, "boost": 1.0 } }, "_source": { "includes": [ "variables.name", "variables.rawValue" ], "excludes": [] }, "sort": [ { "createTime": { "order": "desc" } } ] }
尝试过嵌套查询但没成功,求修复方法。
解决方案
单纯用_source过滤无法直接从数组里提取指定元素,需要用**脚本字段(Script Fields)**来处理逻辑,同时extData的rawValue是JSON字符串,需要先解析再提取rowType。
1. 修改后的查询语句
以下查询通过脚本字段分别提取目标值:
{ "from": 0, "size": 100, "query": { "bool": { "must": [ { "terms": { "prcKey": ["K-112"], "boost": 1.0 } } ], "must_not": [ { "exists": { "field": "endDate", "boost": 1.0 } }, { "term": { "personInCharge": { "value": "ABC", "boost": 1.0 } } } ], "adjust_pure_negative": true, "boost": 1.0 } }, "script_fields": { "manager_value": { "script": { "source": """ for (item in doc['variables.name']) { if (item.value == 'MANAGER') { def idx = doc['variables.name'].indexOf(item); return doc['variables.rawValue'][idx].value; } } return null; """ } }, "ext_data_row_type": { "script": { "source": """ for (item in doc['variables.name']) { if (item.value == 'extData') { def idx = doc['variables.name'].indexOf(item); def rawJson = doc['variables.rawValue'][idx].value; def jsonObj = JSON.parse(rawJson); return jsonObj[0].rowType; } } return null; """ } } }, "_source": false, // 关闭原始_source返回,只取脚本字段结果 "sort": [ { "createTime": { "order": "desc" } } ] }
2. Java代码实现(SearchSourceBuilder)
对应上述查询,用ElasticSearchClient的Java API构建:
import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import java.io.IOException; import java.util.Collections; public class EsQueryExample { public static void main(String[] args) throws IOException { RestHighLevelClient client = new RestHighLevelClient(/* 此处填入你的客户端配置 */); // 构建bool查询条件 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() .must(QueryBuilders.termsQuery("prcKey", "K-112")) .mustNot(QueryBuilders.existsQuery("endDate")) .mustNot(QueryBuilders.termQuery("personInCharge", "ABC")); // 提取MANAGER对应的rawValue脚本 Script managerScript = new Script( ScriptType.INLINE, "painless", """ for (item in doc['variables.name']) { if (item.value == 'MANAGER') { def idx = doc['variables.name'].indexOf(item); return doc['variables.rawValue'][idx].value; } } return null; """, Collections.emptyMap() ); // 提取extData中的rowType脚本 Script extDataScript = new Script( ScriptType.INLINE, "painless", """ for (item in doc['variables.name']) { if (item.value == 'extData') { def idx = doc['variables.name'].indexOf(item); def rawJson = doc['variables.rawValue'][idx].value; def jsonObj = JSON.parse(rawJson); return jsonObj[0].rowType; } } return null; """, Collections.emptyMap() ); // 构建查询源 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder() .query(boolQuery) .from(0) .size(100) .sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC)) .fetchSource(false) .scriptField("manager_value", managerScript) .scriptField("ext_data_row_type", extDataScript); // 执行查询(此处省略SearchRequest构建与执行逻辑) // SearchRequest searchRequest = new SearchRequest("kn-tas-*").source(sourceBuilder); // SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); } }
注意事项
- 确保Elasticsearch版本支持Painless脚本与JSON解析(6.0及以上版本均支持)
- 若
variables数组规模较大,脚本遍历可能影响性能,建议后续优化数据结构(比如将数组改为键值对嵌套对象) - 如需提取多个指定name的字段,在
script_fields中添加对应脚本即可
内容的提问来源于stack exchange,提问作者user3147473
相关产品推荐
相关产品推荐

