ElasticSearch 5學習(10)——結構化查詢(包括新特性)

以前咱們全部的查詢都屬於命令行查詢,可是不利於複雜的查詢,並且通常在項目開發中不使用命令行查詢方式,只有在調試測試時使用簡單命令行查詢,可是,若是想要善用搜索,咱們必須使用請求體查詢(request body search)API。之因此這麼稱呼,是由於大多數的參數以JSON格式所容納而非查詢字符串。請求體查詢,並不只僅用來處理查詢,並且還能夠高亮返回結果中的片斷,而且給出幫助你的用戶找尋最好結果的相關數據建議。html

空查詢

咱們以最簡單的search API開始,空查詢將會返回索引中全部的文檔。java

GET /_search
{}

同字符串查詢同樣,能夠查詢一個,多個或_all索引(indices)或類型(types):sql

也可使用from及size參數進行分頁:數組

GET /_search
{
  "from": 30,
  "size": 10
}

須要注意的是:攜帶內容的GET請求?緩存

任何一種語言(特別是js)的HTTP庫都不容許GET請求中攜帶交互數據。 事實上,有些用戶很驚訝GET請求中竟然會容許攜帶交互數據。nosql

真實狀況是,http://tools.ietf.org/html/rfc7231#page-24[RFC 7231], 一份規定HTTP語義及內容的RFC中並未規定GET請求中容許攜帶交互數據! 因此,有些HTTP服務容許這種行爲,而另外一些(特別是緩存代理),則不容許這種行爲。elasticsearch

Elasticsearch的做者們傾向於使用GET提交查詢請求,由於他們以爲這個詞相比POST來講,能更好的描述這種行爲。然而,由於攜帶交互數據的GET請求並不被普遍支持,因此searchAPI一樣支持POST請求,相似於這樣:ide

POST /_search
{
  "from": 30,
  "size": 10
}

這個原理一樣應用於其餘攜帶交互數據的GET API請求中。工具

結構化查詢 Query DSL

結構化查詢是一種靈活的,多表現形式的查詢語言。 Elasticsearch在一個簡單的JSON接口中用結構化查詢來展示Lucene絕大多數能力。 你應當在你的產品中採用這種方式進行查詢。它使得你的查詢更加靈活,精準,易於閱讀而且易於debug。性能

使用結構化查詢,你須要傳遞query參數:

GET /_search
{
    "query": 發查詢體放置於此便可
}

空查詢 - {} - 在功能上等同於使用match_all查詢子句,正如其名字同樣,匹配全部的文檔:

GET /_search
{
    "query": {
        "match_all": {}  #查詢體
    }
}

查詢子句

一個查詢子句通常使用這種結構:

#整個屬於查詢體
{
    QUERY_NAME(查詢命令): {
        ARGUMENT: VALUE,
        ARGUMENT: VALUE,...
    }
}

或指向一個指定的字段:

#整個屬於查詢體
{
    QUERY_NAME(查詢命令): {
        FIELD_NAME(匹配字段): {
            ARGUMENT: VALUE,
            ARGUMENT: VALUE,...
        }
    }
}

例如,你可使用match查詢子句用來找尋在tweet字段中找尋包含,elasticsearch的成員:

{
    "match"(查詢命令): {
        "tweet": "elasticsearch"
    }
}

完整的查詢請求會是這樣:

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

合併多子句

查詢子句就像是搭積木同樣,能夠合併簡單的子句爲一個複雜的查詢語句,好比:

  • 葉子子句(leaf clauses)(好比match子句)用以在將查詢字符串與一個字段(或多字段)進行比較
  • 複合子句(compound)用以合併其餘的子句。例如,bool子句容許你合併其餘的合法子句,mustmust_not或者should,若是可能的話:
{
    "bool": {
        "must":     { "match": { "tweet": "elasticsearch" }},
        "must_not": { "match": { "name":  "mary" }},
        "should":   { "match": { "tweet": "full text" }}
    }
}

複合子句能合併任意其餘查詢子句,包括其餘的複合子句。這就意味着複合子句能夠相互嵌套,從而實現很是複雜的邏輯。

如下實例查詢的是郵件正文中含有「business opportunity」字樣的星標郵件或收件箱中正文中含有「business opportunity」字樣的非垃圾郵件:

#整個屬於查詢體
{
    "bool": {
        "must": { "match":      { "email": "business opportunity" }},
        "should": [
             { "match":         { "starred": true }},
             { "bool": {
                   "must":      { "folder": "inbox" },
                   "must_not":  { "spam": true }
             }}
        ],
        "minimum_should_match": 1
    }
}

查詢與過濾

Elasticsearch使用的DSL具備一組稱爲查詢的組件,它們能夠混合並以無窮組合進行匹配。這一組組件能夠在兩個上下文中使用:過濾上下文和查詢上下文。

