ES入門系列 — 5 查詢語法

簡單寫一個ES系列,希望對NewSql、檢索有興趣的同學可以快速入手,爭取在7月份完成該系列文章

 

ES入門系列

1、Query String查詢

# 查詢在 tweet 類型中 tweet 字段包含 elasticsearch 單詞的所有文檔
GET /_all/tweet/_search?q=tweet:elasticsearch
# 上面等同於
GET /_all/tweet/_search?q=+name:john +tweet:mary
# 下面語句搜索返回包含 mary 的所有文檔。對 _all 字段進行查找,除非設置特定字段,否則查詢字符串就使# # 用 _all 字段進行搜索
GET GET /_search?q=mary

 

當索引一個文檔的時候,Elasticsearch 取出所有字段的值拼接成一個大的字符串,作爲 _all 字段進行索引。例如,當索引這個文檔時:

{
    "tweet":    "However did I manage before Elasticsearch?",
    "date":     "2014-09-14",
    "name":     "Mary Jones",
    "user_id":  1
}

這就好似增加了一個名叫 _all 的額外字段:

"However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1"

注意:剛開始開發一個應用時,_all 字段是一個很實用的特性。之後,你會發現如果搜索時用指定字段來代替 _all 字段,將會更好控制搜索結果。當 _all 字段不再有用的時候,可以將它置爲失效


查詢字符串搜索允許任何用戶在索引的任意字段上執行可能較慢且重量級的查詢,這可能會暴露隱私信息,甚至將集羣拖垮。因爲這些原因,不推薦直接向用戶暴露查詢字符串搜索功能,除非對於集羣和數據來說非常信任他們。相反,我們經常在生產環境中更多地使用功能全面的 request body 查詢API,除了能完成以上所有功能,還有一些附加功能。


 

2、請求體查詢

Elasticsearch 使用的查詢語言(DSL)擁有一套查詢組件,這些組件可以以無限組合的方式進行搭配。包括查詢模式和過濾模式

查詢模式:查詢就變成了一個“評分”的查詢。和不評分的查詢類似,也要去判斷這個文檔是否匹配,同時它還需要判斷這個文檔匹配的有 多好(匹配程度如何)。匹配程度越好評分越高,評分查詢計算每一個文檔與此查詢的 相關程度,同時將這個相關程度分配給表示相關性的字段 _score,並且按照相關性對匹配到的文檔進行排序。

過濾模式:查詢被設置成一個“不評分”或者“過濾”查詢。即,這個查詢只是簡單的問一個問題:“這篇文檔是否匹配?”。回答也是非常的簡單,yes 或者 no ,二者必居其一。

  • 查詢差別:過濾查詢(Filtering queries)只是簡單的檢查包含或者排除,這就使得計算起來非常快。考慮到至少有一個過濾查詢(filtering query)的結果是 “稀少的”(很少匹配的文檔),並且經常使用不評分查詢(non-scoring queries),結果會被緩存到內存中以便快速讀取。評分查詢(scoring queries)不僅僅要找出匹配的文檔,還要計算每個匹配文檔的相關性,計算相關性使得它們比不評分查詢費力的多。同時,查詢結果並不緩存。
  • 性能區別:由於倒排索引(inverted index),一個簡單的評分查詢在匹配少量文檔時可能與一個涵蓋百萬文檔的filter表現的一樣好,甚至會更好。但是在一般情況下,一個filter 會比一個評分的query性能更優異,並且每次都表現的很穩定。
  • 選取:使用查詢(query)語句來進行 全文 搜索或者其它任何需要影響 相關性得分 的搜索。除此以外的情況都使用過濾(filters)

一旦組成了詞項列表,這個查詢會對每個詞項逐一執行底層的查詢,再將結果合併,然後爲每個文檔生成一個最終的相關度評分。

 

3 查詢語句結構

3.1 空查詢

GET /_search
{

}
# 等同於下面查詢
GET /_search
{
    "query": {
        "match_all": {}
    }
}

 

3.2 簡單查詢結構

# 1、典型結構
{
    QUERY_NAME: {
        ARGUMENT: VALUE,
        ARGUMENT: VALUE,...
    }
}
# 2、如果針對某個字段
{
    QUERY_NAME: {
        FIELD_NAME: {
            ARGUMENT: VALUE,
            ARGUMENT: VALUE,...
        }
    }
}

 

