Elasticsearch 進階用法,如何透過 function_score 自定義排序結果?

古古

2025/04/22


Elasticsearch 除了可以用來實作一般的全文搜尋之外,如果想要實作「推薦排序」,針對多個維度綜合比較,Elasticsearch 也是支援這類的用法的!

因此這篇文章我們就來介紹一下,要如何透過 Elasticsearch 中的 function_score,來實作自定義排序的結果吧!

補充:如果對 Elasticsearch 不太熟悉,也可以先回頭參考 Elasticsearch 的基本介紹 Elasticsearch 是什麼?認識地表最強的全文搜尋工具!

回顧:什麼是 Elasticsearch? #

所謂的 Elasticsearch,他是一個強大的「全文搜尋」(Full-Text Search)工具,讓我們可以從大量的數據中,快速找到想要的資料在哪裡。

也因為 Elasticsearch 的專長是「全文搜尋」,所以一般通常會將他用來「架設搜尋引擎」,因此像是旅遊攻略、評論、社群貼文、電商產品….等等諸如此類的查詢,背後都很適合使用 Elasticsearch 來實作!

不過,當大家將 Elasticsearch 應用在旅遊攻略、評論、社群貼文…等等的功能實作時,這時候就會遇到一個非常重要的問題,那就是「什麼樣內容的文章應該排在最前面?」,因此「如何調整文章的排序結果」,這就是這週我們要來探討的主題了!

例子:美食地圖實作 #

想像一下,假設現在你要實作一個美食地圖的查詢功能,在這個美食地圖中,要提供使用者「距離搜尋」、「關鍵字搜尋」、「依照評價排序」…等相關功能,具體大家可以想像成 Google Map 中的搜尋餐廳或是 Uber Eats 中的尋找餐廳的類似功能。

假設現在使用者查詢了「小籠包」這個關鍵字,並且想要根據「評價由大到小」來排序的話,那這個在 Elasticsearch 中很好實作,就是直接使用 order 來排序就好(邏輯類似於 SQL 中的 ORDER BY)。

又或是使用者查詢了「小籠包」這個關鍵字,並且想要查詢他「方圓 2 公里以內」的所有小籠包餐廳,那這個在 Elasticsearch 中也很好實作,即是使用 geo_point 格式來查詢即可(詳細用法可以參考 Elasticsearch 官網,這裡先不詳細展開介紹)。

但是!!問題來了!!!假設今天使用者選擇了「推薦排序」,那麼作為後端的我們,到底應該是把「評價較優」的店家放在前面,還是要把「距離較近」的店家放在前面呢?這真的是個好問題對吧,畢竟誰都想要排在比較前面的位置,這樣子曝光度會比較高,生意也會比其他店家更好。

所以這時 Elasticsearch 就開始思考,有沒有辦法提供一個「多維度的評分機制」,讓我們可以根據多個維度,來決定這個店家的總分為何呢? 答案是有的!我們可以透過 Elasticsearch 中的 function_score,對多個維度的值進行權重加總,最終計算出每一個店家的總分,因此就可以實作出「推薦排序」的功能了!

Elasticsearch 中的推薦排序實作(使用 function_score) #

補充:以下內容較為複雜,建議大家對 function_score 有個概念即可,等到後續真正用到時再回來查相關實作細節。

在 Elasticsearch 中,有一個特別的地方,就是他會針對「每一個文件給出一個 _score 的分數」,這個 _score 就是指「該文件的匹配程度」,因此簡單來說,_score 的值越高的文件,他在搜尋的結果就會被排在越前面,這個就是 Elasticsearch 的運作機制。

所以如果我們想要實作一個「多維度的評分機制」,根據店家距離、評價數、價格…等等的多個維度去決定一個店家的總分,那麼就是要透過 function_score 的用法,手動的去改變 Elasticsearch 的 _score 的計算方式,這樣子最終的排序結果,才會是我們期望的「推薦排序」。

