Elasticsearch 第八篇:資料型別 Array、Nested、Object 的設計與應用

vincentfhr發表於2020-11-19

      在我的上一篇 《Elasticsearch 第七篇:父子結構mapping設計以及相關查詢》中,通過建立電影索引庫,將電影 film 與演員 actor 做了父子關係的聯結,並做了相關查詢,

但是實際上,父子關聯查詢並不是最推薦的設計方式。在實際應用中,通常要設計比較複雜的資料結構,才能滿足業務需求的需要。

      例如:一部電影通常會有多個標籤tag,經常需要通過對 tag 進行過濾和查詢;一部電影有多個演員,每個演員又包含姓名、性別等資訊,怎樣設計資料結構會比較合理呢?

這一篇,就針對電影庫來簡單說明。

       注意,這裡討論的,都是 Elasticsearch 7.8 版本的,不同版本會有差異,請自行閱讀相關資料。

一、Elasticsearch 資料型別

       基本資料型別:string(elasticsearch 7.x 版本之後分為 keyword 和 text ,區別是 keyword 支援排序、聚合,不支援分詞;text 支援分詞,不支援排序、聚合)、integer (整數型別)、boolean(布林型別)、date(時間型別)等。

       特殊資料型別:geo_point(地理座標型別)、ip(IP型別)等。

       複合型別:object(物件型別)、array(陣列型別)、nested(巢狀型別)。

1、Object

      個人認為 object 型別實際上並沒有什麼用途,完全可以把二級欄位轉化為一級欄位,不需要特別地去做區分,所以不予討論,可以見第七篇的父子結構設計。

2、Array

      array 型別其實也是比較簡單,例如現在有一部電影《畫皮》,資料結構如下:

{
    "id":"000-111-222",
    "name":"畫皮",
    "desc":"該電影由聊齋改編,講述的是...",
    "tag":["古裝","魔幻","愛情"]
}

       現在希望Elasticsearch 也為這三個標籤建立索引,在這樣的應用場景下,就可以用 array 型別來儲存電影的3個標籤——古裝、魔幻、愛情。

       Elasticsearch 的資料對映可以這樣定義:

{
  "mappings": {
    "properties": {
      "tag": {
        "type": "keyword" 
      },
      ...
    }
  }
}

       這樣,就可以提交資料,也可以通過 tag 標籤來查詢想要的電影,下文將會詳細說明。

3、Nested

       nested 型別,指的是巢狀型別,也是本節需要重點講解的型別。什麼時候用到巢狀型別呢?

       還是以電影庫作為例子,每一部電影都會有一個演員列表,每個演員有包含姓名、性別、年齡等等資訊,如果我們不僅要搜尋電影的基本資訊,也希望能搜尋演員的相關資訊,那麼,再這樣的場景下,就可以用 nested 這樣的資料型別,下文將會詳細說明。

二、建立測試索引庫

       現在,建立電影索引庫,涉及到三個主體:

       1、電影  film  ,包括 id(編號)、name(電影名稱)、desc (電影介紹)

       2、電影標籤 tag ,例如:愛情、戰爭、魔幻、古裝

       3、演員 actors , 包括 id(編號)、name(演員姓名)、sex (演員性別)

       關聯關係是:一部電影對應多個演員、一部電影對應多個標籤

       根據以上的分析,建立電影索引庫 myfilm ,如下:

put http://localhost:9200/myfilm
{ 
    "mappings": { 
        "properties": { 
            "id":{
                "type": "keyword",
                "store":true
            },
            "name":{
                "type": "keyword",
                "store":true
            },
            "desc":{
                "type":"text",
                "store":true,
                "analyzer": "ik_max_word"
            },
            "tag":{
                "type": "keyword",
                "store":true
            },
            "actors":{
                "type": "nested",
                "properties":{
                    "id":{
                        "type": "keyword",
                        "store":true
                    },
                    "name":{
                        "type": "keyword",
                        "store":true
                    },
                    "sex":{
                        "type": "integer",
                        "store":true
                   }
               }
            }
        } 
    } 
}

 現在可以往庫插入資料《甄嬛傳》,程式碼如下:

