一、背景

在咱们工作的过程中,有些时候咱们须要用到父子文档的关系映射。比方:一个问题有多个答案、一本书籍有多个评论等等。此处咱们能够应用 es 的 jion数据类型或 nested来实现。此处咱们应用join来建设es中的父子文档关系。

二、需要

咱们须要创立一个打算(plan),打算下存在流动(activity)和书籍(book),书籍下存在评论(comments)。

即层级构造为:

     plan    /    \   /      \activity  book           |           |          comments

三、前置常识

  1. 每一个mapping下只能有一个join类型的字段。
  2. 父文档和子文档必须在同一个分片(shard)上。即: 增删改查一个子文档都必须和父文档应用雷同的 routing key。
  3. 每个元素只能有一个父,然而能够存在多个子。
  4. 能够为一个曾经存在的 join 字段减少新的关联关系。
  5. 能够为一个曾经是父的元素减少一个子元素。

join数据类型在elasticsearch中不应该像关系型数据库那种应用。而且has_childhas_parent都是比拟耗费性能的。

只有当 子的数据 远远大于 父的数据时,应用join才是有意义的。比方:一个博客下,有多个评论。

四、实现步骤

1、创立 mapping

PUT /plan_index{  "settings": {    "number_of_shards": 3,    "number_of_replicas": 1  },  "mappings": {    "properties": {      "plan_id":{        "type": "keyword"      },      "plan_name":{        "type": "text",        "fields": {          "keyword":{            "type" : "keyword",            "ignore_above" : 256          }        }      },      "act_id":{        "type": "keyword"      },      "act_name":{        "type": "text",        "fields": {          "keyword":{            "type" : "keyword",            "ignore_above" : 256          }        }      },      "comment_id":{        "type": "keyword"      },      "comment_name":{        "type": "text",        "fields": {          "keyword":{            "type" : "keyword",            "ignore_above" : 256          }        }      },      "creator":{        "type": "keyword"      },      "create_time":{        "type": "date",        "format": "yyyy-MM-dd||yyyy-MM-dd HH:mm:ss"      },      "plan_join": {        "type": "join",        "relations": {          "plan": ["activity", "book"],          "book": "comments"        }      }    }  }}

留神⚠️

2、增加父文档数据

此处增加的是 (plan) 数据。

PUT /plan_index/_doc/plan-001{  "plan_id": "plan-001",  "plan_name": "四月打算",  "creator": "huan",  "create_time": "2021-04-07 16:27:30",  "plan_join": {    "name": "plan"  }}PUT /plan_index/_doc/plan-002{  "plan_id": "plan-002",  "plan_name": "五月打算",  "creator": "huan",  "create_time": "2021-05-07 16:27:30",  "plan_join": "plan"}

留神⚠️:

1、如果是创立父文档,则须要应用 plan_join 指定父文档的关系的名字(此处为plan)。

2、plan_join为创立索引的 mapping时指定join的字段的名字。

3、指定父文档时,plan_join的这2种写法都能够。

3、增加子文档

PUT /plan_index/_doc/act-001?routing=plan-001{  "act_id":"act-001",  "act_name":"四月第一个流动",  "creator":"huan.fu",  "plan_join":{    "name":"activity",    "parent":"plan-001"  }}PUT /plan_index/_doc/book-001?routing=plan-001{  "book_id":"book-001",  "book_name":"四月读取的第一本书",  "creator":"huan.fu",  "plan_join":{    "name":"book",    "parent":"plan-001"  }}PUT /plan_index/_doc/book-002?routing=plan-001{  "book_id":"book-002",  "book_name":"编程珠玑",  "creator":"huan.fu",  "plan_join":{    "name":"book",    "parent":"plan-001"  }}PUT /plan_index/_doc/book-003?routing=plan-002{  "book_id":"book-003",  "book_name":"java编程思维",  "creator":"huan.fu",  "plan_join":{    "name":"book",    "parent":"plan-002"  }}# 实践上 comment 的父文档是 book ,然而此处routing应用 plan 也是能够的。PUT /plan_index/_doc/comment-001?routing=plan-001{  "comment_id":"comment-001",  "comment_name":"这本书还能够",  "creator":"huan.fu",  "plan_join":{    "name":"comments",    "parent":"book-001"  }}PUT /plan_index/_doc/comment-002?routing=plan-001{  "comment_id":"comment-002",  "comment_name":"值得一读,棒。",  "creator":"huan.fu",  "plan_join":{    "name":"comments",    "parent":"book-001"  }}

留神⚠️:

1、子文档(子孙文档等)须要和父文档应用雷同的路由键。

2、须要指定父文档的id。

3、须要指定join的名字。

4、查问文档

1、依据父文档id查问它下方的子文档

需要:返回父文档id是plan-001下的类型为book的所有子文档。

GET /plan_index/_search{  "query":{    "parent_id": {      "type":"book",      "id":"plan-001"    }  }}

2、has_child返回满足条件的父文档

需要:返回创建者(creator)是huan.fu,并且子文档起码有2个的父文档。

GET /plan_index/_search{  "query": {    "has_child": {      "type": "book",      "min_children": 2,        "query": {        "match": {          "creator": "huan.fu"        }      }    }  }}

3、has_parent返回满足父文档的子文档

需要:返回父文档(book)的创建者是huan.fu的所有子文档

GET /plan_index/_search{  "query": {    "has_parent": {      "parent_type": "book",      "query": {        "match": {          "creator":"huan.fu"        }      }    }  }}

五、Nested Object 和 join 比照

Nested Objectjoin (Parent/Child)
1、文档存储在一起,读取性能高1、父子文档独自存储,互不影响。然而为了保护join的关系,须要占用额定的内容,读取性能略差。
2、更新父文档或子文档时,须要更新整个文档。2、父文档和子文档能够独自更新。
3、实用于查问频繁,子文档偶然更新的状况。3、实用于更新频繁的状况,且子文档的数量远远超过父文档的数量。

六、参考文档

1、join数据类型

2、has child查问

3、has parent查问

4、parent id查问