elasticsearch 教程


輕量搜索

一個 GET 是相當簡單的,可以直接得到指定的文檔。 現在嘗試點兒稍微高級的功能,比如一個簡單的搜索!

第一個嘗試的幾乎是最簡單的搜索了。我們使用下列請求來搜索所有雇員:

GET /megacorp/employee/_search

可以看到,我們仍然使用索引庫 megacorp 以及類型 employee,但與指定一個文檔 ID 不同,這次使用 _search 。返回結果包括了所有三個文檔,放在數組 hits 中。一個搜索默認返回十條結果。

{ "took": 6, "timed_out": false, "_shards": { ... }, "hits": { "total": 3, "max_score": 1, "hits": [ { "_index": "megacorp", "_type": "employee", "_id": "3", "_score": 1, "_source": { "first_name": "Douglas", "last_name": "Fir", "age": 35, "about": "I like to build cabinets", "interests": [ "forestry" ] } }, { "_index": "megacorp", "_type": "employee", "_id": "1", "_score": 1, "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] } }, { "_index": "megacorp", "_type": "employee", "_id": "2", "_score": 1, "_source": { "first_name": "Jane", "last_name": "Smith", "age": 32, "about": "I like to collect rock albums", "interests": [ "music" ] } } ] } }

注意:返回結果不僅告知匹配了哪些文檔,還包含了整個文檔本身:顯示搜索結果給最終用戶所需的全部信息。

接下來,嘗試下搜索姓氏為 ``Smith`` 的雇員。為此,我們將使用一個 高亮 搜索,很容易通過命令行完成。這個方法一般涉及到一個 查詢字符串 (query-string) 搜索,因為我們通過一個URL參數來傳遞查詢信息給搜索接口:

GET /megacorp/employee/_search?q=last_name:Smith

我們仍然在請求路徑中使用 _search 端點,並將查詢本身賦值給參數 q= 。返回結果給出了所有的 Smith:

{ ... "hits": { "total": 2, "max_score": 0.30685282, "hits": [ { ... "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] } }, { ... "_source": { "first_name": "Jane", "last_name": "Smith", "age": 32, "about": "I like to collect rock albums", "interests": [ "music" ] } } ] } }



使用查詢表達式搜索

Query-string 搜索通過命令非常方便地進行臨時性的即席搜索 ,但它有自身的局限性(參見 輕量 搜索 )。Elasticsearch 提供一個豐富靈活的查詢語言叫做 查詢表達式 , 它支持構建更加復雜和健壯的查詢。

領域特定語言 (DSL), 使用 JSON 構造了一個請求。我們可以像這樣重寫之前的查詢所有名為 Smith 的搜索 :

GET /megacorp/employee/_search { "query" : { "match" : { "last_name" : "Smith" } } }

返回結果與之前的查詢一樣,但還是可以看到有一些變化。其中之一是,不再使用 query-string 參數,而是一個請求體替代。這個請求使用 JSON 構造,並使用了一個 match 查詢(屬於查詢類型之一,后面將繼續介紹)。

 

 

 

 

 

 

 

 

 

更復雜的搜索

現在嘗試下更復雜的搜索。 同樣搜索姓氏為 Smith 的員工,但這次我們只需要年齡大於 30 的。查詢需要稍作調整,使用過濾器 filter ,它支持高效地執行一個結構化查詢。

GET /megacorp/employee/_search { "query" : { "bool": { "must": { "match" : { "last_name" : "smith"  } }, "filter": { "range" : { "age" : { "gt" : 30 }  } } } } }
 

這部分與我們之前使用的 match 查詢 一樣。

 

這部分是一個 range 過濾器 , 它能找到年齡大於 30 的文檔,其中 gt 表示_大於_(great than)。

目前無需太多擔心語法問題,后續會更詳細地介紹。只需明確我們添加了一個 過濾器 用於執行一個范圍查詢,並復用之前的 match 查詢。現在結果只返回了一名員工,叫 Jane Smith,32 歲。

{ ... "hits": { "total": 1, "max_score": 0.30685282, "hits": [ { ... "_source": { "first_name": "Jane", "last_name": "Smith", "age": 32, "about": "I like to collect rock albums", "interests": [ "music" ] } } ] } }







全文搜索

截止目前的搜索相對都很簡單:單個姓名,通過年齡過濾。現在嘗試下稍微高級點兒的全文搜索——一項 傳統數據庫確實很難搞定的任務。

搜索下所有喜歡攀岩(rock climbing)的員工:

GET /megacorp/employee/_search { "query" : { "match" : { "about" : "rock climbing" } } }

顯然我們依舊使用之前的 match 查詢在`about` 屬性上搜索 “rock climbing” 。得到兩個匹配的文檔:

{ ... "hits": { "total": 2, "max_score": 0.16273327, "hits": [ { ... "_score": 0.16273327,  "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] } }, { ... "_score": 0.016878016,  "_source": { "first_name": "Jane", "last_name": "Smith", "age": 32, "about": "I like to collect rock albums", "interests": [ "music" ] } } ] } }
 

相關性得分

Elasticsearch 默認按照相關性得分排序,即每個文檔跟查詢的匹配程度。第一個最高得分的結果很明顯:John Smith 的 about 屬性清楚地寫着 “rock climbing” 。

但為什么 Jane Smith 也作為結果返回了呢?原因是她的 about 屬性里提到了 “rock” 。因為只有 “rock” 而沒有 “climbing” ,所以她的相關性得分低於 John 的。

這是一個很好的案例,闡明了 Elasticsearch 如何  全文屬性上搜索並返回相關性最強的結果。Elasticsearch中的 相關性 概念非常重要,也是完全區別於傳統關系型數據庫的一個概念,數據庫中的一條記錄要么匹配要么不匹配。

 
          

短語搜索

找出一個屬性中的獨立單詞是沒有問題的,但有時候想要精確匹配一系列單詞或者_短語_ 。 比如, 我們想執行這樣一個查詢,僅匹配同時包含 “rock”  “climbing” ,並且 二者以短語 “rock climbing” 的形式緊挨着的雇員記錄。

為此對 match 查詢稍作調整,使用一個叫做 match_phrase 的查詢:

GET /megacorp/employee/_search { "query" : { "match_phrase" : { "about" : "rock climbing" } } }

毫無懸念,返回結果僅有 John Smith 的文檔。

{ ... "hits": { "total": 1, "max_score": 0.23013961, "hits": [ { ... "_score": 0.23013961, "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] } } ] } }







高亮搜索

許多應用都傾向於在每個搜索結果中 高亮 部分文本片段,以便讓用戶知道為何該文檔符合查詢條件。在 Elasticsearch 中檢索出高亮片段也很容易。

再次執行前面的查詢,並增加一個新的 highlight 參數:

GET /megacorp/employee/_search { "query" : { "match_phrase" : { "about" : "rock climbing" } }, "highlight": { "fields" : { "about" : {} } } }

當執行該查詢時,返回結果與之前一樣,與此同時結果中還多了一個叫做 highlight 的部分。這個部分包含了 about 屬性匹配的文本片段,並以 HTML 標簽 <em></em> 封裝:

{ ... "hits": { "total": 1, "max_score": 0.23013961, "hits": [ { ... "_score": 0.23013961, "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] }, "highlight": { "about": [ "I love to go <em>rock</em> <em>climbing</em>"  ] } } ] } }
 

原始文本中的高亮片段

關於高亮搜索片段,可以在 highlighting reference documentation 了解更多信息。

 

 

 

 

 

處理 Null 值

回想在之前例子中,有的文檔有名為 tags (標簽)的字段,它是個多值字段,一個文檔可能有一個或多個標簽,也可能根本就沒有標簽。如果一個字段沒有值,那么如何將它存入倒排索引中的呢?

這是個有欺騙性的問題,因為答案是:什么都不存。讓我們看看之前內容里提到過的倒排索引:

Token

DocIDs

open_source

2

search

1,2

如何將某個不存在的字段存儲在這個數據結構中呢?無法做到!簡單的說,一個倒排索引只是一個 token 列表和與之相關的文檔信息,如果字段不存在,那么它也不會持有任何 token,也就無法在倒排索引結構中表現。

最終,這也就意味着,null[] (空數組)和 [null] 所有這些都是等價的,它們無法存於倒排索引中。

顯然,世界並不簡單,數據往往會有缺失字段,或有顯式的空值或空數組。為了應對這些狀況,Elasticsearch 提供了一些工具來處理空或缺失值。

存在查詢

第一件武器就是 exists 存在查詢。這個查詢會返回那些在指定字段有任何值的文檔,讓我們索引一些示例文檔並用標簽的例子來說明:

POST /my_index/posts/_bulk { "index": { "_id": "1" }} { "tags" : ["search"] }  { "index": { "_id": "2" }} { "tags" : ["search", "open_source"] }  { "index": { "_id": "3" }} { "other_field" : "some data" }  { "index": { "_id": "4" }} { "tags" : null }  { "index": { "_id": "5" }} { "tags" : ["search", null] }
 

tags 字段有 1 個值。

 

tags 字段有 2 個值。

 

tags 字段缺失。

 

tags 字段被置為 null 。

 

tags 字段有 1 個值和 1 個 null 。

以上文檔集合中 tags 字段對應的倒排索引如下:

Token

DocIDs

open_source

2

search

1,2,5

我們的目標是找到那些被設置過標簽字段的文檔,並不關心標簽的具體內容。只要它存在於文檔中即可,用 SQL 的話就是用 IS NOT NULL 非空進行查詢:

SELECT tags FROM posts WHERE tags IS NOT NULL

在 Elasticsearch 中,使用 exists 查詢的方式如下:

GET /my_index/posts/_search { "query" : { "constant_score" : { "filter" : { "exists" : { "field" : "tags" } } } } }

這個查詢返回 3 個文檔:

"hits" : [ { "_id" : "1", "_score" : 1.0, "_source" : { "tags" : ["search"] } }, { "_id" : "5", "_score" : 1.0, "_source" : { "tags" : ["search", null] }  }, { "_id" : "2", "_score" : 1.0, "_source" : { "tags" : ["search", "open source"] } } ]
 

盡管文檔 5 有 null 值,但它仍會被命中返回。字段之所以存在,是因為標簽有實際值( search )可以被索引,所以 null 對過濾不會產生任何影響。

顯而易見,只要 tags 字段存在項(term)的文檔都會命中並作為結果返回,只有 3 和 4 兩個文檔被排除。

缺失查詢

這個 missing 查詢本質上與 exists 恰好相反:它返回某個特定  值字段的文檔,與以下 SQL 表達的意思類似:

SELECT tags FROM posts WHERE tags IS NULL

我們將前面例子中 exists 查詢換成 missing 查詢:

GET /my_index/posts/_search { "query" : { "constant_score" : { "filter": { "missing" : { "field" : "tags" } } } } }

按照期望的那樣,我們得到 3 和 4 兩個文檔(這兩個文檔的 tags 字段沒有實際值):

"hits" : [ { "_id" : "3", "_score" : 1.0, "_source" : { "other_field" : "some data" } }, { "_id" : "4", "_score" : 1.0, "_source" : { "tags" : null } } ]

對象上的存在與缺失

不僅可以過濾核心類型, exists and missing 查詢 還可以處理一個對象的內部字段。以下面文檔為例:

{ "name" : { "first" : "John", "last" : "Smith" } }

我們不僅可以檢查 name.first 和 name.last 的存在性,也可以檢查 name ,不過在 映射 中,如上對象的內部是個扁平的字段與值(field-value)的簡單鍵值結構,類似下面這樣:

{ "name.first" : "John", "name.last" : "Smith" }

那么我們如何用 exists 或 missing 查詢 name 字段呢? name 字段並不真實存在於倒排索引中。

原因是當我們執行下面這個過濾的時候:

{ "exists" : { "field" : "name" } }

實際執行的是:

{ "bool": { "should": [ { "exists": { "field": "name.first" }}, { "exists": { "field": "name.last" }} ] } }

這也就意味着,如果 first 和 last 都是空,那么 name 這個命名空間才會被認為不存在。

 

 

 

 

 

 










 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM