Elasticsearch多元素文档多字段聚合结果异常求助
现有如下结构的Elasticsearch文档(简化示例):
"documents": [ { "name": "Document 1", "collections" : [ { "id": 30, "title" : "Research" }, { "id": 45, "title" : "Events" }, { "id" : 52, "title" : "International" } ] }, { "name": "Document 2", "collections" : [ { "id": 45, "title" : "Events" }, { "id" : 63, "title" : "Development" } ] } ]
单独对collections.title做terms聚合时结果正常:
"aggs": { "collections": { "terms": { "field": "collections.title", "size": 30 } } }
但尝试同时聚合collections.id(期望每个title对应正确的id)时,无论是嵌套聚合还是multi_terms聚合,都出现了对应错误——比如"Development"对应的id应为63,却显示为45。问题根源是文档包含多个collections元素,默认处理会导致字段扁平化,id和title失去关联。
要实现title与对应id的正确聚合,核心是让Elasticsearch保留collections数组中每个对象的独立性,需要使用nested类型映射配合nested聚合:
1. 确保索引映射为nested类型
首先需要将collections字段定义为nested类型(若已创建索引,需重新建立映射并重新导入数据):
PUT /your_index_name { "mappings": { "properties": { "name": { "type": "text" }, "collections": { "type": "nested", "properties": { "id": { "type": "integer" }, "title": { "type": "keyword" } } } } } }
2. 使用nested + multi_terms聚合
通过nested聚合进入每个collections对象的上下文,再用multi_terms同时聚合title和id,确保二者来自同一对象:
GET /your_index_name/_search { "size": 0, "aggs": { "nested_collections": { "nested": { "path": "collections" }, "aggs": { "title_id_pairs": { "multi_terms": { "terms": [ { "field": "collections.title" }, { "field": "collections.id" } ] } } } } } }
3. 备选方案:nested + 嵌套terms聚合
如果不想使用multi_terms,可以先按title聚合,再在每个title分组下聚合对应的id,同样能保证关联正确:
GET /your_index_name/_search { "size": 0, "aggs": { "nested_collections": { "nested": { "path": "collections" }, "aggs": { "group_by_title": { "terms": { "field": "collections.title" }, "aggs": { "group_by_id": { "terms": { "field": "collections.id" } } } } } } } }
原理说明
默认情况下,Elasticsearch会将对象数组扁平化处理,把collections.id和collections.title拆分为两个独立的多值字段,聚合时会交叉组合所有id和title,导致关联错误。而nested类型会将数组中的每个对象当作独立的子文档存储,聚合时会基于完整的子文档上下文计算,从而保证title和id的对应关系正确。
内容的提问来源于stack exchange,提问作者Ted

