elasticsearch 高階搜尋示例 es7.0

busman發表於2020-08-04

1 基礎資料

1.1 建立索引

PUT mytest
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "text",
            "analyzer": "standard"
          }
        }
      },
      "tag": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "content": {
        "type": "text",
        "fields": {
          "std": {
            "type": "text",
            "analyzer": "standard"
          },
          "cn": {
            "type": "text",
            "analyzer": "ik_smart"
          }
        }
      },
      "score": {
        "type": "byte"
      },
      "time":{
        "type": "date"
      }
    }
  },
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0"
    }
  }
}

1.2 寫入資料

POST mytest/_doc/001
{
  "title": "好評不錯",
  "tag": "精彩",
  "content": "這裡必須有一些內容,來表示這個評論是好評",
  "score": 90,
  "time": 1596441469000
}
POST mytest/_doc/002
{
  "title": "一般評價",
  "tag": "普通",
  "content": "這裡可以有一些內容,來表示這個評論是一般的",
  "score": 80,
  "time": 1596355069000
}
POST mytest/_doc/003
{
  "title": "很差的評價",
  "tag": "TCL",
  "content": "這裡沒有一些內容,來表示這個評論是OK的",
  "score": 20,
  "time": 1596268669000
}
POST mytest/_doc/004
{
  "title": "超級好評",
  "tag": "精彩",
  "content": "這裡必須有一些內容,來表示這個評論是好評好評好評好評好評好評好評好評好評好評好評好評好評",
  "score": 2,
  "time": 1596441469000
}

2 短語匹配

2.1 不指定匹配的 fields 時候, 是否會查詢全部欄位?

不指定 fields 搜尋

POST mytest/_search
{
  "explain": true,
  "query": {
    "match": {
      "content": "好評"
    }
  }
}

搜尋結果為 3 條。在 explain 的結果中可以看到, details 評分統計了兩個欄位的結果

為了驗證上面結果, 在指定域搜尋只能看到一條結果

POST mytest/_search
{
  "query": {
    "match": {
      "content.cn": "好評"
    }
  }
}

2.2 match 和 match_phrase 區別

POST mytest/_search
{
  "explain": false,
  "query": {
    "match": {
      "content.cn":{
        "query": "這裡可以有一些內容",
        "analyzer": "ik_smart"
      }
    }
  }
}

"這裡可以有一些內容" 通過 ik_smart 分詞器被分成了 [這裡 可以 有 一些 內容]
五個詞都放入到 es 中搜尋, 所以這裡可以搜尋出三個結果
可以通過比例限制匹配的結果, 限制到 90% 後, 只能匹配到 1 條結果

POST mytest/_search
{
  "query": {
    "match": {
      "content.cn":{
        "query": "這裡可以有一些內容",
        "analyzer": "ik_smart",
        "minimum_should_match": "90%"
      }
    }
  }
}

match_phrase 只能匹配到 1 條結果, 如果把文字換成 "這裡可以有內容一些", 就不能匹配到內容了
這時需要使用 slop 完成近似匹配, 允許有順序的差異. 同時, 使用 match 匹配的時候, 詞的順序不影響結果得分

POST mytest/_search
{
  "query": {
    "match_phrase": {
      "content.cn":{
        "query": "這裡可以有一些內容",
        "analyzer": "ik_smart",
        "slop": 0
      }
    }
  }
}

2.3 精確搜尋的時候, 可以使用預設分詞器, 以達到精確的目的

在搜尋中輸入 "以有一些內容" 不完整的內容, 也可以搜尋到精確的結果
修改為 match 可以

POST mytest/_search
{
  "query": {
    "match_phrase": {
      "content.std":{
        "query": "以有一些內容",
        "analyzer": "standard"
      }
    }
  }
}

3 自定義打分

3.1 打分公式

score(q,d) = queryNorm(q)            //歸一化因子
             ·coord(q,d)             //協調因子
             ·∑( tf(t in d)          //詞頻
                ·idf(t)²             //逆向文件頻率
                ·t.getBoost()        //權重
                ·norm(t,d)           //欄位長度歸一值
              )(t in q)

