Elasticsearch查詢

不要亂摸發表於2018-12-01

Query DSL

Elasticsearch提供了一個基於JSON的完整的查詢DSL(領域特定語言)。它定義的查詢語言由兩種型別的子句組成:“葉子查詢子句”和“組合查詢子句”。

葉子查詢子句

葉子查詢子句查詢特定欄位中的特定值,例如 matchtermrange 查詢。

複合查詢子句

複合查詢子句包裝其他葉子或複合查詢,並用於以邏輯方式組合多個查詢(如 bool 或 dis_max 查詢),或更改其行為(如 constant_score 查詢)。

1.  Query and filter context

查詢子句的行為取決於它是用在查詢上下文(query context)還是用在過濾器上下文(filter context):

1.1.  Query context

在查詢上下文中的查詢子句回答了“這個文件與這個查詢子句的匹配程度是怎樣的?”問題。除了決定文件是否匹配以外,查詢子句還會計算一個“_score”,它表示文件與其他文件的相關程度。

1.2.  Filter context

在過濾器上下文中,一個查詢子句回答了“這個文件與查詢子句匹配嗎?”的問題。這個答案是簡單的Yes或者No,也不會計算分數。過濾上下文主要用於過濾結構化資料,例如:

  • 這個timestamp在2015年到2016年的範圍內嗎?
  • 這個status欄位的值是“published”嗎?

PS:Query VS Filter

  1. 查詢反應的是文件與查詢子句的匹配程度,而過濾反應的是文件是否匹配查詢子句
  2. 一個是篩選是否滿足條件,情況無非兩種:是或不是;一個是看滿足條件的記錄與查詢條件的匹配程度
  3. 哪些滿足條件,這是過濾;滿足條件的這些記錄與條件的匹配程度,這是查詢
  4. 過濾不會計算評分,查詢會計算評分

頻繁使用的過濾器將被Elasticsearch自動快取,以提高效能。

當查詢子句中被傳遞了一個filter引數時過濾器上下文就生效了。例如,bool查詢中的filter引數或者must_not引數。

下面是一個查詢子句的例子,這個查詢將匹配滿足以下所有條件的文件:

  • title 欄位包含單詞“search
  • content 欄位包含單詞“elasticsearch
  • status 欄位包含明確的單詞“published
  • publish_date 欄位的包含的日期大於或等於2015-01-01
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": { 
        "bool": { 
            "must": [
                { "match": { "title":   "Search"        }}, 
                { "match": { "content": "Elasticsearch" }}  
            ],
            "filter": [ 
                { "term":  { "status": "published" }}, 
                { "range": { "publish_date": { "gte": "2015-01-01" }}} 
            ]
        }
    }
}
'

關於上面的查詢子句作如下說明:

  1. quary 參數列示這是一個查詢上下文
  2. bool 和 兩個match子句用在查詢上下文中,表明它們參與每條文件的打分
  3. filter 參數列明這是過濾器上下文
  4. termrange 子句用在過濾器上下文中,它們會過濾掉不匹配的文件,而且不會影響匹配文件的分數

(PS:類比SQL的話,match相當於模糊查詢,term相當於精確查詢,range相當於範圍查詢)

2.  Match All Query

最簡單的查詢,匹配所有文件,使它們的_score為1.0

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match_all": {}
    }
}
'

_score可以被改變,通過用boost引數

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match_all": { "boost" : 1.2 }
    }
}
'

match_all相反的是match_none,它不匹配任何文件

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match_none": {}
    }
}
'

3.  Full text queries

3.1.  Match Query

match查詢接受文字/數值/日期型別的資料,分析它們,並構造一個查詢。

match是一種布林型別的查詢。這意味著它對提供的文字進行分析,並在分析的過程中為提供的文字構造一個布林查詢。operator 選項可以設定為 or 或者 and 以此來控制布林子句(預設是 or )。例如:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match" : {
            "message" : "this is a test"
        }
    }
}
'

注意,查詢語句都是以“query”開頭的,這裡“message”是欄位名

你也可以加一些引數,比如:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match" : {
            "message" : {
                "query" : "this is a test",
                "operator" : "and"
            }
        }
    }
}
'

(PS:match是模糊查詢)

