Function_query
function_score允許你修改檢索文檔的分?jǐn)?shù)。例如桦山,如果score函數(shù)的計算開銷非常大尘分,并且它計算經(jīng)過篩選的一組文檔上的分?jǐn)?shù)便足夠了,那么這將非常有用膘婶。
function_score?查詢?是用來控制評分過程的終極武器,它允許為每個與主查詢匹配的文檔應(yīng)用一個函數(shù)蛀醉,?以達(dá)到改變甚至完全替換原始查詢評分?_score?的目的悬襟。
實際上,也能用過濾器對結(jié)果的?子集?應(yīng)用不同的函數(shù)拯刁,這樣一箭雙雕:既能高效評分脊岳,又能利用過濾器緩存。
要使用function_score垛玻,用戶必須定義查詢一個或多個函數(shù)割捅,這些函數(shù)計算查詢返回的每個文檔的新分?jǐn)?shù)。
如果沒有?function_score?查詢帚桩,就不能將全文查詢與最新發(fā)生這種因子結(jié)合在一起評分亿驾,而不得不根據(jù)評分?_score?或時間?date?進(jìn)行排序;這會相互影響抵消兩種排序各自的效果账嚎。這個查詢可以使兩個效果融合:可以仍然根據(jù)全文相關(guān)度進(jìn)行排序莫瞬,但也會同時考慮最新發(fā)布文檔、流行文檔郭蕉、或接近用戶希望價格的產(chǎn)品疼邀。正如所設(shè)想的,查詢要考慮所有這些因素會非常復(fù)雜召锈,讓我們先從簡單的例子開始旁振,然后順著梯子慢慢向上爬,增加復(fù)雜度涨岁。
function_score?僅使用一個函數(shù)如下示例:
GET /_search
{
????"query": {
????????"function_score": {
????????????"query": { "match_all": {} },
????????????"boost": "5",
????????????"random_score": {},
????????????"boost_mode":"multiply"
????????}
????}
}
此外拐袜,可以組合幾個功能。在這種情況下卵惦,只有當(dāng)文檔匹配給定的篩選查詢時阻肿,才可以選擇應(yīng)用該函數(shù)瓦戚。
GET /_search{
????"query":?{
????????"function_score":?{
??????????"query":?{?"match_all":?{}?},
??????????"boost":?"5",?
??????????"functions":?[
??????????????{
??????????????????"filter":?{?"match":?{?"test":?"bar"?}?},
??????????????????"random_score":?{},?
??????????????????"weight":?23
??????????????},
??????????????{
??????????????????"filter":?{?"match":?{?"test":?"cat"?}?},
??????????????????"weight":?42
??????????????}
??????????],
??????????"max_boost":?42,
??????????"score_mode":?"max",
??????????"boost_mode":?"multiply",
??????????"min_score"?:?42
????????}
????}}
Boost?:提升整個查詢沮尿。
有關(guān)受支持函數(shù)的列表,請參閱函數(shù)得分查詢较解。
注意:每個函數(shù)的過濾查詢產(chǎn)生的分?jǐn)?shù)無關(guān)緊要畜疾。
如果沒有給出函數(shù)的過濾器,這相當(dāng)于指定?"match_all": {}
首先印衔,每個文檔都由定義的函數(shù)評分啡捶。該參數(shù)score_mode指定計算得分的組合方式:
multiply:得分乘以(默認(rèn))
sum:得分總和
avg:得分是平均的
first:應(yīng)用具有匹配過濾器的第一個函數(shù)
max:使用最高分
min:使用最低分?jǐn)?shù)
? ? ? 因為得分可以在不同的尺度上(例如,衰減函數(shù)的得分在0到1之間奸焙,但是field_value_factor的得分是任意的)瞎暑,而且有時函數(shù)對得分的影響不同彤敛,因此每個函數(shù)的得分可以根據(jù)用戶定義的權(quán)重進(jìn)行調(diào)整。權(quán)重可以在函數(shù)數(shù)組(上面的例子)中為每個函數(shù)定義了赌,并與各自函數(shù)計算的得分相乘墨榄。如果在沒有任何其他函數(shù)聲明的情況下給出權(quán)值,權(quán)值就充當(dāng)一個函數(shù)勿她,該函數(shù)只返回權(quán)值袄秩。
? ? ?如果score_mode被設(shè)置為avg,則各個分?jǐn)?shù)將通過加權(quán)平均值進(jìn)行組合逢并。例如之剧,如果兩個函數(shù)返回得分1和2,并且它們各自的權(quán)重分別為3和4砍聊,那么它們的得分將組合為(1*3+2*4)/(3+4)而不是(1*3+2*4)/2背稼。
? ? ?通過設(shè)置max_boost參數(shù),可以將新分?jǐn)?shù)限制為不超過某個限制玻蝌。max_boost的默認(rèn)值是FLT_MAX雇庙。
新計算的分?jǐn)?shù)與查詢的分?jǐn)?shù)組合。該參數(shù)boost_mode
新分?jǐn)?shù)為functions內(nèi)的灶伊,查詢分?jǐn)?shù)為query內(nèi)的疆前。
boost_mode參數(shù)說明:
multiply:查詢得分和功能得分相乘(默認(rèn))
replace:僅使用函數(shù)得分,忽略查詢得分
sum:查詢得分和函數(shù)得分被添加
avg:平均
max:查詢分?jǐn)?shù)與函數(shù)分?jǐn)?shù)最大值
min:查詢分?jǐn)?shù)與函數(shù)分?jǐn)?shù)最小值
默認(rèn)情況下聘萨,修改分?jǐn)?shù)不會更改匹配的文檔竹椒。要排除不滿足某個分?jǐn)?shù)閾值的文檔,可以將min_score參數(shù)設(shè)置為所需的分?jǐn)?shù)閾值。
要使min_score工作,需要對查詢返回的所有文檔進(jìn)行評分傲醉,然后逐一過濾迁霎。
function_score函數(shù)分類
weight
為每個文檔應(yīng)用一個簡單而不被規(guī)范化的權(quán)重提升值:當(dāng)weight?為?2?時,最終結(jié)果為?2 * _score?逝钥。
field_value_factor
使用這個值來修改_score?,如將?popularity?或?votes?(受歡迎或贊)作為考慮因素。
random_score
為每個用戶都使用一個不同的隨機(jī)評分對結(jié)果排序锨能,但對某一具體用戶來說,看到的順序始終是一致的芍耘。
衰減函數(shù)?(decay functions)——?linear?址遇、?exp?、?gauss
將浮動值結(jié)合到評分_score?中斋竞,例如結(jié)合?publish_date?獲得最近發(fā)布的文檔倔约,結(jié)合?geo_location?獲得更接近某個具體經(jīng)緯度(lat/lon)地點的文檔,結(jié)合?price?獲得更接近某個特定價格的文檔坝初。
script_score
如果需求超出以上范圍時浸剩,用自定義腳本可以完全控制評分計算钾军,實現(xiàn)所需邏輯。
Script_score
該script_score函數(shù)允許您使用腳本表達(dá)式包裝另一個查詢并自定義其評分绢要,可選地使用從doc中的其他數(shù)字字段值派生的計算巧颈。這是一個簡單的示例:
GET /_search{
????"query":?{
????????"function_score":?{
????????????"query":?{
????????????????"match":?{?"message":?"elasticsearch"?}
????????????},
????????????"script_score"?:?{
????????????????"script"?:?{
??????????????????"source":?"Math.log(2 + doc['likes'].value)"
????????????????}
????????????}
????????}
????}}
script_score函數(shù)產(chǎn)生的分?jǐn)?shù)必須是非負(fù)數(shù),否則將引發(fā)錯誤袖扛。
????在不同的腳本字段值和表達(dá)式之上砸泛,可以使用_score腳本參數(shù)根據(jù)包裝好的查詢檢索分?jǐn)?shù)。
腳本編譯被緩存蛆封,以便更快地執(zhí)行唇礁。如果腳本有需要考慮的參數(shù),最好重用相同的腳本惨篱,并為其提供參數(shù):
GET /_search{
????"query":?{
????????"function_score":?{
????????????"query":?{
????????????????"match":?{?"message":?"elasticsearch"?}
????????????},
????????????"script_score"?:?{
????????????????"script"?:?{
????????????????????"params":?{
????????????????????????"a":?5,
????????????????????????"b":?1.2
????????????????????},
????????????????????"source":?"params.a / Math.pow(params.b, doc['likes'].value)"
????????????????}
????????????}
????????}
????}}
注意盏筐,與custom_score查詢不同,查詢的得分與腳本得分的結(jié)果相乘砸讳。如果您想要禁止這個琢融,請設(shè)置“boost_mode”:“replace”
Weight(權(quán)重查詢)
權(quán)重分?jǐn)?shù)允許您將分?jǐn)?shù)乘以提供的權(quán)重。這有時是需要的簿寂,因為針對特定查詢設(shè)置的boost值會被規(guī)范化漾抬,而對于這個score函數(shù)則不會。number值的類型為float常遂。
"weight"?:?number
Random
random_score生成從0到但不包括1的均勻分布的分?jǐn)?shù)纳令。默認(rèn)情況下,它采用了內(nèi)部Lucene的文檔ID作為隨機(jī)源克胳,這是非常有效的平绩,但不幸的是不可復(fù)制,因為文檔可能被合并(merge)重新編號漠另。
如果您希望分?jǐn)?shù)可以重現(xiàn)捏雌,可以提供seed?和field。最后的分?jǐn)?shù)將被計算在此基礎(chǔ)上種子(seed),字段的最小值計算的考慮文檔和鹽基于索引名稱和碎片id這樣的文檔有相同的值,但存儲在不同的索引得到不同的分?jǐn)?shù)笆搓。注意性湿,相同切分內(nèi)的文檔和字段值相同的文檔將獲得相同的分?jǐn)?shù),因此通常希望使用對所有文檔都具有惟一值的字段砚作。一個好的默認(rèn)選擇可能是使用_seq_no字段窘奏,它唯一的缺點是,如果文檔被更新葫录,那么分?jǐn)?shù)會發(fā)生變化,因為update操作也會更新_seq_no字段的值领猾。
可以在不設(shè)置字段的情況下設(shè)置種子米同,但這已被棄用骇扇,因為這需要在_id字段上加載fielddata,這會消耗大量內(nèi)存面粮。
GET /_search{
????"query":?{
????????"function_score":?{
????????????"random_score":?{
????????????????"seed":?10,
????????????????"field":?"_seq_no"
????????????}
????????}
????}}
Field_value_factor(接受歡迎度提升權(quán)重)
field_value_factor功能允許您使用文檔中的字段來影響分?jǐn)?shù)少孝。它與使
用該script_score函數(shù)類似,但它避免了腳本的開銷熬苍。它類似于使用script_score函數(shù)稍走,但是,它避免了腳本開銷柴底。如果用于多值字段婿脸,則僅在計算中使用該字段的第一個值。
參數(shù)配置詳細(xì)說明:
例如柄驻,假設(shè)您有一個使用數(shù)字likes?字段索引的文檔狐树,并希望使用此字段影響文檔的分?jǐn)?shù),這樣做的示例如下所示:
GET /_search{
????"query":?{
????????"function_score":?{
????????????"field_value_factor":?{
????????????????"field":?"likes",
????????????????"factor":?1.2,
????????????????"modifier":?"sqrt",
????????????????"missing":?1
????????????}
????????}
????}
這將轉(zhuǎn)化為以下評分公式:
sqrt(1.2 * doc['likes'].value)
field_value_factor函數(shù)有許多選項:
field要從文檔中提取的字段鸿脓。
factor將字段值乘以的可選因子抑钟,默認(rèn)為1。
modifier修改適用于該字段的值野哭,可以是一個:none在塔,log,?log1p拨黔,log2p心俗,ln,ln1p蓉驹,ln2p城榛,square,sqrt态兴,或reciprocal狠持。默認(rèn)為none。
修改(modifier)含義
none不要對字段值應(yīng)用任何乘數(shù)
log取字段值的常用對數(shù)瞻润。因為如果在0和1之間的值上使用此函數(shù)將返回負(fù)值并導(dǎo)致錯誤喘垂,建議改為使用log1p。
log1p將1添加到字段值并采用常用對數(shù)
log2p將2添加到字段值并采用常用對數(shù)
ln取字段值的自然對數(shù)绍撞。因為如果在0和1之間的值上使用此函數(shù)將返回負(fù)值并導(dǎo)致錯誤正勒,建議改為使用ln1p。
ln1p將1添加到字段值并采用自然對數(shù)
ln2p將2添加到字段值并采用自然對數(shù)
square平方字段值(乘以它自己)
sqrt取字段值的平方根
reciprocal報答字段值傻铣,同1/x那里x是該字段的值
missing
如果文檔沒有該字段章贞,則使用的值。修飾符和因子仍然應(yīng)用于它非洲,就像從文檔中讀取一樣鸭限。
field_value_score函數(shù)產(chǎn)生的分?jǐn)?shù)必須是非負(fù)數(shù)蜕径,否則將引發(fā)錯誤。的log和ln如果0和1一定要限制的字段的值與一系列過濾器败京,以避免這種情況兜喻,或者使用之間對值的修飾符會產(chǎn)生負(fù)值log1p和ln1p。
請記住赡麦,將log()設(shè)為0或負(fù)數(shù)的平方根是非法操作朴皆,并拋出異常。請務(wù)必使用范圍過濾器限制字段的值以避免這種情況泛粹,或使用log1p和ln1p遂铡。
案例詳解:
設(shè)想有個網(wǎng)站供用戶發(fā)布博客并且可以讓他們?yōu)樽约合矚g的博客點贊,我們希望將更受歡迎的博客放在搜索結(jié)果列表中相對較上的位置戚扳,同時全文搜索的評分仍然作為相關(guān)度的主要排序依據(jù)忧便,可以簡單的通過存儲每個博客的點贊數(shù)來實現(xiàn)它:
PUT /blogposts/post/1{
??"title":???"About popularity",
??"content":?"In this post we will talk about...",
??"votes":???6
}
在搜索時,可以將function_score?查詢與?field_value_factor?結(jié)合使用帽借,即將點贊數(shù)與全文相關(guān)度評分結(jié)合:
GET /blogposts/post/_search{
??"query":?{
????"function_score":?{?
??????"query":?{?
????????"multi_match":?{
??????????"query":????"popularity",
??????????"fields":?[?"title",?"content"?]
????????}
??????},
??????"field_value_factor":?{?
????????"field":?"votes"?
??????}
????}
??}}
然而這并不會帶來出人意料的好結(jié)果珠增,全文評分_score?通常處于 0 到 10 之間,如下圖“受歡迎度的線性關(guān)系基于?_score?的原始值?2.0”?中砍艾,有 10 個贊的博客會掩蓋掉全文評分蒂教,而 0 個贊的博客的評分會被置為 0 。
受歡迎度的線性關(guān)系基于?_score?的原始值?2.0
一種融入受歡迎度更好方式是用modifier?平滑?votes?的值脆荷。換句話說凝垛,我們希望最開始的一些贊更重要,但是其重要性會隨著數(shù)字的增加而降低蜓谋。0 個贊與 1 個贊的區(qū)別應(yīng)該比 10 個贊與 11 個贊的區(qū)別大很多梦皮。
對于上述情況,典型的modifier?應(yīng)用是使用?log1p?參數(shù)值桃焕,公式如下:
new_score = old_score * log(1 + number_of_votes)
log?對數(shù)函數(shù)使?votes?贊字段的評分曲線更平滑剑肯,如圖??“受歡迎度的對數(shù)關(guān)系基于?_score?的原始值?2.0”?:
受歡迎度的對數(shù)關(guān)系基于?_score?的原始值?2.0
帶?modifier?參數(shù)的請求如下:
GET /blogposts/post/_search{
??"query":?{
????"function_score":?{
??????"query":?{
????????"multi_match":?{
??????????"query":????"popularity",
??????????"fields":?[?"title",?"content"?]
????????}
??????},
??????"field_value_factor":?{
????????"field":????"votes",
????????"modifier":?"log1p"?
??????}
????}
??}}
修飾語modifier 的值可以為:none?(默認(rèn)狀態(tài))、?log?观堂、?log1p?让网、?log2p?、?ln?师痕、?ln1p?溃睹、?ln2p?、?square?胰坟、?sqrt?以及?reciprocal?因篇。想要了解更多信息請參照:?field_value_factor?文檔.
Factor
可以通過將votes?字段與?factor?的積來調(diào)節(jié)受歡迎程度效果的高低:
GET /blogposts/post/_search{
??"query": {
????"function_score": {
??????"query": {
????????"multi_match": {
??????????"query": ???"popularity",
??????????"fields": [ "title", "content" ]
????????}
??????},
??????"field_value_factor": {
????????"field": ???"votes",
????????"modifier": "log1p",
????????"factor": ??2
??????}
????}
??}}
雙倍效果
添加了factor?會使公式變成這樣:
new_score = old_score * log(1 + factor * number_of_votes)
factor?值大于1?會提升效果,?factor?值小于?1?會降低效果,如圖??“受歡迎度的對數(shù)關(guān)系基于多個不同因子”?惜犀。
boost_mode編輯
或許將全文評分與field_value_factor?函數(shù)值乘積的效果仍然可能太大铛碑,我們可以通過參數(shù)boost_mode來控制函數(shù)與查詢評分?_score?合并后的結(jié)果狠裹,參數(shù)接受的值為:
multiply
評分_score?與函數(shù)值的積(默認(rèn))
sum
評分_score?與函數(shù)值的和
min
評分_score?與函數(shù)值間的較小值
max
評分_score?與函數(shù)值間的較大值
replace
函數(shù)值替代評分_score
與使用乘積的方式相比虽界,使用評分_score?與函數(shù)值求和的方式可以弱化最終效果,特別是使用一個較小?factor?因子時:
GET /blogposts/post/_search{
??"query": {
????"function_score": {
??????"query": {
????????"multi_match": {
??????????"query": ???"popularity",
??????????"fields": [ "title", "content" ]
????????}
??????},
??????"field_value_factor": {
????????"field": ???"votes",
????????"modifier": "log1p",
????????"factor": ??0.1
??????},
??????"boost_mode": "sum"
????}
??}}
?將函數(shù)計算結(jié)果值累加到評分?_score?涛菠。
之前請求的公式現(xiàn)在變成下面這樣(參見圖32 “使用?sum?結(jié)合受歡迎程度”?):
new_score = old_score + log(1 + 0.1 * number_of_votes)
max_boost編輯
最后莉御,可以使用max_boost?參數(shù)限制一個函數(shù)的最大效果:
GET /blogposts/post/_search{
??"query": {
????"function_score": {
??????"query": {
????????"multi_match": {
??????????"query": ???"popularity",
??????????"fields": [ "title", "content" ]
????????}
??????},
??????"field_value_factor": {
????????"field": ???"votes",
????????"modifier": "log1p",
????????"factor": ??0.1
??????},
??????"boost_mode": "sum",
??????"max_boost": ?1.5
????}
??}}
無論field_value_factor?函數(shù)的結(jié)果如何,最終結(jié)果都不會大于?1.5?俗冻。max_boost?只對函數(shù)的結(jié)果進(jìn)行限制礁叔,不會對最終評分?_score?產(chǎn)生直接影響。
Decay functions(衰變函數(shù))
其他事項:
1迄薄、數(shù)據(jù)平滑處理——log1p()和exmp1()
在數(shù)據(jù)預(yù)處理時首先可以對偏度比較大的數(shù)據(jù)用log1p函數(shù)進(jìn)行轉(zhuǎn)化琅关,使其更加服從高斯分布,此步處理可能會使我們后續(xù)的分類結(jié)果得到一個更好的結(jié)果讥蔽;
平滑處理很容易被忽略掉涣易,導(dǎo)致模型的結(jié)果總是達(dá)不到一定的標(biāo)準(zhǔn),同樣使用逼格更高的log1p能避免復(fù)值得問題——復(fù)值指一個自變量對應(yīng)多個因變量冶伞;
log1p的使用就像是將一個數(shù)據(jù)壓縮到了一個區(qū)間新症,與數(shù)據(jù)的標(biāo)準(zhǔn)化類似。下面再說說它的逆運算expm1函數(shù)响禽。
由于前面使用過log1p將數(shù)據(jù)進(jìn)行了壓縮徒爹,所以最后需要記得將預(yù)測出的平滑數(shù)據(jù)進(jìn)行一個還原,而還原過程就是log1p的逆運算expm1芋类。
log1p和expm1的功能:
log1p函數(shù)有它存在的意義隆嗅,即保證了x數(shù)據(jù)的有效性,當(dāng)x很小時(如兩個數(shù)值相減后得到)侯繁,由于太小超過數(shù)值有效性胖喳,用計算得到結(jié)果為0,換作log1p則計算得到一個很小卻不為0的結(jié)果巫击,這便是它的意義(好像是用泰勒公式來展開運算的禀晓,不確定)。
同樣的道理對于expm1坝锰,當(dāng)x特別小粹懒,
就會急劇下降出現(xiàn)如上問題,甚至出現(xiàn)錯誤值顷级。
在最開始看到這樣的處理方式的時候凫乖,不是很理解包括為什么是逆運算(一下子沒有想到),后來慢慢摸索就優(yōu)點清晰了,比如為什么兩這是逆運算(簡單處理):
是e為底的對數(shù)帽芽,
是e為底的指數(shù)删掀,根據(jù)對數(shù)的規(guī)則,再進(jìn)行變換推導(dǎo)可以得到: