【ES】ElasticSearch 結構化查詢和過濾

[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 子句允許合并其他的合法子句,mustmust_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, jogsprint的文檔
  • 同時包含著 quick, brownfox--- 單詞間離得越近,該文檔的相關性越高
  • 標識著 lucene, searchjava--- 標識詞越多,該文檔的相關性越高

一條查詢語句會計算每個文檔與查詢語句的相關性,然后給出一個相關性評分 _score,并且按照相關性對匹配到的文檔進行排序。

2.2、Filter DSL

在上下文過濾語境中,查詢語句主要解決文檔是否匹配的問題,而不會在意匹配程度(相關性評分)。

例如:

  • created 的日期范圍是否在 20132014 ?
  • 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

termsterm 有點類似,但 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 權威指南

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容