ES[7.6.x]學習筆記(九)搜尋

牛初九發表於2020-05-21

搜尋是ES最最核心的內容,沒有之一。前面章節的內容,索引、動態對映、分詞器等都是鋪墊,最重要的就是最後點選搜尋這一下。下面我們就看看點選搜尋這一下的背後,都做了哪些事情。

分數(score)

ES的搜尋結果是按照相關分數的高低進行排序的,咦?! 怎麼沒說搜尋先說搜尋結果的排序了?我們們這裡先把這個概念提出來,因為在搜尋的過程中,會計算這個分數。這個分數代表了這條記錄匹配搜尋內容的相關程度。分數是一個浮點型的數字,對應的是搜尋結果中的_score欄位,分數越高代表匹配度越高,排序越靠前。

在ES的搜尋當中,分為兩種,一種計算分數,而另外一種是不計算分數的。

查詢(query context)

查詢,代表的是這條記錄與搜尋內容匹配的怎麼樣,除了決定這條記錄是否匹配外,還要計算這條記錄的相關分數。這個和我們們平時的查詢是一樣的,比如我們搜尋一個關鍵詞,分詞以後匹配到相關的記錄,這些相關的記錄都是查詢的結果,那這些結果誰排名靠前,誰排名靠後呢?這個就要看匹配的程度,也就是計算的分數。

過濾(filter context)

過濾,代表的含義非常的簡單,就是YES or NO,這條記錄是否匹配查詢條件,它不會計算分數。頻繁使用的過濾還會被ES加入到快取,以提升ES的效能。下面我們看一個查詢和過濾的例子,這個也是ES官網中的例子。

GET /_search
{
  "query": { 
    "bool": { 
      "must": [
        { "match": { "title":   "Search"        }},
        { "match": { "content": "Elasticsearch" }}
      ],
      "filter": [ 
        { "term":  { "status": "published" }},
        { "range": { "publish_date": { "gte": "2015-01-01" }}}
      ]
    }
  }
}

我們看一下請求的路徑/_search,這個是請求的路徑,而請求的方法是GET,我們再看請求體中,有一個query,這個代表著查詢的條件。而bool中的must被用作query context,它在查詢的時候會計算記錄匹配的相關分數。filter中的條件用作過濾,只會把符合條件的記錄檢索出來,不會計算分數。

組合查詢

組合查詢包含了其他的查詢,像我們前面提到的query contextfilter context。在組合查詢中,分為很多種型別,我們挑重點的型別給大家介紹一下。

Boolean Query

boolean查詢,前面我們寫的查詢語句就是一個boolean查詢,boolean查詢中有幾個關鍵詞,表格如下:

關鍵詞 描述
must 必須滿足的條件,而且會計算分數,
filter 必須滿足的條件,不會計算分數
should 可以滿足的條件,會計算分數
must_not 必須不滿足的條件,不會計算分數

我們看看下面的查詢語句:

POST _search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "user" : "kimchy" }
      },
      "filter": {
        "term" : { "tag" : "tech" }
      },
      "must_not" : {
        "range" : {
          "age" : { "gte" : 10, "lte" : 20 }
        }
      },
      "should" : [
        { "term" : { "tag" : "wow" } },
        { "term" : { "tag" : "elasticsearch" } }
      ],
      "minimum_should_match" : 1,
      "boost" : 1.0
    }
  }
}

上面的查詢是一個典型的boolean組合查詢,裡邊的關鍵詞都用上了。很多小夥伴們可能對mustshould的區別不是很瞭解,must是必須滿足的條件,我們的例子中must裡只寫了一個條件,如果是多個條件,那麼裡邊的所有條件必須滿足。而should就不一樣了,should裡邊現在列出了兩個條件,並不是說這兩個條件必須滿足,到底需要滿足幾個呢?我們看一下下面的關鍵字minimum_should_match,從字面上我們就可以看出它的含義,最小should匹配數,在這裡設定的是1,也就是說,should裡的條件只要滿足1個,就算匹配成功。在boolean查詢中,如果存在一個should條件,而沒有filtermust條件的話,那麼minimum_should_match的預設值是1,其他情況預設值是0。

我們再看一個實際的例子吧,還記得前面我們建立的ik_index索引嗎?索引中存在著幾條資料,資料如下:

_index _type _id ▲_score id title desc
ik_index _doc fEsN-HEBZl0Dh1ayKWZb 1 1 蘋果 蘋果真好吃
ik_index _doc 2 1 1 香蕉 香蕉真好吃
ik_index _doc 1 1 1 香蕉 香蕉真好吃
ik_index _doc 3 1 1 橘子 橘子真好吃
ik_index _doc 4 1 1 桃子 桃子真好吃

只有5條記錄,我們新建一個查詢語句,如下:

POST /ik_index/_search
{
    "query":{
        "bool":{
            "must":[
                {
                    "match":{
                        "desc":"香蕉好吃"
                    }
                }
            ]
        }
    },
    "from":0,
    "size":10,
}

我們查詢的條件是desc欄位滿足香蕉好吃,由於我們使用的ik分詞器,查詢條件香蕉好吃會被分詞為香蕉好吃,但是5的資料的desc中都有好吃欄位,所有5條資料都會被查詢出來,我們執行一下,看看結果:

_index _type _id ▲_score id title desc
ik_index _doc 2 0.98773474 1 香蕉 香蕉真好吃
ik_index _doc 1 0.98773474 1 香蕉 香蕉真好吃
ik_index _doc 3 0.08929447 1 橘子 橘子真好吃
ik_index _doc 4 0.08929447 1 桃子 桃子真好吃
ik_index _doc fEsN-HEBZl0Dh1ayKWZb 0.07893815 1 蘋果 蘋果真好吃

哈哈,5條資料全部查詢出來了,和我們的預期是一樣的,但是,我們需要注意一點的是_score欄位,它們的分數是不一樣的,我們的查詢條件是香蕉好吃,所以既包含香蕉又包含好吃的資料分數高,我們看到分數到了0.98,而另外3條資料只匹配了好吃,所以分數只有0.7,0.8。

Boosting Query

這個查詢比較有意思,它有兩個關鍵詞positivenegativepositive是“正”,所有滿足positive條件的資料都會被查詢出來,negative是“負”,滿足negative條件的資料並不會被過濾掉,而是會扣減分數。那麼扣減分數要扣減多少呢?這裡邊有另外一個欄位negative_boost,這個欄位是得分的係數,它的分數在0~1之間,滿足了negative條件的資料,它們的分數會乘以這個係數,比如這個係數是0.5,原來100分的資料如果滿足了negative條件,它的分數會乘以0.5,變成50分。我們看看下面的例子,

POST /ik_index/_search
{
  "query": {
    "boosting": {
      "positive": {
        "term": {
          "desc": "好吃"
        }
      },
      "negative": {
        "term": {
          "desc": "香蕉"
        }
      },
      "negative_boost": 0.5
    }
  }
}

positive條件是好吃,只要desc中有“好吃”的資料都會被查詢出來,而negative的條件是香蕉,只要desc中包含“香蕉”的資料都會被扣減分數,扣減多少分數呢?它的得分將會變為原分數*0.5。我們執行一下,看看效果,

index type _id score _source.id source.title source.desc
ik_index _doc 3 0.08929447 1 橘子 橘子真好吃
ik_index _doc 4 0.08929447 1 桃子 桃子真好吃
ik_index _doc fEsN-HEBZl0Dh1ayKWZb 0.07893815 1 蘋果 蘋果真好吃
ik_index _doc 2 0.044647235 1 香蕉 香蕉真好吃
ik_index _doc 1 0.044647235 1 香蕉 香蕉真好吃

我們可以看到前3條資料的分數都在0.09左右,而後兩條的資料在0.044左右,很顯然,後兩條資料中的desc包含香蕉,它們的得分會乘以0.5的係數,所以分數只有前面資料的分數的一半。

全文檢索

在前面幾節的內容中,我們介紹過,只有欄位的型別是text,才會使用全文檢索,全文檢索會使用到分析器,在我們的ik_index索引中,titledesc欄位都是text型別,所以,這兩個欄位的搜尋都會使用到ik中文分詞器。全文檢索比起前面的組合檢索要簡單一點,當然,在ES的官方文件中,全文檢索中的內容還是挺多的,在這裡我們只介紹一個標準的全文檢索。

我們看看下面的語句,

POST /ik_index/_search
{
  "query": {
    "match": {
      "desc": {
        "query": "蘋果"
      }
    }
  }
}

在請求體中,match代替了之前的boolmatch是標準的全文索引的查詢。match後面跟的欄位是要查詢的欄位名,在我們們的例子中,查詢的欄位是desc,如果有多個欄位,可以列舉多個。desc欄位裡,query就是要查詢的內容。我們還可以在欄位中指定分析器,使用analyzer關鍵字,如果不指定,預設就是索引的分析器。我們執行一下上面的查詢,結果如下:

index type _id score source.id source.title source.desc
ik_index _doc fEsN-HEBZl0Dh1ayKWZb 1.2576691 1 蘋果 蘋果真好吃

我們可以看到相應的資料已經檢索出來了。

最後

在ES中,檢索的花樣是比較多的,這裡也不能一一給大家介紹了,只介紹一些最基本、最常用的查詢功能。下一篇我們看一下ES的聚合查詢功能。

相關文章