文檔
在Elasticsearch中,文檔以JSON格式進行存儲,可以是復雜的結構,如:
{ "_index": "haoke", "_type": "user", "_id": "1001", "_version": 1, "found": true, "_source": { "id": 1001, "name": "張三", "age": 21, "sex": "女" } }
元數據(metadata)
一個文檔不只有數據。它還包含了元數據(metadata)——關於文檔的信息。三個必須的元數據節點是:
_index
- 索引(index)類似於關系型數據庫里的“數據庫”——它是我們存儲和索引關聯數據的地方。
提示:事實上,我們的數據被存儲和索引在分片(shards)中,索引只是一個把一個或多個分片分組在一起的邏輯空間。然而,這只是一些內部細節——我們的程序完全不用關心分片。
_type
- 在應用中,我們使用對象表示一些“事物”,例如一個用戶、一篇博客、一個評論,或者一封郵件。每個對象都屬於一個類(class),這個類定義了屬性或與對象關聯的數據。 user 類的對象可能包含姓名、性別、年齡和Email地址。
- 在關系型數據庫中,我們經常將相同類的對象存儲在一個表里,因為它們有着相同的結構。同理,在Elasticsearch中,我們使用相同類型(type)的文檔表示相同的“事物”,因為他們的數據結構也是相同的。
- 每個類型(type)都有自己的映射(mapping)或者結構定義,就像傳統數據庫表中的列一樣。所有類型下的文檔被存儲在同一個索引下,但是類型的映射(mapping)會告訴Elasticsearch不同的文檔如何被索引。
- _type 的名字可以是大寫或小寫,不能包含下划線或逗號。我們將使用 blog 做為類型名。
_id
- id僅僅是一個字符串,它與 _index 和 _type 組合時,就可以在Elasticsearch中唯一標識一個文檔。當創建一個文檔,你可以自定義 _id ,也可以讓Elasticsearch幫你自動生成(32位長度)。
查詢響應
pretty
可以在查詢url后面添加pretty參數,使得返回的json更易查看。
指定響應字段
在響應的數據中,如果我們不需要全部的字段,可以指定某些需要的字段進行返回。
http://121.40.152.53:9200/haoke/user/1001?_source=id,name
如不需要返回元數據,僅僅返回原始數據,可以這樣:
還可這樣:
判斷文檔是否存在
如果我們只需要判斷文檔是否存在,而不是查詢文檔內容,那么可以這樣:
當然,這只表示你在查詢的那一刻文檔不存在,但並不表示幾毫秒后依舊不存在。另一個進程在這期間可能創建新文檔。
批量查詢
{ "docs": [ { "_index": "haoke", "_type": "user", "_id": "1001", "_version": 1, "found": true, "_source": { "id": 1001, "name": "張三", "age": 21, "sex": "女" } }, { "_index": "haoke", "_type": "user", "_id": "1002", "_version": 1, "found": true, "_source": { "id": 1001, "name": "張三1", "age": 22, "sex": "女" } } ] }
如果,某一條數據不存在,不影響整體響應,需要通過found的值進行判斷是否查詢到數據。
_bulk操作
在Elasticsearch中,支持批量的插入、修改、刪除操作,都是通過_bulk的api完成的。
請求格式如下:(請求格式不同尋常)
結果:
{ "took": 71, "errors": false, "items": [ { "create": { "_index": "haoke", "_type": "user", "_id": "2001", "_version": 1, "result": "created", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "created": true, "status": 201 } }, { "create": { "_index": "haoke", "_type": "user", "_id": "2002", "_version": 1, "result": "created", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "created": true, "status": 201 } }, { "create": { "_index": "haoke", "_type": "user", "_id": "2003", "_version": 1, "result": "created", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "created": true, "status": 201 } } ] }
批量刪除:
結果:
{ "took": 3, "errors": false, "items": [ { "delete": { "found": true, "_index": "haoke", "_type": "user", "_id": "2001", "_version": 2, "result": "deleted", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "status": 200 } }, { "delete": { "found": true, "_index": "haoke", "_type": "user", "_id": "2002", "_version": 2, "result": "deleted", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "status": 200 } }, { "delete": { "found": true, "_index": "haoke", "_type": "user", "_id": "2003", "_version": 2, "result": "deleted", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "status": 200 } } ] }
一次請求多少性能最高?
整個批量請求需要被加載到接受我們請求節點的內存里,所以請求越大,給其它請求可用的內存就越小。有一
個最佳的bulk請求大小。超過這個大小,性能不再提升而且可能降低。
最佳大小,當然並不是一個固定的數字。它完全取決於你的硬件、你文檔的大小和復雜度以及索引和搜索的負載。
幸運的是,這個最佳點(sweetspot)還是容易找到的:試着批量索引標准的文檔,隨着大小的增長,當性能開始
降低,說明你每個批次的大小太大了。開始的數量可以在1000~5000個文檔之間,如果你的文檔非常大,可以
使用較小的批次。
通常着眼於你請求批次的物理大小是非常有用的。一千個1kB的文檔和一千個1MB的文檔大不相同。一個好的
批次最好保持在5-15MB大小間。
分頁
和SQL使用 LIMIT 關鍵字返回只有一頁的結果一樣,Elasticsearch接受 from 和 size 參數:
size: 結果數,默認10 from: 跳過開始的結果數,默認0
http://121.40.152.53:9200/haoke/user/_search?size=2&from=1
{ "took": 1, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1.0, "hits": [ { "_index": "haoke", "_type": "user", "_id": "1002", "_score": 1.0, "_source": { "id": 1001, "name": "張三1", "age": 22, "sex": "女" } }, { "_index": "haoke", "_type": "user", "_id": "1003", "_score": 1.0, "_source": { "id": 1003, "name": "張1", "age": 25, "sex": "女" } } ] } }
映射
前面我們創建的索引以及插入數據,都是由Elasticsearch進行自動判斷類型,有些時候我們是需要進行明確字段類型的,否則,自動判斷的類型和實際需求是不相符的。
自動判斷的規則如下:
JSON type | Field type |
Boolean: true or false | "boolean" |
Whole number: 123 | "long" |
Floating point: 123.45 | "double" |
String, valid date: "2014-09-15" | "date" |
String: "foo bar" | "string" |
Elasticsearch中支持的類型如下:
類型 | 表示的數據類型 |
String | string , text , keyword |
Whole number | byte , short , integer , long |
Floating point | float , double |
Boolean | boolean |
Date | date |
string類型在ElasticSearch 舊版本中使用較多,從ElasticSearch 5.x開始不再支持string,由text和keyword類型替代。
text 類型,當一個字段是要被全文搜索的,比如Email內容、產品描述,應該使用text類型。設置text類型
以后,字段內容會被分析,在生成倒排索引以前,字符串會被分析器分成一個一個詞項。text類型的字段
不用於排序,很少用於聚合。
keyword類型適用於索引結構化的字段,比如email地址、主機名、狀態碼和標簽。如果字段需要進行過
濾(比如查找已發布博客中status屬性為published的文章)、排序、聚合。keyword類型的字段只能通過精
確值搜索到。
http://121.40.152.53:9200/dalianpai
{ "settings": { "index": { "number_of_shards": "2", "number_of_replicas": "0" } }, "mappings": { "person": { "properties": { "name": { "type": "text" }, "age": { "type": "integer" }, "mail": { "type": "keyword" }, "hobby": { "type": "text" } } } } }
查看映射:
http://121.40.152.53:9200/dalianpai/_mapping
插入數據:
結果:
結果:
{ "took": 10, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1.3773504, "hits": [ { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZhA", "_score": 1.3773504, "_source": { "name": "孫七", "age": 24, "mail": "555@qq.com", "hobby": "聽音樂、看電影" } }, { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg-", "_score": 1.1655893, "_source": { "name": "王五", "age": 22, "mail": "333@qq.com", "hobby": "羽毛球、籃球、游泳、聽音樂" } } ] } }
term查詢
term 主要用於精確匹配哪些值,比如數字,日期,布爾值或 not_analyzed 的字符串(未經分析的文本數據類型):
結果:
{ "took": 31, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 1.0, "hits": [ { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg8", "_score": 1.0, "_source": { "name": "張三", "age": 20, "mail": "111@qq.com", "hobby": "羽毛球、乒乓球、足球" } } ] } }
terms查詢
terms 跟 term 有點類似,但 terms 允許指定多個匹配條件。 如果某個字段指定了多個值,那么文檔需要一起去做匹配:
結果:
{ "took": 2, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1.0, "hits": [ { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg9", "_score": 1.0, "_source": { "name": "李四", "age": 21, "mail": "222@qq.com", "hobby": "羽毛球、乒乓球、足球、籃球" } }, { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg8", "_score": 1.0, "_source": { "name": "張三", "age": 20, "mail": "111@qq.com", "hobby": "羽毛球、乒乓球、足球" } } ] } }
range查詢
range 過濾允許我們按照指定范圍查找一批數據:
范圍操作符包含:
gt :: 大於
gte :: 大於等於
lt :: 小於
lte :: 小於等於
示例:
結果:
{ "took": 4, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1.0, "hits": [ { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg9", "_score": 1.0, "_source": { "name": "李四", "age": 21, "mail": "222@qq.com", "hobby": "羽毛球、乒乓球、足球、籃球" } }, { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg8", "_score": 1.0, "_source": { "name": "張三", "age": 20, "mail": "111@qq.com", "hobby": "羽毛球、乒乓球、足球" } }, { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg-", "_score": 1.0, "_source": { "name": "王五", "age": 22, "mail": "333@qq.com", "hobby": "羽毛球、籃球、游泳、聽音樂" } } ] } }
exists 查詢
exists 查詢可以用於查找文檔中是否包含指定字段或沒有某個字段,類似於SQL語句中的 IS_NULL 條件
結果:
match查詢
match 查詢是一個標准查詢,不管你需要全文本查詢還是精確查詢基本上都要用到它。
如果你使用 match 查詢一個全文本字段,它會在真正查詢之前用分析器先分析 match 一下查詢字符:
結果:
{ "took": 1, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.25811607, "hits": [ { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg9", "_score": 0.25811607, "_source": { "name": "李四", "age": 21, "mail": "222@qq.com", "hobby": "羽毛球、乒乓球、足球、籃球" } } ] } }
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 }} ] } }
過濾查詢
Elasticsearch也支持過濾查詢,如term、range、match等。
示例:查詢年齡為20歲的用戶。
結果:
{ "took": 3, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.0, "hits": [ { "_index": "dalianpai", "_type": "person", "_id": "AXFeHQMrEXg1j8gSlZg8", "_score": 0.0, "_source": { "name": "張三", "age": 20, "mail": "111@qq.com", "hobby": "羽毛球、乒乓球、足球" } } ] } }
查詢和過濾的對比:
- 一條過濾語句會詢問每個文檔的字段值是否包含着特定值。
- 查詢語句會詢問每個文檔的字段值與特定值的匹配程度如何。
一條查詢語句會計算每個文檔與查詢語句的相關性,會給出一個相關性評分 _score,並且 按照相關性對匹配到的文檔進行排序。 這種評分方式非常適用於一個沒有完全配置結果的全文本搜索。
- 一個簡單的文檔列表,快速匹配運算並存入內存是十分方便的, 每個文檔僅需要1個字節。這些緩存的過濾結果集與后續請求的結合使用是非常高效的。
- 查詢語句不僅要查找相匹配的文檔,還需要計算每個文檔的相關性,所以一般來說查詢語句要比 過濾語句更耗時,並且查詢結果也不可緩存。
建議:做精確匹配搜索時,最好用過濾語句,因為過濾語句可以緩存數據。