基於Elasticsearch實現搜索建議

搜索建議是搜索的一個重要組成部分,一個搜索建議的實現一般須要考慮建議詞的來源、匹配、排序、聚合、關聯的文檔數和拼寫糾錯等,本文介紹一個基於Elasticsearch實現的搜索建議。html

問題描述

電商網站的搜索是最基礎最重要的功能之一,搜索框上面的良好體驗能爲電商帶來更高的收益,咱們先來看看淘寶、京東、亞馬遜網站的搜索建議。數據庫

在淘寶的搜索框輸入【衛衣】時,下方的搜索建議包括建議詞以及相關的標籤:
淘寶的搜索建議緩存

在京東的搜索框輸入【衛衣】時,下方搜索建議右方顯示建議詞關聯的商品數量:
京東的搜索建議bash

在亞馬遜的搜索框輸入【衛衣】時,搜索建議上部分能支持在特定的分類下進行搜索:
亞馬遜的搜索建議elasticsearch

經過上述對比能夠看出,不一樣的電商對於搜索建議的側重點略有不一樣,但核心的問題包括:ide

  • 匹配:可以經過用戶的輸入進行前綴匹配;
  • 排序:根據建議詞的優先級進行排序;
  • 聚合:可以根據建議詞關聯的商品進行聚合,好比聚合分類、聚合標籤等;
  • 糾錯:可以對用戶的輸入進行拼寫糾錯;

搜索建議實現

在咱們的搜索建議實現裏,主要考慮了建議詞的來源、匹配、排序、關聯的商品數量和拼寫糾錯。post

SuggestionDiscovery

  • SuggestionDiscovery的職責是發現建議詞;
  • 建議詞的來源能夠是商品的分類名稱、品牌名稱、商品標籤、商品名稱的高頻詞、熱搜詞,也能夠是一些組合詞,好比「分類 + 性別」和「分類 + 標籤」,還能夠是一些自定義添加的詞;
  • 建議詞維護的時候須要考慮去重,好比「衛衣男」和「衛衣 男」應該是相同的,「Nike」和「nike」也應該是相同的;
  • 因爲建議詞的來源一般比較穩定,因此執行的週期能夠比較長一點,好比每週一次;

SuggestionCounter

  • SuggestionCounter的職責是獲取建議詞關聯的商品數量,若是須要能夠進行一些聚合操做,好比聚合分類和標籤;
  • SuggestionCounter的實現的時候因爲要真正地調用搜索接口,應該儘可能避免對用戶搜索的影響,好比在凌晨執行而且使用單線程調用;
  • 爲了提高效率,應該使用Elasticsearch的Multi Search接口批量進行count,同時批量更新數據庫裏建議詞的count值;
  • 因爲SuggestionCounter是比較耗資源的,能夠考慮延長執行的週期,可是這可能會帶來count值與實際搜索時偏差較大的問題,這個須要根據實際狀況考慮;

SuggestionIndexRebuiler

  • SuggestionIndexRebuiler的職責是負責重建索引;
  • 考慮到用戶的搜索習慣,可使用Multi-fields來給建議詞增長多個分析器。好比對於【衛衣 套頭】的建議詞使用Multi-fields增長不分詞字段、拼音分詞字段、拼音首字母分詞字段、IK分詞字段,這樣輸入【weiyi】和【套頭】均可以匹配到該建議詞;
  • 重建索引時經過是經過bulk批量添加到臨時索引中,而後經過別名來更新;
  • 重建索引的數據依賴於SuggestionCounter,所以其執行的週期應該與SuggestionCounter保持一致;

SuggestionService

  • SuggestionService是真正處於用戶搜索建議的服務類;
  • 一般的實現是先到緩存中查詢是否能匹配到緩存記錄,若是能匹配到則直接返回;不然的話調用Elasticsearch的Prefix Query進行搜索,因爲咱們在重建索引的時候定義了Multi-fields,在搜索的時候應該用boolQuery來處理;若是此時Elasticsearch返回不爲空的結果數據,那麼加入緩存並返回便可;
POST /suggestion/_search
{
  "from" : 0,
  "size" : 10,
  "query" : {
    "bool" : {
      "must" : {
        "bool" : {
          "should" : [ {
            "prefix" : {
              "keyword" : "衛衣"
            }
          }, {
            "prefix" : {
              "keyword.keyword_ik" : "衛衣"
            }
          }, {
            "prefix" : {
              "keyword.keyword_pinyin" : "衛衣"
            }
          }, {
            "prefix" : {
              "keyword.keyword_first_py" : "衛衣"
            }
          } ]
        }
      },
      "filter" : {
        "range" : {
          "count" : {
            "from" : 5,
            "to" : null,
            "include_lower" : true,
            "include_upper" : true
          }
        }
      }
    }
  },
  "sort" : [ {
    "weight" : {
      "order" : "desc"
    }
  }, {
    "count" : {
      "order" : "desc"
    }
  } ]
}
複製代碼
  • 若是Elasticsearch返回的是空結果,此時應該須要增長拼寫糾錯的處理(拼寫糾錯也能夠在調用Elasticsearch搜索的時候帶上,可是一般狀況下用戶並無拼寫錯誤,因此建議仍是在後面單獨調用suggester);若是返回的suggest不爲空,則根據新的詞調用建議詞服務;好比用戶輸入了【adidss】,調用Elasticsearch的suggester獲取到的結果是【adidas】,則再根據adidas進行搜索建議詞處理。
POST /suggestion/_search
{
  "size" : 0,
  "suggest" : {
    "keyword_suggestion" : {
      "text" : "adidss",
      "term" : {
        "field" : "keyword",
        "size" : 1
      }
    }
  }
}
複製代碼
  • 關於排序:在咱們的實現裏面是經過weight和count進行排序的,weight目前只考慮了建議詞的類型(好比分類 > 品牌 > 標籤);

實現效果和後續改進

  • 經過上面的實現,咱們已經能實現一個比較強大的搜索建議詞了,實際的效果以下所示:

最終效果

  • 後續能夠考慮的改進:參考亞馬遜增長分類的聚合展現、增長用戶個性化的處理支持更好的建議詞排序、基於用戶的搜索歷史支持更好的建議詞推薦;

參考資料

相關文章
相關標籤/搜索