3.2.  Match Phrase Query

match_phrase 查詢與 match類似,但是它是用於精確匹配或單詞接近匹配的。例如:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match_phrase" : {
            "message" : "this is a test"
        }
    }
}
'

當然,你也可以加引數

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match_phrase" : {
            "message" : {
                "query" : "this is a test",
                "analyzer" : "my_analyzer"
            }
        }
    }
}
'

這裡“analyzer”是用來設定用那個分析器來分析文字

3.3.  Match Phrase Prefix Query

類似於match_phrase查詢,但是對最後一個單詞進行萬用字元搜尋。

match_phrase_prefix允許文字的最後一個單詞進行字首匹配

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match_phrase_prefix" : {
            "message" : "quick brown f"
        }
    }
}
'

除了match_phrase允許的那些引數外,match_phrase_prefix還可以接受一個max_expansions引數,它是用來控制最後一個單詞可以擴充套件多少字尾(預設50)。

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match_phrase_prefix" : {
            "message" : {
                "query" : "quick brown f",
                "max_expansions" : 10
            }
        }
    }
}
'

3.4.  Multi Match Query

multi_match 相當於 match 的多欄位版本

顧名思義,multi_match可以指定多個欄位,而match只能針對一個欄位

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "multi_match" : {
      "query":    "this is a test", 
      "fields": [ "subject", "message" ] 
    }
  }
}
'

另外,欄位可以用萬用字元,例如下面的例子中可以查詢 title , first_name , last_name 等欄位:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "multi_match" : {
      "query":    "Will Smith",
      "fields": [ "title", "*_name" ] 
    }
  }
}
'

單個欄位可以被提升,例如:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "multi_match" : {
      "query" : "this is a test",
      "fields" : [ "subject^3", "message" ] 
    }
  }
}
'

上面的例子,subject欄位的重要性是message欄位的三倍

3.5.  Query String Query

支援Lucene查詢字串語法,允許指定 AND | OR | NOT ,並且在單個查詢字串中進行多欄位查詢

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "query_string" : {
            "default_field" : "content",
            "query" : "this AND that OR thus"
        }
    }
}
'

query_string查詢解析輸入並圍繞操作符拆分文字,每個文字部分都是獨立分析的,例如:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "query_string" : {
            "default_field" : "content",
            "query" : "(new york city) OR (big apple)"
        }
    }
}
'

上面的例子中,將被拆分成 “new york city” 和 “big apple” 兩部分,並且每一部分都被分析器獨立分析

注意,按操作符拆分

query_string的引數包括:

query  例項被解析的查詢文字

default_field  如果沒有指定字首欄位的話,這是預設的查詢欄位。(預設查詢所有欄位)

default_operator  如果沒有明確指定操作符的話,那麼這是預設的操作符。例如,如果預設操作符是OR的話,那麼“my name is jack”將被翻譯成“my OR name OR is OR jack”,同理,如果是AND,則被翻譯成“my AND name AND is AND jack”

analyzer  用來解析查詢字串的解析器的名字

allow_leading_wildcard  如果設定了,那麼 * 或 ? 允許作為第一個字元。預設是true

lenient  如果設定為true,則格式失敗將被忽略

在query_string中,多欄位查詢應該這樣寫:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "query_string" : {
            "fields" : ["content", "name"],
            "query" : "this AND that"
        }
    }
}
'

等價於

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "query_string": {
            "query": "(content:this OR name:this) AND (content:that OR name:that)"
        }
    }
}
'

上面兩個是等價的

3.6.  Simple Query String Query

simple_query_string 是query_string的一個更簡單、更健壯、更適合面向使用者的版本

使用SimpleQueryParser解析上下文的查詢。與常規的query_string查詢不同,simple_query_string查詢永遠不會丟擲異常,並丟棄查詢的無效部分。

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "simple_query_string" : {
        "query": "\"fried eggs\" +(eggplant | potato) -frittata",
        "fields": ["title^5", "body"],
        "default_operator": "and"
    }
  }
}
'

3.7.  例項練習

準備資料

//    刪除索引
curl -X DELETE "192.168.1.134:9200/book"

