本文主要介紹 ElasticSearch 搜索相關(guān)的知識(shí),首先會(huì)介紹下 URI Search 和 Request Body Search蔓纠,同時(shí)也會(huì)學(xué)習(xí)什么是搜索的相關(guān)性辑畦,如何衡量相關(guān)性。
Search API
我們可以把 ES 的 Search API 分為兩大類腿倚,第一類是 URI Search纯出,用 HTTP GET 的方式在 URL 中使用查詢參數(shù)已達(dá)到查詢的目的;另一類為 Request Body Search敷燎,可以使用 ES 提供的基于 JSON 格式的格式更加完備的查詢語言 Query DSL(Domain Specific Language)
語法 | 范圍 |
---|---|
/_search | 集群上所有的索引 |
/jvm/_search | jvm |
/jvm,sql/_search | jvm 和 sql |
/jvm*/_search | 以 jvm 開頭的索引 |
在查詢的時(shí)候需要通過 _search
來標(biāo)明這個(gè)請(qǐng)求為搜索請(qǐng)求暂筝,同時(shí)可以指定 index,也可以指定多個(gè) index硬贯,也可以使用通配符的方式對(duì) index 進(jìn)行搜索焕襟。
下面來看下 URI Search:
URI Search
GET /users/_search?q=username:wupx
URI Search 使用的是 GET 方式,其中 q
指定查詢語句饭豹,語法為 Query String Syntax鸵赖,是 KV 鍵值對(duì)的形式;上面的請(qǐng)求表示對(duì) username
字段進(jìn)行查詢拄衰,查詢包含 wupx
的所有文檔它褪。
URI Search 有很多參數(shù)可以指定,除了 q
還有如下參數(shù):
- df:默認(rèn)字段翘悉,不指定時(shí)會(huì)對(duì)所有字段進(jìn)行查詢
- sort:根據(jù)字段名排序
- from:返回的索引匹配結(jié)果的開始值茫打,默認(rèn)為 0
- size:搜索結(jié)果返回的條數(shù),默認(rèn)為 10
- timeout:超時(shí)的時(shí)間設(shè)置
- fields:只返回索引中指定的列妖混,多個(gè)列中間用逗號(hào)分開
- analyzer:當(dāng)分析查詢字符串的時(shí)候使用的分詞器
- analyze_wildcard:通配符或者前綴查詢是否被分析老赤,默認(rèn)為 false
- explain:在每個(gè)返回結(jié)果中,將包含評(píng)分機(jī)制的解釋
- _source:是否包含元數(shù)據(jù)源葫,同時(shí)支持
_source_includes
和_source_excludes
- lenient:若設(shè)置為 true诗越,字段類型轉(zhuǎn)換失敗的時(shí)候?qū)⒈缓雎裕J(rèn)為 false
- default_operator:默認(rèn)多個(gè)條件的關(guān)系息堂,AND 或者 OR嚷狞,默認(rèn)為 OR
- search_type:搜索的類型块促,可以為
dfs_query_then_fetch
或query_then_fetch
,默認(rèn)為query_then_fetch
在了解了基本的查詢參數(shù)后床未,讓我們先來看下什么是指定字段查詢和什么是泛查詢竭翠?
比如 GET /movies/_search?q=2012&df=title
這個(gè)例子就是指定字段查詢,同樣 GET /movies/_search?q=title:2012
也可以達(dá)到指定字段查詢的目的薇搁。
再舉一個(gè)泛查詢的例子 GET /movies/_search?q=2012
斋扰,會(huì)對(duì)所有字段進(jìn)行查詢。
接下來啃洋,看下什么是 Term Query 和 Phrase Query:
比如:Beautiful Mind
等效于 Beautiful
OR Mind
传货;"Beautiful Mind"
等效于 Beautiful
AND Mind
,另外還要求前后順序保存一致宏娄。
當(dāng)為 Term Query 的時(shí)候问裕,就需要把這兩個(gè)詞用括號(hào)括起來,請(qǐng)求為 GET /movies/_search?q=title:(Beautiful Mind)
孵坚,意思就是查詢 title
中包括 Beautiful
或者 Mind
粮宛。
當(dāng)為 Phrase Query 的時(shí)候就需要用引號(hào)包起來,請(qǐng)求為 GET /movies/_search?q=title:"Beautiful Mind"
卖宠。
另外還支持布爾操作巍杈,比如 AND(&&)、OR(||)扛伍、NOT(?昶琛),需要注意大寫蜒秤,不能小寫汁咏。
在這里舉一個(gè) NOT 的例子:GET /movies/_search?q=title:(Beautiful NOT Mind)
,這個(gè)請(qǐng)求表示查詢 title
中必須包括 Beautiful
不能包括 Mind
的文檔作媚。
URI Search 還包括一些范圍查詢和數(shù)學(xué)運(yùn)算符號(hào)攘滩,比如指定電影的年份大于 1994:GET /movies/_search?q=year:>=1994
。
URI Search 還支持通配符查詢(查詢效率低纸泡,占用內(nèi)存大漂问,不建議使用,特別是放在最前面)女揭,還支持正則表達(dá)式蚤假,以及模糊匹配和近似查詢。
URI Search 好處就是操作簡單吧兔,只要寫個(gè) URI 就可以了磷仰,方便測試,但是 URI Search 只包含一部分查詢語法境蔼,不能覆蓋所有 ES 支持的查詢語法灶平。
因此讓我們來看下 Request Body Search:
Request Body Search
在 ES 中一些高階用法只能在 Request Body 里做伺通,所以我們盡量使用 Request Body Search,它支持 GET 和 POST 方式對(duì)索引進(jìn)行查詢逢享,需要指定操作的索引名稱罐监,同樣也要通過 _search
來標(biāo)明這個(gè)請(qǐng)求為搜索請(qǐng)求,我們可以在請(qǐng)求體中使用 ES 提供的 DSL瞒爬,下面這個(gè)例子就是簡單的 Query DSL:
POST /users/_search
{
"query": {
"match_all": {}
}
}
上面的請(qǐng)求的意思就是把所以的結(jié)果都返回弓柱。
也可以在 Request Body 中加入 from
和 size
參數(shù)以達(dá)到分頁的效果:
POST /movies/_search
{
"from":10,
"size":20,
"query":{
"match_all": {}
}
}
默認(rèn) from 從 0 開始,返回 10 個(gè)結(jié)果侧但,獲取靠后的翻頁成本較高矢空。
如果想對(duì)搜索的結(jié)果排序也可以在請(qǐng)求體中加上 sort
參數(shù):
POST /movies/_search
{
"sort":[{"year":"desc"}],
"query":{
"match_all": {}
}
}
最好在“數(shù)字型”與“日期型”字段上排序,因?yàn)閷?duì)于多值類型或者分析過的字段排序俊犯,系統(tǒng)會(huì)選一個(gè)值妇多,無法得知該值伤哺。
如果 _source
的數(shù)據(jù)量比較大燕侠,有些字段也不需要拿到這個(gè)信息,那么就可以對(duì)它的 _source
進(jìn)行過濾立莉,把需要的信息加到 _source
中绢彤,比如以下請(qǐng)求就是 _source
中只返回 title
:
POST /movies/_search
{
"_source":["title"],
"query":{
"match_all": {}
}
}
如果
_source
沒有存儲(chǔ),那就只返回匹配的文檔的元數(shù)據(jù)蜓耻,同時(shí)_source
也支持使用通配符茫舶。
接下來介紹下腳本字段,腳本字段可以使用 ES 中的 painless
的腳本去算出一個(gè)新的字段結(jié)果刹淌。
GET /movies/_search
{
"script_fields": {
"new_field": {
"script": {
"lang": "painless",
"source": "doc['year'].value+'_hello'"
}
}
},
"query": {
"match_all": {}
}
}
這個(gè)例子中就使用 painless
把電影的年份和 _hello
進(jìn)行拼接形成一個(gè)新的字段 new_field
饶氏。
在上面我們剛介紹了在 URI Search 中的 Term Query
和 Phrase Query
,接下來讓我們看下 Request Body 中是怎么做的吧有勾!
在此之前先來插播一條小知識(shí)-字段類查詢疹启,字段類查詢主要包括以下兩類:
- 全文匹配:針對(duì) text 類型的字段進(jìn)行全文檢索,會(huì)對(duì)查詢語句先進(jìn)行分詞處理蔼卡,如 match喊崖,match_phrase 等 query 類型
- 單詞匹配:不會(huì)對(duì)查詢語句做分詞處理,直接去匹配字段的倒排索引雇逞,如 term荤懂,terms,range 等 query 類型
好了塘砸,現(xiàn)在我們來接著往下看节仿。
可以在 Request Body 中使用在 query match
的方式把信息填在里面,我們先來看下 Match Query
掉蔬,比如下面這個(gè)例子廊宪,填入兩個(gè)單詞查近,默認(rèn)是 wupx
or huxy
的查詢條件,如果想查詢兩者同時(shí)出現(xiàn)挤忙,可以通過加 "operator": "and"
來實(shí)現(xiàn)霜威。
POST /users/_search
{
"query": {
"match": {
"title": "wupx huxy"
"operator": "and"
}
}
}
我們通過一張圖來看下 Match Query
的流程:
首先對(duì)查詢語句進(jìn)行分詞,分成 wupx
和 huxy
兩個(gè) Term册烈,然后 ES 會(huì)拿到 username
的倒排索引戈泼,對(duì) wupx
和 huxy
去進(jìn)行匹配的算分,比如 wupx
對(duì)應(yīng)的文檔是 1 和 2赏僧,huxy
對(duì)應(yīng)的文檔為 1大猛,然后 ES 會(huì)利用算分算法(比如 TF/IDF 和 BM25,BM25 模型 5.x 之后的默認(rèn)模型)列出文檔跟查詢的匹配得分淀零,然后 ES 會(huì)對(duì) wupx
huxy
的文檔的得分結(jié)果做一個(gè)匯總挽绩,最終根據(jù)得分排序,返回匹配文檔驾中。
Request Body 中還支持 Match Phrase
查詢唉堪,但在 query 條件中的詞必須順序出現(xiàn)的,可以通過 slop
參數(shù)控制單詞間的間隔肩民,比如加上 "slop" :1
唠亚,表示中間可以有一個(gè)其他的字符。
POST /movies/_search
{
"query": {
"match_phrase": {
"title":{
"query": "one love"
"slop":1
}
}
}
}
了解完 Match Query持痰,讓我們?cè)賮砜聪?Term Query:
如果不希望 ES 對(duì)輸入語句作分詞處理的話灶搜,可以用 Term Query,將查詢語句作為整個(gè)單詞進(jìn)行查詢工窍,使用方法和 Match 類似割卖,只需要把 match
換為 term
就可以了,如下所示:
POST /users/_search
{
"query": {
"term": {
"username":"wupx"
}
}
}
Terms Query 顧名思義就是一次可以傳入多個(gè)單詞進(jìn)行查詢患雏,關(guān)鍵詞是 terms
鹏溯,如下所示:
POST /users/_search
{
"query": {
"terms": {
"username": [
"wupx",
"huxy"
]
}
}
}
另外 DSL 還支持特定的 Query String
的查詢抄肖,比如指定默認(rèn)查詢的字段名 default_field
就和前面介紹的 df
是一樣的达箍,在 query
中也可以使用 AND
來實(shí)現(xiàn)一個(gè)與的操作。
POST users/_search
{
"query": {
"query_string": {
"default_field": "username",
"query": "wupx AND huxy"
}
}
}
下面來看下 Simple Query String Query
腻异,它其實(shí)和 Query String
類似攻人,但是會(huì)忽略錯(cuò)誤的查詢語法取试,同時(shí)只支持部分查詢語法,不支持 AND
OR
NOT
怀吻,會(huì)當(dāng)作字符串處理瞬浓,Term 之間默認(rèn)的關(guān)系是 OR,可以指定 default_operator
來實(shí)現(xiàn) AND 或者 OR蓬坡,支持用 +
替代 AND猿棉,用 |
替代 OR磅叛,用 -
替代 NOT。
下面這個(gè)例子就是查詢 username
字段中同時(shí)包含 wu
和px
的請(qǐng)求:
{
"query": {
"simple_query_string": {
"query": "wu px",
"fields": ["username"],
"default_operator": "AND"
}
}
}
到此為止萨赁,我們就對(duì) DSL 做了個(gè)簡單介紹弊琴,更高階的 DSL 會(huì)在以后的文章中進(jìn)行介紹。
然后杖爽,我們來看下請(qǐng)求后返回的結(jié)果 Response 長什么樣吧敲董!
Response
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.9808292,
"hits" : [
{
"_index" : "users",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.9808292,
"_source" : {
"username" : "wupx",
"age" : "18"
}
}
]
}
}
其中 took
表示花費(fèi)的時(shí)間;total
表示符合條件的總文檔數(shù)慰安;hits
為結(jié)果集腋寨,默認(rèn)是前 10 個(gè)文檔;_index
為索引名化焕;_id
為文檔 id萄窜;_score
為相關(guān)性評(píng)分;_source
為文檔的原始信息撒桨。
搜索的相關(guān)性(Relevance)
那么我們平時(shí)在搜索的時(shí)候查刻,比如輸入小米手機(jī)
,會(huì)返回很多結(jié)果元莫,從用戶角度關(guān)心的有:是否找到所有相關(guān)的內(nèi)容赖阻,有多少不相關(guān)的內(nèi)容被返回了,比如輸入的小米手機(jī)
的時(shí)候不應(yīng)該返回糧食的小米給用戶踱蠢,同時(shí)文檔應(yīng)該按照打分的方式進(jìn)行排序,也就是搜索結(jié)果中的 _score
棋电,另外茎截,搜索引擎需要結(jié)合業(yè)務(wù)需求,平衡結(jié)果排名赶盔。
如何評(píng)估相關(guān)性企锌?
在信息檢索學(xué)中對(duì)相關(guān)性是有指標(biāo)去評(píng)估的,第一個(gè)是查準(zhǔn)率(Precision)于未,具體含義是盡可能返回較少的無關(guān)文檔給用戶撕攒;第二個(gè)為查全率(Recall),也就是盡量返回較多的相關(guān)文檔烘浦;第三個(gè)為是否能夠按照相關(guān)度進(jìn)行排序(Ranking)抖坪。
下面通過一張圖來對(duì)查準(zhǔn)率和查全率有一個(gè)更形象的理解:
其中黃色的三角形代表不相關(guān)的內(nèi)容,綠色的圓代表相關(guān)的內(nèi)容闷叉;在搜索結(jié)果中擦俐,黃色的三角形起名為 False Positive(納偽,簡寫 fp)握侧,通常稱作誤報(bào)蚯瞧,綠色的圓起名為 True Positive(納真嘿期,簡寫 tp);在沒有被搜索到的范圍中埋合,綠色的圓的起名為 False Negatives(去真备徐,簡寫 fn),也常稱作漏報(bào)甚颂,黃色的三角形起名為 True Negative(去偽坦喘,簡寫 tn)。
那么我們可以得到:
- 查準(zhǔn)率等于正確的搜索結(jié)果除以全部返回的結(jié)果西设,即 Precision = tp / ( tp + fp )
- 查全率等于正確的搜索結(jié)果除以所有應(yīng)該返回的結(jié)果瓣铣,即 Recall = tp / ( tp + fn )
在 ES 中提供了許多的查詢相關(guān)參數(shù)來改善搜索的 Precision 和 Recall。
總結(jié)
本文主要簡單介紹了 ES Search API 的兩種形式贷揽,學(xué)習(xí)了 URI Search 的基本方法棠笑,還學(xué)習(xí)了 Term Search 和 Phrase Search 的區(qū)別,同時(shí)介紹了什么叫搜索相關(guān)性禽绪,以及如何評(píng)估相關(guān)性蓖救。
參考文獻(xiàn)
《Elasticsearch技術(shù)解析與實(shí)戰(zhàn)》
Elastic Stack從入門到實(shí)踐
Elasticsearch頂尖高手系列
Elasticsearch核心技術(shù)與實(shí)戰(zhàn)
https://www.elastic.co/guide/en/elasticsearch/reference/7.1/search.html