eg :

GET /_search
{
    "match": {
        "tweet": "elasticsearch"
    }
}

# 上面查詢完整結構如下:
GET /_search
{
    "query": {
        "match": {
            "tweet": "elasticsearch"
        }
    }
}

 

3.3 複合查詢結構

查詢語句(Query clauses) 就像一些簡單的組合塊,這些組合塊可以彼此之間合併組成更復雜的查詢。這些語句可以是如下形式:

  • 葉子語句(Leaf clauses) (就像 match 語句) 被用於將查詢字符串和一個字段(或者多個字段)對比。
  • 複合(Compound) 語句 主要用於 合併其它查詢語句。 比如,一個 bool 語句 允許在你需要的時候組合其它語句,無論是 must 匹配、 must_not 匹配還是 should 匹配,同時它可以包含不評分的過濾器(filters)

複合查詢列子:

Get /_search
{
    "bool": {
        "must":     { "match": { "tweet": "elasticsearch" }},
        "must_not": { "match": { "name":  "mary" }},
        "should":   { "match": { "tweet": "full text" }},
        "filter":   { "range": { "age" : { "gt" : 30 }} }
    }
}

 

4 查詢模式

所有查詢會或多或少的執行相關度計算,但不是所有查詢都有分析階段。和一些特殊的完全不會對文本進行操作的查詢(如 bool 或 function_score )不同,文本查詢可以劃分成兩大家族:

基於詞項的查詢

如 term 或 fuzzy 這樣的底層查詢不需要分析階段,它們對單個詞項進行操作。用 term 查詢詞項 Foo 只要在倒排索引中查找 準確詞項 ,並且用 TF/IDF 算法爲每個包含該詞項的文檔計算相關度評分 _score 。

記住 term 查詢只對倒排索引的詞項精確匹配,這點很重要,它不會對詞的多樣性進行處理(如, foo 或 FOO )。這裏,無須考慮詞項是如何存入索引的。如果是將 ["Foo","Bar"] 索引存入一個不分析的( not_analyzed )包含精確值的字段,或者將 Foo Bar 索引到一個帶有 whitespace 空格分析器的字段,兩者的結果都會是在倒排索引中有 Foo 和 Bar 這兩個詞。

基於全文的查詢

像 match 或 query_string 這樣的查詢是高層查詢,它們瞭解字段映射的信息:

  • 如果查詢 日期(date) 或 整數(integer) 字段,它們會將查詢字符串分別作爲日期或整數對待。
  • 如果查詢一個( not_analyzed )未分析的精確值字符串字段,它們會將整個查詢字符串作爲單個詞項對待。
  • 但如果要查詢一個( analyzed )已分析的全文字段,它們會先將查詢字符串傳遞到一個合適的分析器,然後生成一個供查詢的詞項列表

 

4.1 match_all 查詢

match_all 查詢簡單的匹配所有文檔,在沒有指定查詢方式時,它是默認的查詢

{
    "match_all": { }
}

 

4.2 match 查詢

match 查詢是你可用的標準查詢。如果你在一個全文字段上使用 match 查詢,在執行查詢前,它將用正確的分析器去分析查詢字符串;如果在一個精確值的字段上使用它,例如數字、日期、布爾或者一個 not_analyzed 字符串字段,那麼它將會精確匹配給定的值。

# 全文字符串
{ "match": { "tweet": "About Search" }}
# 精確值,例如數字、日期、布爾或者一個 not_analyzed 字符串字段
{ "match": { "age":    26           }}
{ "match": { "date":   "2014-09-01" }}
{ "match": { "public": true         }}
{ "match": { "tag":    "full_text"  }}

 

4.3 match_all 查詢

match_all 查詢可以在多個字段上執行相同的 match 查詢

{
    "multi_match": {
        "query":    "full text search",
        "fields":   [ "title", "body" ]
    }
}

 

4.4 range 查詢

range 查詢找出那些落在指定區間內的數字或者時間

  • gt :大於
  • gte :大於等於
  • lt :小於
  • lte :小於等於
{
    "range": {
        "age": {
            "gte":  20,
            "lt":   30
        }
    }
}

 

