用過 Kibana 的同學(xué)應(yīng)該都注意過其頂部的搜索框,像下圖這樣。
這個(gè)輸入框接受符合 query string 語法的查詢語句。在日常開發(fā)中我們常用的都是 elastic query dsl(domain specific language),都是 json 格式的,像下面這個(gè)簡單的查詢語句。
{ "query":{ "match":{ "name":"elastic" } } }
這個(gè)查詢?nèi)绻?query string 來表示的話,如下所示:
name:elastic
看起來是不是簡潔了很多呢?對(duì)于 kibana 這種 UI 界面,輸入越是簡單,越是接近自然語言,對(duì)于用戶也就越是友好,所以 kibana 的搜索框默認(rèn)選擇了 query string 的搜索語法。雖然 query string 展示起來簡潔了很多,但是要用好卻沒有那么簡單,如果不好好閱讀官方文檔并做實(shí)驗(yàn),那你會(huì)吃不少苦頭。下面我就來幫大家掃清障礙。
注解:
如果非要用 query dsl 的話,kibana 也是支持的,你只需要把 query 中的內(nèi)容放到搜索框中就可以了,比如上面的查詢語句,你只要放下面的內(nèi)容就可以查詢了。
{ "match":{ "name":"elastic" } }
由來
elasticsearch 的 query string 其實(shí)就是 lucene 的 query language。elasticsearch 本身就是構(gòu)建于 lucene 之上的,它支持 lucene 的 query language 也是很簡單的事情。那這個(gè) query language 是怎么來的呢?根據(jù) lucene 文檔中介紹,雖然 lucene 已經(jīng)提供了構(gòu)建查詢條件的 API,但對(duì)于人類直接使用而言不夠友好和自然,所以 lucene 提供了這樣一種接近自然語言的查詢語言,方便人工輸入和記憶查詢條件。lucene 不鼓勵(lì)在代碼中直接使用 query language,因?yàn)樗€要經(jīng)過一層 query parser 的轉(zhuǎn)換,有性能損耗,另外 query language 也只覆蓋了一部分查詢 API,并不完備。
基本概念
這里先講解兩個(gè)術(shù)語: single term 和 phrase。前者是指單個(gè)詞(分詞后的最小單位),后者指短語。舉例來說,word、sun 這些都是 single term,而用雙引號(hào)包裹起來 “word sun“ 就成了 phrase 。所以這兩者的區(qū)別在于 phrase 是由 term 組成的,包裹在雙引號(hào)中。而 phrase 中的詞在匹配是有順序要求的,這也就是 elasticsearch 中 match query 和 match phrase query 的區(qū)別之一。
接下來再看一個(gè)術(shù)語:field,也就是字段。我們?cè)谑褂?query dsl 來寫搜索條件時(shí),都會(huì)指明是要在哪一個(gè) field 上做匹配。同理,在 query string 中也可以做到只匹配指定的 field,用法是將 字段名稱: 放到查詢內(nèi)容前面即可,比如 name:tom
。當(dāng)不指定 field 時(shí),會(huì)使用默認(rèn)的 field,默認(rèn)配置為 _all字段,這個(gè)是可以自己指定的(index.query.default_field)。關(guān)于 _all 字段,可以簡單理解為所有文檔字段的拼接結(jié)果,這里就不展開講了。比如下面這個(gè)查詢語句就是查詢 name 為 tom 的所有文檔。
name:tom
如果不寫 name: ,只寫 tom
,那么相當(dāng)于執(zhí)行 _all:tom
這個(gè)查詢。
另外* field* 是有作用域的,只對(duì)緊跟其后的 term 生效。
name:"Tom Lee"
上面這是一個(gè) phrase 查詢,查詢匹配 Tom Lee 的所有文檔。
name:Tom Lee
上面這個(gè)查詢實(shí)際等效于
name:Tom OR _all:Lee
這也是在實(shí)際使用中很多人容易踩坑的地方,使用的過程中要切記,否則查詢出的結(jié)果肯定不會(huì)如你預(yù)期的那樣。
如果你想對(duì)一個(gè) field 指定復(fù)雜的查詢條件,那么可以使用括號(hào)將查詢條件包起來用(field grouping)。比如下面的這個(gè)語句:
name:(tom lee)
等價(jià)于
name:(tom OR lee)
查詢 name 為 tom 或者 lee的所有文檔。
另外括號(hào)不僅可以作用在 field 上,還可以作用在外層的邏輯處理中。比如下面這種查詢組合也是支持的(大家先忽略還沒有講到的操作符)。
(name:tom && age:10)||city:(shanghai beijing)
有心的讀者可能會(huì)發(fā)現(xiàn)為什么 && 的語句還要加括號(hào)呢,它的優(yōu)先級(jí)不是比 || 高嗎?我 也是這么認(rèn)為的,但動(dòng)手測試后發(fā)現(xiàn)貌似 lucene 沒有實(shí)現(xiàn)這個(gè)優(yōu)先級(jí)的解析,所以大家使用的時(shí)候注意括號(hào)的使用。
下面再講一下布爾操作符,大家對(duì)這個(gè)應(yīng)該很熟悉了,比如 AND、OR、NOT等。這里要注意的一點(diǎn)是:query string 中的布爾操作符必須大寫。如果小寫,比如 and、or、not,query解析器會(huì)把他們當(dāng)做普通 term 解析。比如下面這個(gè)語句:
name:tom and age:10
上面的查詢實(shí)際對(duì)應(yīng)下面的語句:
name:tom OR _all:and OR age:10
愛思考的同學(xué)看到上面的解釋應(yīng)該就有疑問了。
“這個(gè) OR 是從哪里來的呢?”
這個(gè) OR 是默認(rèn)的布爾操作符,當(dāng)多個(gè)查詢條件之間沒有指定布爾關(guān)系時(shí),就會(huì)使用 OR。
另外可以用 && 和 ||* 分別代替 AND 和 OR,使得查詢語句更簡潔易懂。
NOT 就是非操作,簡寫符號(hào)為 !。用法如下:
name:(tom NOT lee)
查詢 name 為 tom,但不是 lee 的所有文檔。
除去 AND、OR、NOT ,query string 還支持 + 和 -,分別對(duì)應(yīng) must 和 must not 的含義。
name:(tom +lee -alfred)
上面的語句查詢 name 中 含有 lee,不含有 alfred,但可能含有 tom 的所有文檔。
大家可以想一下如果用 AND、OR、NOT 來重寫上面的語句是什么樣子呢?
name:((lee && !alfred) || (tom && lee && !alfred))
你是不是發(fā)現(xiàn) + 和 - 的好處了?
查詢功能
講完基礎(chǔ)概念,我們?cè)賮砜纯?query string 支持的幾個(gè)查詢功能。
范圍查詢 range search
數(shù)字、日期類型等都是可以指定范圍的,對(duì)于這類可以使用范圍查詢,閉區(qū)間用[],開區(qū)間用{}。舉幾個(gè)例子大家一看就明白了。
age:[1 TO 10] 意為 1<=age<=10 age:[1 TO 10} 意為 1<=age<10 age:[1 TO ] 意為 age>=1 age:[* TO 10] 意為 age<=10
日期也是一樣的用法,只要將數(shù)字替換為 2017-01-01 即可。
如果你覺得這樣寫太麻煩了,那可以使用簡略寫法,如下:
age:(>=1 && <=10) 或者 age:(+>=1 +<=10) age:(>=1 && <10) 或者 age:(+>=1 +<10) age:>=1 age:<=10
通配符查詢 wildcard search
大家對(duì)于通配符應(yīng)該都不陌生,有 ? 和 * 兩個(gè),前者代表一個(gè)字符,后者代表0或多個(gè)字符。
name:t?m name:tom* name:t*m
上面的使用方式都可以,但是不能把 ? 和 *** 放在最前面,因?yàn)檫@會(huì)導(dǎo)致 elasticsearch 將所有的分詞都比對(duì)一遍,效率低下。
通配符查詢的效率很低,也會(huì)占用較多的內(nèi)存(因?yàn)橐阉蟹蠗l件的分詞進(jìn)行比對(duì)),建議大家謹(jǐn)慎使用。
正則查詢 regular expressionsearch
正則查詢?nèi)缤置嬉饬x所講的,支持正則表達(dá)式進(jìn)行匹配。
name:/[mb]oat/
但這里并不支持所有的正則語法,使用的時(shí)候要注意查看官方文檔說明。另外正則查詢的內(nèi)存壓力也很大,要謹(jǐn)慎使用。
模糊查詢 fuzzy search
所謂模糊查詢是指允許搜索和匹配的詞(term)之間有差異,比如搜索 surprize,可以匹配到surprise。
name:roam~
上面的語句會(huì)匹配到 foam 和 roams,波浪號(hào)后面可以指定一個(gè) 0~2 的浮點(diǎn)值,用以表示模糊度,我在實(shí)際使用中用的不多,就不展開來講了。
近似度查詢 proximity search
所謂近似度查詢是指在一個(gè)短語(phrase)中,詞(term)與詞之間距離的匹配。
name:"tom lee"~2
匹配時(shí)允許 tom 和 lee 之間有 2 個(gè)詞的距離,筆者用的也不多,所以不班門弄斧了。
提升查詢權(quán)重 boosting term
查詢時(shí)如果想改變某個(gè)查詢條件的權(quán)重,可以使用 ^ 來實(shí)現(xiàn)。
name:(tom^4 lee)
上面的查詢表示,當(dāng) name 中包含 tom 時(shí),其權(quán)重是 lee 的4倍,這就意味著相應(yīng)得分也會(huì)高,排序也會(huì)靠前。
特殊字符過濾
query string 本身已經(jīng)占據(jù)了一些關(guān)鍵字,如下
+ - && || ! ( ) { } [ ] ^ " ~ * ? : \ /
當(dāng)遇到這些關(guān)鍵詞時(shí),需要使用 \ 做轉(zhuǎn)義,比如如果你要搜索 (1+1):2
,那么查詢條件需要寫成\(1\+1\)\:2
。
總結(jié)
至此,query string就講完了,大家可以去愉快地和 kibana 玩耍了,遇到問題時(shí)歡迎和筆者我討論哦~