put  http://localhost:9200/myfilm/_doc/film_001
{
    "id":"film_001",
    "name":"甄嬛傳",
    "desc":"雍正元年,結束了血腥的奪位之爭,新的君主(陳建斌 飾)繼位,國泰民安,政治清明,但在一片祥和的表象之下,一股暗流蠢蠢欲動,尤其後宮,華妃(蔣欣 飾)與皇后(蔡少芬 飾)分庭抗禮,各方勢力裹挾其中,凶險異常。十七歲的甄嬛(孫儷飾)與好姐妹眉莊(斕曦飾)、陵容(陶昕然飾)參加選秀,她本抱著來充個數的念頭,可皇帝(陳建斌飾)偏相中了她的智慧、氣節與端莊,最後三人一同入選。但因華妃(蔣欣飾)囂張,步步緊逼,眉莊被冤,陵容變心,天真的甄嬛慢慢變成了後宮精明的女子。皇帝發現年羹堯(孫寧飾)的野心,令甄父剪除年氏一族,甄嬛終於鬥倒了華妃。但由於甄嬛與先故純元皇后的神似,皇后設計以純元皇后的禮服陷害甄嬛,父親(沈保平飾)也被文字獄牽連和姦人陷害而遭牢獄之災,生下女兒後,心灰意冷的甄嬛選擇出宮修行。在宮外幸得十七爺允禮(李東學飾)悉心照顧,二人相親相愛,只等有機會遠走高飛。後因誤傳十七爺死訊,甄嬛為保全腹中骨肉,設計與皇帝相遇,狠心斷絕對十七爺的愛戀,重回宮中,再度與皇后相鬥。後因生下雙生子,同時甄父的冤案得以平反,重新被皇帝重用,甄氏一族再度崛起。甄嬛多次躲過皇后的陷害,最終扳倒皇后。可造化弄人,由於皇帝的疑心,最終卻只能看著心上人允禮死在自己懷中,而與葉瀾依(熱依扎飾)合謀弒君。皇帝駕崩後,甄嬛養子弘曆登基,甄嬛被尊為聖母皇太后,即便享盡榮華,但眼見一生姐妹沈眉莊血崩而亡,一生愛人允禮為保其周全而無憾自盡,不過是一代封建王朝的悲情故夢罷了。",
    "tag":["後宮","古裝","清朝","愛情","宮鬥"],
    "actors":[
        {
            "id":"actor_001",
            "name":"孫儷",
            "sex":0
        },
        {
            "id":"actor_002",
            "name":"陳建斌",
            "sex":1
        },
        {
            "id":"actor_003",
            "name":"蔡少芬",
            "sex":0
        },
        {
            "id":"actor_004",
            "name":"蔣欣",
            "sex":0
        },
        {
            "id":"actor_005",
            "name":"藍盈盈",
            "sex":0
        }
    ]
}

再加入測試資料《畫皮》:

put  http://localhost:9200/myfilm/_doc/film_002

{
    "id":"film_002",
    "name":"畫皮",
    "desc":"秦漢年間,都尉王生率王家軍在西域與沙匪激戰中救回一絕色女子,並帶回江都王府。對方為九霄美狐小唯披人皮所變。其皮必須用人心養護,故小唯的隱形助手小易,一隻沙漠蜥蜴修成的妖,每隔幾天便殺人取心供奉小唯,以表對小唯的愛意,江都城因此陷入一片恐怖中。小唯因王家軍首領王生勇猛英俊對其萌生愛意,並不停用妖術誘惑王生,想取代王生妻子佩蓉的地位。王家軍前統領龐勇武功高強,與王生、佩蓉情同手足,並暗戀佩蓉。後佩蓉嫁給王生,龐勇辭官出走成為流浪俠士。佩蓉發現小唯愛戀自己的丈夫,並覺察到她不是常人,暗中求助龐勇求他救助王生",
    "tag":["神話","古裝","愛情","恐怖","鬼神","聊齋"],
    "actors":[
        {
            "id":"actor_101",
            "name":"趙薇",
            "sex":0
        },
        {
            "id":"actor_102",
            "name":"陳坤",
            "sex":1
        },
        {
            "id":"actor_103",
            "name":"周迅",
            "sex":0
        },
        {
            "id":"actor_104",
            "name":"孫儷",
            "sex":0
        },
        {
            "id":"actor_105",
            "name":"甄子丹",
            "sex":1
        }
    ]
}

再加入資料《紅高粱》:

http://localhost:9200/myfilm/_doc/film_003

