引出问题:
“某头条新闻APP”新闻内容和新闻评论是1对多的关系?
在ES6.X该如何存储、如何进行高效检索、聚合操作呢?
相信阅读本文,你就能得到答案!
Mysql中多表关联,我们可以通过left join 或者Join等实现;
ES5.X版本,借助父子文档实现多表关联,类似数据库中Join的功能;实现的核心是借助于ES5.X支持1个索引(index)下多个类型(type)。
ES6.X版本,由于每个索引下面只支持单一的类型(type)。
幸好,ES6.X新推出了Join类型,主要解决类似Mysql中多表关联的问题。
仍然是一个索引下,借助父子关系,实现类似Mysql中多表关联的操作。
Join类型的Mapping如下:
核心
- 1) “my_join_field”为join的名称。
PUT my_join_index
{
"mappings": {
"_doc": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"question": "answer"
}
}
}
}
}
}
直接上以下简化的形式,更好理解些。
如下,定义了两篇父文档。
文档类型为父类型:”question”。
PUT my_join_index/_doc/1?refresh
{
"text": "This is a question",
"my_join_field": "question"
}
PUT my_join_index/_doc/2?refresh
{
"text": "This is another question",
"my_join_field": "question"
}
- 路由值是强制性的,因为父文件和子文件必须在相同的分片上建立索引。
- “answer”是此子文档的加入名称。
- 指定此子文档的父文档ID:1。
PUT my_join_index/_doc/3?routing=1&refresh
{
"text": "This is an answer",
"my_join_field": {
"name": "answer",
"parent": "1"
}
}
PUT my_join_index/_doc/4?routing=1&refresh
{
"text": "This is another answer",
"my_join_field": {
"name": "answer",
"parent": "1"
}
}
GET my_join_index/_search
{
"query": {
"match_all": {}
},
"sort": ["_id"]
}
返回结果如下:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 4,
"max_score": null,
"hits": [
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "1",
"_score": null,
"_source": {
"text": "This is a question",
"my_join_field": "question"
},
"sort": [
"1"
]
},
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "2",
"_score": null,
"_source": {
"text": "This is another question",
"my_join_field": "question"
},
"sort": [
"2"
]
},
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "3",
"_score": null,
"_routing": "1",
"_source": {
"text": "This is an answer",
"my_join_field": {
"name": "answer",
"parent": "1" }
},
"sort": [
"3"
]
},
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "4",
"_score": null,
"_routing": "1",
"_source": {
"text": "This is another answer",
"my_join_field": {
"name": "answer",
"parent": "1" }
},
"sort": [
"4"
]
}
]
}
}
GET my_join_index/_search
{
"query": {
"has_parent" : {
"parent_type" : "question",
"query" : {
"match" : {
"text" : "This is"
}
}
}
}
}
返回结果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "3",
"_score": 1,
"_routing": "1",
"_source": {
"text": "This is an answer",
"my_join_field": {
"name": "answer",
"parent": "1" }
}
},
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "4",
"_score": 1,
"_routing": "1",
"_source": {
"text": "This is another answer",
"my_join_field": {
"name": "answer",
"parent": "1" }
}
}
]
}
}
GET my_join_index/_search
{
"query": {
"has_child" : {
"type" : "answer",
"query" : {
"match" : {
"text" : "This is question"
}
}
}
}
}
返回结果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "1",
"_score": 1,
"_source": {
"text": "This is a question",
"my_join_field": "question"
}
}
]
}
}
以下操作含义如下:
GET my_join_index/_search
{
"query": {
"parent_id": {
"type": "answer",
"id": "1"
}
},
"aggs": {
"parents": {
"terms": {
"field": "my_join_field#question",
"size": 10
}
}
},
"script_fields": {
"parent": {
"script": {
"source": "doc['my_join_field#question']"
}
}
}
}
返回结果:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 0.13353139,
"hits": [
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "3",
"_score": 0.13353139,
"_routing": "1",
"fields": {
"parent": [
"1"
]
}
},
{
"_index": "my_join_index",
"_type": "_doc",
"_id": "4",
"_score": 0.13353139,
"_routing": "1",
"fields": {
"parent": [
"1"
]
}
}
]
},
"aggregations": {
"parents": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "1",
"doc_count": 2
}
]
}
}
}
如下,一个父文档question与多个子文档answer,comment的映射定义。
PUT join_ext_index
{
"mappings": {
"_doc": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"question": ["answer", "comment"]
}
}
}
}
}
}
实现如下图的祖孙三代关联关系的定义。
question
/ \
/ \
comment answer
|
|
vote
PUT join_multi_index
{
"mappings": {
"_doc": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"question": ["answer", "comment"],
"answer": "vote"
}
}
}
}
}
}
孙子文档导入数据,如下所示:
PUT join_multi_index/_doc/3?routing=1&refresh
{
"text": "This is a vote",
"my_join_field": {
"name": "vote",
"parent": "2"
}
}
注意:
- 孙子文档所在分片必须与其父母和祖父母相同
- 孙子文档的父代号(必须指向其父亲answer文档)
虽然ES官方文档已经很详细了,详见:
http://t.cn/RnBBLgp
但手敲一遍,翻译一遍,的的确确会更新认知,加深理解。
和你一起,死磕ELK Stack!
2018年03月31日 23:18 于家中床前
作者:铭毅天下
转载请标明出处,原文地址:
https://blog.csdn.net/laoyang360/article/details/79774481
如果感觉本文对您有帮助,请点击‘顶’支持一下,您的支持是我坚持写作最大的动力,谢谢!