大多數(shù)情況下,我們需要對查詢結(jié)果排序,比方說按最新時間降序、按金額降序等。我們只需要對相應(yīng)的字段 sort 即可。但有時候也會出現(xiàn)一些復(fù)雜的情況,比方說有A、B、C、D、E類數(shù)據(jù),他想讓你給這類數(shù)據(jù)重新定義優(yōu)先級,按照B、E、D、A、C的順序展示,并且每類數(shù)據(jù)內(nèi)部按時間降序。然而最近我們也提出了一個類似這樣的需求,查閱相關(guān)文檔后,發(fā)現(xiàn)Elasticsearch里的function_socre函數(shù)可以實現(xiàn)這一功能, 遂將此學(xué)習(xí)內(nèi)容做一個記錄。
先來看看function_score是什么,它能做什么?根據(jù)官網(wǎng)的原話:
The
function_scoreallows you to modify the score of documents that are retrieved by a query.
function_score允許你修改通過查詢檢索出來的文檔的得分。
下面我們通過一些簡單的例子來看看function_score怎么使用。
function_score可以為所有文檔生成一個隨機分?jǐn)?shù):
GET us_police_shooting_index/_search
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"random_score": {},
"boost": 5,
"boost_mode": "min"
}
}
}
還可以組合不同的過濾條件,設(shè)置權(quán)重:
GET us_police_shooting_index/_search
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"functions": [
{
"filter": {
"match": {
"race": "Asian"
}
},
"weight": 8
},
{
"filter": {
"match": {
"race": "White"
}
},
"weight": 2
}
],
"max_boost": 20,
"score_mode": "max",
"boost_mode": "sum",
"min_score": 3
}
}
}
如果
functions里的filter未給出,那么將會匹配所有文檔,相當(dāng)于"match_all":{}。
我們看看function_score為我們提供了哪些參數(shù):
-
score_mode指定了該如何去合并每個文檔生成的評分:
score_mode |
定義 |
|---|---|
multiply |
函數(shù)結(jié)果相乘(默認(rèn)) |
sum |
函數(shù)結(jié)果相加 |
avg |
函數(shù)結(jié)果的平均值 |
first |
使用首個函數(shù)的結(jié)果做為最終結(jié)果 |
max |
函數(shù)結(jié)果的最大值 |
min |
函數(shù)結(jié)果的最小值 |
-
boost_mode可以用來控制函數(shù)與查詢評分_score合并后的結(jié)果:
boost_mode |
定義 |
|---|---|
multiply |
評分_score與函數(shù)值的乘積(默認(rèn)) |
replace |
評分_score會被忽略,僅使用函數(shù)值 |
sum |
評分_score與函數(shù)值之和 |
avg |
評分_score與函數(shù)值的平均值 |
max |
評分_score與函數(shù)值間的最大值 |
min |
評分_score與函數(shù)值間的最小值 |
-
min_score可以設(shè)置為期望分?jǐn)?shù)的閾值,能夠排出不符合特定分?jǐn)?shù)閾值的文檔。 -
max_boost可以限制函數(shù)的最大效果,但是不會對最終的評分_score產(chǎn)生直接的影響。
function_score還提供幾種類型的評分函數(shù):
-
script_score:腳本評分函數(shù)允許計算自定義查詢的評分,腳本表達(dá)式需使用文檔中的數(shù)值字段。查詢的分?jǐn)?shù)將與腳本評分的結(jié)果相乘,如果不想使用這種方式,可通過設(shè)置"boost_mode":"replace"來禁止。GET /_search { "query": { "function_score": { "query": { "match": { "message": "elasticsearch" } }, "script_score": { "script": { "source": "Math.log(2 + doc['likes'].value)" } } } } }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)" } } } } } weight:權(quán)重函數(shù)可以將評分與weight值相乘,weight的值是float類型。-
random_score:隨機評分函數(shù)會產(chǎn)生一個0到1之間的分?jǐn)?shù),當(dāng)種子feed值相同時,生成的隨機結(jié)果是一致的。GET /_search { "query": { "function_score": { "random_score": { "seed": 10, "field": "_seq_no" } } } } -
field_value_factor:通過使用文檔中的某個字段來影響評分。如果這個字段有多個值,那么只有第一個值才被用來計算評分。GET /_search { "query": { "function_score": { "field_value_factor": { "field": "likes", "factor": 1.2, "modifier": "sqrt", "missing": 1 } } } } # 上面評分的計算相當(dāng)于 sqrt(1.2 * doc['likes'].value)-
filed:文檔中提取的字段。 -
factor:字段值乘以的可選因子,默認(rèn)是1。 -
modifier:none,log,log1p,log2p,ln,ln1p,ln2p,square,sqrt,reciprocal。默認(rèn)值是none.
-
-
decay_functions:衰減函數(shù)的功能與范圍查詢類似,但它具有更平滑的邊緣。衰減函數(shù)支持gauss、linear、exp中任意一種函數(shù),并且都能接收以下參數(shù):-
origin:中心點或字段可能的最佳值,落在原點origin上的文檔評分_score為滿分1.0。字段必須是數(shù)值、日期或地理坐標(biāo)類型。 -
scale:衰減率,一個文檔從原點origin下落時,評分_score改變的速度。 -
offset:以原點origin為中心點,為其設(shè)置一個非零的offset覆蓋一個范圍,而不只是單個原點。在范圍-offset <= origin <= +offset內(nèi)的所有評分_score都是1.0。 -
decay:從原點origin衰減到scale所得的評分_score,默認(rèn)是0.5。
GET /_search { "query": { "function_score": { "functions": [ { "gauss": { "price": { "origin": "0", "scale": "20" } } }, { "gauss": { "location": { "origin": "11, 12", "scale": "2km" } } } ], "query": { "match": { "properties": "balcony" } }, "score_mode": "multiply" } } } -
官方文檔有這么一張圖片說明了三個函數(shù)的衰減曲線形狀:

以上就是function_score函數(shù)的大部分內(nèi)容?,F(xiàn)在我們來具體實現(xiàn)文章開頭提到的一個需求。我準(zhǔn)備了一份不同歌手的歌曲發(fā)行時間的測試數(shù)據(jù),主要字段有歌手名name、歌曲名song、發(fā)行時間publishDate。
首先我們先按歌手名降序,發(fā)行時間升序,很容易能寫出下面的DSL語句:
GET music_index/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"name.keyword": {
"order": "desc"
},
"publishDate.keyword": {
"order": "asc"
}
}
]
}
現(xiàn)在我想按許冠杰、鄧麗君、陳百強的順序進(jìn)行展示,并且各自的歌曲按發(fā)行時間升序,function_score就體現(xiàn)出它的作用了:
GET music_index/_search
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"functions": [
{
"filter": {
"term": {
"name.keyword": "許冠杰"
}
},
"weight": 5
},
{
"filter": {
"term": {
"name.keyword": "鄧麗君"
}
},
"weight": 4
},
{
"filter": {
"term": {
"name.keyword": "陳百強"
}
},
"weight": 3
}
]
}
},
"sort": [
{
"_score": {
"order": "desc"
},
"publishDate.keyword": {
"order": "asc"
}
}
]
}
由于查詢文檔太長,我就不粘貼查詢結(jié)果了。感興趣的可以自己動手嘗試嘗試,如果需要測試數(shù)據(jù),公眾號回復(fù)0816即可獲取相關(guān)文件。
相關(guān)鏈接:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/function-score-query.html
獲取最新文章,可關(guān)注博客地址:https://jenkinwang.github.io/,或掃碼關(guān)注微信公眾號:一只慵懶的程序猿。
