Elasticsearch搜索服务学习之十二——数据建模(四)——祖孙关系模型

高新技术,ElasticSearch

2017-09-04

147

0

目录


上一篇我们简单认识了父子关系模型,现在我们看看这个模型的扩展——祖孙关系模型。

祖孙关系模型

祖孙关系模型是父子关系模型的一种扩展,父子关系模型是两个类型间的关系,而祖孙则为3个类型甚至多个类型的关系。与父子关系模型一样,这种对个类型关联关系的要求仍然是:满足这些关联关系的多个类型的文档必须处于同一分片上被索引。

我们修改一下上篇的这个示例,将其改为祖孙关系:
PUT /company
{
  "mappings": {
    "country": {},
    "branch": {
      "_parent": {
        "type": "country" // branch 是 country 的子辈。
      }
    },
    "employee": {
      "_parent": {
        "type": "branch" // employee是branch的子辈。即是说employee是country的孙辈
      }
    }
  }
}
country 和 branch 之间是一层简单的父子关系,所以索引与父子关系模型并没有什么区别:
POST /company/country/_bulk
{ "index": { "_id": "uk" }}
{ "name": "UK" }
{ "index": { "_id": "france" }}
{ "name": "France" }
 
POST /company/branch/_bulk
{ "index": { "_id": "london", "parent": "uk" }}
{ "name": "London Westmintster" }
{ "index": { "_id": "liverpool", "parent": "uk" }}
{ "name": "Liverpool Central" }
{ "index": { "_id": "paris", "parent": "france" }}
{ "name": "Champs Élysées" }
parent ID使得每一个 branch 文档被路由到与其父文档 country 相同的分片上进行操作。然而,当我们使用相同的方法来操作 employee 这个孙辈文档时,会发生什么呢?
PUT /company/employee/1?parent=london
{ "name": "Alice Smith", "dob": "1970-10-24", "hobby": "hiking" }
employee 文档的路由依赖其父文档 ID — 也就是 london — 但是 london 文档的路由却依赖 其本身的 父文档 ID — 也就是 uk 。此种情况下,孙辈文档很有可能最终和父辈、祖辈文档不在同一分片上,导致不满足祖辈和孙辈文档必须在同一个分片上被索引的要求。
 
解决方案是添加一个额外的 routing 参数,将其设置为祖辈的文档 ID ,以此来保证三代文档路由到同一个分片上。如下所示:
// routing 的值会取代 parent 的值作为路由选择。
PUT /company/employee/1?parent=london&routing=uk 
{
  "name":  "Alice Smith",
  "dob":   "1970-10-24",
  "hobby": "hiking"
}

此时,parent主要用以标识employee文档和其父文档的关系,而routing参数用以计算分片所在的位置,以保证employee和london、uk在同一分片上被索引。

多代文档关联查询

联合多代文档进行查询和聚合是可行的,只需要一代代的进行设定即可。例如:
GET /company/country/_search
{
  "query": {
    "has_child": {
      "type": "branch", // 第一层
      "query": {
        "has_child": {
          "type": "employee", // 第二层
          "query": {
            "match": {
              "hobby": "hiking" // 查询:雇员喜欢远足
            }
          }
        }
      }
    }
  }
}

上边的查询目标是country,但是需要考虑country下branch和branch中的employee。整个查询的含义为:查询国家(country)的各个城市(branch)下,有喜欢徒步旅行(hiking)的雇员(employee)的这些国家(country)。

性能建议

多代文档的联合查询必须考虑如下的代价:
  • 联合越多,性能越差。
  • 每一代的父文档都要将其字符串类型的_id字段存储在内存中,这会占用大量内存。
当你考虑父子关系或祖孙关系是否适合你现有关系模型时,请考虑下面这些建议 :
  • 尽量少地使用父子关系,仅在子文档远多于父文档时使用。
  • 避免在一个查询中使用多个父子联合语句。
  • 在 has_child 查询中使用 filter 上下文,或者设置 score_mode 为 none 来避免计算文档得分。
  • 保证父 IDs 尽量短,以便在 doc values 中更好地压缩,被临时载入时占用更少的内存。

总结

祖孙关系其实是父子关系的一种多级扩展,虽然理论上可以支持无限层级的关系,但是层级越多,查询和索引的性能越低。因此,在考虑是否使用父子关系或祖孙关系时,请考虑以上的建议。

前一篇:elasticsearch路由一个文档到一个分片
后一篇: Elasticsearch搜索服务学习之十二——数据建模(五)——嵌套关系和父子关系结合建模示例

belonk

轻轻地我走了,正如我轻轻地来,我挥一挥衣袖,不带走一片云彩