4.5 term 查詢

term 查詢被用於精確值匹配,這些精確值可能是數字、時間、布爾或者那些 not_analyzed 的字符串

{ "term": { "age":    26           }}
{ "term": { "date":   "2014-09-01" }}
{ "term": { "public": true         }}
{ "term": { "tag":    "full_text"  }}

注意:term 和 terms 是 包含(contains) 操作,而非 等值(equals) (判斷)。說明一下:terms 查詢好比是 term 查詢的複數形式,多個term查詢詞項,或關係,只有命中其中一個term查詢項,及滿足

#比如下面查詢體
{ "term" : { "tags" : "search" } }
#可以匹配到下面兩個文檔,因爲term或者terms是查詢是基於包含關係
{ "tags" : ["search"] }
{ "tags" : ["search", "open_source"] } 

 

4.6 existing 查詢 和 missing 查詢

exists 查詢和 missing 查詢被用於查找那些指定字段中有值 (exists) 或無值 (missing) 的文檔。這與SQL中的 IS_NULL (missing) 和 NOT IS_NULL (exists) 在本質上具有共性

{
    "exists":   {
        "field":    "title"
    }
}

 

5 過濾模式

5.1 bool 查詢

你可以用 bool 查詢來實現你的需求。這種查詢將多查詢組合在一起,成爲用戶自己想要的布爾查詢。它接收以下參數:

  • must:文檔 必須 匹配這些條件才能被包含進來。
  • must_not:文檔 必須不 匹配這些條件才能被包含進來。
  • should:如果滿足這些語句中的任意語句,將增加 _score ,否則,無任何影響。它們主要用於修正每個文檔的相關性得分。
  • filter必須 匹配,但它以不評分、過濾模式來進行。這些語句對評分沒有貢獻,只是根據過濾標準來排除或包含文檔。

記住:如果沒有 must 語句,那麼至少需要能夠匹配其中的一條 should 語句。但如果存在至少一條 must 語句,則對 should 語句的匹配沒有要求。

下面的查詢用於查找 title 字段匹配 how to make millions 並且tag不爲 spam 的文檔。那些tag爲 starred 或在2014之後的文檔,將比另外那些文檔擁有更高的排名。如果 兩者 都滿足,那麼它排名將更高

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }},
            { "range": { "date": { "gte": "2014-01-01" }}}
        ]
    }
}

上面例子,如果我們不想因爲文檔的時間而影響得分,可以用 filter 語句來重寫上面的例子

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }}
        ],
        "filter": {
          "range": { "date": { "gte": "2014-01-01" }} 
        }
    }
}

上面例子,如果你需要通過多個不同的標準來過濾你的文檔,bool 查詢本身也可以被用做不評分的查詢。簡單地將它放置到 filter 語句中並在內部構建布爾邏輯

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }}
        ],
        "filter": {
          "bool": { 
              "must": [
                  { "range": { "date": { "gte": "2014-01-01" }}},
                  { "range": { "price": { "lte": 29.99 }}}
              ],
              "must_not": [
                  { "term": { "category": "ebooks" }}
              ]
          }
        }
    }
}

 

5.2 constant_score 查詢

constant_score 查詢也是你工具箱裏有用的查詢工具。它將一個不變的常量評分應用於所有匹配的文檔。它被經常用於你只需要執行一個 filter 而沒有其它查詢(例如,評分查詢)的情況下。可以使用它來取代只有 filter 語句的 bool 查詢。在性能上是完全相同的,但對於提高查詢簡潔性和清晰度有很大幫助

# term 查詢被放置在 constant_score 中,轉成不評分的 filter。這種方式可以用來取代只有 filter 語句的 bool 查詢
{
    "constant_score":   {
        "filter": {
            "term": { "category": "ebooks" } 
        }
    }
}

 

6 查詢精度控制

6.1 精度控制operator 

match 查詢還可以接受 operator 操作符作爲輸入參數,默認情況下該操作符是 or,也可以改成and 

#下面查詢必須同時命中brown、dog纔會命中查詢
GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": {      
                "query":    "BROWN DOG!",
                "operator": "and"
            }
        }
    }
}

 

