[TOC]
一、DSL
在 ElasticSearch 中,提供了功能十分豐富、多種表現形式的查詢語言—— DSL 查詢。
Query DSL 又叫結構化查詢,使用 JSON 格式的請求體 與 ElasticSearch 交互,使查詢語句更靈活、更精確、更易讀且易調試。
使用結構化查詢,你需要傳遞 query
參數:
GET /_search
{
"query": YOUR_QUERY_HERE
}
主要包含兩種類型的查詢語句:葉子查詢語句和復合查詢語句。
1.1、葉子查詢語句
這種查詢可以單獨使用,針對指定的字段查詢指定的值,例如:match, term, range 等。
一個葉子查詢語句一般使用這種結構:
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
或指向一個指定的字段:
{
QUERY_NAME: {
FIELD_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
}
例如,可以使用 match
查詢子句用來找尋在 tweet
字段中找尋包含 elasticsearch
的成員:
GET /_search
{
"query": {
"match": {
"tweet": "elasticsearch"
}
}
}
1.2、復合查詢語句
這種查詢可以合并其他的葉子查詢或復合查詢,從而實現非常復雜的查詢邏輯。
例如,bool
子句允許合并其他的合法子句,must
,must_not
或者 should
:
{
"bool": {
"must": { "match": { "tweet": "elasticsearch" }},
"must_not": { "match": { "name": "mary" }},
"should": { "match": { "tweet": "full text" }}
}
}
二、Query DSL 和 Filter DSL
Elasticsearch 使用的查詢語言(DSL) 擁有一套查詢組件,這些組件可以以無限組合的方式進行搭配。這套組件可以在以下兩種情況下使用:查詢情況 query context
和過濾情況 filtering context
,也即結構化查詢 Query DSL
和結構化過濾 Filter DSL
。
查詢與過濾語句非常相似,但是它們由于使用目的不同而稍有差異。
2.1、Query DSL
在上下文查詢語境中,查詢語句會詢問文檔與查詢語句的匹配程度,它會判斷文檔是否匹配并計算相關性評分(_score)的值。
例如:
- 查找與
full text search
這個詞語最佳匹配的文檔 - 查找包含單詞
run
,但是也包含runs
,running
,jog
或sprint
的文檔 - 同時包含著
quick
,brown
和fox
--- 單詞間離得越近,該文檔的相關性越高 - 標識著
lucene
,search
或java
--- 標識詞越多,該文檔的相關性越高
一條查詢語句會計算每個文檔與查詢語句的相關性,然后給出一個相關性評分 _score
,并且按照相關性對匹配到的文檔進行排序。
2.2、Filter DSL
在上下文過濾語境中,查詢語句主要解決文檔是否匹配的問題,而不會在意匹配程度(相關性評分)。
例如:
-
created
的日期范圍是否在2013
到2014
? -
status
字段中是否包含單詞 "published" ? -
lat_lon
字段中的地理位置與目標點相距是否不超過10km ?
2.3、比較
相關度:
-
filter
—— 只根據搜索條件過濾出符合的文檔,將這些文檔的評分固定為1,忽略 TF/IDF 信息,不計算相關度分數; -
query
—— 先查詢符合搜索條件的文檔, 然后計算每個文檔對于搜索條件的相關度分數,再根據評分倒序排序。
性能:
-
filter
性能更好,無排序 —— 不計算相關度分數,不用根據相關度分數進行排序,同時 ES 內部還會緩存(cache)比較常用的 filter 的數據 (使用bitset <0或1> 來記錄包含與否); -
query
性能較差, 有排序 —— 要計算相關度分數, 要根據相關度分數進行排序, 并且沒有cache功能。
原則上來說,使用查詢語句做全文本搜索或其他需要進行相關性評分的時候,剩下的全部用過濾語句。
在進行搜索時,常常會在查詢語句中,結合查詢和過濾來達到查詢目的:
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"range": { "date": { "gte": "2014-01-01" }}
}
}
}
三、重要的查詢過濾語句
3.1、match
match
查詢是一個標準查詢,不管全文本查詢還是精確查詢基本上都要用到它。
如果使用 match
查詢一個全文本字段,它會在真正查詢之前用分析器先分析查詢字符:
{
"match": {
"tweet": "About Search"
}
}
如果用match
下指定了一個確切值,在遇到數字,日期,布爾值或者not_analyzed
的字符串時,它將搜索給定的值:
{ "match": { "age": 26 }}
{ "match": { "date": "2014-09-01" }}
{ "match": { "public": true }}
{ "match": { "tag": "full_text" }}
提示: 做精確匹配搜索時,最好用過濾語句,因為過濾語句可以緩存數據。
3.2、multi_match
multi_match
查詢允許做match
查詢的基礎上同時搜索多個字段:
{
"multi_match": {
"query": "full text search",
"fields": [ "title", "body" ]
}
}
3.3、match_phrase
短語查詢,精確匹配。查詢a red
會匹配包含a red
短語的,而不會進行分詞查詢,也不會查詢出包含a 其他詞 red
這樣的文檔。
{
"query": {
"match_phrase": {
"ad": "a red"
}
}
}
3.4、match_all
使用match_all
可以查詢到所有文檔,是沒有查詢條件下的默認語句:
{
"match_all": {}
}
此查詢常用于合并過濾條件。 比如說需要檢索所有的郵箱,所有的文檔相關性都是相同的,所以得到的_score
為1。
3.5、term
term
主要用于精確匹配哪些值,比如數字,日期,布爾值或 not_analyzed
的字符串(即不進行分詞器分析,文檔中必須包含整個搜索的詞匯):
{ "term": { "age": 26 }}
{ "term": { "date": "2014-09-01" }}
{ "term": { "public": true }}
{ "term": { "tag": "full_text" }}
3.6、terms
terms
跟 term
有點類似,但 terms
允許指定多個匹配條件。 如果某個字段指定了多個值,那么文檔需要一起去做匹配,類似于 MySQL 的 in 條件:
{
"terms": {
"tag": [ "search", "full_text", "nosql" ]
}
}
3.7、range
range
允許按照指定范圍查找一批數據:
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
范圍操作符包含:
-
gt
:: 大于 -
gte
:: 大于等于 -
lt
:: 小于 -
lte
:: 小于等于
3.8、exists
用于查找那些指定字段中有值或無值的文檔。
指定title
字段有值:
{
"exists": {
"field": "title"
}
}
指定title
字段無值:
{
"query": {
"bool": {
"must_not": {
"exists": {
"field": "group"
}
}
}
}
}
注:missing 查詢無值已經被取消。
3.9、bool
bool
可以用來合并多個條件查詢結果的布爾邏輯,它包含一下操作符:
-
must
:: 多個查詢條件的完全匹配,相當于and
-
should
:: 至少有一個查詢條件匹配,相當于or
-
must_not
:: 多個查詢條件的相反匹配,相當于not
,忽略相關性評分 -
filter
:: 必須匹配,忽略相關性評分
POST /_search
{
"query": {
"bool" : {
"must" : {
"term" : { "last_name" : "smith" }
},
"filter": {
"term" : { "info.interests" : "musics" }
},
"must_not" : {
"range" : {
"info.age" : { "gte" : 10, "lte" : 25 }
}
},
"should" : [
{ "term" : { "full_name" : "john" } },
{ "term" : { "full_name" : "smith" } }
]
}
}
}
提示: 如果bool
查詢下沒有must
子句,那至少應該有一個should
子句。但是 如果有must
子句,那么沒有should
子句也可以進行查詢。
四、驗證查詢
查詢語句可以變得非常復雜,特別是與不同的分析器和字段映射相結合后,就會有些難度。
validate
API 可以驗證一條查詢語句是否合法。
GET /gb/tweet/_validate/query
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
請求的返回值說明這條語句是非法的:
{
"valid" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
}
}
想知道語句非法的具體錯誤信息,需要加上 explain
參數:
GET /gb/tweet/_validate/query?explain
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
explain
參數可以提供語句錯誤的更多詳情,很顯然,這里把 query 語句的 match
與字段名位置弄反了。
五、參考資料
ES 權威指南