首先创建两条数据,两条数据中有一个district字段,表示涉及到的地区,district是个数组,district下包含province和city
PUT test/data/1 { "district":[ {"province":"江苏省", "city":["南京市"] },{ "province":"广东省", "city":["广州市"] } ] } PUT test/data/2 { "district":[ {"province":"四川省", "city":["成都市"] },{ "province":"浙江省", "city":["杭州市"] } ] }假如想对省份聚合,很简单,执行下面语句:
GET test/data/_search { "aggs": { "province_group": { "terms": { "field": "district.province.keyword", "size": 10 } } } }聚合结果如下,可以看出聚合结果并没有什么问题:
接下来再加一层聚合,在省份下面加一层按城市聚合,统计每个省份下每个城市的数量:
GET test/data/_search { "aggs": { "province_group": { "terms": { "field": "district.province.keyword", "size": 10 }, "aggs": { "city_group": { "terms": { "field": "district.city.keyword", "size": 10 } } } } } }ES的分组结果如下:
现在问题来了,四川省明明只有一个成都市,为什么会出现一个杭州市的分组,其他省份的问题也如此。
查看官方文档,因为添加数据之前没有设置映射,district字段默认映射为object类型,district是一个对象数组,ES会将它处理为如下的扁平式键值对的结构:
以文档2为例:
{
"district.province": ["四川省","浙江省"]
"district.city": ["成都市","杭州市"]
}
从扁平式的键值对结构中已经看不出省和市的对应关系,因此,在分组的时候就出现了上面的问题,四川省下会有成都市和杭州市的分组。
使用ES的嵌套对象nested object可以解决这个问题,每一个嵌套对象都会被索引为一个隐藏的独立文档,对象中字段的相关性被保留了下来。
还以文档2为例,如果使用nested object,会变成如下形式,省份与城市之前的关系被保留了下来:
{
"district.province": "四川省"
"district.city": ["成都市"]
}
{
"district.province": "浙江省"
"district.city": ["杭州市"]
}
使用嵌套对象需要设置映射,设置映射也很简单,只需在district下加一个type为nested的属性:
PUT test/data/_mapping { "data": { "properties": { "district": { "type": "nested", "properties": { "city": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "province": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } } }对嵌套对象使用聚合时,语句也有些变化:
GET test/data/_search { "aggs": { "district": { "nested": { "path": "district" }, "aggs": { "province_group": { "terms": { "field": "district.province.keyword", "size": 10 }, "aggs": { "city_group": { "terms": { "field": "district.city.keyword", "size": 10 } } } } } } } }聚合结果:
ES版本:6.1
参考:https://www.elastic.co/guide/cn/elasticsearch/guide/cn/nested-objects.html