//    建立索引
curl -X PUT "192.168.1.134:9200/book" -H 'Content-Type: application/json' -d'
{
    "settings" : {
        "number_of_shards" : 1
    },
    "mappings" : {
        "_doc" : {
            "properties" : {
                "title":        { "type": "text"  }, 
                "author":         { "type": "text"  }, 
                "introduction": { "type": "text"  },
                "publish_date": { 
                    "type": "date",
                    "format": "yyyy-MM-dd"
                }
            }
        }
    }
}
'

//    檢視索引
curl -X GET "192.168.1.134:9200/book?pretty"

//    插入文件
curl -X PUT "192.168.1.134:9200/book/_doc/1" -H 'Content-Type: application/json' -d'
{
    "title" : "Hello Java",
    "author": "zhangsan",
    "publish_date" : "2008-11-15",
    "introduction" : "This is a book for novice."
}
'

//    檢視文件
curl -X GET "192.168.1.134:9200/book/_search?pretty" -H 'Content-Type: application/json' -d'
{
    "query": {
        "match_all": {}
    }
}
'

 

match查詢(注意,match查詢只能是針對單個欄位)

這個例子中,我們用“Java”查詢到2條,接下來用“Java入門”將查到5條

這是因為解析器會將“Java入門”拆分為“Java”和“入門”兩個單詞,而且預設的操作符是or

也就是說,查詢的結果是title中包含“Java”或者“入門”的記錄

現在變成查詢title中同時包含“Java”和“入門”的記錄,因此只有1條

 multi_match多欄位查詢

 match_phrase查詢

對比不難發現,同樣的關鍵字“Java從”,用match查出5條,用match_phrase只查出1條

 

query_string查詢

4.  Term level queries(單詞級別查詢)

全文字查詢會在執行之前對查詢字串進行分析,而單詞級別查詢會對儲存在反向索引中的精確的term進行操作。

這些查詢通常用於結構化的資料,比如:numbers , dates ,enums 等,而不是對全文字欄位。

(PS:也就是說,全文字查詢之前要先對文字內容進行分詞,而單詞級別的查詢直接在相應欄位的反向索引中精確查詢,單詞級別的查詢一般用於數值、日期等型別的欄位上)

4.1.  Term Query

在指定的欄位中查詢包含指定的精確的term的文件

term查詢將在反向索引(或者叫倒排索引)中查詢包含特定的精確的term的文件。例如:

curl -X POST "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "term" : { "user" : "Kimchy" } 
  }
}
'

上面的例子,在user欄位的反向索引中查詢包含精確的Kimchy的文件

還可以指定一個boost引數,使這個term查詢比另一個查詢具有更高的相關性得分。例如:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "bool": {
            "should": [
            {
                "term": {
                    "status": {
                        "value": "urgent",
                        "boost": 2.0 
                    }
                }
            },
            {
                "term": {
                    "status": "normal" 
                }
            }
          ]
        }
    }
}
'

這個例子中,urgent查詢子句有一個boost引數值為2.0,這就意味著它的重要程度是後面的normal查詢子句的兩倍,normal子句預設的boost是1.0

4.2.  Terms Query

查詢包含指定欄位中指定的任何確切term的文件

篩選出與所提供的terms中任何一個匹配的文件

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "terms" : { "user" : ["kimchy", "elasticsearch"]}
    }
}
'

4.3.  Range Query

查詢指定欄位在指定範圍內包含值(日期、數字或字串)的文件。

下面的例子返回age欄位的值在10到20之間的文件:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "range" : {
            "age" : {
                "gte" : 10,
                "lte" : 20,
                "boost" : 2.0
            }
        }
    }
}
'

range查詢可以接受下列引數:

gte  大於或等於

gt    大於

lte   小於或等於

lt     小於

boost  設定boost值,預設是1.0

4.3.1.  Range on date fields

當range查詢用於date型別的欄位時,範圍可以用Date Math表示:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "range" : {
            "date" : {
                "gte" : "now-1d/d",
                "lt" :  "now/d"
            }
        }
    }
}
'

當使用Date Math將日期四捨五入到最近的日期、月份、小時等時,四捨五入日期取決於範圍的兩端是包含的還是排除的。

例如:

rounded up 向上舍入

rounded down 向下舍入

