第七章 父 子關係文檔 打虎親兄弟,上陣父子兵。 本章作為 複雜搜索 的鋪墊,介紹父子文檔是為了更好的介紹複雜場景下的ES操作。 在非關係型資料庫資料庫中,我們常常會有表與表的關聯查詢。例如學生表和成績表的關聯查詢就能查出學會的信息和成績信息。在ES中,父子關係文檔就類似於表的關聯查詢。 背景 ES ...
第七章-父-子關係文檔
打虎親兄弟,上陣父子兵。
本章作為複雜搜索的鋪墊,介紹父子文檔是為了更好的介紹複雜場景下的ES操作。
在非關係型資料庫資料庫中,我們常常會有表與表的關聯查詢。例如學生表和成績表的關聯查詢就能查出學會的信息和成績信息。在ES中,父子關係文檔就類似於表的關聯查詢。
背景
ES5.x開始藉助父子關係文檔實現多表關聯查詢,核心是一個索引Index下可以創建多個類型Type。但ES6.x開始只允許一個索引Index下創建一個類型Type,甚至在未來的版本中將會移除創建類型Type。為了繼續支持多表關聯查詢,ES6.x推出了join
新類型來支持父子關係文檔的創建。
問題
假設現在有這樣的需求場景:一個博客有多篇文章,文章有標題、內容、作者、日期等信息,同時一篇文章中會有評論,評論有評論的內容、作者、日期等信息,通過ES來存儲博客的文章及評論信息。
此時文章本身就是"父",而評論就是"子",這類問題也可以通過nested
嵌套對象實現,大部分情況下netsted
嵌套對象和parent-child
父子對象能夠互相替代,但他們仍然不同的優缺點。下麵將介紹這兩種數據結構。
nested嵌套對象
一篇文章的數據結構如下圖所示:
{
"title":"ElasticSearch6.x實戰教程",
"author":"OKevin",
"content":"這是一篇水文",
"created":1562141626000,
"comments":[{
"name":"張三",
"content":"寫的真菜",
"created":1562141689000
},{
"name":"李四",
"content":"辣雞",
"created":1562141745000
}]
}
通過RESTful API創建索引及定義映射結構:
PUT http://localhost:9200/blog
{
"mappings":{
"article":{
"properties":{
"title":{
"type":"text",
"analyzer":"ik_smart",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256
}
}
},
"author":{
"type":"text",
"analyzer":"ik_smart",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256
}
}
},
"content":{
"type":"text",
"analyzer":"ik_smart"
},
"created":{
"type":"date"
},
"comments":{
"type":"nested",
"properties":{
"name":{
"type":"text",
"analyzer":"ik_smart",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256
}
}
},
"content":{
"type":"text",
"analyzer":"ik_smart",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256
}
}
},
"created":{
"type":"date"
}
}
}
}
}
}
}
插入數據:
POST http://localhost:9200/blog/article
{
"title":"ElasticSearch6.x實戰教程",
"author":"OKevin",
"content":"這是一篇水文",
"created":1562141626000,
"comments":[{
"name":"張三",
"content":"寫的真菜",
"created":1562141689000
},{
"name":"李四",
"content":"辣雞",
"created":1562141745000
}]
}
POST http://localhost:9200/blog/article
{
"title":"ElasticSearch6.x從入門到放棄",
"author":"OKevin",
"content":"這是一篇ES從入門到放棄文章",
"created":1562144089000,
"comments":[{
"name":"張三",
"content":"我已入門",
"created":1562144089000
},{
"name":"李四",
"content":"我已放棄",
"created":1562144089000
}]
}
POST http://localhost:9200/blog/article
{
"title":"ElasticSearch6.x原理解析",
"author":"專家",
"content":"這是一篇ES原理解析的文章",
"created":1562144089000,
"comments":[{
"name":"張三",
"content":"牛逼,專家就是不一樣",
"created":1562144089000
},{
"name":"李四",
"content":"大牛",
"created":1562144089000
}]
}
- 查詢作者為“OKevin”文章的所有評論(父查子)
GET http://localhost:9200/blog/article/_search
{
"query":{
"bool":{
"must":[{
"match":{
"author.keyword":"OKevin"
}
}]
}
}
}
ES結果返回2條作者為"OKevin"的全部數據。
- 查詢評論中含有“辣雞”的文章(子查父)
GET http://localhost:9200/blog/article/_search
{
"query":{
"bool":{
"must":[{
"match":{
"author.keyword":"OKevin"
}
},{
"nested":{
"path":"comments",
"query":{
"bool":{
"must":[{
"match":{
"comments.content":"辣雞"
}
}]
}
}
}
}]
}
}
}
ES確實只返回了包含"辣雞"的數據。
兩次查詢都直接返回了整個文檔數據。
parent-child父子文檔
既然父子文檔能實現表的關聯查詢,那它的數據結構就應該是這樣:
文章數據結構
{
"title":"ElasticSearch6.x實戰教程",
"author":"OKevin",
"content":"這是一篇實戰教程",
"created":1562141626000,
"comments":[]
}
評論數據結構
{
"name":"張三",
"content":"寫的真菜",
"created":1562141689000
}
ES6.x以前是將這兩個結構分別存儲在兩個類型Type中關聯(這看起來更接近關係型資料庫表與表的關聯查詢),但在ES6.x開始一個索引Index只能創建一個類型Type,要再想實現表關聯查詢,就意味著需要把上述兩張表揉在一起,ES6.x由此定義了一個新的數據類型——join
。
通過RESTful API創建索引及定義映射結構:
{
"mappings":{
"article":{
"properties":{
"title":{
"type":"text",
"analyzer":"ik_smart",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256
}
}
},
"author":{
"type":"text",
"analyzer":"ik_smart",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256
}
}
},
"content":{
"type":"text",
"analyzer":"ik_smart"
},
"created":{
"type":"date"
},
"comments":{
"type":"join",
"relations":{
"article":"comment"
}
}
}
}
}
}
重點關註其中的"comments"欄位,可以看到類型定義為join
,relations定義了誰是父誰是子,"article":"comment"表示article是父comment是子。
父子文檔的插入是父與子分別插入(因為可以理解為把多個表塞到了一張表裡)。
插入父文檔:
POST http://localhost:9200/blog/article/1
{
"title":"ElasticSearch6.x實戰教程",
"author":"OKevin",
"content":"這是一篇水文",
"created":1562141626000,
"comments":"article"
}
POST http://localhost:9200/blog/article/2
{
"title":"ElasticSearch6.x從入門到放棄",
"author":"OKevin",
"content":"這是一篇ES從入門到放棄文章",
"created":1562144089000,
"comments":"article"
}
POST http://localhost:9200/blog/article/3
{
"title":"ElasticSearch6.x原理解析",
"author":"專家",
"content":"這是一篇ES原理解析的文章",
"created":1562144089000,
"comments":"article"
}
插入子文檔:
POST http://localhost:9200/blog/article/4?routing=1
{
"name":"張三",
"content":"寫的真菜",
"created":1562141689000,
"comments":{
"name":"comment",
"parent":1
}
}
POST http://localhost:9200/blog/article/5?routing=1
{
"name":"李四",
"content":"辣雞",
"created":1562141745000,
"comments":{
"name":"comment",
"parent":1
}
}
POST http://localhost:9200/blog/article/6?routing=2
{
"name":"張三",
"content":"我已入門",
"created":1562144089000,
"comments":{
"name":"comment",
"parent":2
}
}
POST http://localhost:9200/blog/article/7?routing=2
{
"name":"李四",
"content":"我已放棄",
"created":1562144089000,
"comments":{
"name":"comment",
"parent":2
}
}
POST http://localhost:9200/blog/article/8?routing=3
{
"name":"張三",
"content":"牛逼,專家就是不一樣",
"created":1562144089000,
"comments":{
"name":"comment",
"parent":3
}
}
POST http://localhost:9200/blog/article/9?routing=3
{
"name":"李四",
"content":"大牛",
"created":1562144089000,
"comments":{
"name":"comment",
"parent":3
}
}
如果查詢索引數據會發現一共有9條數據,並不是nested
那樣將"評論"嵌套"文章"中的。
- 查詢作者為“OKevin”文章的所有評論(父查子)
GET http://localhost:9200/blog/article/_search
{
"query":{
"has_parent":{
"parent_type":"article",
"query":{
"match":{
"author.keyword":"OKevin"
}
}
}
}
}
ES只返回了comment評論結構中的數據,而不是全部包括文章數據也返回。這是嵌套對象查詢與父子文檔查詢的區別之一——子文檔可以單獨返回。
- 查詢評論中含有“辣雞”的文章(子查父)
GET http://localhost:9200/blog/artice/_search
{
"query":{
"has_child":{
"type":"comment",
"query":{
"match":{
"content":"辣雞"
}
}
}
}
}
ES同樣也只返回了父文檔的數據,而沒有子文檔(評論)的數據。
nested
嵌套對象和parent-child
父子文檔之間最大的區別,嵌套對象中的"父子"是一個文檔數據,而父子文檔的中的"父子"是兩個文檔數據。這意味著嵌套對象中如果涉及對嵌套文檔的操作會對整個文檔造成影響(重新索引,但查詢快),包括修改、刪除、查詢。而父子文檔子文檔或者父文檔本身就是獨立的文檔,對子文檔或者父文檔的操作並不會相互影響(不會重新索引,查詢相對慢)。
關註公眾號:CoderBuff,回覆“es”獲取《ElasticSearch6.x實戰教程》完整版PDF。
這是一個能給程式員加buff的公眾號 (CoderBuff)