iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
Software Development

由淺入深來探討Elasticsearch,從基礎語法到底層相關原理系列 第 19

【Day 19】由淺入深來探討Elasticsearch - Compound queries

  • 分享至 

  • xImage
  •  

Compound queries可以包裝其他的複合查詢以及leaf queries
讓我們可以在查詢時進行更多的邏輯判斷與結果篩選
包含以下幾種類型:

  • boolean query:
  • boosting query
  • constant_score query
  • dis_max query
  • function_score query

Boolean Query
主要由幾種參數來進行判斷:

  • must and must_not:其中must代表文檔必須匹配must內的query,而must_not則反之
    • must會影響相關分數,而must_not則是忽略相關分數,因為其透過filter context進行文檔過濾
    • filter context用來過濾簡單的yes, no問題,並且會進行快取,增進查詢表現
  • filter:也是代表文檔需匹配查詢內容
    • 與must不同的是不會影響相關分數,跟must_not一樣是在filter context執行
    • 所以如果不在意排名,單純想要過濾結果,可以用filter,並且因為filter context機制表現會更好
  • should:不一定需要匹配,但是如果文檔有符合條件,相關分數會更高
    • 如果有用should,至少要有一個must或是must_not
    • 可以使用minimum_should_match來調整結果輸出。例如我的should裡面放了多個加分條件,我又希望查詢的結果至少能符合其中部分幾種時可設置
GET /index_name/_search
{
  "query": {
    "bool" : {
      "must" : {
        "field_name" : "value"
      },
      "filter": {
        "field_name" : "value"
      },
      "must_not" : {
        "range" : {
          "create_time" : { "gte" : "2021", "lte" : "2023" }
        }
      },
      "should" : [
        { "term" : { "field_name" : "value" } }
      ],
      "minimum_should_match" : 1
    }
  }
}

Boosting Query

  • 在boolean查詢中should可以為分數加權,而想降低的話可以用boosting query
  • boosting查詢中包含positive query與negative query
    • positive query:必須項,代表結果需匹配結果,相當於must
    • negative query:必須項,如果匹配到會降低相關分數,會將positive匹配到的分數再進行加權
      • 由negative_boost控制加權值
      • 如果negative_boost設0.5,ES會先將其normalized後,再*positive算出來的分數
GET /index_name/_search
{
  "query": {
    "boosting": {
      "positive": {
        "must": [
					"match_all": {}
				]
      },
      "negative": {
        "term": {
          "text": ""
        }
      },
      "negative_boost": 0.5
    }
  }
}

組合拳: 如果我今天想要title欄位出現有關咖啡的字樣,但是我更希望能出現有關淺焙的品項,最好避開深焙的豆子

GET /bool_and_boost/_search
{
  "query": {
    "boosting": {
      "positive": {
        "bool": {
          "must": [ // 標題要包含coffee
            {
              "match": {
                "title": "coffee"
              }
            }
          ],
          "should": [ // 如果是淺焙會加分
            {
              "term": {
                "category": "light"
              }
            }
          ]
        }
      },
      "negative": {  // 如果是深焙會扣分
        "match": {
          "category": "dark"
        }
      },
      "negative_boost": 0.5
    }
  }
}

Constant_score query

  • 前面提到filter不影響相關分數,而constant_score則是將符合filter條件的相關分數返回boost設定的數字(默認為1)
GET /_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": { "user.id": "kimchy" }
      },
      "boost": 1.2
    }
  }
}

Dis_max query

GET /dis_max/_search
{
  "query": {
    "dis_max": {
      "tie_breaker": 0.7,
      "queries": [
        {
          "match": {
            "FIELD_1": "TEXT"
          }
        },
        {
          "match": {
            "FIELD_2": "TEXT"
          }
        }
      ]
    }
  }
}
  • 在沒有設置tie_breaker情形下,如果match field_1獲得最高的相關分數,即使field_2也有匹配,也會被忽略相關分數
  • 設製tie_breaker後會跟先前提到multi_match一樣,會為其他匹配子查詢的相關分數加權並納入計算

Function score query

  • 最主要的目的就是根據個人需求,可以調整計算相關分數的計算方式,影響輸出結果
  • 最基礎的寫法如下
GET /_search
{
  "query": {
    "function_score": {
      "query": { "match_all": {} }, //查詢語句,提供原始分數
      "boost": "5", // 提供基礎分數 
      "random_score": {}, // 我們要另外計算的方式 
      "boost_mode": "multiply" // 代表拿到原始分數與額外計算分數後,最終分數的處理方式
    }
  }
}
  • boost_mode:決定原始分數與額外計算分數如何合併
    • multiply(默認): 兩著相乘
    • sum:兩者相加
    • min:取其中較小的值
    • max:取其中較大的值
    • replace:額外計算分數直接取代原始分數
  • 提供幾種不同的額外計算方法
    • script_score
    • weight
    • random_score
    • field_value_factor
    • decay functions
  • 如果有多種function一起處理,設置score_mode處理每個方法算出來的分數取得最終額外計算分數

