Elasticsearch嵌套字段查询:如何仅返回指定语言的文本?
Elasticsearch嵌套文档字段过滤优化方案
场景说明
我在Elasticsearch中创建了包含嵌套类型的索引mapping,插入多语言产品名称数据后,需要查询符合特定语言ID和名称的文档,并且只返回该文档中匹配条件的产品名称条目,而非所有嵌套条目。目前通过script_fields实现了需求,但担心脚本会影响查询性能,希望找到更高效的方案。
创建的Mapping
PUT /test { "mappings": { "properties": { "product_id": { "type": "integer" }, "product_name": { "type": "nested", "include_in_parent": true, "properties": { "lang_id": { "type": "byte" }, "name": { "type": "text" } } } } } }
插入的测试数据
POST /test/_doc { "product_id": 111, "product_name": [ { "lang_id": 1, "name": "one" }, { "lang_id": 2, "name": "ein" } ] } POST /test/_doc { "product_id": 222, "product_name": [ { "lang_id": 1, "name": "two" }, { "lang_id": 2, "name": "zwei" } ] }
初始查询及问题
需求是返回lang_id=2且name='ein'的文档,但只保留匹配条件的product_name条目。初始查询语句如下:
POST /test/_search { "query": { "nested": { "path": "product_name", "query": { "bool": { "must": [ { "term": { "product_name.name": "ein" } }, { "term": { "product_name.lang_id": 2 } } ] } } } }, "_source": [ "product_id", "product_name" ] }
但查询结果返回了文档的所有product_name条目,不符合需求:
... "_source": { "product_id": 111, "product_name": [ { "lang_id": 1, "name": "one" }, { "lang_id": 2, "name": "ein" } ] } ...
当前实现(Script Fields)
通过script_fields遍历数组筛选符合条件的条目,实现了需求:
POST /test/_search { "query": { "nested": { "path": "product_name", "query": { "bool": { "must": [ { "term": { "product_name.name": "ein" } }, { "term": { "product_name.lang_id": 2 } } ] } } } }, "_source": [ "product_id" ], "script_fields": { "selected_name": { "script": { "source": "def name_langid = 0;for (int i = 0; i < params._source.product_name.length; i++) {if (params._source.product_name[i].lang_id == params.langid) {name_langid = params._source.product_name[i].name;}}name_langid;", "params": { "langid": 2 } } } } }
得到预期结果:
... "_source": { "product_id": 111 }, "fields": { "selected_name": [ "ein" ] } ...
但脚本遍历会增加查询开销,存在性能隐患。
更高效的解决方案:Nested Inner Hits
使用Elasticsearch原生的Inner Hits特性,专门用于返回嵌套文档中匹配查询条件的条目,无需脚本,性能更优。查询语句如下:
POST /test/_search { "query": { "nested": { "path": "product_name", "query": { "bool": { "must": [ { "term": { "product_name.name": "ein" } }, { "term": { "product_name.lang_id": 2 } } ] } }, "inner_hits": { "_source": ["lang_id", "name"], // 指定返回嵌套条目的字段 "name": "matched_product_name" // 自定义结果键名,方便识别 } } }, "_source": ["product_id"] // 只返回主文档的product_id字段 }
查询结果
返回结果中会包含inner_hits部分,仅展示匹配条件的嵌套条目:
... "_source": { "product_id": 111 }, "inner_hits": { "matched_product_name": { "hits": { "total": { "value": 1, "relation": "eq" }, "max_score": 1.0, "hits": [ { "_index": "test", "_id": "...", "_nested": { "field": "product_name", "offset": 1 }, "_score": 1.0, "_source": { "lang_id": 2, "name": "ein" } } ] } } } ...
方案优势
- 原生支持:Inner Hits是Elasticsearch为嵌套文档设计的优化特性,避免了脚本遍历的性能损耗
- 精准过滤:直接定位匹配的嵌套条目,无需处理整个数组
- 灵活配置:可自定义返回字段、结果名称,甚至分页排序嵌套条目
内容的提问来源于stack exchange,提问作者Lay András
相关产品推荐
相关产品推荐

