Elasticsearch核心技术与实战学习笔记 49 | 对象及Nested对象

一 序

  本文属于极客时间Elasticsearch核心技术与实战学习笔记系列。

二 数据的关联关系

真实世界中有很多关系,软件也是对此一种抽象。

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

范式化设计(Normalization)的主要目标是 “减少不必要的更新”
副作用:一个完全范式化设计的数据库经常面临 “查询缓慢” 的问题

  • 数据库余额范式化,就需要 Join 越多的表

范式化节省了储存空间,但是储存空间越来越便宜
范式化简化了更新,但是数据 “读” 取操作可能越多

2.2Denormalization

 反范式化设计

  • 数据 “Flattening”, 不使用关联关系,而是在文档中保存冗余的数据拷贝

优点:无需处理 Joins 操作,数据读取性能好

  • Elasticsearch 通过压缩_source 字段,减少磁盘空间的开销

缺点:不适合在数据频繁修改的场景

  • 一条数据(用户名)的改动,可能会引起很多数据的更新

2.3 在 Elasticsearch 中处理关联关系

关系型数据库,一般会考虑 Normalize 数据;在 Elasticsearch,往往考虑 Denormalize 数据

  • Denormalize 的好处:读的速度变快、无需表连接、无需行锁

Elasticsearch 并不擅长处理关联关系,我们一般采用以下四种方法处理关联

  • 对象类型
  • 嵌套对象(Nested Object)
  • 父子关联关系(Parent 、Child)
  • 应用端关联

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

数据准备:

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"
  }
}

这是bool的query,es满足搜索需求。

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

数据准备:

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"
    }

  ]
}

查询到数据,但是不存在满足查询条件的数据。

为啥搜到不需要的数据?

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

Nested Data Type

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

数据准备:

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"
    }
  ]
}  

这个索引增加了    "type": "nested",指定为嵌套的对象。

3 嵌套查询

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

上面指定嵌套关键字:nested,path指定属性。下面增加actor_name做子聚合。就可以实现嵌套对象的term分桶。

# 普通 aggregation不工作
POST my_movies/_search
{
  "size": 0,
  "aggs": {
    "NAME": {
      "terms": {
        "field": "actors.first_name",
        "size": 10
      }
    }
  }
}

猜你喜欢

转载自blog.csdn.net/bohu83/article/details/107008650
49