赞
踩
前面所说的对象类型虽然可按JSON对象格式保存结构化的对象数据,但由于Lucene并不支持对象类型,所以 Elastiesearch在存储这种类型的字段时会将它们平铺为单个属性。
例如:
PUT colleges/_doc/1
{
"address": {
"country": "CN",
"city": "BJ"
},
"age": 10
}
在示例中的colleges文档,address字段会被平铺为address.country和address.city两个字段存储。这种平铺存储的方案在存储单个对象时没有什么问题,但如果在存储数组时会丢失单个对象内部字段的匹配关系。
例如:
PUT colleges/_doc/2
{
"address": [
{
"country": "CN",
"city": "BJ"
},
{
"country": "US",
"city": "NY"
}
],
"age": 10
}
示例中的colleges文档在实际存储时,会被拆解为"dress.country":["CN,"US"]
和"address.city":["BJ","NY"]
两个数组字段。这样一来,单个对象内部,country字段和city字段之间的匹配关系就丢失了。换句话说,使用CN与NY作为共同条件检索的文档时,上述文档也会被检索出来,这在逻辑上就出现了错误:
POST colleges/_search?filter_path=hits
{
"query": {
"bool": {
"must": [
{
"match": {
"address.country": "CN"
}
},
{
"match": {
"address.city": "NY"
}
}
]
}
}
}
输出结果:
{
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.77041256,
"hits" : [
{
"_index" : "colleges",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.77041256,
"_source" : {
"address" : [
{
"country" : "CN",
"city" : "BJ"
},
{
"country" : "US",
"city" : "NY"
}
],
"age" : 10
}
}
]
}
}
在示例中使用了bool组合查询,要求country字段为CN而city字段为NY。这样的文档显然并不存在,但由于数组中的对象被平铺为两个独立的数组字段,文档仍然会被检索出来。
为了解决对象类型在数组中丢失内部字段之间匹配关系的问题,Elasticsearch提供了一种特殊的对象类型nested。这种类型会为数组中的每一个对象创建一个单独的文档, 以保存对象的字段信息并使它们可检索。由于这类文档并不直接可见,而是藏置在父文档之中,所以这类文档可以称为为隐式文档或嵌入文档。
还是以colleges索引为例,我们把原有的索引删除,将它的address字段设置为nested类型:
PUT colleges
{
"mappings": {
"properties": {
"address": {
"type": "nested"
},
"age": {
"type": "integer"
}
}
}
}
然后重新存入文档1和2,当字段被设置为nested类型后,再使用原来查询中的bool组合查询就不能检索出来了。这是因为对nested类型字段的检索实际上是对隐式文档的检索,在检索时必须要将检索路由到隐式文档上,所以必须使用专门的检索方法。也就是说,现在即使将原来查询中的查询条件设置为CN和BJ也不会检索出结果。
nested类型字段可使用的检索方法包括DSL的nested查询,还有聚集查询中的nested和reverse_nested两种聚集。
nested查询只能针对nested类型字段,需要通过path参数指定nested类型字段的路径,而在query参数中则包含了针对隐式文档的具体查询条件。
例如:
POST /colleges/_search?filter_path=hits
{
"query": {
"nested": {
"path": "address",
"query": {
"bool": {
"must": [
{
"match": {
"address.country": "CN"
}
},
{
"match": {
"address.city": "NY"
}
}
]
}
}
}
}
}
输出结果:
{
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
在示例中再次使用CN与NY共同作为查询条件,但由于使用nested类型后会将数组中的对象转换成隐式文档,所以在 nested查询中将不会有文档返回了。
将条件更换为CN和BJ,则有文档返回。
POST /colleges/_search?filter_path=hits
{
"query": {
"nested": {
"path": "address",
"query": {
"bool": {
"must": [
{
"match": {
"address.country": "CN"
}
},
{
"match": {
"address.city": "BJ"
}
}
]
}
}
}
}
}
输出结果:
{
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 0.8836655,
"hits" : [
{
"_index" : "colleges",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.8836655,
"_source" : {
"address" : {
"country" : "CN",
"city" : "BJ"
},
"age" : 10
}
},
{
"_index" : "colleges",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.8836655,
"_source" : {
"address" : [
{
"country" : "CN",
"city" : "BJ"
},
{
"country" : "US",
"city" : "NY"
}
],
"age" : 10
}
}
]
}
}
nested聚集是一个单桶聚集,也是通过path参数指定nested字段的路径,包含在path指定路径中的隐式文档都将落入桶中。所以nested字段保存数组的长度就是单个文档落入桶中的文档数量,而整个文档落入桶中的数量就是所有文档nested 字段数组长度的总和。
有了nested聚集,就可以针对nested数组中的对象做各种聚集运算,例如:
POST /colleges/_search?filter_path=aggregations
{
"aggs": {
"nested_address": {
"nested": {
"path": "address"
},
"aggs": {
"city_names": {
"terms": {
"field": "address.city.keyword",
"size": 10
}
}
}
}
}
}
输出结果:
{
"aggregations" : {
"nested_address" : {
"doc_count" : 3,
"city_names" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "BJ",
"doc_count" : 2
},
{
"key" : "NY",
"doc_count" : 1
}
]
}
}
}
}
在示例中,nested_address是一个nested聚集的名称,它会将address字段的隐式文档归入一个桶中。而嵌套在nested_address聚集中的city_names聚集则会在这个桶中再做terms聚集运算,这样就将对象中city字段所有的词项枚举
出来了。
reverse_nested聚集用于在隐式文档中对父文档做聚集,所以这种聚集必须作为nested聚集的嵌套聚集使用。
例如:
POST /colleges/_search?filter_path=aggregations
{
"aggs": {
"nested address": {
"nested": {
"path": "address"
},
"aggs": {
"city names": {
"terms": {
"field": "address.city.keyword",
"size": 10
},
"aggs": {
"avg_age_in_city": {
"reverse_nested": {},
"aggs": {
"avg_age": {
"avg": {
"field": "age"
}
}
}
}
}
}
}
}
}
}
输出结果:
{
"aggregations" : {
"nested address" : {
"doc_count" : 3,
"city names" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "BJ",
"doc_count" : 2,
"avg_age_in_city" : {
"doc_count" : 2,
"avg_age" : {
"value" : 10.0
}
}
},
{
"key" : "NY",
"doc_count" : 1,
"avg_age_in_city" : {
"doc_count" : 1,
"avg_age" : {
"value" : 10.0
}
}
}
]
}
}
}
}
在示例中,city_ names聚集也是将隐式文档中city字段的词项全部聚集出来。不同的是在这个聚集中还嵌套了一个名为avg_age_in_city的聚集,这个聚集就是个reverse_ nested聚集。它会在隐式文档中将city字段具有相同词项的文档归入
一个桶中,而avg_age_in_city聚集嵌套的另外一个名为avg_age的聚集,它会把落入这个桶中文档的age字段的平均值计算出来。所以从总体上来看,这个聚集的作用就是将在同一城市中大学的平均校龄计算出来。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。