設定 Elasticsearch 自動完成

最近幫公司的搜尋實現自動完成功能,筆記一下

自動完成直覺想到的關聯是「建議」以及「打字即呈現」,在 Elasticsearch 官方文件找的話會有Suggestersearch as you type等關鍵字,以下分別說明兩者設定方式與差異

這裡假設已經有安裝 ik 分詞器,還沒裝的可以餐考 我之前的文章

設定方式

Suggester

官方提到共有四種類型如下

名稱描述
Termedit distance 為算法基礎(顧名思義,某個字詞改變多少字元就能轉變為另個字詞),基於 analyze 過的單一 term 給予建議,不考慮 term 之間的關係。
Phrase基於前者的基礎上考慮其關係,通常能提供更符合語意的結果。
Completion針對 auto completion 的應用場景,其原理是將 token 編碼成 FST 後放在索引裡,由於是在 memory 因此回應速度很快,不過因其資料結構限制所以只能做 prefix 查詢
Context是前者的進階使用,由於自動完成有時需要考慮情境(比如輸入 star 跑出 coffee,因為 starbuck 的存在)而出現的類型

這邊只介紹 Completion Suggester 的部分

  1. set mapping
PUT <index>
{
    "mappings": {
        "properties" : {
            "suggest" : {
                "type" : "completion",
                "analyzer": "ik_max_word"
            },
            "title" : {
                "type": "keyword"
            }
        }
    }
}
  1. add data to suggest
PUT <index>/_doc/1?refresh
{
    "suggest" : {
        "input": [ "甲狀腺腫大", "甲狀腺凸眼症" ],
    }
}
  1. get completion suggest
POST <index>/_search?pretty
{
    "suggest": {
        "_doc" : {
            "prefix" : "甲",
            "completion" : {
                "field" : "suggest"
            }
        }
    }
}
##### response #####
{
    ...
  "suggest": {
    "_doc" : [ {
        ...
      "options" : [ {
        "text" : "甲狀腺腫大",
        "_index": "music",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          "suggest": ["甲狀腺腫大", "甲狀腺凸眼症"]
        }
      } ]
    } ]
  }
}

可以直接拿 _source 裡面的東西或是 text 作為 api 回傳的資料

search_as_you_type

這是 7.2 之後才推出的欄位,使用 ngram 為基礎並打造的自動完成 field type,因此可以做到 infix 自動完成
搜尋 edge_ngram auto completion elasticsearch或是參考我的 Reference 可以知道 7.2 之前的版本大概都是怎麼完成 infix 的

  1. set mapping
PUT <index>
{
    "mappings": {
        "properties" : {
            "completion_field" : {
                "type" : "search_as_you_type",
                "analyzer": "ik_max_word"
            }
        }
    }
}
  1. update index
PUT <index>/_doc/1?refresh
{
  "completion_field": "甲狀腺腫大"
}
PUT <index>/_doc/2?refresh
{
  "completion_field": "甲狀腺凸眼症"
}
  1. get result
GET my_index/_search
{
  "_source": ["completion_field"],
  "query": {
    "multi_match": {
      "query": "甲狀",
      "type": "best_fields", # 按照有對應到的順序計算
      "fields": [
        "completion_field"
      ]
    }
  }
}
##### response #####
{
    ...
  "hits" : {
      ...
    "hits" : [
      {
        "_index" : "completion_field",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.8630463,
        "_source" : {
          "completion_field" : "甲狀腺腫大"
        }
      },
      {
        "_index" : "completion_field",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.8630463,
        "_source" : {
          "completion_field" : "甲狀腺凸眼症"
        }
      }
    ]
  }
}

_source 裡面的就是結果

呈現結果示意圖

補充: analyzer 是什麼

Analyzer = Character Filter + Tokenizer + Token Filter

名稱簡介
Character Filter針對原始文件進行處理,例如:去除HTML tag,一個 analyzer 可設定多個 char_filter
Tokenizer將前者的結果依據規則切分 token,比如以空白切分的 whitespace,必須要有一個 tokenizer
Token Filter將前者個結果進行增修(stop、lowercase、synonym…),但不可移動其位置,可設定多個 filter

Reference

cmd + /