該文章同步發佈於:我的部落格
也歡迎關注我的 Facebook 以及 Instagram 接收軟體相關的資訊!
在 全文搜尋 的文章中,我們只有介紹了使用 match
來進行搜尋。
但其實 Elasticsearch ( 以下簡稱 ES ) 還支援同時搜尋多個欄位的功能,有幾種方式可以做到這樣的效果,其中一個就是使用 multi_match
的搜尋條件。
使用起來其實非常直覺,如下面的示範:
// GET /movies/_search
{
"query": {
"multi_match": {
"query": "Horror",
"fields": ["genres", "title"]
}
}
}
結果如下圖:
非常不意外的,genres
以及 title
內含有 Horror 的資料都會被返回在結果之中。
但這邊我們希望可以讓更多 title
中含有 Horror 的資料排名往前,也就是我們希望可以 boost 一下這個 title
的欄位,我們希望它變得更重要,我們可以怎麼做呢?
透過在欄位後方加入 ^2
的方式,當然你要 ^3
也可以。
// GET /movies/_search
{
"query": {
"multi_match": {
"query": "Horror",
"fields": ["genres", "title^2"]
}
}
}
接著再次搜尋,可以看到第一個原本只有 7 分相關的資料,馬上變成 14 分,是因為我們把 title
這個欄位的權重給提高了。
大概可以猜到 ^2 就是乘以 2 的概念。
其實在 ES 的內部,可以簡單地想像它把剛剛我們搜尋的內容拆解成兩個部分:
{
"query": {
"match": {
"genres": "Horror",
}
}
}
// 以及
{
"query": {
"match": {
"title": "Horror",
}
}
}
所以得出的結果是 OR
而不是 AND
也是很正確的,這部分可以透過 operator
的方式去調整,我們有在 全文搜尋 中介紹過。
而 ES 是如何在這麼多的資料中排名分數的呢?以預設的狀況下,ES 採用的是 best_fields
的策略,也就是找到所有資料,並且以分數最高的那個欄位作為該 document 的 _score
。
以三份 documents 來舉例,7.16 是第一份 document 的最高分,就會是這份 document 的 _score
,以此類推:
// document #1
{
"title": "The Amityville Horror", // 7.164084
"genres": ["Horror", "Supernatural"] // 5.123333
}
// document #2
{
"title": "Book of Shadows: Blair Witch 2", // N/A
"genres": ["Horror"] // 3.0575924
}
// document #3
{
"title": "Dracula 2000", // N/A
"genres": ["Horror"] // 3.0575924
}
所以如果你也跑一次相同的搜尋,你會看到最高分就是 7.164084,而後面的資料都是相同的分數,是因為 genres
內只含有 Horror
這個單字的資料都是 3.0575924 這個分數。
再次說明這樣的取分排名方式,是預設的 best_fields
,ES 有提供很多不一樣的策略,讓開發者可以根據需求去更改搜尋的策略。
Multi Match Query 的 types,官方連結
這邊要介紹一種方式,可以把其餘的分數也一起加進來計算的參數,叫做 tie_breaker
,這個名詞很常在一些競技比賽中出現,中文叫做『 突破僵局制 』。
而 ES 在這邊要帶進來的概念就是,不要捨棄掉一些也有被計分的欄位,讓它們一起加入計算,並且給予它們權重,可以算是某一種突破僵局,我覺得蠻有趣的,這樣可以進一步地讓資料之間的分數不相同,變相的打破很多資料的分數相同的僵局。
以下是範例:
// GET /movies/_search
{
"query": {
"multi_match": {
"query": "American",
"fields": ["extract", "title"],
"tie_breaker": 0.3
}
}
}
這樣會怎麼排列資料呢?
以預設情況來說,會找到最高分的欄位,並且當作該 document 的 _score
;但加入了 tie_breaker
之後,會加上 extract
欄位的分數乘以 0.3 當作 _score
來計算:
{
"title": "American Psycho", // 5.39793
"extract": "American Psycho is a 2000 satirical horror film directed by Mary Harron, who co-wrote the...." // 0.33 * 0.3
}
// 5.39793 + ( 0.33 * 0.3 ) = 5.49793...
所以總分不再是以最高計算,而是會加入其餘欄位的計分乘以 tie_breaker
的參數。
如果你嘗試再跑一次上面的搜尋,沒有 tie_breaker
和帶有 tie_breaker
的搜尋排序結果是不同的,原本第一名的 American Psycho 會被放到後面去,反而被 American Wedding 給取代,這就是因為 American Wedding 的 extract
欄位分數乘以 0.3 之後的表現更好。
小小的實驗,如果
tie_breaker
帶的比 0.000001 還小,第一名依然會是 American Psycho,其實這樣大概就可以求出 American Wedding 的extract
分數大約落在哪裡了。
所以根據不同的使用場景,這個 tie_breaker
的值可能是需要做調整和變動的,而這就是工程師的責任了!