Elasticsearch学习-嵌套文档
本文以Elasticsearch 6.8.4版本为例,介绍Elasticsearch嵌套文档的使用。
最近一段时间都在搞Elasticsearch搜索相关的工作,总结一下搜索知识点供大家参考。
在Elasticsearch取消了多个索引内创建多个type的机制,由于场景需要,所以调研了嵌套文档和父子文档
以文章和文章留言为例,嵌套文档都在一个文档内,而父子文档则分开存储了父文档与子文档,本文我们来学习嵌套文档的使用。
1、嵌套文档
嵌套文档看似与文档内有一个集合字段类似,但是实则有很大区别,以上面图中嵌套文档为例,留言1,留言2,留言3虽然都在当前文章所在的文档内,但是在内部其实存储为4个独立文档,如下图所示。
同时,嵌套文档的字段类型需要设置为nested,设置成nested后的不能被直接查询,需要使用nested查询,这里不做具体介绍,详细查看1.2。
1.1 创建索引
接下来,介绍一下如何创建嵌套文档索引,比如有这样的数据,如下:
{
"title": "这是一篇文章",
"body": "这是一篇文章,从哪里说起呢? ... ...",
"comments": [
{
"name": "张三",
"comment": "写的不错",
"age": 28,
"date": "2020-05-04"
},
{
"name": "李四",
"comment": "写的很好",
"age": 20,
"date": "2020-05-04"
},
{
"name": "王五",
"comment": "这是一篇非常棒的文章",
"age": 31,
"date": "2020-05-01"
}
]
}
创建索引名和type均为blog的索引,其中comments字段为嵌套文档类型,需要将type设置为nested,其余都是一些正常的字段,创建索引语句如下:
PUT http://localhost:9200/blog/
{
"mappings": {
"blog": {
"properties": {
"comments": {
"type": "nested",
"properties": {
"date": {
"type": "date"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"comment": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"age": {
"type": "long"
}
}
},
"body": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
}
如下图所示
1.2 插入数据
将1.1中示例的数据插入blog索引,对嵌套文档来说,插入没什么特别的,如下:
PUT http://localhost:9200/blog/blog/1/
{
"title":"这是一篇文章",
"body":"这是一篇文章,从哪里说起呢? ... ...",
"comments":[
{
"name":"张三",
"comment":"写的不错",
"age":28,
"date":"2020-05-04"
},
{
"name":"李四",
"comment":"写的很好",
"age":20,
"date":"2020-05-04"
},
{
"name":"王五",
"comment":"这是一篇非常棒的文章",
"age":31,
"date":"2020-05-01"
}
]
}
如图所示:
1.3 查询
在前面说到,使用嵌套文档时,直接查询nested文档时查询不到的,这里试一下,先查询一下根文档的内容(文章内容),查询title包含‘文章’的内容:
POST http://localhost:9200/blog/blog/_search/
{
"query": {
"bool": {
"filter": [
{
"bool": {
"must": [
{
"match_phrase": {
"title": {
"query": "文章"
}
}
}
]
}
}
]
}
}
}
Elasticsearch-Head,如下图所示
接下来我们查询一下,留言中name为张三的数据,查询如下:
{
"query": {
"bool": {
"filter": [
{
"bool": {
"must": [
{
"match_phrase": {
"comments.name": {
"query": "张三"
}
}
}
]
}
}
]
}
}
}
Elasticsearch-Head 如下图所示
这里举例,我们要查询title中包含‘文章’且留言name中包含‘张三’的数据,使用如下查询:
POST http://localhost:9200/blog/blog/_search/
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "文章"
}
},
{
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{
"match": {
"comments.name": "张三"
}
}
]
}
}
}
}
]
}
}
}
Elasticsearch-Head 如下图所示
其实从查询语句中可以看出,nested中查询的是嵌套文档的内容,语法与正常查询时一致。
使用嵌套文档时,文档的分数计算需要注意,参考官方文档的描述:
nested 查询肯定可以匹配到多个嵌套的文档。每一个匹配的嵌套文档都有自己的相关度得分,但是这众多的分数最终需要汇聚为可供根文档使用的一个分数。
默认情况下,根文档的分数是这些嵌套文档分数的平均值。可以通过设置 score_mode 参数来控制这个得分策略,相关策略有 avg (平均值), max (最大值), sum (加和) 和 none (直接返回 1.0 常数值分数)。
1.4 排序
可能有一些场景需要按照嵌套文档的字段记性排序,举例:
为了符合上述场景,新增两条数据:
PUT http://localhost:9200/blog/blog/2/
{
"title": "这是一篇文章2",
"body": "这是一篇文章2,从哪里说起呢? ... ...",
"comments": [
{
"name": "张三",
"comment": "写的不错",
"age": 28,
"date": "2020-05-11"
},
{
"name": "李四",
"comment": "写的很好",
"age": 20,
"date": "2020-05-16"
},
{
"name": "王五",
"comment": "这是一篇非常棒的文章",
"age": 31,
"date": "2020-05-01"
}
]
}
PUT http://localhost:9200/blog/blog/3/
{
"title": "这是一篇文章3",
"body": "这是一篇文章3,从哪里说起呢? ... ...",
"comments": [
{
"name": "张三",
"comment": "写的不错",
"age": 28,
"date": "2020-05-03"
},
{
"name": "李四",
"comment": "写的很好",
"age": 20,
"date": "2020-05-20"
},
{
"name": "王五",
"comment": "这是一篇非常棒的文章",
"age": 31,
"date": "2020-05-01"
}
]
}
查询title中包含‘文章’且留言name中包含‘张三’,并且按照留言date字段倒序排序,查询语句如下:
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "文章"
}
},
{
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{
"match": {
"comments.name": "张三"
}
}
]
}
}
}
}
]
}
},
"sort": {
"comments.date": {
"order": "desc",
"mode": "max",
"nested_path": "comments",
"nested_filter": {
"bool": {
"must": [
{
"match": {
"comments.name": "张三"
}
}
]
}
}
}
}
}
需要注意的是,在sort内,又添加了nested_filter来过滤一遍上面嵌套文档的查询条件,原因是这样的,在嵌套文档查询排序时是先按照条件进行查询,查询后再进行排序,那么可能由于数据的原因,导致排序的字段不是按照匹配上的数据进行排序,比如这是本文正确的结果,如下图所示(为了方便查看,使用图表展示的数据)。
如果我们去掉nested_filter,在查询,由于文章3中李四评论的日期是20号,导致这条记录排在了最前面,这就是为什么使用nested_filter的原因,查询结果如下:
1.5 聚合
聚合的场景可能也比较常见,其实熟悉上面嵌套文档的使用的话,对聚合文档使用难度应该也不大,
新增一条数据:
PUT http://localhost:9200/blog/blog/4/
{
"title": "这是一篇文章4",
"body": "这是一篇文章4,从哪里说起呢? ... ...",
"comments": [
{
"name": "张三",
"comment": "写的不错",
"age": 28,
"date": "2020-03-03"
},
{
"name": "李四",
"comment": "写的很好",
"age": 20,
"date": "2020-04-20"
},
{
"name": "王五",
"comment": "这是一篇非常棒的文章",
"age": 31,
"date": "2020-06-01"
}
]
}
举例:需要查询每个月评论人数的平均数,查询语句如下:
POST http://localhost:9200/blog/blog/_search/
{
"size": 0,
"aggs": {
"comments": {
"nested": {
"path": "comments"
},
"aggs": {
"by_month": {
"date_histogram": {
"field": "comments.date",
"interval": "month",
"format": "yyyy-MM"
},
"aggs": {
"avg_stars": {
"avg": {
"field": "comments.age"
}
}
}
}
}
}
}
}
结果如下图所示:
1.6 使用建议
- 正如本文所说,嵌套文档中,所有内容都在同一个文档内,这就导致嵌套文档进行增加、修改或者删除时,整个文档都要重新被索引。嵌套文档越多,这带来的成本就越大。当时就是由于这个原因,最终没有选择使用嵌套文档。
- 嵌套文档的分数计算问题需要注意,可以参考本文1.3最后部分。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
SpringBoot 使用JestClient操作Elasticsearch
1.Jest介绍 操作Elasticsearch的客户端有很多,SpringBoot也提供了方式去操作,这里介绍另外一种方式去使用Elasticsearch --- JestClient JestClient是一款基于HTTP方式操作的Elasticsearch的客户端,支持同步和异步操作,同时也可以结合ElasticSearch的依赖进行操作Elasticsearch。 支持多个版本的Elasticsearch,如下: = 6.0.0 | 6= 5.0.0 | 5= 2.0.0 | 20.1.0 - 1.0.0 | 1 <= 0.0.6 | < 1 更多信息可以查看github,地址是:https://github.com/searchbox-io/Jest 2.SpringBoot整合JestClient 接下来介绍如何在SpringBoot中使用JestClient操作Elasticsearch。 2.1 前置工作 首先启动Elasticsearch,我这里是在本地启动的Elasticsearch,版本是6.8.2,为了方便查看数据,这里使用Elasticsearch...
-
下一篇
hbase shell实现原理简析
hbase的交互式命令行是通过jruby实现的,当我们输入hbase shell时,实际上最终执行的是org.jruby.Main,并以bin/hirb.rb作为参数,注意是根目录下bin目录中的hirb.rb,而不是hbase-shell中的irb/hirb.rb;这个类来自jruby的包,作用是把ruby编写的代码转换成java字节码,进而能够运行在JVM中; 实现逻辑大体可分为2个阶段:初始化阶段和命令执行阶段,前者是启动shell时的执行逻辑,后者是输入命令后的执行逻辑,以下分别简述其流程; 初始化阶段 1、创建HBaseConfiguration实例,并将启动时带的键值对参数设置进去;2、创建Hbase实例,初始化connection,代码在hbase.rb中;3、创建Shell实例,此时会执行一些load_command_group方法,这些方法实际上是初始化了commands和command_groups这2个map变量,commands中存放了各个命令的name与class的映射关系,代码在shell.rb中;4、接下来执行Shell实例的export_commands...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker容器配置,解决镜像无法拉取问题
- Windows10,CentOS7,CentOS8安装Nodejs环境
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合Redis,开启缓存,提高访问速度
- 设置Eclipse缩进为4个空格,增强代码规范