搜尋是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 context
和filter 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組合查詢,裡邊的關鍵詞都用上了。很多小夥伴們可能對must
和should
的區別不是很瞭解,must
是必須滿足的條件,我們的例子中must
裡只寫了一個條件,如果是多個條件,那麼裡邊的所有條件必須滿足。而should
就不一樣了,should
裡邊現在列出了兩個條件,並不是說這兩個條件必須滿足,到底需要滿足幾個呢?我們看一下下面的關鍵字minimum_should_match
,從字面上我們就可以看出它的含義,最小should
匹配數,在這裡設定的是1,也就是說,should
裡的條件只要滿足1個,就算匹配成功。在boolean查詢中,如果存在一個should
條件,而沒有filter
和must
條件的話,那麼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
這個查詢比較有意思,它有兩個關鍵詞positive
和negative
,positive
是“正”,所有滿足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
索引中,title
和desc
欄位都是text型別,所以,這兩個欄位的搜尋都會使用到ik中文分詞器。全文檢索比起前面的組合檢索要簡單一點,當然,在ES的官方文件中,全文檢索中的內容還是挺多的,在這裡我們只介紹一個標準的全文檢索。
我們看看下面的語句,
POST /ik_index/_search
{
"query": {
"match": {
"desc": {
"query": "蘋果"
}
}
}
}
在請求體中,match
代替了之前的bool
,match
是標準的全文索引的查詢。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的聚合查詢功能。