6.2 精度控制minimum_should_match 

match 查詢支持 minimum_should_match 最小匹配參數,這讓我們可以指定必須匹配的詞項數用來表示一個文檔是否相關。我們可以將其設置爲某個具體數字,更常用的做法是將其設置爲一個百分數。minimum_should_match是向下取整,eg: minimum_should_match=75%,查詢詞項3個,則需要命中 3 * 75% = 2.25 ==> 向下取整等於2,既需要命中2個此項

#下面查詢需要命中2個詞項才認爲命中
GET /my_index/my_type/_search
{
  "query": {
    "match": {
      "title": {
        "query": "quick brown dog",
        "minimum_should_match": "75%"
      }
    }
  }
}

 

7 權重控制boost

boost 可以用來控制任何查詢語句的相對的權重,默認值1。boost 參數被用來提升一個語句的相對權重( boost 值大於 1 )或降低相對權重( boost 值處於 0 到 1 之間),但是這種提升或降低並不是線性的,換句話說,如果一個 boost 值爲 2 ,並不能獲得兩倍的評分 _score 。

GET /_search
{
    "query": {
        "bool": {
            "must": {
                "match": {  
                    "content": {
                        "query":    "full text search",
                        "operator": "and"
                    }
                }
            },
            "should": [
                { "match": {
                    "content": {
                        "query": "Elasticsearch",
                        "boost": 3 
                    }
                }},
                { "match": {
                    "content": {
                        "query": "Lucene",
                        "boost": 2 
                    }
                }}
            ]
        }
    }
}

 

 

8 分析及驗證

8.1 查詢分析

#假設mytype類型的title字段,使用默認的 standard 標準分析器,返回詞項 foxes
GET /my_index/_analyze
{
  "field": "my_type.title",   
  "text": "Foxes"
}
#假設mytype類型的english_title字段,使用默認的 english 標準分析器,返回詞項 fox
GET /my_index/_analyze
{
  "field": "my_type.english_title",   
  "text": "Foxes"
}

 

8.2 validate 驗證

req:

GET /gb/tweet/_validate/query
{
   "query": {
      "tweet" : {
         "match" : "really powerful"
      }
   }
}

resp:

{
  "valid" :         false,
  "_shards" : {
    "total" :       1,
    "successful" :  1,
    "failed" :      0
  }
}

 

8.3 explain參數

對於不合法查詢,explanations 給出不合法的具體信息;對於合法查詢,使用 explain 參數將返回可讀的描述,我們查詢的每一個 index 都會返回對應的 explanation ,因爲每一個 index 都有自己的映射和分析器。

eg1 不合法查詢:

GET /gb/tweet/_validate/query?explain 
{
   "query": {
      "tweet" : {
         "match" : "really powerful"
      }
   }
}

# 下面返回告訴:我們將查詢類型(match)與字段名稱 (tweet)搞混了
{
  "valid" :     false,
  "_shards" :   { ... },
  "explanations" : [ {
    "index" :   "gb",
    "valid" :   false,
    "error" :   "org.elasticsearch.index.query.QueryParsingException:
                 [gb] No query registered for [tweet]"
  } ]
}

eg2 合法查詢:

GET /_validate/query?explain
{
   "query": {
      "match" : {
         "tweet" : "really powerful"
      }
   }
}

# 下面返回,從 explanation 中可以看出,匹配 really powerful 的 match 查詢被重寫爲兩個針對 tweet # 字段的 single-term 查詢,一個single-term查詢對應查詢字符串分出來的一個term。
# 當然,對於索引 us ,這兩個 term 分別是 really 和 powerful ,而對於索引 gb ,term 則分別是     # realli 和 power 。之所以出現這個情況,是由於我們將索引 gb 中 tweet 字段的分析器修改爲 english # 分析器。
{
  "valid" :         true,
  "_shards" :       { ... },
  "explanations" : [ {
    "index" :       "us",
    "valid" :       true,
    "explanation" : "tweet:really tweet:powerful"
  }, {
    "index" :       "gb",
    "valid" :       true,
    "explanation" : "tweet:realli tweet:power"
  } ]
}

 

==========

待更新

==========

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章