gt 大於2014-11-18||/M  變成 2014-11-30T23:59:59.999 

gte 大於或等於2014-11-18||/M  變成 2014-11-01

lt 小於2014-11-18||/M  變成  2014-11-01

lte 小於或等於2014-11-18||/M  變成2014-11-30T23:59:59.999 

這個其實很好理解,

大於2014-11-18||/M相當於是大於2014年11月,因此大於2014-11-18||/M等價於大於2014-11-30 23:59:59

也就是說,大於11月,相當於是大於11月的最後一天,即11-30 23:59:59

同理,大於或等於2014-11-18||/M,相當於大於或等於11月,自然是11月的第一天,即2014-11-01

同理,小於2014-11-18||/M,相當於小於11月,自然是小於11月1日,故而小於2014-11-18||/M等價於小於2014-11-01

同理,小於或等於2014-11-18||/M,等於11月自然是包含11月的,意味著小於11月30日,故而小於或等於2014-11-18||/M等價於小於或等於2014-11-30 23:59:59

4.3.2.  Date format in range query

在日期範圍查詢的時候,我們可以指定日期格式。例如:

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "range" : {
            "born" : {
                "gte": "01/01/2012",
                "lte": "2013",
                "format": "dd/MM/yyyy||yyyy"
            }
        }
    }
}
'

這個例子是查詢在2012-01-01到2013-12-31之間出生的人

下面看時間範圍查詢

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "range" : {
            "timestamp" : {
                "gte": "2015-01-01 00:00:00", 
                "lte": "now", 
                "time_zone": "+01:00"
            }
        }
    }
}
'

4.4.  Exsit Query

在特定的欄位中查詢非空值的文件

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "exists" : { "field" : "user" }
    }
}
'

4.5.  Prefix Query

查詢包含帶有指定字首的term的文件

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{ "query": {
    "prefix" : { "user" : "ki" }
  }
}
'

可以關聯boost

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{ "query": {
    "prefix" : { "user" :  { "value" : "ki", "boost" : 2.0 } }
  }
}
'

4.6.  Wildcard Query

支援萬用字元查詢,*表示任意字元,?表示任意單個字元

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "wildcard" : { "user" : "ki*y" }
    }
}
'

可以加boost引數

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "wildcard" : { "user" : { "value" : "ki*y", "boost" : 2.0 } }
    }
}
'

4.7.  Regexp Query

正規表示式查詢

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "regexp":{
            "name.first": "s.*y"
        }
    }
}
'

4.8.  Ids Query

用_uid欄位查詢

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "ids" : {
            "type" : "_doc",
            "values" : ["1", "4", "100"]
        }
    }
}
'

4.9.  例項練習

5.  複合查詢

複合查詢包裝其他複合查詢或葉子查詢,以組合它們的結果和得分,更改它們的行為,或從查詢切換到篩選上下文。

5.1.  固定分數查詢

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "constant_score" : {
            "filter" : {
                "term" : { "user" : "kimchy"}
            },
            "boost" : 1.2
        }
    }
}
'

5.2.  布林查詢

關於should子句,特別要注意:

如果這個布林查詢位於查詢上下文,並且有must或者filter子句,那麼即使should子句沒有匹配任何文件,也沒關係
如果是位於過濾器上下文,或者既沒有must也沒有filter,那麼至少有一個should查詢必須匹配文件。
這個行為可以通過設定minimum_should_match引數來顯式地控制。

舉個例子:

curl -X POST "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
    "query": {
        "bool" : {
            "must" : {
                "term" : { "user" : "kimchy" }
            },
            "filter": {
                "term" : { "tag" : "tech" }
            },
            "must_not" : {
                "range" : {
                    "age" : { "gte" : 10, "lte" : 20 }
                }
            },
            "should" : [
                { "term" : { "tag" : "wow" } },
                { "term" : { "tag" : "elasticsearch" } }
            ],
            "minimum_should_match" : 1,
            "boost" : 1.0
        }
    }
}
'

查詢user為“kimchy”,並且tag為“tech”,並且age不在10~20之間,並且tag為wow或elasticsearch的文件

filter查詢分數預設是0

curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}
'

5.3.  例項練習

 

參考

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html

 

相關文章