1. 程式人生 > >ES基本概念和常用REST查詢

ES基本概念和常用REST查詢


一、相關概念

1.1 倒排索引

    Elasticsearch使用倒排索引來達到加速檢索的目的。

    倒排索引:根據屬性值來確定記錄位置

    倒排索引源於實際應用中需要根據屬性的值來查詢記錄。這種索引表中的每一項都包括一個屬性值和具有該屬性值的各記錄的地址。由於不是由記錄來確定屬性值,而是由屬性值來確定記錄的位置,因而稱為倒排索引(inverted index)。帶有倒排索引的檔案我們稱為倒排索引檔案,簡稱倒排檔案(inverted file)。

    在Elasticsearch中,每一個欄位的資料都是預設被索引的。也就是說,每個欄位專門有一個反向索引用於快速檢索。而且,與其它資料庫不同,它可以在同一個查詢中利用所有的這些反向索引,以驚人的速度返回結果。

    通常,我們可以認為物件(object)文件(document)是等價相通的。不過,他們還是有所差別:物件(Object)是一個JSON結構體——類似於雜湊、hashmap、字典或者關聯陣列;物件(Object)中還可能包含其他物件(Object)。 在Elasticsearch中,文件(document)這個術語有著特殊含義。它特指最頂層結構或者根物件(root object)序列化成的JSON資料(以唯一ID標識並存儲於Elasticsearch中)。

1.2 分析器

    當我們索引(index)一個文件,全文欄位會被分析為單獨的詞來建立倒排索引。不過,當我們在全文欄位搜尋(search)時,我們要讓查詢字串經過同樣的分析流程處理,以確保這些詞在索引中存在。

    全文查詢我們將在稍後討論,理解每個欄位是如何定義的,這樣才可以讓它們做正確的事:

  • 當你查詢全文(full text)欄位,查詢將使用相同的分析器來分析查詢字串,以產生正確的詞列表。
  • 當你查詢一個確切值(exact value)欄位,查詢將不分析查詢字串,但是你可以自己指定。

1.3 指定分析器

    當Elasticsearch在你的文件中探測到一個新的字串欄位,它將自動設定它為全文string欄位並用standard分析器分析。

    你不可能總是想要這樣做。也許你想使用一個更適合這個資料的語言分析器。或者,你只想把字串欄位當作一個普通的欄位——不做任何分析,只儲存確切值,就像字串型別的使用者ID或者內部狀態欄位或者標籤。

    為了達到這種效果,我們必須通過對映(mapping)人工設定這些欄位。

小提示

錯誤的對映,例如把age欄位對映為string型別而不是integer型別,會造成查詢結果混亂。

要檢查對映型別,而不是假設它是正確的!

1.4 查詢和過濾--效能差異

    使用過濾語句得到的結果集 -- 一個簡單的文件列表,快速匹配運算並存入記憶體是十分方便的, 每個文件僅需要1個位元組。這些快取的過濾結果集與後續請求的結合使用是非常高效的。

    查詢語句不僅要查詢相匹配的文件,還需要計算每個文件的相關性,所以一般來說查詢語句要比 過濾語句更耗時,並且查詢結果也不可快取。

    幸虧有了倒排索引,一個只匹配少量文件的簡單查詢語句在百萬級文件中的查詢效率會與一條經過快取 的過濾語句旗鼓相當,甚至略佔上風。 但是一般情況下,一條經過快取的過濾查詢要遠勝一條查詢語句的執行效率。

    過濾語句的目的就是縮小匹配的文件結果集,所以需要仔細檢查過濾條件。

 1.5 查詢和過濾--什麼情況下使用

原則上來說,使用查詢語句做全文字搜尋或其他需要進行相關性評分的時候,剩下的全部用過濾語句

前面我們講到的是關於結構化查詢語句,事實上我們可以使用兩種結構化語句: 結構化查詢(Query DSL)和結構化過濾(Filter DSL)。 查詢與過濾語句非常相似,但是它們由於使用目的不同而稍有差異。

一條過濾語句會詢問每個文件的欄位值是否包含著特定值:

  • created 的日期範圍是否在 2013 到 2014 ?

  • status 欄位中是否包含單詞 "published" ?

  • lat_lon 欄位中的地理位置與目標點相距是否不超過10km ?

一條查詢語句與過濾語句相似,但問法不同:

查詢語句會詢問每個文件的欄位值與特定值的匹配程度如何?

查詢語句的典型用法是為了找到文件:

  • 查詢與 full text search 這個詞語最佳匹配的文件

  • 查詢包含單詞 run ,但是也包含runsrunningjog 或 sprint的文件

  • 同時包含著 quickbrown 和 fox --- 單詞間離得越近,該文件的相關性越高

  • 標識著 lucenesearch 或 java --- 標識詞越多,該文件的相關性越高

一條查詢語句會計算每個文件與查詢語句的相關性,會給出一個相關性評分 _score,並且 按照相關性對匹配到的文件進行排序。 這種評分方式非常適用於一個沒有完全配置結果的全文字搜尋。

二、最重要的查詢過濾語句

2.1 term 過濾

    term主要用於精確匹配哪些值,比如數字,日期,布林值或 not_analyzed的字串(未經分析的文字資料型別):

2.2 terms 過濾

    terms 跟 term 有點類似,但 terms 允許指定多個匹配條件。 如果某個欄位指定了多個值,那麼文件需要一起去做匹配:

2.3 range 過濾

    range過濾允許我們按照指定範圍查詢一批資料:

2.4 exists 和 missing 過濾

    exists 和 missing 過濾可以用於查詢文件中是否包含指定欄位或沒有某個欄位,類似於SQL語句中的IS_NULL條件