舉例來說,一個基本的 function_score 模板如下圖所示:

GET /_search
{
    "query": {
        "function_score": {
            // 主查詢,查詢完後這裡自己會有一個評分,就是 old_score
            "query": {.....},

            // 這裡可以寫上多個加強函數
            // 每一個加強函數會產生一個 boost_score(加強 score),因此有多個 functions 就會有多個 boost_score
            "functions": [   
                { "field_value_factor": ... },
                { "gauss": ... },
                { "filter": {...}, "weight": ... }
            ],

            // 決定多個 boost_score 們要怎麼合併成一個 total_boost_score
            "score_mode": "sum",

            // 決定 total_boost_score 怎麼和原始的 old_score 合併
            "boost_mode": "sum" 
        }
    }
}

如果拆解一下這個模板的話,基本上就是在 functions 的地方,可以去添加各種不同維度的評分,最終再透過 score_modeboost_mode,將這些不同維度的評分給加總(或是相乘)起來,最終得到每一個店家的總分。

所以以上面的美食地圖的功能為例,我們就可以在主 query 中寫上「小籠包」的查詢,並且在 functions 裡面,寫上我們想要根據「評價數」和「距離」分別去評分,最終再透過 score_modeboost_mode,將這些分數全部加總起來,就是這個店家的最終得分 _score 了。

因此透過 function_score 的用法,我們就可以成功的在 Elasticsearch 中實作出「推薦排序」的功能,進而去針對多個維度,去進行綜合性的比較了!

補充:如果大家想了解更多 function_score 的具體實作,也可以參考我之前寫的 function_score 系列文章

Elasticsearch 中的 function_score 總結 #

所以總結上述的介紹的話,如果將來我們想要在 Elasticsearch 中實作「推薦排序」這類較為複雜的排序,需要同時針對多個維度的方向來排序時,那麼就可以透過 function_score 的用法,手動的改變 Elasticsearch 中的 _score 的評分機制,最終得到我們想要的排序結果。

並且因為 Elasticsearch 的 function_score 是可以一直往上加的,所以不管是要一個維度、還是要 N 個維度,都可以根據當下的需求來擴展,因此擴展性可以說是非常的高!

不過,也因為 function_score 改變的是「排序的結果」,所以這個其實沒有絕對的對錯,畢竟青菜蘿蔔各有所好,你認為好吃的店家,其他人可能不覺得好吃,所以要如何調出一個「完美的 function_score 參數」,反而才是最困難的部分。

另外也隨著 AI 的興起,使得「推薦排序」的實作不一定要透過 Elasticsearch 來實作,而是可以考慮改成 AI 的演算法來實時運算,透過 AI 取得當下最推薦的排序。但老實說 AI 這部分我還沒有太多的研究,所以可能沒辦法分享相關的資訊給大家。

所以總結來說的話,Elasticsearch 的 function_score 目前是處在一個有那麼一點點尷尬的位置,不過他雖然效果有限,但是至少在過渡期是可以拿來擋一下的,畢竟要引入 AI、要實作更厲害的推薦排序演算法,可能就意味著更多的錢錢投入,在成本的考量之下,或許 function_score 還能算是一個高 cp 值的替代方案吧XD。

結語 #

這篇文章我們深入了解了 Elasticsearch 的進階用法,了解要如何使用 function_score 實作多維度的推薦排序,如果大家知道什麼更神的推薦排序的演算法實作,也歡迎在底下留言分享~。

如果你對後端技術有興趣的話,也歡迎免費訂閱《古古的後端筆記》電子報,每週二為你送上一篇後端技術分享,那我們就下一篇文章見啦!

補充:我開設的 Spring Boot 零基礎入門Spring Security 零基礎入門GitHub 免費架站術 已在 Hahow 平台上架啦!輸入折扣碼「HH202504KU」即可享 85 折優惠。

免費訂閱《古古的後端筆記》電子報

每週二學習後端技術,和 3300 人一起變強💪