Elasticsearch自定義文檔得分并排序

大多數(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_score allows 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。
    • modifiernone, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt, reciprocal。默認(rèn)值是 none.
  • decay_functions:衰減函數(shù)的功能與范圍查詢類似,但它具有更平滑的邊緣。衰減函數(shù)支持gausslinear、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ù)的衰減曲線形狀:


Screen Shot 2020-08-16 at 11.46.44.png

以上就是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)注微信公眾號:一只慵懶的程序猿。

一只慵懶的程序猿.jpg

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容