當用於過濾上下文時,該查詢被稱爲「非評分」或「過濾」查詢。也就是說,查詢只詢問問題:「此文檔是否匹配?」。答案老是一個簡單的二進制yes|no。

  • created的日期範圍是否在 2013 到 2014 ?
  • status字段中是否包含單詞 "published" ?
  • lat_lon字段中的地理位置與目標點相距是否不超過10km ?

當在查詢上下文中使用時,查詢變爲「評分」查詢。相似於其非評分兄弟,這肯定文檔是否匹配以及文檔匹配的程度。

查詢的典型用法:

  • 查找與 full text search 這個詞語最佳匹配的文檔
  • 查找包含單詞 run ,可是也包含runs, running, jogsprint的文檔
  • 同時包含着 quick, brownfox --- 單詞間離得越近,該文檔的相關性越高
  • 標識着 lucene, searchjava --- 標識詞越多,該文檔的相關性越高

評分查詢計算每一個文檔與查詢的相關程度,併爲其分配相關性_score,稍後用於按相關性對匹配文檔進行排序。這種相關性的概念很是適合於全文搜索,其中不多有徹底「正確」的答案。

新特性:歷史上,查詢和過濾器是Elasticsearch中的單獨組件。從Elasticsearch 2.0開始,過濾器在技術上被消除,而且全部查詢都得到了成爲非評分的能力。

然而,爲了清楚和簡單,將使用term「過濾器」來表示在非評分過濾上下文中使用的查詢。能夠將term「過濾器」,「過濾查詢」和「非評分查詢」視爲相同。
相似地,若是單獨使用term「查詢」而不使用限定符,指的是「評分查詢」。

關於具體的Query DSL變化能夠查看Query DSL changes

性能差別

過濾查詢是對集合包含/排除的簡單檢查,這使得計算很是快。當您的過濾查詢中至少有一個是「稀疏」(匹配文檔較少)時,能夠利用各類優化,而且能夠將常用的非評分查詢緩存在內存中以便更快地訪問。

相比之下,評分查詢不只必須找到匹配的文檔,並且還要計算每一個文檔的相關程度,這一般使得他們比他們的非評分對手更重。此外,查詢結果不可緩存。

因爲倒排索引,只匹配幾個文檔的簡單評分查詢可能與跨越數百萬個文檔的過濾器同樣好或更好。然而,通常來講,過濾器將賽過評分查詢。

過濾的目的是減小必須由評分查詢檢查的文檔的數量。

什麼狀況下使用

做爲通常規則,對全文搜索或任何會影響相關性分數的條件使用查詢子句,並對其餘全部條件使用過濾器。

最重要的查詢過濾語句

match_all 查詢

match_all查詢只匹配全部文檔。若是未指定任何查詢,則是使用的默認查詢:

{「match_all」:{}}

此查詢常常與過濾器結合使用,例如,用於檢索收件箱文件夾中的全部電子郵件。全部文件被認爲是同等相關的,因此他們都得到1的中性分數。

match 查詢

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"  }}

提示: 作精確匹配搜索時,你最好用過濾語句,由於過濾語句能夠緩存數據。

不像咱們在ElasticSearch 5學習(4)——簡單搜索筆記中介紹的字符查詢,match查詢不能夠用相似"+usid:2 +tweet:search"這樣的語句。 它只能就指定某個確切字段某個確切的值進行搜索,而你要作的就是爲它指定正確的字段名以免語法錯誤。

multi_match 查詢

multi_match查詢容許你作match查詢的基礎上同時搜索多個字段:

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

range 過濾

range過濾容許咱們按照指定範圍查找一批數據:

{
    "range": {
        "age": {
            "gte":  20,
            "lt":   30
        }
    }
}

範圍操做符包含:

gt :: 大於

gte:: 大於等於

lt :: 小於

lte:: 小於等於

term 查詢

term用於按照精確值進行搜索,不管是數字,日期,布爾值仍是未分析的精確值字符串字段:

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

term不對輸入文本執行分析,所以它將精確查找提供的值。

terms 查詢

terms查詢與term查詢相同,但容許您指定多個值進行匹配。若是字段包含任何指定的值,則文檔匹配:

{ "terms": { "tag": [ "search", "full_text", "nosql" ] }}

term查詢相似,不對輸入文本執行分析。它正在尋找精確匹配(包括大小寫,重音,空格等)。

exists and missing 查詢

existsmissing查詢用於查找指定字段具備一個或多個值(exists)或沒有任何值(missing)的文檔。
它在本質上相似於SQL中的IS_NULL(缺失)和NOT IS_NULL(存在):

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

這些查詢常常用於僅在存在字段時應用條件,以及在缺乏條件時應用不一樣的條件。