queryNorm 查詢歸化因子: 會被應用到每個文件, 不能被更改, 總而言之, 可以被忽略
coord 協調因子: 可以為那些查詢詞包含度高的文件提供獎勵, 文件裡出現的查詢詞越多, 它越有機會成為好的匹配結果
協調因子將評分與文件裡匹配詞的數量相乘,然後除以查詢裡所有詞的數量,如果使用協調因子,評分會變成:
文件裡有 fox → 評分: 1.5 * 1 / 3 = 0.5
文件裡有 quick fox → 評分: 3.0 * 2 / 3 = 2.0
文件裡有 quick brown fox → 評分: 4.5 * 3 / 3 = 4.5
協調因子能使包含所有三個詞的文件比只包含兩個詞的文件評分要高出很多

3.2 提升查詢權重

POST mytest/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": {"query": "好評", "boost": 10}
          }
        },
        {"match": {"content": "有一些內容"}
        }
      ]
    }
  }
}

3.3 結合 function_score 查詢 與 field_value_factor 查詢可以實現按照文件的欄位來影響文件評分

POST mytest/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {"query": "好評","fields": ["title", "content"]}
      },
      "functions": [
        {"field_value_factor": {"field": "score"}}
      ]
    }
  }
}

new_score = old_score * number_of_votes
這樣會導致 votes 為 0 的文件評分為 0,而且 votes 值過大會掩蓋掉全文評分

3.4 一般會使用 modifier 引數來平滑 votes 的值

POST mytest/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {"query": "好評","fields": ["title", "content"]}
      },
      "functions": [
        {"field_value_factor": {"field": "score", "modifier": "log1p"}}
      ]
    }
  }
}

應用值為 log1p 的 modifier 後的評分計算公式:
new_score = old_score * log(1 + number_of_votes)

modifier 的可以為: none ,log ,log1p ,log2p ,ln ,ln1p ,ln2p ,square ,sqrt ,reciprocal

3.5 factor 可以通過將 votes 欄位與 factor 的積來調節受歡迎程度效果的高低

"functions": [
        {"field_value_factor": {"field": "score", "modifier": "ln1p", "factor": 10}}
      ]

新增了 factor 會使公式變成這樣:
new_score = old_score * log(1 + factor * number_of_votes)

3.6 通過引數 boost_mode 來控制函式與查詢評分 _score 合併後的結果,引數接受的值

multiply: 評分 _score 與函式值的積(預設)
sum: 評分 _score 與函式值的和
min: 評分 _score 與函式值間的較小值
max: 評分 _score 與函式值間的較大值
replace: 函式值替代評分 _score

"functions":[],
"boost_mode": "sum"

之前請求的公式現在變成下面這樣:
new_score = old_score + log(1 + 0.1 * number_of_votes)

3.7 可以使用 max_boost 引數限制一個函式的最大效果

"boost_mode": "sum"
"max_boost": 1.5

無論 field_value_factor 函式的結果如何,最終結果都不會大於 1.5 。
注意 max_boost 只對函式的結果進行限制,不會對最終評分 _score 產生直接影響。

3.8 評分模式 score_mode

每個函式返回一個結果,所以需要一種將多個結果縮減到單個值的方式,然後才能將其與原始評分 _score 合併。
評分模式 score_mode 引數正好扮演這樣的角色, 它接受以下值:

multiply : 函式結果求積(預設)。
sum : 函式結果求和。
avg : 函式結果的平均值。
max : 函式結果的最大值。
min : 函式結果的最小值。
first : 使用首個函式(可以有過濾器,也可能沒有)的結果作為最終結果
在本例中,我們將每個過濾器匹配結果的權重 weight 求和,並將其作為最終評分結果,所以會使用 sum 評分模式。
不與任何過濾器匹配的文件會保有其原始評分, _score 值的為 1 。

3.9 衰減函式

這需要使用 function_score 查詢提供的一組 衰減函式(decay functions)
linear 線性函式
exp 指數函式
gauss 高斯函式

所有三個函式都接受如下引數:
origin:中心點 或欄位可能的最佳值,落在原點 origin 上的文件評分 _score 為滿分 1.0 。
scale:衰減率,即一個文件從原點 origin 下落時,評分 _score 改變的速度。(例如,每 £10 歐元或每 100 米)。
decay:從原點 origin 衰減到 scale 所得的評分 _score ,預設值為 0.5 。
offset:以原點 origin 為中心點,為其設定一個非零的偏移量 offset 覆蓋一個範圍,而不只是單個原點。在範圍 -offset <= origin <= +offset 內的所有評分 _score 都是 1.0

      "functions": [
        {
          "exp": {
            "time": {
              "origin": "1596530751000",
              "scale": "10d",
              "offset": "5d",
              "decay": 0.5
            }
          }
        }
      ]

相關文章