探索ES-对象和嵌套对象(3)前文回顾
上一篇文章写了探索ES-入门Kibana(2),基本完成了ElasticSearch
和Kibana
安装和基本概念。今天,让我们正式谈谈一些ElasticSearch
使用中会遇到的问题和解决问题的方法。
ElasticSearch
作为一个Nosql
其中一个特点是不支持数据库多表关联的
。所以,在ElasticSearch
中间的数据都是以反范式
存储的方式。简单来说,它将被存储为一个大Json对象
。
对象数据在ElasticSearch
中是作为object
类型存储。但是,如果对象数据是数组,则在查询过程中会出现无法相关查询的问题。
不知道有没有朋友看到这里一脸懵比?
如果您不知道这是什么问题,请参见下面的例子。
如果你知道这是什么意思,请看下面的解决方案。
对象举一个博客系统的例子。
假设我们要在那里ElasticSearch
中建立一个blog
索引。blog
索引包括以下内容。
- title:博客的标题
- content:博客的内容
- comment:博客的评论
- comment.username:用户对博客进行评论
- comment.content:博客评论的内容
不知道这里有没有人发现博客和评论是一对多的关系,没有发现也没关系,继续往下看。
根据上面分解的字段,我们在ElasticSearch
建立以下索引。
PUT blog{ "mappings": { "_doc": { "properties": { "blog": { "properties": { "title": { "type": "keyword" }, "content": { "type": "text" }, "comment": { "properties": { "content": { "type": "text" }, "username": { "type": "keyword" } } } } } } } }}复制代码
可以发现blog
和comment
都是作为object
存储类型。在ElasticSearch
默认情况下,中会将字段作为object
来存储。
我们插入一个文档。
PUT blog/_doc/1{ "blog": { "title": "this is my first blog", "content": "this is my first blog content", "comment": { "content": "so bad!", "username": "li si" } }}复制代码
本文档的内容大致是有人写了一篇博客,然后李四同学在下面批评so bad
。
我们以评论为维度进行搜索。
GET blog/_search{ "query":{ "match": { "blog.comment.content": "bad" } }}复制代码
可以搜索数据。
扁平化这里需要注意的是在ES中,对象数据最终会被flat(扁平化)
。
什么意思?
也就是说,上面的内容数据终于在了ES
以下形式存储。
blog:this is my first blogcontent:this is my first blog contentcomment.content:so badcomment.username:li 复制代码的si
然而,一般来说,博客肯定不止一个数据。一般来说,会有多个数据,即上述一对多关系。
我们更新了以前的数据。
PUT blog/_doc/1{ "blog": { "title": "this is my first blog", "content": "this is my first blog content", "comment": [ { "content": "oh so good!", "username": "zhang san" }, { "content": "so bad!", "username": "zhang san" }, { "content": "so bad!", "username": "li si" } ] }}复制代码
以上数据的意思是,除了李四说bad之外,还有张三大哥说good。
根据之前的查询方法,我们也可以正常查询。但是,如果我们现在想联系查询,我们不仅要求评论员是李四,还要求李四说谷歌查询。让我们写一个bool
查询。bool
可以写多个查询,不管是不是term
还是match
只能写一个查询。
GET blog/_search{ "query": { "bool": { "must": [ { "match": { "blog.comment.content": "good" } }, { "match": { "blog.comment.username": "li si" } } ] } }}复制代码
然而,我们仍然可以查询这些数据。显然,李四说的是bad而不是good。为什么还能查询?
那是因为对象被扁平化后,在ES
就是这样存储的。
blog.comment.content:["oh so good!","so bad!"]blog.comment.username:["zhang san","li si复制代码
两个字段都以数组的形式存储数据。数组和数组之间没有对应的关系。
ES
先在blog.comment.coetent
看看有没有good
,发现有。再次发现blog.comment.username
看看有没有li si
发现还是有的。
最后,查询了这个数据。
那么如何实现相关查询呢?使用nested
,嵌套对象即可。
默认字段的类型是object
使用类型nested
需要显式指定。删除之前的索引后,重新建立索引。因为在ES
在中间,我们无法修改以前的内容mapping
,我们只能删除并重建它。如果需要以前的数据,可以使用reindex
将数据导入新索引。
PUT blog{ "mappings": { "_doc": { "properties": { "blog": { "properties": { "title": { "type": "keyword" }, "content": { "type": "text" }, "comment": { "type":"nested", "properties": { "content": { "type": "text" }, "username": { "type": "keyword" } } } } } } } }}复制代码
我们再次插入一个数据。
PUT blog/_doc/1{ "blog": { "title": "this is my first blog", "content": "this is my first blog content", "comment": [ { "content": "oh so good!", "username": "zhang san" }, { "content": "so bad!", "username": "zhang san" }, { "content": "so bad!", "username": "li si" } ] }}复制代码
我们进行相关搜索。
GET blog/_search{ "query": { "bool": { "must": [ { "match": { "blog.comment.content": "good" } }, { "match": { "blog.comment.username": "li si" } } ] } }}复制代码
此时,我们发现我们无法搜索相应的数据。因为在这个时候,ES
对nested
对象之间的关系被额外存储。