Script_score

  • 可以自己寫腳本自定義評分計算
  • 用腳本意味效率差,可以避免使用
  • 如果算的分數是負數會返回錯誤
GET /_search
{
  "query": {
    "function_score": {
      "query": {
        "match": { "message": "elasticsearch" }
      },
      "script_score": {
        "script": {
          "source": "Math.log(2 + doc['my-int'].value)"
        }
      }
    }
  }
}

Weight

  • 跟boost查詢很像,會對查詢結果的相關分數*weight
  • 但是boost的分數會被normalized,weight會直接應用
"weight" : number

Random_score:

  • 根據來源提供0-1不等的數字,當作匹配文檔的_score
  • 適合使用在隨機抽樣的情境下
  • 因為預設使用內部Lucene doc ids來當作計算來源,因此如果遇到segment file merge等情形,會導致計算的分數無法重現
    • 可以設置seed與field來讓結果能重現
    • seed代表讓最終分數在seed的基礎上進行計算
    • _seq_no是不錯的field選項,除非有進行update不然不太會變
    • field沒有設置的話預設用_id,但是會導致消耗過多資源
GET /_search
{
  "query": {
    "function_score": {
      "random_score": {
        "seed": 10,
        "field": "_seq_no"
      }
    }
  }
}

Field_value_factor

  • 可以使用document中的特定欄位去改變相關分數
  • 跟script_score概念很像,但是避免使用腳本帶來的消耗
  • 範例如下:
GET /_search
{
  "query": {
    "function_score": {
      "field_value_factor": {
        "field": "my-int", // 選取作為額外計算的欄位
        "factor": 1.2, // 預設為1,會*上面的field
        "modifier": "sqrt", // 會將上面經由factor算完的值再進行處理,這邊是算平方根
        "missing": 1 // 如果field沒有值,就拿來當作field的值
      }
    }
  }
}

Decay functions

  • 我們提供一個數字類型(numeric, date or geo_pint)的欄位,根據某個基準值來判斷衰減程度,進行評分的標準
  • 例如我們可以設置一個中心地理位置,距離越遠的地理位置文檔,會*衰減參數取得較低的相關分數
  • 有幾種參數可以設置
    • origin:我們需要提供origin欄位,用來定義計算距離用的基準點
    • offset:讓衰減函式僅計算距離大於offset的文檔
    • scale:定義衰減的比例。origin跟offset計算完後,scale再添加上去
    • decay:為衰減函式的參數
GET /_search
{
  "query": {
    "function_score": {
      "gauss": { // 定義使用的decay函式類型,可以選擇gauss, exp跟linear
        "@timestamp": {
          "origin": "2023-09-17", 
          "scale": "10d",
          "offset": "5d",          
          "decay": 0.5            
        }
      }
    }
  }
}
// 這樣的範例代表,在2023-09-12到2023-9-22的區間範圍的文檔們,距離他們10天以上的文檔就必須*0.5

可能這樣介紹下來function score query比較亂,這邊再統整一次

  • 我們如果對于特定需求,有自己想要計算相關分數的方式
    • 直接進行加權(weight)
    • 依據需求來決定要用腳本寫,或是直接用某些能計算的欄位做加權(script, field_value)
    • 可能如果是計算跟某時間點、數值或是地理位置之間的空間或時間上的差異(decay)
    • 想要抽樣特定索引的文檔(random)
  • 透過score_mode來算出所有額外算法的總分
  • 再來決定我這個額外計算的分數要怎麼跟原始分數進行合併來當最終相關分數(boost_mode)

今天我們介紹了ES強大的compound query

  • 能根據需求進行簡單影響分數的boosting query
  • 能夠進行多層邏輯判斷的boolean query
  • 以及可以自定義相關分數的function score query
    可以根據用戶的搜尋需求,慢慢的調教搜尋引擎的查找邏輯~
    讓其返回的資料,更能貼近使用者的需求~

參考資料
boolean query:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html
filter context:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html
boosting query:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-boosting-query.html
constant score query:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html
function score query:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html


上一篇
【Day 18】由淺入深來探討Elasticsearch - Searching for Data (2)
下一篇
【Day 20】由淺入深來探討Elasticsearch - Aggregation(1)
系列文
由淺入深來探討Elasticsearch,從基礎語法到底層相關原理30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言