前言
Elasticsearch
作為一款分散式搜尋工具,其搜尋功能非常強大,本文主要介紹下 Elasticsearch
中高階搜尋的使用。
Search APIs
搜尋 APIs
按照查詢方式主要可以分為兩大類,那就是:URI earch
和 Request Body Search
。在查詢語句中,一般使用 _search
來表示當前是一個搜尋語句。
- /_search:查詢叢集上的所有索引資料,一般不建議這麼使用。
- index1,index2/_search:查詢指定一個或者多個索引的資料。
- index*/_search:利用萬用字元查詢當前叢集上的索引資料。
URI Search
顧名思義,URI Search
指的是直接使用 URL
進行查詢,引數直接拼在 URL
上。
URI Search
中,主要有以下引數:
- q:指定查詢語句,使用
Query String Syntax
語法(KV
鍵值對)。 - df:預設欄位,如果不指定,則會對所有欄位進行查詢。
- sort:排序。
- explain:對每一個結果,都會返回
_explanation
結果,包含了當前資料分值的計算方式和結果。 - from/size:用於分頁,
from
表示從哪條資料開始,size
表示當前需要查詢多少條資料。 - _source:
false
表示不返回源資料(_source
欄位),預設為true
。 - _source_includes:表示
_source
內只返回當前指定的欄位。 - _source_excludes:表示
_source
內不返回當前指定的欄位,當前引數優先順序大於_source_includes
。 - timeout:指定超時時間,預設沒有超時時間。
bulk 插入演示資料
為了便於後面演示,我們通過 bulk
操作來批量插入一些比較直觀的資料:
POST index_001/_doc/_bulk
{"index":{}}
{"id":"1","name":"lonely wolf","result":true}
{"index":{}}
{"id":"2","name":"lonely hello wolf","result":true}
{"index":{}}
{"id":"3","name":"lonely hello word wolf","result":true}
{"index":{}}
{"id":"4","name":"lonely","result":false}
{"index":{}}
{"id":"5","name":"wolf","result":false}
或者執行以下語句:
POST /_bulk
{"index":{"_index":"index_001"}}
{"id":"1","name":"lonely wolf","result":true}
{"index":{"_index":"index_001"}}
{"id":"2","name":"lonely hello wolf","result":true}
{"index":{"_index":"index_001"}}
{"id":"3","name":"lonely hello word wolf","result":true}
{"index":{"_index":"index_001"}}
{"id":"4","name":"lonely","result":false}
{"index":{"_index":"index_001"}}
{"id":"5","name":"wolf","result":false}
基礎查詢
- 指定欄位查詢:
# 指定name欄位查詢
GET index_001/_search?q=name:wolf
- 使用預設欄位查詢:
GET index_001/_search?q=wolf&df=name
上面這兩句話查詢效果是一樣的,均可以查詢出 4
條資料,執行 profile
分析一下,確實只匹配了 name
一個欄位:
- 泛查詢(不指定任何欄位)
GET index_001/_search?q=wolf
這條語句也是返回 4
條資料,但是這條語句和上面不同的是其沒有通過 q
指定篩選欄位,也沒有通過 df
指定預設欄位,所以會查詢所有欄位:
執行 profile
查詢可以發現,這條語句會查詢所有欄位,而且有些型別不匹配則會報錯,所以這種查詢效率是很低的,生產環境中應該儘量避免。
- 指定
source
查詢
再看下面的一個 source
查詢例子:
GET index_001/_search?q=name:wolf&_source_includes=name,result&_source_excludes=result&timeout=1ms
這個例子中因為同時指定了 _source_excludes
和 _source_includes
,但是因為 _source_excludes
優先順序比較高,故而最終只會返回 name
一個欄位:
Term 查詢
GET /index_001/_search?q=name:lonely wolf
這個查詢會返回所有資料,因為預設情況下這個查詢會使用 Term
查詢,會查詢 name
為 lonely
或者 wolf
的欄位,而如果想把 lonely wolf
作為一個整體,則可以使用 Phrase
查詢。
布林操作
在上面 Term
查詢中,我們發現當兩個 Term
查詢在一起,預設使用的是 or
的操作,而如果要使用 and
,則可以使用布林操作。
布林操作支援以下符號(必須大寫):AND
,OR
,NOT
,&&
,||
,!
。如下例子則只會查詢出 3
條資料。
GET /index_001/_search?q=name:lonely AND wolf
# 建議使用 () 來明確表示分組
GET /index_001/_search?q=name:(lonely AND wolf)
同時,布林操作還支援一些高階查詢,如:+
表示 must
,-
表示 must not
:
GET /index_001/_search?q=name:(+lonely -wolf)
這句話就只能查詢出 id
為 4
的這條資料,name
含有 lonely
關鍵字且不含 wolf
關鍵字。
Phrase 查詢
假如我們想把一句話當成一個整體來查詢,則可以使用 Phrase
查詢:
GET /index_001/_search?q=name:"lonely wolf"
這個時候就只會查詢出一條資料。
萬用字元和正則查詢
萬用字元查詢中,?
表示 1
個字元,*
表示 0
或者多個字元。
# 沒有符合條件的資料GET /index_001/_search?q=name:lone?# 1-4條資料都符合條件GET /index_001/_search?q=name:lone*
萬用字元查詢是一種 like
查詢,效率相對會比較低,所以一般也不建議使用。
此外,還可以通過正規表示式查詢:
# 查詢出 id 為 2 或 3 的資料GET /index_001/_search?q=id:(2|3)
近似查詢
有些時候我們查百度的時候發現輸錯了字也能被查出來,這就是利用了近似查詢,如下所示:
# 輸錯一個字元,查詢不出結果GET /index_001/_search?q=name:loneyy# 允許一個字元錯誤,查詢出 4 條資料GET /index_001/_search?q=name:loneyy~1# 允許兩個字元錯誤,查詢出 4 條資料GET /index_001/_search?q=name:loniyy~2
另外,針對上面的 Phrase
查詢中,因為是把兩個單詞作為一個整體,那麼也可以通過近似查詢來設定允許中間有其他字元,如下:
GET /index_001/_search?q=name:"lonely wolf" ~1
這裡表示允許 lonely
和 wolf
之間插入一個其他字元,所以可以查詢出 2
條資料:
Request Body Query
Request Body
查詢是 Elasticsearch
中基於 json
格式提供的一種 DSL
語言(Query Domain Specific Language)。一般情況下,相比較於 URI Request
,雖然說 URI Query
也可以實現一定複雜程度的查詢,但是一般情況下我們還是更推薦使用 Request Body
查詢來實現更加複雜的一些組合查詢。
通過 URI Search
中能實現的搜尋方式,都可以通過 Request Body
來實現,下面就讓我們一起來看看如何利用 Request Body
來進行搜尋查詢。
分頁查詢
Request Body
分頁查詢也是通過 from
和 size
來實現:
POST index_001/_search
{
"from": 0,
"size": 2
}
排序
排序通過 sort
來實現。注意,預設 text
型別不能排序,如果需要排序則使用 field.keyword
來查詢:
POST index_001/_search
{
"sort": [
{
"name.keyword": {
"order": "desc"
}
}
]
}
source 查詢
同樣的,Request Body Query
也可以使用 source
來指定返回欄位,而且也支援萬用字元的方式來指定:
POST index_001/_search
{
"_source": ["id","*e*"]
}
這時候查詢會返回 id
,name
和 result
三個欄位。
使用 match 查詢
如果需要指定條件查詢,在 Request Body
查詢中則通過 match
來實現:
POST index_001/_search
{
"query": {
"match": {
"name": "lonely wolf"
}
}
}
同樣的,這個查詢預設的也是“或”的關係,所以這裡也是能將 5
條資料全部查詢出來,如果要使用 AND
,則可以通過以下方式實現:
POST index_001/_search
{
"query": {
"match": {
"name": {
"query": "lonely wolf",
"operator": "and"
}
}
}
}
這樣子就只能查詢出前三條資料。
如果要查詢全部資料,則可以利用 match_all
進行查詢:
POST index_001/_search
{
"query": {
"match_all": {}
}
}
match phrase 查詢
上面的查詢中,即使指定了 and
,也能查詢出 3
條資料,如果需要精確查詢,那麼可以使用 match phrase
查詢:
POST index_001/_search
{
"query": {
"match_phrase": {
"name": {
"query": "lonely wolf",
"slop": 0
}
}
}
}
這裡如果不加 slot
,則預設為 0
,也就是不允許 lonely wolf
兩個單詞之間存在其他字元,這樣子就只能查詢出第一條資料。
指令碼 script field 查詢
有時候我們需要利用指令碼來對搜尋結果進行進一步處理,這時候就可以指令碼來實現一些功能,不過需要注意的是,text
欄位預設也是不能使用指令碼,如果需要是用同樣需要加上 .keyword
:
POST index_001/_search
{
"script_fields": {
"id-name": {
"script": {
"lang": "painless",
"source": "doc['id.keyword'].value + '-' + doc['name.keyword']"
}
}
},
"query": {
"match_all": {}
}
}
返回結果如下,新的結果會作為欄位 id-name
返回:
總結
本文主要介紹了 Elasticsearch
中的兩種查詢方式,並分別介紹了一些非常常用的查詢方式,下一篇我們會專門介紹下本文提到的 Term
查詢和 Phrase
查詢,並徹底理解這兩種查詢方式的區別。
請關注我,和孤狼一起學習進步。