ElasticSearch7.2 父子文档

建立父-子文档语法

首先看一下如何建立父子文档,明显和网上”_parent”的方式不一样,说明es后期版本已经修改了语法

1
2
3
4
5
6
7
8
9
10
11
12
13
PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": { 
        "type": "join",
        "relations": {
          "question": "answer" 
        }
      }
    }
  }
}

 

这段代码建立了一个my_index的索引,其中my_join_field是一个用于join的字段,type为join,关系relations为:父为question, 子为answer
至于建立一父多子关系,只需要改为数组即可:"question": ["answer", "comment"]

插入数据

插入两个父文档,语法如下

1
2
3
4
5
6
7
PUT my_index/_doc/1?refresh
{
  "text": "This is a question",
  "my_join_field": {
    "name": "question" 
  }
}

 

同时也可以省略name

1
2
3
4
5
PUT my_index/_doc/1?refresh
{
  "text": "This is a question",
  "my_join_field": "question"
}

 

插入子文档

子文档的插入语法如下,注意routing是父文档的id,平时我们插入文档时routing的默认就是id
此时name为answer,表示这是个子文档

1
2
3
4
5
6
7
PUT /my_index/_doc/3?routing=1
{
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", 
    "parent": "1" 
  }

 

通过parent_id查询子文档

通过parent_id query传入父文档id即可

1
2
3
4
5
6
7
8
9
GET my_index/_search
{
  "query": {
    "parent_id": { 
      "type": "answer",
      "id": "1"
    }
  }
}

 

父-子文档的性能及限制性

父-子文档主要适用于一对多的实体关系,将其反范式存入文档中

父-子文档主要由以下特性:

  • Only one join field mapping is allowed per index.
    每个索引只能有一个join字段
  • Parent and child documents must be indexed on the same shard. This means that the same routing value needs to be provided when getting, deleting, or updating a child document.
    父-子文档必须在同一个分片上,也就是说增删改查一个子文档,必须使用和父文档一样的routing key(默认是id)
  • An element can have multiple children but only one parent.
    每个元素可以有多个子,但只有一个父
  • It is possible to add a new relation to an existing join field.
    可以为一个已存在的join字段添加新的关联关系
  • It is also possible to add a child to an existing element but only if the element is already a parent.
    可以在一个元素已经是父的情况下添加一个子

总结

es中通过父子文档来实现join,但在一个索引中只能有一个一父多子的join

关系字段

es会自动生成一个额外的用于表示关系的字段:field#parent
我们可以通过以下方式查询

1
2
3
4
5
6
7
8
9
10
POST my_index/_search
{
 "script_fields": {
    "parent": {
      "script": {
         "source": "doc['my_join_field#question']" 
      }
    }
  }
}

 

部分响应为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "8",
"_score" : 1.0,
"fields" : {
  "parent" : [
    "8"
  ]
}
},
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_routing" : "10",
"fields" : {
  "parent" : [
    "10"
  ]
}
}

 

有_routing字段的说明是子文档,它的parent字段是父文档id,如果没有_routing就是父文档,它的parent指向当前id

全局序列

父-子文档的join查询使用一种叫做全局序列(Global ordinals)的技术来加速查询,它采用预加载的方式构建,防止在第一次查询或聚合时出现太长时间的延迟,但在索引元数据改变时重建,父文档越多,构建时间就越长,重建在refresh时进行,这会造成refresh大量延迟时间(在refresh时也是预加载).
如果join字段很少用,可以关闭这种预加载模式:"eager_global_ordinals": false

全局序列的监控

1
2
3
4
# 每个索引
curl -X GET "localhost:9200/_stats/fielddata?human&fields=my_join_field#question&pretty"
# 每个节点上的每个索引
curl -X GET "localhost:9200/_nodes/stats/indices/fielddata?human&fields=my_join_field#question&pretty"

一父多子的祖孙结构

考虑以下结构

1
2
3
4
5
6
7
   question
    /    \
   /      \
comment  answer
           |
           |
          vote

 

建立索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
          "question": ["answer", "comment"],  
          "answer": "vote" 
        }
      }
    }
  }
}

 

插入孙子节点

注意这里的routing和parent值不一样,routing指的是祖父字段,即question,而parent指的就是字面意思answer

1
2
3
4
5
6
7
8
PUT my_index/_doc/3?routing=1&refresh 
{
  "text": "This is a vote",
  "my_join_field": {
    "name": "vote",
    "parent": "2" 
  }
}

 

has-child查询

查询包含特定子文档的父文档,这是一种很耗性能的查询,尽量少用。它的查询标准格式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET my_index/_search
{
    "query": {
        "has_child" : {
            "type" : "child",
            "query" : {
                "match_all" : {}
            },
            "max_children": 10, //可选,符合查询条件的子文档最大返回数
            "min_children": 2, //可选,符合查询条件的子文档最小返回数
            "score_mode" : "min"
        }
    }
}

 

测试代码

部分测试代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
DELETE my_index

PUT /my_index?pretty
{
  "mappings": {
    "properties": {
      "my_join_field": { 
        "type": "join",
        "relations": {
          "question": "answer" 
        }
      }
    }
  }
}


# 插入父
PUT /my_index/_doc/8?refresh&pretty
{
  "text": "This is a question",
  "my_join_field": {
    "name": "question" 
  }
}

PUT /my_index/_doc/10?refresh&pretty
{
  "text": "This is a new question",
  "my_join_field": {
    "name": "question"
  }
}

PUT /my_index/_doc/12?refresh&pretty
{
  "text": "This is a new question",
  "my_join_field": {
    "name": "question"
  }
}

# 插入子
PUT /my_index/_doc/3?routing=8&refresh&pretty
{
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", 
    "parent": "8" 
  }
}


PUT /my_index/_doc/4?routing=10&refresh&pretty
{
  "text": "This is another answer",
  "my_join_field": {
    "name": "answer",
    "parent": "10"
  }
}

# 通过parent_id查询子文档
GET my_index/_search
{
  "query": {
    "parent_id": { 
      "type": "answer",
      "id": "8"
    }
  }
}

# 查询relation
POST my_index/_search
{
 "script_fields": {
    "parent": {
      "script": {
         "source": "doc['my_join_field#question']" 
      }
    }
  }
}
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值