一、背景
在咱们工作的过程中,有些时候咱们须要用到父子文档的关系映射。比方:一个问题有多个答案、一本书籍有多个评论等等。此处咱们能够应用 es 的 jion
数据类型或 nested
来实现。此处咱们应用 join
来建设 es 中的父子文档关系。
二、需要
咱们须要创立一个打算 (plan
),打算下存在流动(activity
) 和书籍(book
),书籍下存在评论(comments
)。
即层级构造为:
plan
/ \
/ \
activity book
|
|
comments
三、前置常识
- 每一个
mapping
下只能有一个join
类型的字段。 - 父文档和子文档必须在同一个分片 (
shard
) 上。即: 增删改查一个子文档都必须和父文档应用雷同的 routing key。 - 每个元素只能有一个父,然而能够存在多个子。
- 能够为一个曾经存在的 join 字段减少新的关联关系。
- 能够为一个曾经是父的元素减少一个子元素。
join
数据类型在elasticsearch
中不应该像关系型数据库那种应用。而且has_child
和has_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 Object | join (Parent/Child) |
---|---|
1、文档存储在一起,读取性能高 | 1、父子文档独自存储,互不影响。然而为了保护 join 的关系,须要占用额定的内容,读取性能略差。 |
2、更新父文档或子文档时,须要更新整个文档。 | 2、父文档和子文档能够独自更新。 |
3、实用于查问频繁,子文档偶然更新的状况。 | 3、实用于更新频繁的状况,且子文档的数量远远超过父文档的数量。 |
六、参考文档
1、join 数据类型
2、has child 查问
3、has parent 查问
4、parent id 查问