Elasticsearch:能否先折叠再聚合嵌套字段的数据?
Elasticsearch按指定字段分组并聚合嵌套字段
需求说明
我在使用Elasticsearch时,需要实现两个核心需求:
- 按
groupId字段对文档分组,每组返回指定数量(比如前2条)的文档; - 对每个分组内所有文档的
people嵌套字段进行聚合,生成包含该分组全部人员的groupPeople字段,不受返回文档数量的限制。
示例数据准备
创建索引映射
PUT test/_mapping { "properties": { "groupId":{ "type":"keyword" }, "id":{ "type":"keyword" }, "name":{ "type":"text" }, "people":{ "type":"nested", "properties":{ "email":{ "type":"keyword" } } } } }
插入测试文档
PUT test/_doc/1 { "name": "docs1", "groupId": "1", "people":[{ "email":"people1@test.com" }] } PUT test/_doc/2 { "name": "docs2", "groupId": "1", "people":[{ "email":"people2.1@test.com" }, { "email":"people2.2@test.com" }] } PUT test/_doc/3 { "name": "docs3", "groupId": "2", "people":[{ "email":"people3.1@test.com" }, { "email":"people2.2@test.com" }] } PUT test/_doc/4 { "name": "docs4", "groupId": "1", "people":[{ "email":"people4.1@test.com" }, { "email":"people4.2@test.com" }] } PUT test/_doc/5 { "name": "docs5", "groupId": "3", "people":[{ "email":"people5.1@test.com" }, { "email":"people5.2@test.com" }] }
原查询的局限性
之前使用collapse的方式只能获取每组内指定数量文档的people数据,无法聚合整个分组的所有人员(比如groupId=1分组有3条文档,共5个人员,但原查询只能拿到前2条文档的3个人员)。
解决方案:使用聚合组合实现
通过terms聚合按groupId分组,同时嵌套两个子聚合:
top_docs:用top_hits返回每组前2条文档;group_people:用nested聚合+terms聚合统计分组内所有人员的email,再用top_hits或terms返回全部人员。
最终查询语句
GET test/_search { "size": 0, // 不返回顶层hits,只看聚合结果 "aggs": { "group_by_groupId": { "terms": { "field": "groupId", "size": 10 // 控制返回的分组数量 }, "aggs": { "top_docs": { "top_hits": { "size": 2 // 每组返回前2条文档 } }, "group_people": { "nested": { "path": "people" }, "aggs": { "all_people": { "terms": { "field": "people.email", "size": 100 // 控制返回的人员数量,确保覆盖所有人员 } } } } } } } }
查询结果说明
返回的聚合结果中,每个groupId分组会包含:
top_docs:该分组的前2条完整文档;group_people.all_people:该分组内所有人员的email统计(自动去重),比如groupId=1会包含people1@test.com、people2.1@test.com、people2.2@test.com、people4.1@test.com、people4.2@test.com这5个人员。
如果需要保留重复的人员(比如同一email出现多次也保留),可以将terms聚合替换为top_hits聚合:
"group_people": { "nested": { "path": "people" }, "aggs": { "all_people": { "top_hits": { "size": 100, "_source": ["people.email"] } } } }
内容的提问来源于stack exchange,提问作者feng
相关产品推荐
相关产品推荐

