笔记四十二:对象及 Nested 对象

数据的关联关系

  • 真实世界中很多重要的关联关系
    • 博客、作者、评论
    • 银行账户有多次交易记录
    • 客户有很多银行账户
    • 目录文件有很多文件和子目录

关系型数据库的范式化设计

  • 范式化设计(Normalization)的主要目标是“减少不必要的更新”
  • 副作用:一个完全范式化设计的数据库经常面临“查询缓慢”的问题
    • 数据库余额范式化,就需要Join越多的表
  • 范式化节省了储存空间,但是储存空间越来越便宜
  • 范式化简化了更新,但是数据“读”取操作可能越多
  • ES笔记四十二:对象及 Nested 对象

Denormalization

  • 反范式化设计
    • 数据“Flattening”,不使用关联关系,而是在文档中保存冗余的数据拷贝
  • 优点:无需处理Joins操作,数据读取性能好
    • Elasticsearch 通过压缩_source字段,减少磁盘空间的开销
  • 缺点:不适合在数据频繁修改的场景
    • 一条数据(用户名)的改动,可能会引起很多数据的更新

在 Elasticsearch 中处理关联关系

  • 关系型数据库,一般会考虑 Normalize 数据;在Elasticsearch,往往考虑 Denormalize 数据
    • Denormalize 的好处:读的速度变快、无需表连接、无需行锁
  • Elasticsearch 并不擅长处理关联关系,我们一般采用以下四种方法处理关联
    • 对象类型
    • 嵌套对象(Nested Object)
    • 父子关联关系(Parent 、Child)
    • 应用端关联

案例1:博客和其作者信息

  • 对象类型
    • 在每个博客的问下中都保留作者的信息
    • 如果作者信息发生变化,需要修改相关的博客文档
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"}}
        ]
      }
    }
    }

ES笔记四十二:对象及 Nested 对象

案例2:包含对象数组的文档

# 电影的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"
    }

  ]
}
# 查询电影信息
POST my_movies/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {"actors.first_name": "Keanu"}},
        {"match": {"actors.last_name": "Hopper"}}
      ]
    }
  }
}

ES笔记四十二:对象及 Nested 对象

为啥搜不到

  • 储存时,内部对象的边界没有在考虑在内,JSON格式被处理成扁平键值对的结构
  • 当对多个字段进行查询时,导致了意外的搜索结果
  • 可以用 Nested Data Type 解决这个问题

Nested Data Type

  • Nested 数据类型:允许对象数组中的对象呗独立索引
  • 使用 Nested 和 Properties 关键词,将所有 actors 索引到对个分隔的文档
  • 在内部,Nested 文档会被保存在两个 Lucene 文档中,查询时做join处理
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}}
        }
      }
    }
}

嵌套查询

  • 在内部,Nested 文档被保存在两个Lucene 文档中

ES笔记四十二:对象及 Nested 对象

# Nested 查询
POST my_movies/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {"title": "Speed"}},
        {
          "nested": {
            "path": "actors",
            "query": {
              "bool": {
                "must": [
                  {"match": {
                    "actors.first_name": "Keanu"
                  }},
                  {"match": {
                    "actors.last_name": "Hopper"
                  }}
                ]
              }
            }
          }
        }
      ]
    }
  }
}
//返回
 "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }

嵌套聚合

# Nested Aggregation
POST my_movies/_search
{
  "size": 0,
  "aggs": {
    "actors": {
      "nested": {
        "path": "actors"
      },
      "aggs": {
        "actor_name": {
          "terms": {
            "field": "actors.first_name",
            "size": 10
          }
        }
      }
    }
  }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
快乐就是解决一个又一个的问题!
CrazyZard
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!