{
    "id":"film_003",
    "name":"紅高粱",
    "desc":"20世紀30年代初,山東高密地區土匪橫行,民不聊生。東北鄉破落地主家19歲的女兒九兒,被貪財的父親許給了麻風病的酒坊主兒子單扁郎,孔武有力的槓子頭餘佔鰲喜歡九兒,殺掉了單家父子,九兒和餘佔鰲開始了一段不被鄉民認可的愛情,並有了兩個孩子。在釀酒師傅羅漢等人的幫助下,九兒逐漸從一個單純的少女成長為幹練的高粱酒坊女掌櫃, 振興了單家酒坊。餘佔鰲則帶領兄弟們組成自己的武裝力量,周旋於當地政府,土匪花脖子以及鐵板會等多股勢力之間。七七事變爆發,日軍進佔山東,打破了高密縣往日的繁榮,在民族大義面前,餘佔鰲和各方勢力不計前嫌,停止爭端,共同抗日。內憂外患之際,九兒帶領隊伍,將日本鬼子引到了高粱地,點燃紅高粱,與敵人同歸於盡,用自己的生命在這片充滿生命力的山東高密大地上撰寫了愛與征服,野心和意志的傳奇故事。",
    "tag":["抗戰","山東","愛情","土匪","倫理","民國"],
    "actors":[
        {
            "id":"actor_201",
            "name":"周迅",
            "sex":0
        },
        {
            "id":"actor_202",
            "name":"朱亞文",
            "sex":1
        },
        {
            "id":"actor_203",
            "name":"於榮光",
            "sex":1
        },
        {
            "id":"actor_204",
            "name":"秦海璐",
            "sex":0
        }
    ]
}

三、Array 資料型別查詢

       上面已經建立了測試庫 myfilm ,現在可以通過對標籤 tag 進行過濾,來查詢我們想要的電影。

       場景:查詢包含標籤 “鬼神” 的影視作品,程式碼如下:

get  http://localhost:9200/myfilm/_search
{
   "query": {
       "bool":{
             "must":[
                   {"match":{"tag":"鬼神"}}
             ]
       }
   }   
}

        這時,電影《畫皮》被搜尋出來,與上面測試資料對比,是預期的效果。

       考慮到標籤大部分時候是一個詞,不需要分詞,所以上面 tag 資料型別設定為 keyword ,如果標籤是一段文字,也可以將 tag 型別設定為 text ,並進行分詞,但在這個應用場景中,用 keyword 即可。

四、Nested 資料型別查詢

         現在,可以對子級列表,也就是通過對演員 actors 的篩選,來搜尋電影。

1、場景:搜尋周迅演過的作品

       這時候通過簡單地文字對比即可,但是查詢語句會有 “nested” 標記,巢狀比較多,如下

get  http://localhost:9200/myfilm/_search
{
   "query": {
       "bool":{
             "must":[
                {
                  "nested":{
                      "path":"actors",
                          "query":{
                                "bool":{
                                    "must":[
                                        {
                                            "match":{"actors.name":"周迅"}
                                        }
                                    ]
                                }
                         }
                    }
               }
           ]
       }
   }   
}

        這時候《畫皮》、《紅高粱》都命中,與預期相吻合,如果只想查出一部,還可以加過濾條件,例如  actors.name=“周迅”  actors.id=actor_201 ,這時候是:

get  http://localhost:9200/myfilm/_search

{
   "query": {
       "bool":{
             "must":[
                  {
                        "nested":{
                            "path":"actors",
                            "query":{
                                "bool":{
                                    "must":[
                                        {
                                            "match":{"actors.name":"周迅"}
                                        }
                                    ]
                                }
                            }
                        }
                }
             ]
       }
   }   
}

2、場景:查詢周迅演過的、民國時期的作品

      分析:這個場景中,涉及到父子條件的綜合查詢,子級需要篩選演員名字,父級需要查詢標籤包含 “民國” ,查詢條件會比較複雜,但是思路還是很清晰的,如下

http://localhost:9200/myfilm/_search

{
   "query": {
       "bool":{
             "must":[
                {
                        "nested":{
                            "path":"actors",
                            "query":{
                                "bool":{
                                    "must":[
                                        {
                                            "term":{"actors.name":"周迅"}
                                        }
                                    ]
                                }
                            }
                        }
                },
                {
                    "term":{"tag":"民國"}
                }
             ]
       }
   }   
}

       Nested 還可以增加、修改、刪除,最簡單粗暴的方法,就是直接覆蓋某一部電影,當然還有更小範圍的修改,有時間我再記錄一下!

相關文章