一、elasticsearch之建議器簡介
首先,環境是elasticsearch版本是5.x以上。低版本的沒有測試!
目前為止,瀏覽器都已經具備Suggest as you type
功能,即在我們輸入搜索的過程中,進行自動的補全或者糾錯功能,協助用戶輸入更精確的關鍵詞,提高搜索階段的文檔匹配程度。例如我們在百度或谷歌瀏覽器輸入搜索關鍵詞時,雖然我們輸入的有誤,但是瀏覽器依然能夠提示出我們想要的正確結果。
在谷歌瀏覽器中,再輸入剛開始時,會自動補全,而當輸入內容達到一定長度時,如果因為單詞拼寫錯誤而無法補全時,就開始嘗試提示相似的詞。
這些功能,我們能通過elasticsearch實現嗎?答案就在Suggesters API中。
在elasticsearch中,建議功能通過使用建議器基於提供的文本建議類似的詞(官網說部分功能仍在開發中.........)。
注意,目前_suggest
已經棄用,我們可以通過_search
來做建議器的查詢。在5.0版本中,_search
經過優化,變得非常的方便。
PUT s1
{
"mappings": {
"doc": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard"
}
}
}
}
}
PUT s1/doc/1
{
"title": "Lucene is cool"
}
PUT s1/doc/2
{
"title":"Elasticsearch builds on top of lucene"
}
GET s1/doc/_search
{
"query": {
"match": {
"title": "Lucene"
}
},
"suggest": {
"my_suggest": {
"text": "Elasticsear lucen",
"term": {
"field": "title"
}
}
}
}
上例是一個包含建議的查詢請求,查詢query
我們已經了然。
讓我們注意suggest
,每個建議器都有自己名稱my-suggestion
,es根據text
字段返回建議結果。建議類型是term
。從field
字段生成建議。
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 0.2876821,
"hits" : [
{
"_index" : "s1",
"_type" : "doc",
"_id" : "2",
"_score" : 0.2876821,
"_source" : {
"title" : "Elasticsearch builds on top of lucene"
}
},
{
"_index" : "s1",
"_type" : "doc",
"_id" : "1",
"_score" : 0.2876821,
"_source" : {
"title" : "Lucene is cool"
}
}
]
},
"suggest" : {
"my_suggest" : [
{
"text" : "elasticsear",
"offset" : 0,
"length" : 11,
"options" : [
{
"text" : "elasticsearch",
"score" : 0.8181818,
"freq" : 1
}
]
},
{
"text" : "lucen",
"offset" : 12,
"length" : 5,
"options" : [
{
"text" : "lucene",
"score" : 0.8,
"freq" : 2
}
]
}
]
}
}
正如結果所示。對於輸入的每個詞條的建議結果,es都會放在options
中,如果沒有建議結果,options
將會為空。
如果我們僅需要建議而不需要查詢功能,我們可以忽略query
而直接使用suggest
對象返回建議:
GET s1/doc/_search
{
"suggest": {
"my_sugget": {
"text": "Elasticsear lucen",
"term": {
"field": "title"
}
}
}
}
可以根據需要指定幾組建議器,每組建議器都有自己的名稱。如下例的my_suggest1
和my_suggest2
。
GET s1/doc/_search
{
"suggest": {
"my_sugget1": {
"text": "Elasticsear",
"term": {
"field": "title"
}
},
"my_suggest2": {
"text": "lucen",
"term": {
"field": "title"
}
}
}
}
在多個建議器中,如果輸入的text
字段值一致,可以單獨寫出來,以適用於my_suggest1
和my_suggest2
兩個建議器:
GET s1/doc/_search
{
"suggest": {
"text": "Elasticsear lucen",
"my_sugget1": {
"term": {
"field": "title"
}
},
"my_suggest2": {
"term": {
"field": "title"
}
}
}
}
根據需求不同elasticsearch設計了4種suggester,分別是:
- 詞條建議器(term suggester):對於給定文本的每個詞條,該鍵議器從索引中抽取要建議的關鍵詞,這對於短字段(如分類標簽)很有效。
- 詞組建議器(phrase suggester):我們可以認為它是詞條建議器的擴展,為整個文本(而不是單個詞條)提供了替代方案,它考慮了各詞條彼此臨近出現的頻率,使得該建議器更適合較長的字段,比如商品的描述。
- 完成建議器(completion suggester):該建議器根據詞條的前綴,提供自動完成的功能(智能提示,有點最左前綴查詢的意思),為了實現這種實時的建議功能,它得到了優化,工作在內存中。所以,速度要比之前說的
match_phrase_prefix
快的多! - 上下文建議器(context suggester):它是完成建議器的擴展,允許我們根據詞條或分類亦或是地理位置對結果進行過濾。
see also: [elasticsearch官網:Suggesters](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html) 歡迎斧正,that's all
二、elasticsearch之term suggester
在建議器簡介部分,我們已經對term suggest
建議器有所了解。
詞條建議器接收輸入的文本,對其進行分析並且分為詞條,然后為每個詞條提供一系列的建議。
准備數據:
PUT s2
{
"mappings": {
"doc": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard"
}
}
}
}
}
PUT s2/doc/1
{
"title": "Lucene is cool"
}
PUT s2/doc/2
{
"title": "Elasticsearch builds on top of lucene"
}
PUT s2/doc/3
{
"title": "Elasticsearch rocks"
}
PUT s2/doc/4
{
"title": "Elastic is the company behind ELK stack"
}
PUT s2/doc/5
{
"title": "elk rocks"
}
PUT s2/doc/6
{
"title": "elasticsearch is rock solid"
}
我們創建一個新的索引並且添加了幾條文檔。現在,我們來查詢一下吧。
GET s2/doc/_search
{
"suggest": {
"my_suggest": {
"text": "luenc",
"term": {
"field": "title"
}
}
}
}
返回的了建議結果:
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : 0.0,
"hits" : [ ]
},
"suggest" : {
"my_suggest" : [
{
"text" : "luenc",
"offset" : 0,
"length" : 5,
"options" : [
{
"text" : "lucene",
"score" : 0.6,
"freq" : 2
}
]
}
]
}
}
上例中,在options
字段中,建議結果是lucene
。我們來看看,在建議器中,都有哪些字段。
- text:建議文本,建議文本是必需的選項,可以通過全局(多個建議器中查詢相同的內容)或者按照單個建議器的格式來。
- field:從field字段中獲取候選建議的字段。這是一個必需的選項,需要全局設置或根據建議設置。
- analyzer:用於分析建議文本的分析器。默認為建議字段的搜索分析器。
- size:個建議文本標記返回的最大條目。
- sort:定義如何根據建議文本術語對建議進行排序。它有兩個可能的值。
- score,先按分數排序,然后按文檔頻率排序,再按術語本身排序。
- frequency,首先按文檔頻率排序,然后按相似性分數排序,然后按術語本身排序。也可以理解為按照流行度排序。
- suggest_mode:控制建議的模式,有3個模式可選擇。
- missing,僅為不在索引中的建議文本術語提供建議。這是默認值。
- popular,僅建議在比原始建議文本術語更多的文檔中出現的建議。也就是說提供比原有輸入詞頻更高的詞條
- always,根據建議文本中的條款建議任何匹配的建議。說白了就是無論如何都會提供建議。
- lowercase_terms:在文本分析之后降低建議文本術語的大小寫。
- min_word_length:建議文本術語必須具有的最小長度才能包含在內。默認為4.(舊名稱
min_word_len
已棄用)。 - shard_size:設置從每個單獨分片中檢索的最大建議數。在減少階段,僅根據size選項返回前N個建議。默認為該 size選項。將此值設置為高於該值的值size可能非常有用,以便以性能為代價獲得更准確的拼寫更正文檔頻率。由於術語在分片之間被划分,因此拼寫校正頻率的分片級文檔可能不准確。增加這些將使這些文檔頻率更精確。
- max_inspections:用於乘以的因子, shards_size以便在碎片級別上檢查更多候選拼寫更正。可以以性能為代價提高准確性。默認為5。
- string_distance:用於比較類似建議術語的字符串距離實現。
- internal,默認值基於damerau_levenshtein,但高度優化用於比較索引中術語的字符串距離。
- damerau_levenshtein,基於Damerau-Levenshtein算法的字符串距離算法。
- levenshtein,基於Levenshtein編輯距離算法的字符串距離算法。
- jaro_winkler,基於Jaro-Winkler算法的字符串距離算法。
- ngram,基於字符n-gram的字符串距離算法。
選擇哪些詞條被建議
了解了各字段的大致含義,我們來探討一下,詞條建議器是如何運作的。以便理解如何確定哪些建議將成為第一名。
詞條建議器使用了Lucene的錯拼檢查器模塊,該模塊會根據給定詞條的編輯距離(es使用了叫做Levenstein edit distance的算法,其核心思想就是一個詞改動多少字符就可以和另外一個詞一致),從索引中返回最大編輯距離不超過某個值的那些詞條。比如說為了從mik
得到mick
,需要加入一個字母(也就是說需要至少要改動一次),所以這兩個詞的編輯距離就是1。我們可以通過配置一系列的選項,來均衡靈活和性能:
- max_edits:最大編輯距離候選建議可以具有以便被視為建議。只能是介於1和2之間的值。任何其他值都會導致拋出錯誤的請求錯誤。默認為2。
- prefix_length:必須匹配的最小前綴字符的數量才是候選建議。默認為1.增加此數字可提高拼寫檢查性能。通常拼寫錯誤不會出現在術語的開頭。(舊名
prefix_len
已棄用)。 - min_doc_freq:建議應出現的文檔數量的最小閾值。可以指定為絕對數字或文檔數量的相對百分比。這可以僅通過建議高頻項來提高質量。默認為0f且未啟用。如果指定的值大於1,則該數字不能是小數。分片級文檔頻率用於此選項。
- max_term_freq:建議文本令牌可以存在的文檔數量的最大閾值,以便包括在內。可以是表示文檔頻率的相對百分比數(例如0.4)或絕對數。如果指定的值大於1,則不能指定小數。默認為0.01f。這可用於排除高頻術語的拼寫檢查。高頻術語通常拼寫正確,這也提高了拼寫檢查的性能。分片級文檔頻率用於此選項。
小結,term suggester
首先將輸入文本經過分析器(所以,分析結果由於采用的分析器不同而有所不同)分析,處理為單個詞條,然后根據單個詞條去提供建議,並不會考慮多個詞條之間的關系。然后將每個詞條的建議結果(有或沒有)封裝到options
列表中。最后由建議器統一返回。
see also: [Elasticsearch Suggester詳解](https://elasticsearch.cn/article/142) | [Elasticsearch官網:Suggesters](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html) | [ElasticSearch suggester](https://blog.csdn.net/zhanglh046/article/details/78536021) | [Elasticsearch官網:suggesters-term](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-term.html)
歡迎斧正,that's all
三、elasticsearch之phrase suggester
詞組建議器和詞條建議器一樣,不過它不再為單個詞條提供建議,而是為整個文本提供建議。
准備數據:
PUT s4
{
"mappings": {
"doc": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard"
}
}
}
}
}
PUT s4/doc/1
{
"title": "Lucene is cool"
}
PUT s4/doc/2
{
"title": "Elasticsearch builds on top of lucene"
}
PUT s4/doc/3
{
"title": "Elasticsearch rocks"
}
PUT s4/doc/4
{
"title": "Elastic is the company behind ELK stack"
}
PUT s4/doc/5
{
"title": "elk rocks"
}
PUT s4/doc/6
{
"title": "elasticsearch is rock solid"
}
現在我們來看看phrase
是如何建議的:
GET s4/doc/_search
{
"suggest": {
"my_s4": {
"text": "lucne and elasticsear rock",
"phrase": {
"field": "title"
}
}
}
}
text
是輸入帶有拼錯的文本。而建議類型則換成了phrase
。來看查詢結果:
{
"took" : 6,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : 0.0,
"hits" : [ ]
},
"suggest" : {
"my_s4" : [
{
"text" : "lucne and elasticsear rock",
"offset" : 0,
"length" : 26,
"options" : [
{
"text" : "lucne and elasticsearch rocks",
"score" : 0.12709484
},
{
"text" : "lucne and elasticsearch rock",
"score" : 0.10422645
},
{
"text" : "lucne and elasticsear rocks",
"score" : 0.10036137
}
]
}
]
}
}
可以看到options
直接返回了相關短語列表。雖然lucene
建議的並不好。但elasticserch
和rock
很不錯。除此之外,我們還可以使用高亮來向用戶展示哪些原有的詞條被糾正了。
GET s4/doc/_search
{
"suggest": {
"my_s4": {
"text": "lucne and elasticsear rock",
"phrase": {
"field": "title",
"highlight":{
"pre_tag":"<em>",
"post_tag":"</em>"
}
}
}
}
}
除了默認的,還可以自定義高亮顯示:
GET s4/doc/_search
{
"suggest": {
"my_s4": {
"text": "lucne and elasticsear rock",
"phrase": {
"field": "title",
"highlight":{
"pre_tag":"<b id='d1' class='t1' style='color:red;font-size:18px;'>",
"post_tag":"</b>"
}
}
}
}
}
需要注意的是,建議器結果的高亮顯示和查詢結果高亮顯示有些許區別,比如說,這里的自定義標簽是pre_tag
和post_tag
而不是之前如這樣的:
GET s4/doc/_search
{
"query": {
"match": {
"title": "rock"
}
},
"highlight": {
"pre_tags": "<b style='color:red'>",
"post_tags": "</b>",
"fields": {
"title": {}
}
}
}
phrase suggester
在term suggester
的基礎上,會考慮多個term
之間的關系,比如是否同時出現索引的原文中,臨近程度,詞頻等。
see also:[phrase suggester](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-phrase.html) 歡迎斧正,that's all
四、elasticsearch之completion suggester
前言
我們來看一下自動完成的建議器——是一個導航功能,提供自動完成、搜索功能,可以在用戶輸入時引導用戶查看相關結果,從而提高搜索精度。但並不適用於拼接檢查或者像term
和phrase
建議那樣的功能。
如果說在2000年左右,自動完成還是很炫酷的功能,那么現在它是必備的了——任何沒有自動完成功能的搜索引擎都是很古老的。用戶期望一個良好的自動完成來幫助用戶實現更快的(特別是移動端)以及更好的(比如輸入e
,搜索引擎就應該知道用戶想要查找的是elasticsearch
)搜索。
一個優秀的自動完成將降低搜索引擎的負載,特別是在用戶有一些快速搜索可用時,也就是直接跳轉到主流的搜索結果而無須執行完整的搜索。
除此之外,一個優秀的自動完成必須是和快速的、相關的:
- 快速是因為它必須在用戶不斷輸入的時候產生建議。
- 相關則是用戶並不希望建議一個沒有搜索結果或者沒有用處的結果。
那我們依靠之前學過的match_phrase_prefix
最左前綴查詢來完成該功能,但是這樣的查詢可能不夠快,因為理想的情況下,搜索引擎需要在用戶輸入下一個字符前返回建議結果。
完成建議器和后面的上下文建議器可以幫助用戶構建更快的自動完成,它們是基於Lucene的suggest建議模塊而構建的,將數據保存在內存中的有限狀態轉移機中(FST)。FST實際上是一種圖。它可以將詞條以壓縮和易於檢索的方式來存儲。
上圖展示了詞條index、search、suggest
是如何存儲的。當然實際中的實現更加復雜,比如它允許我們添加權重。
FST(Finite StateTransducers),通常中文譯作有限狀態傳感器,FST目前在語音識別和自然語言搜索、處理等方向被廣泛應用。
FST的功能更類似於字典,Lucene4.0在查找Term時使用了FST算法,用來快速定位Term的位置。FST的數據結構可以理解成(key:value
)的形式。
在同義詞過濾器(SynonymFilter)的實現中甚至可以用HashMap代替,不過相比較於HashMap,它的優點是:
-
以O(1)的時間復雜度找到key對應的value。
-
以字節的方式來存儲所有的Term,重復利用Term Index的前綴和后綴,使Term - Index小到可以放進內存,減少存儲空間,不過相對的也會占用更多的cpu資源。
-
FST還可以用來快速確定term是否在系統中。
完成建議器:completion suggester
為了告訴elasticsearch我們准備將建議存儲在自動完成的FST中,需要在映射中定義一個字段並將其type
類型設置為completion
:
PUT s5
{
"mappings":{
"doc":{
"properties": {
"title": {
"type": "completion",
"analyzer": "standard"
}
}
}
}
}
PUT s5/doc/1
{
"title":"Lucene is cool"
}
PUT s5/doc/2
{
"title":"Elasticsearch builds on top of lucene"
}
PUT s5/doc/3
{
"title":"Elasticsearch rocks"
}
PUT s5/doc/4
{
"title":"Elastic is the company behind ELK stack"
}
PUT s5/doc/5
{
"title":"the elk stack rocks"
}
PUT s5/doc/6
{
"title":"elasticsearch is rock solid"
}
GET s5/doc/_search
{
"suggest": {
"my_s5": {
"text": "elas",
"completion": {
"field": "title"
}
}
}
}
建議結果不展示了!
上例的特殊映射中,支持以下參數:
- analyzer,要使用的索引分析器,默認為simple。
- search_analyzer,要使用的搜索分析器,默認值為analyzer。
- preserve_separators,保留分隔符,默認為true。 如果您禁用,您可以找到以Foo Fighters開頭的字段,如果您建議使用foof。
- preserve_position_increments,啟用位置增量,默認為true。如果禁用並使用停用詞分析器The Beatles,如果您建議,可以從一個字段開始b。注意:您還可以通過索引兩個輸入來實現此目的,Beatles並且 The Beatles,如果您能夠豐富數據,則無需更改簡單的分析器。
- max_input_length,限制單個輸入的長度,默認為50UTF-16代碼點。此限制僅在索引時使用,以減少每個輸入字符串的字符總數,以防止大量輸入膨脹基礎數據結構。大多數用例不受默認值的影響,因為前綴完成很少超過前綴長於少數幾個字符。
除此之外,該建議映射還可以定義在已存在索引字段的多字段:
PUT s6
{
"mappings": {
"doc": {
"properties": {
"name": {
"type": "text",
"fields": {
"suggest": {
"type": "completion"
}
}
}
}
}
}
}
PUT s6/doc/1
{
"name":"KFC"
}
PUT s6/doc/2
{
"name":"kfc"
}
GET s6/doc/_search
{
"suggest": {
"my_s6": {
"text": "K",
"completion": {
"field": "name.suggest"
}
}
}
}
如上示例中,我們需要索引餐廳這樣的地點,而且每個地點的name
名稱字段添加suggest
子字段。
上例的查詢將肯德基(KFC)和開封菜(kfc)都返回。
在索引階段提升相關性
在進行普通的索引時,輸入的文本在索引和搜索階段都會被分析,這就是為什么上面的示例會將KFC
和kfc
都返回了。我們也可以通過analyzer
和search_analyzer
選項來進一步控制分析過程。如上例我們可以只匹配KFC
而不匹配kfc
:
PUT s7
{
"mappings": {
"doc": {
"properties": {
"name": {
"type": "text",
"fields": {
"suggest": {
"type": "completion",
"analyzer":"keyword",
"search_analyzer":"keyword"
}
}
}
}
}
}
}
PUT s7/doc/1
{
"name":"KFC"
}
PUT s7/doc/2
{
"name":"kfc"
}
GET s7/doc/_search
{
"suggest": {
"my_s7": {
"text": "K",
"completion": {
"field": "name.suggest"
}
}
}
}
建議結果如下:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : 0.0,
"hits" : [ ]
},
"suggest" : {
"my_s7" : [
{
"text" : "K",
"offset" : 0,
"length" : 1,
"options" : [
{
"text" : "KFC",
"_index" : "s7",
"_type" : "doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "KFC"
}
}
]
}
]
}
}
上述的建議結果中,只有KFC
被返回。更多的細節控制可以搭配不同的分析器來完成。
多數的情況下,我們將在單獨的字段中、單獨的索引中甚至是單獨的集群中保存建議。這對於主搜索引擎的性能提升和擴展建議器都是非常有利的。
除此之外,還可以使用input
和可選的weight
屬性,input
是建議查詢匹配的預期文本,weight
是建議評分方式(也就是權重)。例如:
PUT s8
{
"mappings": {
"doc":{
"properties":{
"title":{
"type": "completion"
}
}
}
}
}
添加數據的幾種形式:
PUT s8/doc/1
{
"title":{
"input":"blow",
"weight": 2
}
}
PUT s8/doc/2
{
"title":{
"input":"block",
"weight": 3
}
}
上例分別添加兩個建議並設置各自的權重值。
PUT s8/doc/3
{
"title": [
{
"input":"appel",
"weight": 2
},
{
"input":"apple",
"weight": 3
}
]
}
上例以列表的形式添加建議,設置不同的權重。
PUT s8/doc/4
{
"title": ["apple", "appel", "block", "blow"],
"weght": 32
}
上例是為多個建議設置相同的權重。
查詢的結果由權重決定:
GET s8/doc/_search
{
"suggest": {
"my_s8": {
"text": "app",
"completion": {
"field": "title"
}
}
}
}
比如,我們在設置建議的時候,將apple
建議的權重weight
設置的更高,那么在如上例的查詢中,apple
將會排在建議的首位。
在搜索階段提升相關性
當在運行建議的請求時,可以決定出現哪些建議,就像其他建議器一樣,size
參數控制返回多少項建議(默認為5項);還可以通過fuzzy
參數設置模糊建議,以對拼寫進行容錯。當開啟模糊建議之后,可以設置下列參數來完成建議:
- fuzziness,可以指定所允許的最大編輯距離。
- min_length,指定什么長度的輸入文本可以開啟模糊查詢。
- prefix_length,假設若干開始的字符是正確的(比如block,如果輸入blaw,該字段也認為之前輸入的是對的),這樣可以通過犧牲靈活性提升性能。
這些參數都是在建議的completion
對象的下面:
GET s8/doc/_search
{
"suggest": {
"my_s9": {
"text": "blaw",
"completion": {
"field": "title",
"size": 2,
"fuzzy": {
"fuzziness": 2,
"min_length": 3,
"prefix_length": 2
}
}
}
}
}
結果如下:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : 0.0,
"hits" : [ ]
},
"suggest" : {
"my_s9" : [
{
"text" : "blow",
"offset" : 0,
"length" : 4,
"options" : [
{
"text" : "block",
"_index" : "s8",
"_type" : "doc",
"_id" : "3",
"_score" : 6.0,
"_source" : {
"title" : {
"input" : "block",
"weight" : 3
}
}
},
{
"text" : "blow",
"_index" : "s8",
"_type" : "doc",
"_id" : "2",
"_score" : 4.0,
"_source" : {
"title" : {
"input" : "blow",
"weight" : 2
}
}
}
]
}
]
}
}
其他
_source
為了減少不必要的響應,我們可以對建議結果做一些過濾,比如加上_source
:
GET s8/doc/_search
{
"suggest": {
"completion_suggest": {
"text": "appl",
"completion": {
"field": "title"
}
}
},
"_source": "title"
}
好吧,雖然我們只有一個字段!
size
除了_source
,我們還可以指定size
參數:
GET s8/doc/_search
{
"suggest": {
"completion_suggest": {
"prefix": "app",
"completion": {
"field": "title",
"size": 1
}
}
},
"_source": "title"
}
size
參數指定返回建議數(默認為5),需要注意的是,size must be positive
,也就是說size
參數必須是積極的——非0非負數!
skip_duplicates
我們的建議可能是來自不同的文檔,這其中就會有一些重復建議項,我們可以通過設置skip_duplicates:true
來修改此行為,如果為true
則會過濾掉結果中的重復建議文檔:
GET s8/doc/_search
{
"suggest": {
"completion_suggest": {
"prefix": "app",
"completion": {
"field": "title",
"size": 5,
"skip_duplicates":true
}
}
},
"_source": "title"
}
但需要注意的是,該參數設置為true
的話,可能會降低搜索速度,因為需要訪問更多的建議結果項,才能過濾出來前N個。
最后,完成建議器還支持正則表達式查詢,這意味着我們可以將前綴表示為正則表達式:
GET s5/doc/_search
{
"suggest": {
"completion_suggest": {
"regex": "e[l|e]a",
"completion": {
"field": "title"
}
}
}
}
see also:completion suggester | Weighted Finite-State Transducer Algorithms
An Overview | FST(Finite-State Transducer) 原理
歡迎斧正,that's all
五、elasticsearch之context suggester
前言
雖然完成建議器已經能返回所有和輸入文本相匹配的結果,但有些使用案例需要過濾。這就要用到了上下文過濾器,它在完成建議器的基礎上加入了過濾功能。
上下文建議器允許用戶使用context上下文來進行過濾,上下文可以是分類(詞條)或者地理位置,為了開啟上下文,同樣在映射中指定,然后在文檔和建議中提供上下文。
完成建議器考慮索引中的所有文檔,但通常我們希望提供某些標准過濾或者提升的建議。例如,我們想要推薦某些歌手過濾的歌曲標題,或者我們希望根據其類型推廣歌曲標題。
要實現建議過濾或者提升,我們可以在配置完成字段時添加上下文映射,我們也可以為完成字段定義多個上下文映射,每個上下文映射都有唯一的名稱和類型,有兩種類型category
和geo
。上下文映射contexts
在字段映射中的參數下配置。
注意:在索引查詢啟用上下文的完成字段時,必須提供上下文。
如下示例定義了類型,每個類型都有一個完成字段的兩個上下文映射:
PUT place
{
"mappings": {
"doc":{
"properties":{
"title":{
"type":"completion",
"contexts":[
{①
"name":"place_type",
"type": "category"
},
{②
"name":"location",
"type":"geo",
"precision": 4
}
]
}
}
}
}
}
PUT place_path_category
{
"mappings": {
"doc":{
"properties":{
"title":{
"type":"completion",
"contexts":[
{③
"name":"place_type",
"type":"category",
"path":"cat"
},
{④
"name":"location",
"type":"geo",
"precision": 4,
"path":"loc"
}
]
},
"loc":{
"type":"geo_point"
}
}
}
}
}
- ①,定義category名為place_type的上下文,其中必須與建議一起發送類別。
- ②,定義一個geo名為location的上下文,其中必須使用建議發送類別。
- ③,定義category名為place_type的上下文,其中必須從cat字段字段中讀取類別。
- ④,定義geo名為location的上下文,其中從loc字段中讀取類別。
注意:添加上下文映射會增加完成字段的索引大小,完成索引完全是堆駐留的,我們可以使用Indices Stats監視完成字段索引的大小。
類別上下文
在category上下文允許我們將一個或多個類別與索引時間的建議關聯,在查詢時,建議可以通過其關聯的類別進行過濾和提升。
映射的設置與place_type上面的字段類似,如果path已定義,則從文檔中的該路徑讀取類別,否則必須在建議字段中發送它們,如下示例所示:
PUT place/doc/1
{
"title":{
"input":["timmy's", "starbucks", "dunkin donuts"],
"contexts":{
"place_type":["cafe", "food"] ①
}
}
}
- ①,這些建議將與咖啡館和食品類別相關聯。
如果映射有path, 那么以下索引請求就可以添加類別:
PUT place_path_category/doc/1
{
"title":["timmy's", "sstarbucks", "dunkin donuts"],
"cat":["cafe", "food"] ①
}
- ①,這些建議將與咖啡館和食品類別相關聯。
注意:如果上下文映射引用另一個字段並且類別被明確索引,則使用兩組類別對建議進行索引。
類別查詢
建議可以按一個或多個類別進行過濾,以下按多個類別過濾建議:
POST place/_search
{
"suggest":{
"place_suggestion":{
"prefix":"tim",
"completion":{
"field":"title",
"size": 10,
"contexts":{
"place_type":["cafe", "restaurants"]
}
}
}
}
}
注意,如果在查詢上設置了多個類別或者類別上下文,則將它們合並為分離。這意味着如果建議包含至少一個提供的上下文值,則建議匹配。
某些類別的建議可能會比其他類別高,以下按類別過濾建議,並額外提升與某些類別相關的建議:
POST place/_search
{
"suggest": {
"place_suggestion": {
"prefix": "tim",
"completion": {
"field": "title",
"size": 10,
"contexts":{
"place_type":[ ①
{
"context":"cafe"
},
{
"context":"restaurants", "boost":2
}
]
}
}
}
}
}
- ①,上下文查詢過濾建議與類別咖啡館和餐館相關聯,並且通過因子增強與餐館相關聯的建議
除了接受類別值之外,上下文查詢還可以由多個類別上下文子句組成,category上下文支持以下參數:
- context,要過濾/提升的類別的值,這是強制性的。
- boost,應該提高建議分數的因素,通過將boost乘以建議權重來計算分數,默認為1。
- prefix,是否應該將類別實為前綴,例如,如果設置為true,則可以通過指定類型的類別前綴來過濾type1,type2等類別,默認為false。
注意:如果建議條目與多個上下文匹配,則最終分數被計算為由任何匹配上下文產生的最大分數。
地理位置上下文
一個geo上下文允許我們將一個或多個地理位置或geohash與在索引時間的建議關聯,在查詢時,如果建議位於地理位置特定的距離內,則可以過濾和提升建議。
在內部,地位置被編碼為具有指定精度的地理位置。
地理映射
除了path設置,geo上下文映射還接受以下設置:
- precision,它定義了地理散列的精度要被索引並且可以指定為一個距離值(5km,10km等),或作為原料地理散列精度(1 … 12)。默認為原始geohash精度值6。
注意:索引時間precision設置可以在查詢時使用的最大geohash精度。
索引地理上下文
geo上下文可以通過參數顯式設置或通過path參數從文檔中的地理點地段建立索引,類似於category上下文,將多個地理位置上下文與建立相關聯,將為每個地理位置索引建議,以下索引具有兩個地理位置上下文的建議:
PUT place/doc/2
{
"title":{
"input":"timmy's",
"contexts":{
"location":[
{
"lat":43.6624803,
"lon":-79.3863353
},
{
"lat":43.6624718,
"lon":-79.3873327
}
]
}
}
}
地理位置查詢
建議可以根據它們與一個或多個地理點的接近程度進行過濾和提升,以下過濾建議屬於地理位置點的編碼geohash所代表的區域內的建議:
POST place/_search
{
"suggest": {
"place_suggestion": {
"prefix": "tim",
"completion": {
"field": "title",
"size": 4,
"contexts":{
"location":{
"lat": 43.662,
"lon": -79.380
}
}
}
}
}
}
注意:當指定查詢精度較低的位置時,將考慮屬於該區域內的所有建議。如果在查詢上設置了多個類別或類別上下文,則將它們合並為分離,這意味着如果建議包含至少一個提供的上下文值,則建議才匹配。
在geohash所代表的區域內的建議也可以比其他建議更高,如下所示:
POST place/_search
{
"suggest": {
"place_suggestion": {
"prefix": "tim",
"completion": {
"field": "title",
"size":10,
"contexts":{
"location":[ ①
{
"lat": 43.6624803,
"lon": -79.3863353,
"precision": 2
},
{
"context":{
"lat": 43.6624803,
"lon":-79.3863353
},
"boost": 2
}
]
}
}
}
}
}
- ①,上下文查詢過濾屬於geohash(43.662,-79.380)表示的地理位置的建議,精度為2,並提升屬於(43.6624803,-79.3863353)geohash表示的建議,默認精度為6,因數為2。
注意:如果建議條目與多個上下文匹配,則最終分數被計算為由任何匹配上下文產生的最大分數。
除了接受上下文值之外,上下文查詢還可以由多個上下文子句組成,category上下文子句支持一下參數:
- context,地理點對象或地理哈希字符串,用於過濾提升建議,這是強制性的。
- boost,應該提高建議分數的因素,通過將boost乘以建議權重來計算分數,默認為1。
- precision,geohash對查詢地理點進行編碼的精度,這可以被指定為一個距離值(5m,10km等),或作為原料地理散列精度(1 … 12)。默認為索引時間精度級別。
- neighbours,接受應該考慮相鄰地理位置的精度值數組,精度值可以是距離值(5m, 10km等)或一個原始地理散列精度(1 … 12)。默認為索引時間精度級別生成的臨近值。
see also: [Context Suggester](https://www.elastic.co/guide/en/elasticsearch/reference/7.0/suggester-context.html) 歡迎斧正,that's all