ElasticSearch DSL 查詢
阿新 • • 發佈:2021-02-23
> **公號:碼農充電站pro**
> **主頁:**
**DSL**(Domain Specific Language)查詢也叫做 **Request Body** 查詢,它比 **URI 查詢**更高階,能支援更復雜的查詢。
### 1,分頁
預設情況下,查詢按照算分排序,返回前 10 條記錄。
ES 也支援分頁,分頁使用 [from-size](https://www.elastic.co/guide/en/elasticsearch/reference/7.0/search-request-from-size.html):
- **from**:從第幾個文件開始返回,預設為 0。
- **size**:返回的文件數,預設為 10。
示例:
```shell
POST /index_name/_search
{
"from":10,
"size":20,
"query":{
"match_all": {}
}
}
```
#### 1.1,深度分頁問題
ES 是一個分散式系統,資料儲存在多個分片中,那麼查詢時就需要查詢多個分片。
比如一個查詢 `from = 990; size = 10`,那麼 ES 需要在每個分片上都獲取 1000 個文件:
![](https://img-blog.csdnimg.cn/20210120232414669.png)
然後通過 **Coordinating** 節點彙總結果,最後再通過排序獲取前 1000 個文件。
這種方式,當頁數很深的時候,就會佔用很多記憶體,從而給 ES 叢集帶來很大的開銷,這就是**深度分頁問題**。
因此,ES 為了避免此類問題帶來的巨大開銷,有個預設的限制 [index.max_result_window](https://www.elastic.co/guide/en/elasticsearch/reference/7.10/index-modules.html),`from + size` 必須小於等於 10000,否則就會**報錯**。
比如:
```shell
POST index_name/_search
{
"from": 10000, # 報錯
"size": 1,
"query": {
"match_all": {}
}
}
POST index_name/_search
{
"from": 0, # 報錯
"size": 10001,
"query": {
"match_all": {}
}
}
```
為了解決深度分頁問題,ES 有兩種解決方案:**Search After** 和 **Scroll**。
#### 1.2,Search After
[Search After](https://www.elastic.co/guide/en/elasticsearch/reference/7.10/paginate-search-results.html#search-after) 通過實時獲取下一頁的文件資訊來實現,使用方法:
- 第一步搜尋需要指定 **sort**,並且保證值是唯一的(通過sort by id 來保證)。
- 隨後的搜尋,都使用上一次搜尋的最後一個文件的 sort 值進行搜尋。
**Search After** 的方式不支援指定頁數,只能一頁一頁的往下翻。
**Search After** 的原理:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210121000008616.png)
示例:
```shell
# 插入一些資料
DELETE users
POST users/_doc
{"name":"user1","age":10}
POST users/_doc
{"name":"user2","age":11}
POST users/_doc
{"name":"user2","age":12}
POST users/_doc
{"name":"user2","age":13}
# 第一次搜尋
POST users/_search
{
"size": 1, # size 值
"query": {
"match_all": {}
},
"sort": [
{"age": "desc"} ,
{"_id": "asc"} # sort by id
]
}
# 此時返回的文件中有一個 sort 值
# "sort" : [13, "4dR-IHcB71-f4JZcrL2z"]
# 之後的每一次搜尋都需要用到上一次搜尋結果的最後一個文件的 sort 值
POST users/_search
{
"size": 1,
"query": {
"match_all": {}
},
"search_after": [ # 上一次搜尋結果的最後一個文件的 sort 值放在這裡
13, "4dR-IHcB71-f4JZcrL2z"],
"sort": [
{"age": "desc"} ,
{"_id": "asc"}
]
}
```
#### 1.3,Scroll
[Scroll](https://www.elastic.co/guide/en/elasticsearch/reference/7.10/paginate-search-results.html#scroll-search-results) 通過建立一個**快照**來實現,方法:
- 每次查詢時,輸入上一次的 `Scroll Id`。
**Scroll** 方式的缺點是,當有新的資料寫入時,**新寫入的資料無法被查到**(第一次建立快照時有多少資料,就只能查到多少資料)。
示例:
```shell
# 寫入測試資料
DELETE users
POST users/_doc
{"name":"user1","age":10}
POST users/_doc
{"name":"user2","age":20}
# 第一次查詢前,先建立快照,快照存在時間為 5 分鐘,一般不要太長
POST /users/_search?scroll=5m
{
"size": 1,
"query": {
"match_all" : {}
}
}
# 返回的結果中會有一個 _scroll_id
# 查詢
POST /_search/scroll
{
"scroll" : "1m", # 快照的生存時間,這裡是 1 分鐘
"scroll_id" : "xxx==" # 上一次的 _scroll_id 值
}
# 每次的查詢結果都會返回一個 _scroll_id,供下一次查詢使用
# 所有的資料被查完以後,再查詢就得不到資料了
```
#### 1.4,不同分頁方式的使用場景
分頁方式共 4 種:
- 普通查詢(不使用分頁):需要實時獲取頂部的部分文件。
- From-Size(普通分頁):適用於非深度分頁。
- Search After:需要深度分頁時使用。
- Scroll:需要全部文件,比如匯出全部資料。
### 2,排序
ES 預設使用算分進行排序,我們可以使用 [sort-processor](https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-processor.html)(不需要再計算算分)來指定排序規則;可以對某個欄位進行排序,最好只對**數字型**和**日期型**欄位排序。
示例:
```shell
POST /index_name/_search
{
"sort":[{"order_date":"desc"}], # 單欄位排序
"query":{
"match_all": {}
}
}
POST /index_name/_search
{
"query": {
"match_all": {}
},
"sort": [ # 多欄位排序
{"order_date": {"order": "desc"}},
{"_doc":{"order": "asc"}},
{"_score":{ "order": "desc"}} # 如果不指定 _score,那麼算分為 null
]
}
```
對 **text** 型別的資料進行排序會發生錯誤,可以通過開啟 fielddata 引數(一般**不建議這麼做**),來對 **text** 型別進行排序:
```shell
# 開啟 text的 fielddata
PUT index_name/_mapping
{
"properties": {
"customer_full_name" : { # 欄位名稱
"type" : "text",
"fielddata": true, # 開啟 fielddata
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
```
### 3,欄位過濾
可以使用 `_source` 設定需要返回哪些欄位。示例:
```shell
POST /index_name/_search
{
"_source":["order_date", "xxxxx"],
"query":{
"match_all": {}
}
}
```
`_source` 中可以使用萬用字元,比如 `["name*", "abc*"]`。
### 4,指令碼欄位
可以使用指令碼進行簡單的表示式運算。
```shell
POST /index_name/_search
{
"script_fields": { # 固定寫法
"new_field": { # 新的欄位名稱
"script": { # 固定寫法
"lang": "painless", # 固定寫法
"source": "doc['order_date'].value+'hello'" # 指令碼語句
}
}
},
"query": {
"match_all": {}
}
}
```
### 5,查詢與過濾
查詢會有相關性算分;過濾不需要進行算分,可以利用快取,效能更好。
參考[這裡](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html)。
### 6,全文字查詢
[全文字](https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html)(Full text)查詢會對搜尋字串進行**分詞處理**。
全文字查詢有以下 9 種:
1. [intervals 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-intervals-query.html):可以對匹配項的順序和接近度進行細粒度控制。
2. [match 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html):全文字查詢中的標準查詢,包括模糊匹配、短語和近似查詢。
3. [match_bool_prefix 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-bool-prefix-query.html):
4. [match_phrase 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query-phrase.html):
5. [match_phrase_prefix 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query-phrase-prefix.html):
6. [multi_match 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html):
7. [common terms 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-common-terms-query.html):
8. [query_string 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html):
9. [simple_query_string 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-simple-query-string-query.html):
#### 6.1,Match 查詢
**Match** 查詢是全文搜尋的標準查詢,與下面的幾種查詢相比,更加強大,靈活性也更大,最常使用。
[Match 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html)會先對輸入字串進行分詞,然後對每個詞項進行底層查詢,最後將結果合併。
例如對字串 "**Matrix reloaded**" 進行查詢,會查到包含 "Matrix" 或者 "reloaded" 的所有結果。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210117172328528.png?)
示例:
```shell
POST index_name/_search
{
"query": {
"match": {
"title": "last christmas" # 表示包含 last 或 christmas
}
}
}
POST index_name/_search
{
"query": {
"match": {
"title": { # 表示包含 last 且 包含 christmas,不一定挨著
"query": "last christmas",
"operator": "and"
}
}
}
}
```
#### 6.2,Match Phrase 查詢
使用 [match_phrase](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query-phrase.html) 關鍵字。示例:
```shell
POST index_name/_search
{
"query": {
"match_phrase": {
"title":{
"query": "one love" # "one love" 相當於一個單詞
}
}
}
}
POST index_name/_search
{
"query": {
"match_phrase": {
"title":{
"query": "one love",
"slop": 1 # "one" 和 "love" 之間可以有 1 個字元
}
}
}
}
```
#### 6.3,Query String 查詢
使用 [query_string](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html) 關鍵字。示例:
```shell
POST index_name/_search
{
"query": {
"query_string": {
"default_field": "name", # 預設查詢欄位,相當於 URI 查詢中的 df
"query": "Ruan AND Yiming" # 可以使用邏輯運算子
}
}
}
# 多 fields 與 分組
POST index_name/_search
{
"query": {
"query_string": {
"fields":["name","about"], # 多個 fields
"query": "(Ruan AND Yiming) OR (Java AND Elasticsearch)" # 支援分組
}
}
}
POST index_name/_search
{
"query":{
"query_string":{
"fields":["title","year"],
"query": "2012"
}
}
}
```
#### 6.4,Simple Query String 查詢
使用 [simple_query_string](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-simple-query-string-query.html) 關鍵字。
特點:
- **query** 欄位中不支援 `AND OR NOT`,會當成普通字串。
- `AND` 用 `+` 替代
- `OR` 用 `|` 替代
- `NOT` 用 `-` 替代
- Term 之間預設的關係是 `OR`,可以指定 `default_operator` 來修改。
示例:
```shell
# Simple Query 預設的 operator 是 OR
POST index_name/_search
{
"query": {
"simple_query_string": {
"query": "Ruan AND Yiming", # 這裡的 AND 會當成普通的字串
"fields": ["name"]
}
}
}
POST index_name/_search
{
"query": {
"simple_query_string": {
"query": "Ruan Yiming",
"fields": ["name"],
"default_operator": "AND"
}
}
}
GET index_name/_search
{
"query":{
"simple_query_string":{
"query":"Beautiful +mind",
"fields":["title"]
}
}
}
```
#### 6.5,Multi-match 查詢
一個字串在多個欄位中查詢的情況,如何匹配最終的結果。(還有一個 **dis-max** 查詢也是針對這種情況的)
一個字串在多個欄位中查詢的情況,Multi-match 有 6 種處理方式,如下:
- **best_fields**:最終得分為**分數最高**的那個欄位,預設的處理方式。
- **most_fields**:算分相加。不支援 **AND** 操作。
- **cross_fields**:跨欄位搜尋,將一個查詢字串在**多個欄位**(就像一個欄位)上搜索。
- **phrase**:
- **phrase_prefix**:
- **bool_prefix**:
示例:
```shell
POST index_name/_search
{
"query": {
"multi_match" : { # multi_match 查詢
"query": "brown fox", # 查詢字串
"type": "best_fields", # 處理方式
"fields": [ "subject", "message" ], # 在多個欄位中查詢,fields 是一個數組
"tie_breaker": 0.3
}
}
}
```
### 7,Term 查詢
[Term 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/term-level-queries.html)與**全文字**查詢不同的是,Term 查詢不會對查詢字串進行分詞處理,Term 查詢會在欄位匹配**精確值**。
Term 查詢輸入字串作為一個整體,在倒排索引中查詢匹配的詞項,並且會計算**相關性評分**。
**Term 查詢**包括以下 11 種:
1. [exists 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html)
2. [fuzzy 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-fuzzy-query.html)
3. [ids 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ids-query.html)
4. [prefix 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-prefix-query.html)
5. [range 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html)
6. [regexp 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html)
7. [term 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html):如果某個文件的指定欄位包含某個**確切值**,則返回該文件。
8. [terms 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html)
9. [terms_set 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-set-query.html)
10. [type 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-type-query.html)
11. [wildcard 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-wildcard-query.html)
#### 7.0,結構化資料與查詢
結構化查詢是對**結構化資料**的查詢,可以使用 **Term 語句**進行查詢。
結構化資料有著**固定的格式**,包括:
- 日期:日期比較,日期範圍運算等。
- 布林值:邏輯運算。
- 數字:數字大小比較,範圍比較等。
- 某些文字資料:比如標籤資料,關鍵詞等。
結構化查詢是對結構化資料的邏輯運算,運算結果只有“是”和“否”。
#### 7.1,term 查詢
如果某個文件的指定欄位**包含**某個**確切值**,則返回該文件。
##### 1,示例 1 精確匹配
下面舉一個 term 查詢的例子,首先插入一個文件:
```shell
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "productID" : "XHDK-A-1293-#fJ3","desc":"iPhone" }
```
該文件插入時,會使用**預設的分詞器**進行分詞處理。
使用 term 查詢:
```shell
POST /products/_search
{
"query": {
"term": {
"desc": {
# "value": "iPhone" # 會對 iPhone 精確匹配查詢。
# 文件插入時,iPhone 變成了 iphone
# 所以查 iPhone 查不到任何內容
"value":"iphone" # 查 iphone 能查到
}
}
}
}
```
***keyword 子欄位***
ES 預設會對 **text** 型別的資料建立一個 **keyword** 子欄位,用於精確匹配,這稱為 ES 的**多欄位屬性**。
**keyword 子欄位**將原始資料原封不動的儲存了下來。
可以通過 **mapping** 檢視,如下所示:
```shell
"desc" : { # 欄位名稱
"type" : "text", # text 資料型別
"fields" : {
"keyword" : { # keyword 子欄位
"type" : "keyword", # keyword 子型別
"ignore_above" : 256
}
}
}
```
下面使用 **keyword** 子欄位進行查詢:
```shell
POST /products/_search
{
"query": {
"term": {
"desc.keyword": { # 在 desc 欄位的 keyword 子欄位中查詢
"value": "iPhone" # 能查到
//"value":"iphone" # 查不到
}
}
}
}
```
##### 2,示例 2 查詢布林值
**term 查詢**有算分:
```shell
POST index_name/_search
{
"query": { # 固定寫法
"term": { # term 查詢,固定寫法
"avaliable": true # 查詢 avaliable 欄位的值為 true 的文件
}
}
}
```
如果不需要算分,可以使用 [constant_score 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html),示例:
```shell
POST index_name/_search
{
"query": {
"constant_score": { # constant_score 查詢,固定寫法
"filter": { # 固定寫法
"term": { # constant_score 包裝一個 term 查詢,就沒有了算分
"avaliable": true
}
}
}
}
}
```
#### 7.2,range 查詢
[range 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html)中有幾個常用的比較運算:
| 運算子 | 含義 |
|--|--|
| gt | 大於 |
| gte | 大於等於 |
| lt | 小於 |
| lte | 小於等於 |
##### 1,數字型別 range 查詢
示例:
```shell
POST index_name/_search
{
"query": { # 固定寫法
"range": { # range 查詢
"age": { # 欄位名稱
"gte": 10, # 10 <= age <= 20
"lte": 20
}
}
}
}
```
##### 2,日期型別 range 查詢
對於日期型別有幾個常用的符號:
| 符號 | 含義 |
|--|--|
| y | 年 |
| M | 月 |
| w | 周 |
| d | 天 |
| H / h | 小時 |
| m | 分鐘 |
| s | 秒 |
| now | 現在 |
示例:
```shell
POST index_name/_search
{
"query" : { # 固定寫法
"range" : { # range 查詢
"date" : { # 欄位名稱
"gte" : "now-10y" # 10年之前
}
}
}
}
```
#### 7.3,exists 查詢
**exists 語句**可以判斷文件是否存在**某個欄位**。
搜尋存在某個欄位的文件,示例:
```shell
POST index_name/_search
{
"query" : {
"exists": { # 存在 date 欄位的文件
"field": "date"
}
}
}
```
搜尋不存在某個欄位的文件,需要使用[布林查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html)。
示例:
```shell
POST index_name/_search
{
"query": {
"bool": { # 布林查詢
"must_not": { # 不存在
"exists": { # 不存在 date 欄位的文件
"field": "date"
}
}
}
}
}
```
#### 7.4,terms 查詢
**terms 語句**用於處理多值查詢,相當於一個多值版的 **term 語句**,可以一次查詢多個值。
示例:
```shell
POST index_name/_search
{
"query": {
"terms": { # terms 查詢
"productID.keyword": [ # 欄位名稱
"QQPX-R-3956-#aD8", # 多個值
"JODL-X-1937-#pV7"
]
}
}
}
```
### 8,複合查詢
[複合查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/compound-queries.html)(Compound)能夠包裝其他**複合查詢**或**葉查詢**,以組合其結果和分數,更改其行為或者將查詢轉成**過濾**。
複合查詢有以下 5 種:
- [bool 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html)
- [boosting 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-boosting-query.html)
- [constant_score 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html)
- [dis_max 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-dis-max-query.html)
- [function_score 查詢](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html)
#### 8.1,bool 查詢
**bool 查詢**是一個或**多個子查詢**的組合,共包含以下 4 種子句:
- **must**:必須匹配,**屬於查詢**,貢獻算分。
- **filter**:必須匹配,**屬於過濾器**,不貢獻算分。
- **should**:選擇性匹配,只要有一個條件匹配即可,**屬於查詢**,貢獻算分。
- **must_not**:必須不匹配,**屬於過濾器**,不貢獻算分。
**bool 查詢**的多個子句之間**沒有順序之分**,並且**可以巢狀**。
示例:
```shell
POST index_name/_search
{
"query": {
"bool" : {
"must" : {
"term" : { "user.id" : "kimchy" }
},
"filter": {
"term" : { "tags" : "production" }
},
"must_not" : {
"range" : {
"age" : { "gte" : 10, "lte" : 20 }
}
},
"should" : [ # 是一個數組
{ "term" : { "tags" : "env1" } },
{ "term" : { "tags" : "deployed" } }
],
"minimum_should_match" : 1,
"boost" : 1.0
}
}
}
```
#### 8.2,boosting 查詢
**boosting 查詢**會給不同的查詢條件分配不同的級別(**positive / negative**),不同的級別對算分有著不同的印象,從而影響最終的算分。
**positive** 級別會對算分有**正面影響**, **negative** 級別會對算分有**負面影響**。
我們可以使用 **boosting 查詢**給某些文件**降級**(降低算分),而不是將其從搜尋結果中**排除**。
示例:
```shell
GET index_name/_search
{
"query": {
"boosting": { # boosting 查詢
"positive": { # positive 級別
"term": { # 匹配 apple 的會對算分有正面影響
"text": "apple"
}
},
"negative": { # negative 級別
"term": { # 匹配這個的會對算分有負面影響
"text": "pie tart fruit crumble tree"
}
},
"negative_boost": 0.5 # 降級的力度
}
}
}
```
#### 8.3,constant_score 查詢
**constant_score 查詢**可以將查詢轉成一個**過濾**,可以避免**算分**(降低開銷),並有效利用快取(提高效能)。
示例:
```shell
POST /index_name/_search
{
"query": {
"constant_score": { # constant_score 查詢
"filter": { # 過濾器,固定寫法
"term": { # 包裝了一個 term 查詢,將 term 查詢轉成了過濾
"productID.keyword": "XHDK-A-1293-#fJ3"
}
}
}
}
}
```
#### 8.4,dis_max 查詢
一個字串在多個欄位中查詢的情況,如何匹配最終的結果。(還有一個 **Multi-match** 查詢也是針對這種情況的)
示例:
```shell
POST index_name/_search
{
"query": {
"bool": {
"should": [ # should 語句會綜合所有的欄位的分數,最終給出一個綜合分數
{ "match": { "title": "Brown fox" }},
{ "match": { "body": "Brown fox" }}
]
}
}
}
POST index_name/_search
{
"query": {
"dis_max": { # dis_max 語句不會綜合所有欄位的分數,而把每個欄位單獨來看
"queries": [ # 最終結果是所有的欄位中分數最高的
{ "match": { "title": "Quick pets" }},
{ "match": { "body": "Quick pets" }}
]
}
}
}
```
#### 8.5,function_score 查詢
**function_score 查詢**可以在查詢結束後,對每一個匹配的文件進行**重新算分**,然後再根據新的算分進行排序。
它提供了以下 5 種算分函式:
- **script_score**:自定義指令碼。
- **weight**:為文件設定一個權重。
- **random_score**:隨機算分排序。
- **field_value_factor**:使用該數值來修改算分。
- **decay functions**: gauss, linear, exp:以某個欄位為標準,距離某個值越近,得分越高。
##### 1,field_value_factor 示例
首先插入測試資料:
```shell
DELETE blogs
PUT /blogs/_doc/1
{
"title": "About popularity",
"content": "In this post we will talk about...",
"votes": 0
}
PUT /blogs/_doc/2
{
"title": "About popularity",
"content": "In this post we will talk about...",
"votes": 100
}
PUT /blogs/_doc/3
{
"title": "About popularity",
"content": "In this post we will talk about...",
"votes": 1000000
}
```
查詢示例1:
**新的算分 = 老的算分 * 投票數**
```shell
POST /blogs/_search
{
"query": {
"function_score": {
"query": {
"multi_match": { # 該查詢會有一個算分
"query": "popularity",
"fields": [ "title", "content" ]
}
},
"field_value_factor": { # 最終的算分要乘以 votes 欄位的值
"field": "votes"
}
}
}
}
```
上面這種演算法當出現這兩種情況的時候,會出現問題:
- 投票數為 0
- 投票數特別大
查詢示例2,引入**平滑函式**:
**新的算分 = 老的算分 * 平滑函式(投票數)**
```shell
POST /blogs/_search
{
"query": {
"function_score": {
"query": {
"multi_match": {
"query": "popularity",
"fields": [ "title", "content" ]
}
},
"field_value_factor": {
"field": "votes",
"modifier": "log1p" # 在原來的基礎上加了一個平滑函式
} # 新的算分 = 老的算分 * log(1 + 投票數)
}
}
}
```
**平滑函式**有下面這些:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210119104333757.png)
查詢示例3,引入 **factor** :
**新的算分 = 老的算分 * 平滑函式(factor * 投票數)**
```shell
POST /blogs/_search
{
"query": {
"function_score": {
"query": {
"multi_match": {
"query": "popularity",
"fields": [ "title", "content" ]
}
},
"field_value_factor": {
"field": "votes",
"modifier": "log1p" ,
"factor": 0.1
}
}
}
}
```
引入 **factor** 之後的算分曲線:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210119104829380.png)
##### 2,Boost Mode 和 Max Boost 引數
Boost Mode:
- Multiply:算分與函式值的乘積。
- Sum:算分與函式值的和。
- Min / Max:算分與函式值的最小/最大值。
- Replace:使用函式值替代算分。
Max Boost 可以將算分控制在一個最大值。
示例:
```shell
POST /blogs/_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": 3
}
}
}
```
##### 3,random_score 示例
示例:
```shell
POST /blogs/_search
{
"query": {
"function_score": {
"random_score": { # 將原來的查詢結果隨機排序
"seed": 911119 # 隨機種子
}
}
}
}
```
(本節完。)
---
**推薦閱讀:**
[ElasticSearch 查詢](https://www.cnblogs.com/codeshell/p/14389415.html)
[ElasticSearch URI 查詢](https://www.cnblogs.com/codeshell/p/14429420.html)
[ElasticSearch 文件及操作](https://www.cnblogs.com/codeshell/p/14429409.html)
[ElasticSearch 分詞器](https://www.cnblogs.com/codeshell/p/14389403.html)
[ElasticSearch 搜尋引擎概念簡介](https://www.cnblogs.com/codeshell/p/14389383.html)
---
*歡迎關注作者公眾號,獲取更多技術乾貨。*
![碼農充電站pro](https://img-blog.csdnimg.cn/20200505082843773.png?#pic