ElasticSearch——nested(mapping,query,aggregation)

ElasticSearch 包含nested字段类型,该类型的出现主要是由于对象数组类型的操作往往不能如我们预期,这主要是因为在Lucene内部没有对象的概念,所以ES将层级的JSON数据转化成扁平的键值对列表形式。

例如文档:

PUT my_index/my_type/5
{
  "owner" : "小李",
  "family" : [
    {
      "call" : "dad",
      "name" :  "李俊杰"
    },
    {
      "call" : "mom",
      "name" :  "李翠莲"
    }
  ]
}

转化之后实际上为:

PUT my_index/my_type/5
{
  "owner": "小李",
  "family.call": [
    "dad",
    "mom"
  ],
  "family.name": [
    "李俊杰",
    "李翠莲"
  ]
}

这样的话 我们想搜索爸爸的名称为李翠莲 也能够搜索到结果,这显然和我们的预期不符。

然而ES中nested类型字段,允许对象数组中的每一个对象被独立的索引和查询

我们先做一个通常的处理方式,看看能够得到什么结果:

不使用nest的例子:

使用动态mapping,直接插入数据,对象数组mapping的数据类型是对象类型

第一步:添加数据:

PUT my_index/my_type/1
{
  "owner": "张三",
  "family": [
    {
      "call": "dad",
      "name": "张三爸"
    },
    {
      "call": "mom",
      "name": "张三妈"
    }
  ]
}
PUT my_index/my_type/2
{
  "owner" : "李四",
  "family" : [
    {
      "call" : "dad",
      "name" :  "李四爸"
    },
    {
      "call" : "mom",
      "name" :  "李四妈"
    }
  ]
}
PUT my_index/my_type/3
{
  "owner" : "王五",
  "family" : [
    {
      "call" : "dad",
      "name" :  "王五爸"
    },
    {
      "call" : "mom",
      "name" :  "王五妈"
    }
  ]
}

PUT my_index/my_type/4
{
  "owner" : "赵六",
  "family" : [
    {
      "call" : "dad",
      "name" :  "赵六爸"
    },
    {
      "call" : "mom",
      "name" :  "赵六妈"
    }
  ]
}

PUT my_index/my_type/5
{
  "owner" : "我",
  "family" : [
    {
      "call" : "dad",
      "name" :  "我老爸"
    },
    {
      "call" : "mom",
      "name" :  "我老妈"
    }
  ]
}
执行上面,对索引库my_index的表my_type添加五条数据

第二步:查看mapping结果

{
  "my_index": {
    "mappings": {
      "my_type": {
        "properties": {
          "family": {
            "properties": {
              "call": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          },
          "owner": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}
可以看到family是个对象类型包含两个属性。

第三步查询:

must+term查询

GET my_index/my_type/_search
{
 "query": {
   "bool": {
     "must": [
       {
         "term": {
           "family.name.keyword": "王五妈"
         }
       }, {
         "term": {
           "family.call.keyword": "dad"
         }
       }
     ]
   }
 }
}
预期没有返回结果 因为没有一个爸爸的名字叫做“王五妈”

实际返回的结果如下:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.5753642,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "3",
        "_score": 0.5753642,
        "_source": {
          "owner": "王五",
          "family": [
            {
              "call": "dad",
              "name": "王五爸"
            },
            {
              "call": "mom",
              "name": "王五妈"
            }
          ]
        }
      }
    ]
  }

第四步:聚合,得到所有爸爸的统计

GET my_index/my_type/_search?size=0
{
  "aggs": {
    "call": {
      "filter": {
        "term": {
          "family.call.keyword": "dad"
        }
      },
      "aggs": {
        "name": {
          "terms": {
            "field": "family.name.keyword",
            "size": 10
          }
        }
      }
    }
  }
}
期望得到5个爸爸的统计结果,实际却把妈妈的名称也返回了

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 5,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "call": {
      "doc_count": 5,
      "name": {
        "doc_count_error_upper_bound": 0,
        "sum_other_doc_count": 0,
        "buckets": [
          {
            "key": "张三妈",
            "doc_count": 1
          },
          {
            "key": "张三爸",
            "doc_count": 1
          },
          {
            "key": "我老妈",
            "doc_count": 1
          },
          {
            "key": "我老爸",
            "doc_count": 1
          },
          {
            "key": "李四妈",
            "doc_count": 1
          },
          {
            "key": "李四爸",
            "doc_count": 1
          },
          {
            "key": "王五妈",
            "doc_count": 1
          },
          {
            "key": "王五爸",
            "doc_count": 1
          },
          {
            "key": "赵六妈",
            "doc_count": 1
          },
          {
            "key": "赵六爸",
            "doc_count": 1
          }
        ]
      }
    }
  }
}
可见对于对象数组如果不做特殊处理的话,其结果是不符合预期的。