2.5 bool 過濾

    bool 過濾可以用來合併多個過濾條件查詢結果的布林邏輯,它包含以下操作符:

    must :: 多個查詢條件的完全匹配,相當於 and

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

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

    這些引數可以分別繼承一個過濾條件或者一個過濾條件的陣列:

2.6 match_all 查詢

    使用match_all 可以查詢到所有文件,是沒有查詢條件下的預設語句。

2.7 match 查詢

    match查詢是一個標準查詢,不管你需要全文字查詢還是精確查詢基本上都要用到它。

2.8 multi_match 查詢

    multi_match查詢允許你做match查詢的基礎上同時搜尋多個欄位:

2.9 bool 查詢

    bool 查詢與 bool 過濾相似,用於合併多個查詢子句。不同的是,bool 過濾可以直接給出是否匹配成功, 而bool 查詢要計算每一個查詢子句的 _score (相關性分值)。

    must:: 查詢指定文件一定要被包含。

    must_not:: 查詢指定文件一定不要被包含。

    should:: 查詢指定文件,有則可以為文件相關性加分。

2.10 查詢與過濾條件的合併

    查詢語句和過濾語句可以放在各自的上下文中。 在 ElasticSearch API 中我們會看到許多帶有 query 或filter 的語句。 這些語句既可以包含單條 query 語句,也可以包含一條 filter 子句。 換句話說,這些語句需要首先建立一個queryfilter的上下文關係。

    複合查詢語句可以加入其他查詢子句,複合過濾語句也可以加入其他過濾子句。 通常情況下,一條查詢語句需要過濾語句的輔助,全文字搜尋除外。

    所以說,查詢語句可以包含過濾子句,反之亦然。 以便於我們切換 query 或 filter 的上下文。這就要求我們在讀懂需求的同時構造正確有效的語句。

三、常用REST請求示例

    針對es對外開放的http介面,下面列舉除了一些常用的rest請求示例,同時它們在tcp介面中都有對應的java api實現

3.1 查詢叢集資訊

localhost:9200

3.2 檢視叢集健康狀態

localhost:9200/_cat/health?v

3.3  檢視叢集節點

localhost:9200/_cat/nodes?v

3.4 檢視叢集分片

localhost:9200/_cat/indices?v

3.5 建立索引

PUT localhost:9200/lulijun

3.6 索引文件

PUT localhost:9200/poi/doc/8?pretty

{
"poi_id": 2,
"poi_name": "3_name",
"time":1,
"price": 5
}

3.7 查詢文件

localhost:9200/lulijun/doc/1?pretty

3.8 刪除索引

DELETE localhost:9200/lulijun

3.9 更新文件

POST localhost:9200/lulijun/doc/1/_update

{
"doc":{
"name":"lulijun02"
}
}

3.10 以指令碼更新文件

localhost:9200/lulijun/doc/1/_update

{
"script":"ctx._source.gener = 'male'"
}

3.11 刪除文件

DELETE localhost:9200/lulijun/doc/1

3.12 批量操作

POST localhost:9200/lulijun/doc/_bulk

{"index":{"_id":"1"}}
{"name": "lulijun01","gender":"female" }
{"index":{"_id":"2"}}
{"name": "lulijun02", "gender":"male" }

3.13 查詢全部-request param

localhost:9200/_search?q=*

3.14 查詢全部-request body

POST localhost:9200/poi/_search

{
"query": { "match_all": {} }
}

3.15 limit

localhost:9200/lulijun/_search

{
"query": { "match_all": {} },
"size" : 1
}

3.16 Paging

localhost:9200/poi/_search

{
"query": { "match_all": {} },
"from" : 1,
"size":1
}

3.17 指定返回欄位

POST localhost:9200/lulijun/_search

{
"query":{
"match_all":{}
},
"_source":["gender"]
}

3.18 match查詢

localhost:9200/lulijun/_search

{
"query":{
"match":{
"gender":"male"
}
}
}

3.19 bool查詢-must

localhost:9200/lulijun/_search

{
"query":{
"bool":{
"must":[
{
"match":{
"gender":"male"
}
},
{
"match":{
"name":"lulijun"
}
}
]
}
}
}

3.20 bool查詢-should

localhost:9200/lulijun/_search

{
"query":{
"bool":{
"should":[
{
"match":{
"gender":"male"
}
},
{
"match":{
"name":"lulijun"
}
}
]
}
}
}

3.21 bool查詢-must not

localhost:9200/lulijun/_search

{
"query":{
"bool":{
"must_not":[
{
"match":{
"gender":"xx"
}
},
{
"match":{
"name":"xy"
}
}
]
}
}
}

3.22 groupby+count

localhost:9200/poi/_search

{
"size":0,
"aggs":{
"group_by_state":{
"terms":{
"field":"poi_id"
}
}
}
}

3.23 groupby+sum

localhost:9200/poi/_search

{
"aggregations": {
"poi_id": {
"terms": {
"field": "poi_id",
"size": 2
},
"aggregations": {
"poi_name": {
"terms": {
"field": "time",
"size": 200
},
"aggregations": {
"SUM(price)": {
"sum": {
"field": "price"
}
}
}
}
}
}
}
}

3.24 script field

localhost:9200/poi/_search

{
"from": 0,
"size": 100,
"query":{
"bool":{
"must":[
{
"match":{
"poi_id":1
}
}
]
}
},
"aggregations": {
"poi_id": {
"terms": {
"script": {
"inline": "doc['poi_id'].value + 1"
}
},
"aggregations": {
"price": {
"sum": {
"field": "price"
}
}
}
}
}
}