查詢與過濾條件的合併

現實世界的搜索請求歷來不簡單;他們使用各類輸入文本搜索多個字段,並根據條件數組進行過濾。要構建複雜的搜索,您須要一種將多個查詢組合到一個搜索請求中的方法。

要作到這一點,你可使用bool詢。此查詢在用戶定義的布爾組合中將多個查詢組合在一塊兒。此查詢接受如下參數:

bool 過濾

bool 過濾能夠用來合併多個過濾條件查詢結果的布爾邏輯,它包含一下操做符:

must :: 多個查詢條件的徹底匹配,至關於 and。

must_not :: 多個查詢條件的相反匹配,至關於 not。

should :: 至少有一個查詢條件匹配, 至關於 or。

這些參數能夠分別繼承一個過濾條件或者一個過濾條件的數組:

{
    "bool": {
        "must":     { "term": { "folder": "inbox" }},
        "must_not": { "term": { "tag":    "spam"  }},
        "should": [
                    { "term": { "starred": true   }},
                    { "term": { "unread":  true   }}
        ]
    }
}

由於這是咱們見過的第一個包含其餘查詢的查詢,因此咱們須要談論分數是如何組合的。每一個子查詢子句將單獨計算文檔的相關性分數。一旦計算了這些分數,bool查詢將將分數合併在一塊兒,並返回表示布爾運算的總分數的單個分數。

如下查詢將會找到 title 字段中包含 "how to make millions",而且 tag 字段沒有被標爲 "spam"。 若是有標識爲 "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" }}}
        ]
    }
}

提示: 若是bool查詢下沒有must子句,那至少應該有一個should子句。可是若是有must子句,那麼沒有should子句也能夠進行查詢。

添加過濾查詢

若是咱們不但願文檔的日期影響評分,咱們能夠從新排列前面的示例以使用過濾子句:

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

範圍查詢已從should子句中移出並進入過濾器子句。

經過將範圍查詢移動到過濾子句中,咱們將其轉換爲非評分查詢。它將再也不爲文檔的相關性排名貢獻分數。而且由於它如今是一個非評分查詢,它可使用可用於過濾器的各類優化,這應該提升性能。

任何查詢均可以以這種方式使用。只需將查詢移動到bool查詢的過濾器子句中,它就會自動轉換爲非評分過濾器。

若是你須要過濾許多不一樣的標準,bool查詢自己能夠用做非評分查詢。只需將它放在過濾器子句中,並繼續構建布爾邏輯:

{
    "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" }}
              ]
          }
        }
    }
}

經過在filter子句中嵌入bool查詢,咱們能夠爲咱們的過濾條件添加布爾邏輯。

constant_score 查詢

儘管不像bool查詢那樣常用,可是constant_score查詢在你的工具箱中仍然有用。查詢對全部匹配的文檔應用靜態,常數得分。它主要用於當你想執行一個過濾器,沒有別的(例如沒有評分查詢)。你可使用它而不是一個只有過濾器子句的bool。性能將是相同的,但它能夠幫助查詢簡單/清晰。

{
    "constant_score":   {
        "filter": {
            "term": { "category": "ebooks" } 
        }
    }
}

驗證查詢

查詢語句能夠變得很是複雜,特別是與不一樣的分析器和字段映射相結合後,就會有些難度。

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"
      }
   }
}

顯然,咱們已經將查詢(match)類型與字段名稱(tweet)混淆:

{
  "valid" :     false,
  "_shards" :   { ... },
  "explanations" : [ {
    "index" :   "gb",
    "valid" :   false,
    "error" :   "org.elasticsearch.index.query.QueryParsingException:
                 [gb] No query registered for [tweet]"
  } ]
}

理解查詢語句

使用explain參數具備返回(有效)查詢的可讀描述的附加優勢,這對理解Elasticsearch如何解釋查詢是有用的:

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

爲每一個咱們查詢的索引返回一個解釋,由於每一個索引能夠有不一樣的映射和分析器:

{
  "valid" :         true,
  "_shards" :       { ... },
  "explanations" : [ {
    "index" :       "us",
    "valid" :       true,
    "explanation" : "tweet:really tweet:powerful"
  }, {
    "index" :       "gb",
    "valid" :       true,
    "explanation" : "tweet:realli tweet:power"
  } ]
}

從解釋中,您能夠看到查詢字符串的match查詢really powerful已被重寫爲對tweet字段的兩個單項查詢,每一個term一個。

此外,對於咱們的索引,這兩個termreallypowerful的,而對於gb索引,termreallipower。緣由是咱們改變了gb索引中的tweet字段以使用english分析器。

轉載請註明出處。
做者:wuxiwei
出處:http://www.cnblogs.com/wxw16/p/6204644.html

相關文章
相關標籤/搜索