接下来进行nested相关的操作例子:

第一步:不能任由字段类型动态映射,需要改变对象数组类型映射成nested类型

可以建一个动态映射的模板:

在建模版之前,为了测试方便,把原来的上例子中的测试数据删除,重新开始:

删除执行操作:DELETE  my_index

PUT my_index
{
  "mappings": {
    "my_type": {
      "dynamic_templates": [
        {
          "object_as_nest": {
            "match_mapping_type": "object",
            "mapping": {
              "type": "nested"
            }
          }
        }
      ]
    }
  }
}
这样的话,对象类型就映射为nested类型,其他的字段依旧按照默认的动态映射。

第二步:添加数据(可批量也可以一条条插入)

这里就省略 

使用上面例子中的第一步的插入操作将数据添加进来

得到五条数据。

第三步:查看mapping结果:

{
  "my_index": {
    "mappings": {
      "my_type": {
        "dynamic_templates": [
          {
            "object_as_nest": {
              "match_mapping_type": "object",
              "mapping": {
                "type": "nested"
              }
            }
          }
        ],
        "properties": {
          "family": {
            "type": "nested",
            "properties": {
              "call": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          },
          "owner": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}

可以看到family的类型变成nested

第四步:查询爸爸名称等于“王五妈”,预期没有返回结果

nested+must+term

GET my_index/my_type/_search
{
  "query": {
    "nested": {
      "path": "family",
      "score_mode": "sum",
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "family.call.keyword": "dad"
              }
            },
            {
              "term": {
                "family.name.keyword": "王五妈"
              }
            }
          ]
        }
      }
    }
  }
}
返回结果:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 0,
    "max_score": null,
    "hits": []
  }
}

查询爸爸名称等于“张三爸”

GET my_index/my_type/_search
{
  "query": {
    "nested": {
      "path": "family",
      "score_mode": "sum",
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "family.call.keyword": "dad"
              }
            },
            {
              "term": {
                "family.name.keyword": "张三爸"
              }
            }
          ]
        }
      }
    }
  }
}
查询结果为:一条结果 和预期结果一致

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1.3862944,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 1.3862944,
        "_source": {
          "owner": "张三",
          "family": [
            {
              "call": "dad",
              "name": "张三爸"
            },
            {
              "call": "mom",
              "name": "张三妈"
            }
          ]
        }
      }
    ]
  }
}

第五步:聚合=》所有爸爸名称

nested+filter+term+terms 聚合方式

GET my_index/my_type/_search?size=0
{
  "aggs": {
    "家庭": {
      "nested": {
        "path": "family"
      },
      "aggs": {
        "爸爸": {
          "filter": {
            "term": {
              "family.call.keyword": "dad"
            }
          },
          "aggs": {
            "爸爸集合": {
              "terms": {
                "field": "family.name.keyword",
                "size": 10
              }
            }
          }
        }
      }
    }
  }
}
聚合结果如下:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 5,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "家庭": {
      "doc_count": 10,
      "爸爸": {
        "doc_count": 5,
        "爸爸集合": {
          "doc_count_error_upper_bound": 0,
          "sum_other_doc_count": 0,
          "buckets": [
            {
              "key": "张三爸",
              "doc_count": 1
            },
            {
              "key": "我老爸",
              "doc_count": 1
            },
            {
              "key": "李四爸",
              "doc_count": 1
            },
            {
              "key": "王五爸",
              "doc_count": 1
            },
            {
              "key": "赵六爸",
              "doc_count": 1
            }
          ]
        }
      }
    }
  }
}

聚合所有妈妈的名称:

GET my_index/my_type/_search?size=0
{
  "aggs": {
    "家庭": {
      "nested": {
        "path": "family"
      },
      "aggs": {
        "妈妈": {
          "filter": {
            "term": {
              "family.call.keyword": "mom"
            }
          },
          "aggs": {
            "妈妈集合": {
              "terms": {
                "field": "family.name.keyword",
                "size": 10
              }
            }
          }
        }
      }
    }
  }
}

聚合得到所有妈妈名称的结果是;

{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 5,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "家庭": {
      "doc_count": 10,
      "妈妈": {
        "doc_count": 5,
        "妈妈集合": {
          "doc_count_error_upper_bound": 0,
          "sum_other_doc_count": 0,
          "buckets": [
            {
              "key": "张三妈",
              "doc_count": 1
            },
            {
              "key": "我老妈",
              "doc_count": 1
            },
            {
              "key": "李四妈",
              "doc_count": 1
            },
            {
              "key": "王五妈",
              "doc_count": 1
            },
            {
              "key": "赵六妈",
              "doc_count": 1
            }
          ]
        }
      }
    }
  }
}

以上得到的结果完全符合预期。

而且也相对简单,尤其是动态mapping模板的建立。


















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值