ES数据打平或者flat扁平化 导致嵌套对象查询可能出错

探索ES-对象和嵌套对象(三)

前文回顾

上篇文章写了探索ES-入门Kibana(二),算是基本上讲完了ElasticSearchKibana的安装和基本的概念。今天来正式讲一讲一些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"
                }
              }
            }
          }
        }
      }
    }
  }
}
复制代码

可以发现blogcomment都是作为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 blog
content:this is my first blog content
comment.content:so bad
comment.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": "li si"
      }
    ]
  }
}
复制代码

上面这条数据的意思是除了李四说bad之后,还有个张三大哥说了good。

我们按照之前的查询方式也是可以正常查询到的。但是,我们现在如果要关联查询,我们既要评论者是李四的,又要李四说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": "li si"
      }
    ]
  }
}
复制代码

我们进行关联搜索。

GET blog/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "blog.comment.content": "good"
          }
        },
        {
          "match": {
            "blog.comment.username": "li si"
          }
        }
      ]
    }
  }
}
复制代码

这个时候,我们发现就搜索不到对应的数据了。因为这个时候,ESnested对象的关系额外进行了存储。

 

posted @ 2022-01-04 20:20  bonelee  阅读(901)  评论(0编辑  收藏  举报