title: ElasticSearch(七) 搜索
tags: ElasticSearch
author: Clown95
搜索
在前面,已經介紹了在ElasticSearch索引中處理數據的基礎知識,現在是時候進行核心功能的學習了。
搜索主要有兩種方式:
-
URI Search
- 操作簡便,方便通過命令行測試
- 但是僅包含部分查詢語法
-
Request Body Search
- es 最常用的方式,查詢豐富。
- 提供的完備查詢語法Query DSL(Domain Specific Language)
URI Search
URL檢索是通過提供請求參數純粹使用URI來執行搜索請求。
方法: curl -XGET "http://localhost:9200/movies/doc/_search?參數
,多個參數用&分開。
常用參數如下:
參數 | 描述 |
---|---|
q | 查詢字符串(映射到query_string查詢) |
df | 在查詢中不指定字段是默認查詢的字段,如果不指定字段,ES會查詢所有字段 |
analyzer | 分析查詢字符串時要使用的分析器名稱 |
sort | 排序,可以升序排序和降序排序 |
timeout | 指定超時時間,默認爲無超時 |
from | 返回的索引匹配結果的開始值,默認爲0 |
size | 要返回的搜索條數,默認爲10 |
default_operator | 要使用的默認運算符可以是AND或 OR,默認爲OR |
詳細參數查看官方文檔 :https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-uri-request.html
下面我們來了解下怎麼使用這些參數:
q
我們首先來了解下 q
的使用,現在我們來查找監獄風雲
的信息 :
GET /movies/doc/_search?q=%E7%9B%91%E7%8B%B1%E9%A3%8E%E4%BA%91
E7%9B%91%E7%8B%B1%E9%A3%8E%E4%BA%91 是監獄風雲的 url編碼
在線轉碼工具 :http://tool.oschina.net/encode?type=4
當然我們也可以指定字段查詢,例如:
GET /movies/doc/_search?q=title:%E7%9B%91%E7%8B%B1%E9%A3%8E%E4%BA%91
響應信息:
{
"took": 14,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 5.9509215,
"hits": [
{
"_index": "movies",
"_type": "doc",
"_id": "2",
"_score": 5.9509215,
"_source": {
"title": "監獄風雲1",
"director": "林嶺東",
"year": 1987,
"genres": [
"犯罪",
"劇情",
"動作"
],
"actors": [
"周潤發",
"梁家輝",
"張耀揚"
]
}
},
{
"_index": "movies",
"_type": "doc",
"_id": "3",
"_score": 3.8119292,
"_source": {
"title": "監獄風雲2",
"director": "林嶺東",
"year": 1991,
"genres": [
"犯罪",
"劇情",
"動作"
],
"actors": [
"周潤發",
"徐錦江",
"陳松勇"
]
}
},
{
"_index": "movies",
"_type": "doc",
"_id": "14",
"_score": 1.9059646,
"_source": {
"title": "澳門風雲3",
"director": "王晶",
"year": 2016,
"genres": [
"搞笑",
"動作"
],
"actors": [
"周潤發",
"劉德華",
"張家輝",
"張學友",
"余文樂"
]
}
}
]
}
}
我們可以看到每個被匹配出來的索引信息都有多個_score
和一個max_score
,這就是相關性評分。
默認情況下,Elasticsearch根據結果相關性評分來對結果集進行排序,所謂的「結果相關性評分」就是文檔與查詢條件的匹配程度。很顯然,排名第一的監獄風雲1
的title
字段明確的寫到監獄風雲
。但是爲什麼澳門風雲3
也會出現在結果裏呢?首先我們使用的是ES的默認分詞器,它會把中文內容分成單個漢字,所以只要包含監獄風雲
中任意一個字的文檔都會被匹配出來。又因爲監獄風雲1
完全匹配了監獄風雲
四個字,而澳門風雲3
只匹配了風雲
兩個字,所以監獄風雲1
的_score
比澳門風雲3
的_score
高
我們先了解下這個概念,後面我們再詳細介紹
sort
我們再來了解下排序sort
,我們根據year字段升序排列,例如:
GET /movies/doc/_search?q=title:%E7%9B%91%E7%8B%B1&sort=year:asc&pretty
asc 按升序排序,desc 按降序排序
from和size 分頁
_serarch
返回的內容默認只有10個文檔在hits數組中。我們的movies
有20個左右的文檔,那麼我們如何看到其他文檔?
和SQL使用LIMIT關鍵字返回只有一頁的結果一樣,Elasticsearch接受from和size參數,size: 表示查詢多少條文檔,默認10 ,from: 從第幾行開始,默認0
現在我們想要每頁顯示5個數據,顯示3頁內容:
GET /movies/doc/_search?&size=5&from=0
GET /movies/doc/_search?&size=5&from=5
GET /movies/doc/_search?&size=5&from=10
_source過濾
通常,GET 請求將返回文檔的全部,存儲在_source
參數中。但是可能你感興趣的字段只title
。 請求個別字段可以使用_source
參數。多個字段可以使用逗號分隔:
GET /movies/doc/1?_source=title,actors
操作符
Url Search 支持的操作符有:
- AND(&&), OR(||), NOT(!)
+
-
分別對應must和must not- > < >= <=
注意:
+
-
等符號url不能直接識別,要使用url encode後的結果纔可以。
現在我們來使用AND查詢分類即劇情
,又是同性
GET movies/movie/_search?q=genres:(%e5%89%a7%e6%83%85 AND %e5%90%8c%e6%80%a7)&pretty
或者使用&& ,在下面的命令中我們把 &轉成了%26
GET movies/movie/_search?q=genres:(%e5%89%a7%e6%83%85 %26%26 %e5%90%8c%e6%80%a7)&pretty
我們再來使用 +
,來進行多個匹配,只要任意滿足一個即可匹配到
GET movies/movie/_search?q=genres:(%e5%89%a7%e6%83%85 %2B %e5%90%8c%e6%80%a7)&pretty
我們還可以指定查找的範圍,例如我們查找大於1995年的信息:
curl -XGET "http://localhost:9200/movies/doc/_search?q=year:(>1995)"
查找大於1990且<2000年的信息:
GET /movies/doc/_search?q=year:(>1990 AND <2000)
Request Body Search
前面我們使用的Url search ,我們也可以使用請求正文來搜索信息,我們需要向請求正文中提供查詢,這種方法是我們最常用的搜索方式。
爲了使用ElasticSearch進行搜索,我們使用_search
端點,可選擇使用索引和類型。也就是說,按照以下模式向URL發出請求:
http://localhost:9200/<index>/<type>/_search
。其中,index和type都是可選的。
請求正文是一個JSON對象,除了其它屬性以外,它還要包含一個名稱爲“query”的屬性。這種查詢方法叫查詢DSL
。你可能想知道查詢DSL是什麼。它是ElasticSearch自己基於JSON的域特定語言,可以在其中表達查詢和過濾器。
{
"query": {
Query DSL here
}
}
Query DSL基於JSON定義的查詢語言,主要包含如下兩種類型:
-
字段類查詢 :如term, match, range等,只針對某一 個字段進行查詢
字段類查詢主要包括以下兩類:-
全文匹配
針對text類型的字段進行全文檢索,會對查詢語句先進行分詞處理,如match,match_ phrase等query類型 -
單詞匹配
不會對查詢語句做分詞處理,直接去匹配字段的倒排索引,如term, terms,range等query類型
-
-
複合查詢:如bool查詢等,包含一個或多個字段類查詢或者複合查詢語句。
query_string查詢
query_string
類似 Url serarch
裏面的參數q
。
我使用query_string
(查詢字符串),現在我們來查詢風雲
所對應的文檔。
POST movies/doc/_search
{
"query": {
"query_string": {
"query": "風雲"
}
}
}
term查詢
term 查詢,可以用它處理數字(numbers)、布爾值(Booleans)、日期(dates)以及文本(text),而不是全文本字段。term是代表完全匹配,也就是精確查詢,搜索前不會再對搜索詞進行分詞。
下面我們查詢指定的year
字段:
GET /movies/_search
{
"query" : {
"term": {
"year": {"value": 1994 }
}
}
}
6.X以上版本term
好像是無法直接查詢字符串內容,需要在字段後面添加.keyword
,例如:
GET /movies/doc/_search
{
"query": {
"term": {
"title.keyword": {
"value": "大話西遊"
}
}
}
}
既然已經提到了keyword
,我們就簡單的說下,title.keyword
,是es最新版本內置建立的field,就是不分詞的。所以一個title
過來的時候,會建立兩次索引,一次是自己本身,是要分詞的,分詞後放入倒排索引;另外一次是基於title.keyword,不分詞,保留256個字符最多,直接一個字符串放入倒排索引中。
如果一個字段既需要分詞搜索,又需要精準匹配,我們就可以通過增加keyword
字段來支持精準匹配。
{
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
這樣相當於有address
和address.keyword
兩個字段。
terms查詢
term
查詢對於查找單個值非常有用,但通常我們可能想搜索多個值。如果我們想搜索多個電影信息的文檔該如何處理?
我們不需要使用多個term
查詢,我們只要用terms
查詢,它相當於sql中的 in
。
使用的方法和term
一樣,現在我們來查詢電影分類是動作
和劇情
的文檔:
GET movies/movie/_search
{
"query": {
"terms": {
"genres.keyword": ["劇情","動作"]
}
}
}
可以看到我們只是把terms查詢的內容使用數組替代而已,其他基本上一樣。
match查詢
還記得我們之前使用URL參數搜索監獄風雲
的時候,我們還演示了指定爲title
字段進行搜索。我們再來使用match
查詢來達到這樣的效果,match查詢
它可以被認爲是基本的屬性搜索查詢(就是通過特定的一個或多個屬性來搜索)。
match搜索會先對搜索詞進行分詞,對於最基本的match搜索來說,只要搜索詞的分詞集合中的一個或多個存在於文檔中即可,例如,當我們搜索監獄風雲
,搜索詞會先分詞爲監獄
和風雲
,只要文檔中包含監獄
和風雲
任意一個詞,都會被搜索到 。
GET /movies/_search
{
"query" : {
"match" : { "title" : "監獄風雲" }
}
}
像這樣指定搜索內容爲title
字段這樣的設置稱爲fields
,可用於指定要搜索的字段列表。如果不使用fields
字段,ElasticSearch查詢將默認自動生成的名爲_all
的特殊字段,來基於所有文檔中的各個字段匹配搜索。
fuzzy查詢
我們再使用搜索引擎的時候,肯定出現過拼寫錯誤的情況,比如說我們搜索elasticsearch
但是我不小心拼錯成了elasticsearhc
,但是搜索引擎仍然給我搜索出正確的結果。
使用term
肯定是不行的,因爲它是精確查找,所以我們引出fuzzy
模糊查詢。
爲了方便演示,我們需要添加幾個索引信息。
PUT myname/doc/1
{
"name" :"clown"
}
PUT myname/doc/2
{
"name" :"clwon"
}
PUT myname/doc/3
{
"name" :"colwn"
}
現在我們需要查詢clown
這個文檔:
GET myname/doc/_search
{
"query": {
"fuzzy": {
"name": {
"value": "clown",
"fuzziness": 2
}
}
}
}
fuzziness
是你的搜索文本最多可以糾正幾個字母去跟你的數據進行匹配,默認如果不設置,就是2。
wildcard查詢
wildcard(通配符)查詢和prefix查詢類似,也是一個基於詞條的低級別查詢。但是它能夠讓你指定一個模式(Pattern),而不是一個前綴(Prefix)。它使用標準的shell通配符:?用來匹配任意字符,*用來匹配零個或者多個字符。
我們來演示下:
GET myname/doc/_search
{
"query": {
"wildcard": {
"name": {
"value": "cl*n"
}
}
}
}
multi_match查詢
如果我們需要指定多字段,可以使用multi_match
,來指定一個fields
屬性用來要搜索的字段數組。
GET /movies/_search?pretty
{
"query": {
"multi_match" : {
"query" : "監獄風雲",
"fields" : ["title"]
}
}
}
我們可以看到上面的請求正文,指定了一個查詢,並且使用multi_match
進行多個匹配。然後通過query
指定查詢內容,並通過fields
限制查詢的字段。
字段也可以通過通配符指定:
GET /movies/_search?pretty
{
"query": {
"multi_match" : {
"query" : "監獄風雲",
"fields" : ["tit*"]
}
}
}
filter過濾
filter 類似於 SQL 裏面的where 語句,和上面的基礎查詢比起來,也能實現搜索的功能,同時 filter不計算相關性,並且可以將查詢緩存到內存當中,因此,filter速度要快於query。tan
"filter": {
Filter to apply to the query
}
指定條件過濾
這時候我們就可以使用到過濾, 因爲我知道監獄風雲2是在1991年上映的,所以我使用year
過濾出1991年的監獄風雲
POST /_search
{
"query": {
"bool": {
"must": {
"query_string": {
"query": "監獄風雲"
}
},
"filter": {
"term": { "year": 1991 }
}
}
}
}
無查詢條件過濾
在上面的示例中,使用過濾器限制查詢字符串查詢的結果。如果我們想只是單純的過濾呢?也就是說,我們希望所有電影符合一定的標準。
在這種情況下,我們仍然在搜索請求正文中使用query
屬性。但是,我們不能只是添加一個過濾器,需要將它包裝在某種查詢中。
一個解決方案是修改當前的搜索請求,替換查詢字符串 query 過濾查詢中的match_all
查詢,這是一個查詢,匹配一切內容。
比如說我想過濾出所有電影中上映時間是1994年的電影:
POST /_search
{
"query": {
"bool": {
"must": {
"match_all": {
}
},
"filter": {
"term": { "year": 1994 }
}
}
}
}
過濾非空內容
GET /_search
{
"query": {
"bool": {
"filter": {
"exists": {
"field": "title"
}
}
}
}
}
過濾器緩存
ElasticSearch提供了一種特殊的緩存,即過濾器緩存(filter cache),用來存儲過濾器的結果,被緩存的過濾器並不需要消耗過多的內存(因爲它們只存儲了哪些文檔能與過濾器相匹配的相關信息),而且可供後續所有與之相關的查詢重複使用,從而極大地提高了查詢性能。
注意:ElasticSearch並不是默認緩存所有過濾器,以下過濾器默認不緩存:
numeric_range
script
geo_bbox
geo_distance
geo_distance_range
geo_polygon
geo_shape
and
or
not
exists,missing,range,term,terms默認是開啓緩存的
開啓方式:在filter查詢語句後邊加上 "_catch":true
constant_score
上面的查詢我們有一個更簡單的方法是使用常數分數查詢,該查詢能夠包含一個查詢或者一個過濾器,所有匹配文檔的相關度分值都爲1,不考慮TF/IDF:
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/_search" -d'
{
"query": {
"constant_score": {
"filter": {
"term": { "year": 1994 }
}
}
}
}'
range查詢
範圍查詢主要針對數值和日期類型,range 可以使用比較關鍵詞,也可以使用自帶參數。
比較關鍵詞:
- gte − 大於和等於
- gt − 大於
- lte − 小於和等於
- lt − 小於
參數:
- from - 從什麼時間或者數字開始,等同 gte
- to - 到什麼時間或者數字結束, 等同 lte
- include_lower - 是否包含範圍的左邊界,默認是true , 等同gt
- include_upper - 是否包含範圍的右邊界,默認是true,等同 lt
我們先來查詢下數值範圍
POST movies/movie/_search
{
"query": {
"range": {
"year": {
"gte": 1990,
"lte": 2000
}
}
}
}
當然也可以使用:
POST movies/movie/_search
{
"query": {
"range": {
"year": {
"from": 1990,
"to": 2000
}
}
}
}
因爲我們之前沒有含日期的索引,所以我們現在來創建幾個。
PUT students/doc/1
{
"name" :"Jay",
"birth" :"1995-06-07"
}
PUT students/doc/2
{
"name" :"Arlis",
"birth" :"1990-02-02"
}
PUT students/doc/3
{
"name" :"Nike",
"birth" :"1980-02-02"
}
PUT students/doc/4
{
"name" :"Yves",
"birth" :"2004-05-06"
}
好了現在我們已經有數據,開始查詢:
POST students/doc/_search
{
"query": {
"range": {
"birth": {
"gte": "2000-01-01"
}
}
}
}
日期提供一種更友好的計算方式
POST students/doc/_search
{
"query": {
"range": {
"birth": {
"gte": "now-20y"
}
}
}
}
now
代表當前系統時間, 20y
代表20年 , now-20y
即在現在的時間基礎上減少20年
日期計算的操作符主要有三個 +
-
/d
,例如:
- +1h 加一小時
- -1h 減一小時
- /d 將時間舍入到天
match_phrase查詢
目前我們可以在字段中搜索單獨的一個詞,但是有時候你想要確切的匹配若干個單詞或者短語(phrases). 我們只需要把match
變爲match_phrase
即可。
match_phrase
和match
的區別,match_phrase
首先分析查詢字符串,從分析後的文本中構建短語查詢,這意味着必須匹配短語中的所有分詞,並且保證各個分詞的相對位置不變,通俗的講就是返回的結果,跟搜索內容的分詞的順序是一樣的。
例如:
GET movies/movie/_search
{
"query": {
"match_phrase": {
"title": "羅馬假日"
}
}
}
上面我們使用了match_phrase
搜索了 羅馬假日
,能夠得到索引信息。
下面我們搜索假日羅馬
GET movies/movie/_search
{
"query": {
"match_phrase": {
"title": "假日羅馬"
}
}
}
我們發現沒有得到任何結果。
match_phrase_prefix 查詢
我們還可以使用match_phrase_prefix
來查詢前綴, 爲什麼是match_phrase_prefix
而不是match_prefix
其實也很好理解,因爲前綴是需要順序的,必須在前面。
GET movies/movie/_search
{
"query": {
"match_phrase_prefix": {
"title": "霸王"
}
}
}
sort排序
當搜索的字段有多個時,可以對指定字段進行排序。注意的是文本內容不能進行排序。
我們使用sort
指定字段進行排序, 可以使用order
指定排序方法。
order的值可以爲:
- asc 進行升序排序
- desc 進行降序排序
下面我們對year
進行升序排序:
GET /movies/_search?pretty
{
"query": {
"match_all":{
}
},
"sort": [
{"year": {"order": "asc"}}
]
}
對year
進行降序排序:
GET /movies/_search?pretty
{
"query": {
"match_all":{
}
},
"sort": [
{"year": {"order": "desc"}}
]
}
當一個字段的內容有多個值的時候,系統支持一些計算進行排序,包括min、max、sum、avg、median (中間值)。
mode | 描述 |
---|---|
min | 選擇最低值。 |
max | 選擇最高值。 |
sum | 使用所有值的總和作爲排序值。僅適用於基於數字的數組字段。 |
avg | 使用所有值的平均值作爲排序值。僅適用於基於數字的數組字段。 |
median | 使用所有值的中位數作爲排序值。僅適用於基於數字的數組字段。 |
我們來演示下用法:
GET /movies/_search?pretty
{
"query": {
"match_all":{
}
},
"sort": [
{"year": {"order": "asc","mode":"min"}}
]
}
from size分頁
實現分頁搜索只需要指定from
和size
字段即可,例如:
GET /movies/_search?pretty
{
"from": 0,
"size": 15,
"query": {
"match_all":{
}
}
}
注意:ES的from、size分頁不是真正的分頁,稱之爲淺分頁。from+ size不能超過index.max_result_window 默認爲10,000 的索引設置
scroll滾動
我們在前面說過from、size
分頁是淺分頁,而且最多隻能顯示10000個數據,如果一次性要查出來比如10萬條數據,淺分頁顯然滿足不了我們的要求。此時一般會採取用scoll
滾動查詢,一批一批的查,直到所有數據都查詢完爲止。
scoll
搜索會在第一次搜索的時候,保存一個當時的視圖快照,後續的對文檔的改動(索引、更新或者刪除)都只會影響後面的搜索請求。scoll
採用基於_doc
(不使用_score)進行排序的方式,性能較高.
爲了提現查詢的效果我們添加點數據。
wget https: raw.githubusercontent.com/elastic/elasticsearch/master/docs/src/test/resources/accounts.json
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/account/_bulk" --data-binary "@accounts.json"
爲了使用 scroll,初始搜索請求應該在查詢中指定scroll
參數,這可以告訴 Elasticsearch 需要保持搜索的上下文環境多久,如 scroll=1m
,1m是一分鐘的意思,如果數據很大,那麼建議等待時間長一點。
GET /bank/account/_search?scroll=5m
{
"query": {
"range": {
"age": {
"gte": 20,
"lte": 40
}
}
}
}
使用上面的請求返回的結果中包含一個scroll_id
,這個 ID 可以被傳遞給 scroll API 來檢索下一個批次的結果。
我們用這個scroll_id
來查詢
GET /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAE5FmcxbnNvb0ZKU0Fpb3BMRUFmeEJuaEEAAAAAAAABNxZnMW5zb29GSlNBaW9wTEVBZnhCbmhBAAAAAAAAAToWZzFuc29vRkpTQWlvcExFQWZ4Qm5oQQAAAAAAAAE7FmcxbnNvb0ZKU0Fpb3BMRUFmeEJuaEEAAAAAAAABOBZnMW5zb29GSlNBaW9wTEVBZnhCbmhB"
}
當然這個響應消息還會返回一個scroll_id
,如果我們需要不斷的往下查找就需要不斷的查詢這些scroll_id
。
highlight高亮搜索
很多應用喜歡從每個搜索結果中高亮(highlight)匹配到的關鍵字,這樣用戶可以知道爲什麼這些文檔和查詢相匹配。在Elasticsearch中高亮片段是非常容易的,我們只需要添加highlight
關鍵字。
請求信息:
GET students/doc/_search
{
"query" : {
"match" : {
"title" : "盧旺達飯店"
}
},
"highlight": {
"fields" : {
"title" : {}
}
}
}
響應信息:
{
"took": 6,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 4.9041457,
"hits": [
{
"_index": "movies",
"_type": "doc",
"_id": "11",
"_score": 4.9041457,
"_source": {
"title": "盧旺達飯店",
"director": "特瑞·喬治",
"year": 2004,
"genres": [
"戰爭",
"劇情",
"歷史"
],
"actors": [
"唐·錢德爾",
"蘇菲·奧康內多",
"傑昆·菲尼克斯"
]
},
"highlight": {
"title": [
"<em>盧</em><em>旺</em><em>達</em><em>飯</em><em>店</em>"
]
}
}
]
}
}
響應的內容與之前結果相同,但是在返回結果中會有一個新的部分叫做highlight
,這裏包含了來自titile
字段中的文本,並且用<em></em>
來標識匹配到的內容。
simple_query_string 查詢
Simple_Query_String
類似Query_String
,但是會忽略錯誤的查詢語法,並且僅支持部分查詢語法。
來看下面的例子 ,我們使用|
查詢包含羅馬或者盧旺達的信息,但是符號附近有一些垃圾字符。
GET movies/movie/_search
{
"query": {
"simple_query_string": {
"query": "羅馬 / | 、盧旺達",
"fields": ["title"]
}
}
}
我們成功執行了命令。
現在我們把simple_query_string
換成query_string
,在來試試。
GET movies/movie/_search
{
"query": {
"query_string": {
"query": "羅馬 / | 、盧旺達",
"fields": ["title"]
}
}
}
我們執行命令,發現報錯
bool查詢
bool 查詢允許我們使用布爾邏輯將小的查詢組成大的查詢。
AND查詢
比如說我們需要同時匹配到 title 含有 監獄
和 風雲
的索引信息。
請求內容:
GET /movies/_search
{
"query":{
"bool":
{"must":[
{"match":{"title":"監獄"}},
{"match":{"title":"風雲"}}
]
}
}
}
在上述示例中,`bool` `must` 子句指定了所有匹配文檔必須滿足的條件。
-
OR查詢
相比之下,bool
should
的組合,兩個match查詢並且返回所有title
屬性中包含監獄
或西遊
的任意信息。請求內容:
GET /movies/_search { "query":{ "bool": {"should":[ {"match":{"title":"監獄"}}, {"match":{"title":"西遊"}} ] } } }
在上述例子中
bool
should
子句指定了匹配文檔只要滿足其中的任何一個條件即可匹配。
AND取反查詢
我們還可以使用 `bool` 和`must_not`, 查詢屬性中不包含匹配內容的文檔。
請求內容:
```bash
GET /movies/_search
{
"query":{
"bool":
{"must_not":[
{"match":{"title":"監獄"}},
{"match":{"title":"西遊"}}
]
}
}
}
```
在上述例子中,`bool` `must_not`子句指定了其中的任何一個條件都不滿足時即可匹配
布爾組合查詢
我們可以組合 must 、should 、must_not 進行復雜的查詢。
- A AND NOT B
GET /movies/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "監獄風雲" } }
],
"must_not": [
{ "match": { "year": 1991 } }
]
}
}
}
- A AND (B OR C)
GET /movies/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {}
},
{
"bool": {
"should": [
{
"match": {"genres": "同性"}
},
{"match": {"genres": "科幻"}
}
]}
}
]
}
}
}
aggs聚合查詢
lasticsearch有一個功能叫做聚合(aggregations),它允許你在數據上生成複雜的分析統計。它很像SQL中的GROUP BY但是功能更強大。
準備數據
我們先插入一些數據:
POST /_bulk
{"index":{"_index":"goods","_type":"doc","_id":1}}
{"gname":"薯片","price":5.00,"classes":"零食","num":100}
{"index":{"_index":"goods","_type":"doc","_id":2}}
{"gname":"方便麪","price":3.20,"classes":"零食","num":200}
{"index":{"_index":"goods","_type":"doc","_id":3}}
{"gname":"毛巾" ,"price":13.50, "classes":"日用品","num":60}
{"index":{"_index":"goods","_type":"doc","_id":4}}
{"gname":"麪包" ,"price":4.50,"classes":"零食", "num":80}
{"index":{"_index":"goods","_type":"doc","_id":5}}
{"gname":"牙刷" ,"price":4.50, "classes":"日用品","num":100}
{"index":{"_index":"goods","_type":"doc","_id":6}}
{"gname":"可樂" ,"price":3, "classes":"零食","num":100}
Max求最大值
現在我們來查詢剛剛到商品中價格最高的:
GET goods/doc/_search
{
"aggs": {
"price_of_max": {
"max": {
"field": "price"
}
}
}
}
可以在aggregations
中得到查詢的結果
{
...
"aggregations": {
"maxprice": {
"value": 13.5
}
}
Sum求和
現在我們來統計所有商品的單價和:
如果我們想要屏蔽查詢索引具體內容,可以使用size
,把查詢數量設爲0
GET goods/doc/_search
{
"size" :0,
"aggs": {
"price_of_sum": {
"sum": {
"field": "price"
}
}
}
}
響應信息:
{
...
"aggregations": {
"price_of_sum": {
"value": 33.700000047683716
}
}
}
可以看到和爲 33.7
avg求平均值
下面我們計算商品的平均價格
GET goods/doc/_search
{
"size":0,
"aggs": {
"price_of_avg": {
"avg": {
"field": "price"
}
}
}
}
響應信息:
{
...
"aggregations": {
"price_of_cardi": {
"value": 5.616666674613953
}
}
}
cardinality求基數
GET goods/doc/_search
{
"size":0,
"aggs": {
"price_of_cardi": {
"cardinality": {
"field": "price"
}
}
}
}
terms分組
我們來通過商品類別分組並統計商品數量:
GET goods/doc/_search
{
"size":0,
"aggs": {
"price_group_by": {
"terms": {
"field": "classes.keyword"
}
}
}
}
響應信息:
...
...
{
"aggregations": {
"price_group_by": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "零食",
"doc_count": 4
},
{
"key": "日用品",
"doc_count": 2
}
]
}
}
}
我們可以看到零食的數量爲4 ,日用品數量爲2
Source過濾
數據列過濾允許在查詢的時候不顯示原始數據,或者顯示部分原始字段。
不顯示原始文檔
GET /_search
{
"_source": false,
"query" : {
"term": {"year": {"value": 1994}}
}
}
顯示部分文檔列
比如說我們只想顯示movies
中title
和actors
信息。
GET /_search
{
"_source": "actors",
"query" : {
"term": {"year": {"value": 1994}}
}
}
包含或者排除某些列
我們還可以使用 includes
包含某字段,或者使用excludes
排除某字段。
例如:
GET /_search
{
"_source": {
"includes": [ "title", "director" ],
"excludes": [ "year" ]
},
"query" : {
"term": {"year": {"value": 1994}}
}
}
相關性評分
我們曾經講過,默認情況下,返回結果是按相關性倒序排列的。 但是什麼是相關性? 相關性如何計算?
每個文檔都有相關性評分,用一個相對的浮點數字段_score
來表示 _score
的評分越高,相關性越高。查詢語句會爲每個文檔添加一個 _score
字段,評分的計算方式取決於不同的查詢類型。
不同的查詢語句用於不同的目的:
- fuzzy 查詢會計算與關鍵詞的拼寫相似程度
- terms查詢會計算 找到的內容與關鍵詞組成部分匹配的百分比
但是一般意義上我們說的全文本搜索是指計算內容與關鍵詞的類似程度。
ElasticSearch的相似度算法被定義爲 TF/IDF,即檢索詞頻率/反向文檔頻率,包括一下內容:
- 檢索詞頻率::
檢索詞在該字段出現的頻率,出現頻率越高,相關性也越高。 字段中出現過5次要比只出現過1次的相關性高。 - 反向文檔頻率::
每個檢索詞在索引中出現的頻率,頻率越高,相關性越低。 檢索詞出現在多數文檔中會比出現在少數文檔中的權重更低, 即檢驗一個檢索詞在文檔中的普遍重要性。 - 字段長度準則::
字段的長度是多少,長度越長,相關性越低。 檢索詞出現在一個短的 title 要比同樣的詞出現在一個長的 content 字段。
單個查詢可以使用TF/IDF評分標準或其他方式,比如短語查詢中檢索詞的距離或模糊查詢裏的檢索詞相似度。
相關性並不只是全文本檢索的專屬。也適用於yes|no的子句,匹配的子句越多,相關性評分越高。
如果多條查詢子句被合併爲一條複合查詢語句,比如 bool 查詢,則每個查詢子句計算得出的評分會被合併到總的相關性評分中。
ElasticSearch提供了一個explain參數,將 explain 設爲 true 就可以得到 _score
詳細的信息。
GET /movies/_search?explain=true
{
"query": {
"match": {
"title": "龍貓"
}
}
}
具體的響應消息因爲文本太多,自己執行命令查詢 ,我們這裏只分析幾個重要信息
"_shard": "[movies][3]"
"_node": "g1nsooFJSAiopLEAfxBnhA",
這裏加入了該文檔來自於哪個節點哪個分片上的信息,這對我們是比較有幫助的,因爲詞頻率和 文檔頻率是在每個分片中計算出來的,而不是每個索引中。
_explanation
會包含在每一個入口,告訴你採用了哪種計算方式,並讓你知道計算的結果以及其他詳情:
"description":"score(doc=3,freq=1.0 = termFreq=1.0)), product of:"
檢索詞的頻率
"description":"idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:"
反向文檔頻率
"description":"docCount",
字符長度準則