ElasticSearch - function_score(衰減函數 linear、exp、gauss 具體實例)

古古

2018/07/08


閱讀本文需要先了解 function_score 的相關知識,請看 ElasticSearch - function_score 簡介

  • 很多變量都可以影響用戶對於酒店的選擇,像是用戶可能希望酒店離市中心近一點,但是如果價格足夠便宜,也願意為了省錢,妥協選擇一個更遠的住處

    • 如果我們只是使用一個 filter 排除所有市中心方圓 100 米以外的酒店,再用一個 filter 排除每晚價格超過 100 元的酒店,這種作法太過強硬,可能有一間房在 500 米,但是超級便宜一晚只要 10 元,用戶可能會因此願意妥協住這間房
    • 為了解決這個問題,因此 function_score 查詢提供了一組 衰減函數(decay functions), 讓我們有能力在兩個滑動標準(如地點和價格)之間權衡
  • function_score 支持的衰減函數有三種,分別是 linear、exp 和 gauss

    • linear、exp、gauss 三種衰減函數的差別只在於衰減曲線的形狀,在 DSL 的語法上的用法完全一樣
      • linear : 線性函數是條直線,一旦直線與橫軸 0 相交,所有其他值的評分都是 0
      • exp : 指數函數是先劇烈衰減然後變緩
      • guass(最常用) : 高斯函數則是鐘形的,他的衰減速率是先緩慢,然後變快,最後又放緩
    • 衰減函數們(linear、exp、gauss)支持的參數
      • origin : 中心點,或是字段可能的最佳值,落在原點(origin)上的文檔評分 _score 為滿分 1.0,支持數值、時間 以及 “經緯度地理座標點”(最常用)的字段
      • offset : 從 origin 為中心,為他設置一個偏移量 offset 覆蓋一個範圍,在此範圍內所有的評分 _score 也都是和 origin 一樣滿分 1.0
      • scale : 衰減率,即是一個文檔從 origin 下落時,_score 改變的速度
      • decay : 從 origin 衰減到 scale 所得的評分_score,默認為 0.5(一般不需要改變,這個參數使用默認的就好了)
      • 以上面的圖為例
        • 所有曲線(linear、exp、gauss)的 origin 都是 40,offset 是 5,因此範圍在 40-5 <= value <= 40+5 的文檔的評分 _score 都是滿分 1.0
        • 而在此範圍之外,評分會開始衰減,衰減率由 scale 值(此處是5)和 decay 值(此處是默認值0.5)決定,在 origin +/- (offset + scale) 處的評分是 decay 值,也就是在 30、50 的評分處是 0.5 分
        • 也就是說,在 origin + offset + scale 或是 origin - offset - scale 的點上,得到的分數僅有 decay
  • 具體實例一

    • 先準備數據和索引,在 ES 插入三筆數據,其中 language 是 keyword 類型,like 是 integer 類型(代表點贊量)

      { "language": "java", "like": 5 }
      { "language": "python", "like": 10 }
      { "language": "go", "like": 15 }
      
    • 以 like = 15 為中心,使用 gauss 函數

      GET mytest/doc/_search
      {
          "query": {
              "function_score": {
                  "query": {
                      "match_all": {}
                  },
                  "functions": [
                      {
                          "gauss": {
                              "like": {
                                  "origin": "15", //如果不設置offset,offset默認為0
                                  "scale": "5",
                                  "decay": "0.2"
                              }
                          }
                      }
                  ]
              }
          }
      }
      
      "hits": [
          {
              "_score": 1,
              "_source": { "language": "go", "like": 15 }
          },
          {
              //因為改變了decay=0.2,所以當位於 origin-offset-scale=10 的位置時,分數為decay,就是0.2
              "_score": 0.2,
              "_source": { "language": "python", "like": 10 }
          },
          {
              "_score": 0.0016,
              "_source": { "language": "java", "like": 5 }
          }
      ]
      
  • 具體實例二

    • 假設有一個用戶希望租一個離市中心近一點的酒店,且每晚不超過 100 元的酒店,而且與距離相比,我們的用戶對價格更敏感,那麼使用衰減函數 gauss 查詢如下
      • 其中把 price 語句的 origin 點設為 50 是有原因的,由於價格的特性一定是越低越好,所以 0 ~ 100 元的所有價格的酒店都應該認為是比較好的,而 100 元以上的酒店就慢慢衰減
      • 如果我們將 price 的 origin 點設置成100,那麼價格低於 100 元的酒店的評分反而會變低,這不是我們期望的結果,與其這樣不如將 origin 和 offset 同時設成 50,只讓 price 大於 100 元時評分才會變低
      • 雖然這樣設置也會使得 price 小於 0 元的酒店評分降低沒錯,不過現實生活中價格不會有負數,因此就算 price < 0 的評分會下降,也不會對我們的搜索結果造成影響(酒店的價格一定都是正的)
      • 換句話說,其實只要把 origin + offset 的值設為 100,origin 或 offset 是什麼樣的值都無所謂,只要能確保酒店價格在 100 元以上的酒店會衰減就好了
        GET mytest/doc/_search
        {
            "query": {
                "function_score": {
                    "query": {
                    	"match_all": {}
                    }
                    "functions": [
                        //第一個gauss加強函數,決定距離的衰減率
                        {
                            "gauss": {
                                "location": {
                                    "origin": {  //origin點設成市中心的經緯度座標
                                        "lat": 51.5,
                                        "lon": 0.12
                                    },
                                    "offset": "2km", //距離中心點2km以內都是滿分1.0,2km外開始衰減
                                    "scale": "3km"  //衰減率
                                }
                            }
                        },
                        //第二個gauss加強函數,決定價格的衰減率
                        //因為用戶對價格更敏感,所以給了這個gauss加強函數2倍的權重
                        {
                            "gauss": {
                                "price": {
                                    "origin": "50", 
                                    "offset": "50",
                                    "scale": "20"
                                }
                            },
                            "weight": 2
                        }
                    ]
                }
            }
        }