ElasticSearch之Nested对象

慈云数据 2024-03-15 技术支持 59 0

写在前面

本文看下es的nested嵌套对象相关内容

1:es用了啥范式?

关系型数据库中定义了6大数据库范式,即1,2,3,BC,4,5的NF(normal form),分别如下:

1NF:每个列都不可拆分,即都是原子的
2NF:在满足1NF的基础上,消除部分函数依赖
3NF:在满足2NF的基础上,消除传递函数依赖
BCNF:在满足3NF的基础上,消除主属性对于码的部分函数依赖和传递函数依赖(此时和非主键列没有关系)
4NF:在满足BCNF的基础上,消除表内的多对多关系
5NF:略

数据库范式的目的在于减少更新的复杂度,以及降低磁盘的存储空间。其中对于第二个问题存储设备目前非常廉价而且容量很大,所以不是什么问题了。对于第一个更新的复杂度问题会带来的查询效率变低的问题,因为需要更多的关联join。那么对于es来说它是使用了哪种范式呢?因为es的设计目标是快速查询,所以使用到是反范式,即冗余存储。比如如下的数据

在这里插入图片描述

如果按照关系型数据库范式来设计,user的信息需要存储到单独的一张表中去,但是在es中就是在一个对象中来存储,对于这种存储,es支持非常方便和高效的查询:

  • 准备数据
    DELETE blog
    # 设置blog的 Mapping
    PUT /blog
    {
      "mappings": {
        "properties": {
          "content": {
            "type": "text"
          },
          "time": {
            "type": "date"
          },
          "user": {
            "properties": {
              "city": {
                "type": "text"
              },
              "userid": {
                "type": "long"
              },
              "username": {
                "type": "keyword"
              }
            }
          }
        }
      }
    }
    # 插入一条 Blog 信息
    PUT blog/_doc/1
    {
      "content":"I like Elasticsearch",
      "time":"2019-01-01T00:00:00",
      "user":{
        "userid":1,
        "username":"Jack",
        "city":"Shanghai"
      }
    }
    
    • 查询
      # 查询 Blog 信息
      POST blog/_search
      {
        "query": {
          "bool": {
            "must": [
              {"match": {"content": "Elasticsearch"}},
              {"match": {"user.username": "Jack"}}
            ]
          }
        }
      }
      

      在这里插入图片描述

      我们再来看一个存储对象数组的例子:

      在这里插入图片描述

      • 准备数据
        DELETE my_movies
        # 电影的Mapping信息
        PUT my_movies
        {
              "mappings" : {
              "properties" : {
                "actors" : {
                  "properties" : {
                    "first_name" : {
                      "type" : "keyword"
                    },
                    "last_name" : {
                      "type" : "keyword"
                    }
                  }
                },
                "title" : {
                  "type" : "text",
                  "fields" : {
                    "keyword" : {
                      "type" : "keyword",
                      "ignore_above" : 256
                    }
                  }
                }
              }
            }
        }
        # 写入一条电影信息
        POST my_movies/_doc/1
        {
          "title":"Speed",
          "actors":[
            {
              "first_name":"Keanu",
              "last_name":"Reeves"
            },
            {
              "first_name":"Dennis",
              "last_name":"Hopper"
            }
          ]
        }
        
        • 查询first_name为Keanu并且last_name为Hopper的文档信息
          POST my_movies/_search
          {
            "query": {
              "bool": {
                "must": [
                  {"match": {"actors.first_name": "Keanu"}},
                  {"match": {"actors.last_name": "Hopper"}}
                ]
              }
            }
          }
          

          在这里插入图片描述

          按照正常思维,应该查不到才对,但为什么查到了呢?这和es的数据存储方式有关系,对于数组es默认是按照一种扁平结构来存储的,如下:

          在这里插入图片描述

          这种存储结构的好处是可以加快查询的速度,但坏处呢就是上例中反直觉结果。

          所以如果能够让内部的对象也按照单独文档来存储,就能解决这个查询错误的问题了,而想要使用单独的文档来存储内部的对象,就需要用到es提供的nested对象功能,继续来看(作为本文的主题,必须单开一部分,还必须是一级标题😀😀😀)。

          2:nested对象

          nested是一种定义对象的数据类型,比如可通过如下方式来定义一个nested的类型:

          在这里插入图片描述

          在保存时会被保存为单独的文档,查询时通过join的方式来查询,当然此时会牺牲掉部分查询性能。

          • 创建如下的mapping
            DELETE my_movies
            # 创建 Nested 对象 Mapping
            PUT my_movies
            {
                  "mappings" : {
                  "properties" : {
                    "actors" : {
                      "type": "nested",
                      "properties" : {
                        "first_name" : {"type" : "keyword"},
                        "last_name" : {"type" : "keyword"}
                      }},
                    "title" : {
                      "type" : "text",
                      "fields" : {"keyword":{"type":"keyword","ignore_above":256}}
                    }
                  }
                }
            }
            
            • 接着来插入测试数据
              POST my_movies/_doc/1
              {
                "title":"Speed",
                "actors":[
                  {
                    "first_name":"Keanu",
                    "last_name":"Reeves"
                  },
                  {
                    "first_name":"Dennis",
                    "last_name":"Hopper"
                  }
                ]
              }
              

              此时存储结构为红框中所示:

              在这里插入图片描述

              • 查询first_name为Keanu并且last_name为Hopper的文档信息

                此时就查询不到了:

                POST my_movies/_search
                {
                    "query": {
                        "bool": {
                            "must": [
                                {
                                    "nested": {
                                        "path": "actors",
                                        "query": {
                                            "bool": {
                                                "must": [
                                                    {
                                                        "match": {
                                                            "actors.first_name": "Keanu"
                                                        }
                                                    },
                                                    {
                                                        "match": {
                                                            "actors.last_name": "Hopper"
                                                        }
                                                    }
                                                ]
                                            }
                                        }
                                    }
                                }
                            ]
                        }
                    }
                }
                

                在这里插入图片描述

                当然如果把"actors.last_name": "Hopper"改为Reeves是能查出来数据的:

                在这里插入图片描述

                写在后面

                参考文章列表

                关系型数据库MySQL及其优化 。

微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon