環境:elasticsearch6.1.2 kibana6.1.2
基礎概念:
1、_index元數據
(1)代表一個document存放在哪個index中
(2)類似的數據放在一個索引,非類似的數據放不同索引:product index(包含了所有的商品),sales index(包含了所有的商品銷售數據),inventory index(包含了所有庫存相關的數據)。如果你把比如product,sales,human resource(employee),全都放在一個大的index里面,比如說company index,不合適的。
(3)index中包含了很多類似的document:類似是什么意思,其實指的就是說,這些document的fields很大一部分是相同的,你說你放了3個document,每個document的fields都完全不一樣,這就不是類似了,就不太適合放到一個index里面去了。
(4)索引名稱必須是小寫的,不能用下划線開頭,不能包含逗號。
2、_type元數據
(1)代表document屬於index中的哪個類別(type)
(2)一個索引通常會划分為多個type,邏輯上對index中有些許不同的幾類數據進行分類:因為一批相同的數據,可能有很多相同的fields,但是還是可能會有一些輕微的不同,可能會有少數fields是不一樣的,舉個例子,就比如說,商品,可能划分為電子商品,生鮮商品,日化商品,等等。
(3)type名稱可以是大寫或者小寫,但是同時不能用下划線開頭,不能包含逗號
3、_id元數據
(1)代表document的唯一標識,與index和type一起,可以唯一標識和定位一個document
(2)我們可以手動指定document的id(put /index/type/id),也可以不指定,由es自動為我們創建一個id
一、插入
數據准備(插入):es會自動建立index和type,不需要提前創建,而且es默認會對document每個field都建立倒排索引,讓其可以被搜索。
PUT /ecommerce/product/1 { "name" : "gaolujie yagao", "desc" : "gaoxiao meibai", "price" : 30, "producer" : "gaolujie producer", "tags": [ "meibai", "fangzhu" ] } PUT /ecommerce/product/2 { "name" : "jiajieshi yagao", "desc" : "youxiao fangzhu", "price" : 25, "producer" : "jiajieshi producer", "tags": [ "fangzhu" ] } PUT /ecommerce/product/3 { "name" : "zhonghua yagao", "desc" : "caoben zhiwu", "price" : 40, "producer" : "zhonghua producer", "tags": [ "qingxin" ] }
插入數據時的id生成策略:
手動指定document id:當es的數據來源於其他系統,比如mysql而這些數據都有主鍵ID,建議使用該方式,可以直接將mysql的主鍵作為es中的ID。
自動生成的id:當我們的數據是直接存儲到es中時,建議采用該方式。該方式的特點,長度為20個字符,URL安全,base64編碼,GUID,分布式系統並行生成時不可能會發生沖突。
POST /test_index/test_type { "test_content": "my test" }
結果: { "_index": "test_index", "_type": "test_type", "_id": "fkSoJ2EBuYE9HLKhxglD", "_version": 1, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 0, "_primary_term": 1 }
二、修改
1、document的全量替換型修改:該方式是直接替換類型是 product id=1的文檔。該方式必須帶上所有的field,才能去進行信息的修改。
PUT /ecommerce/product/1 { "name" : "jiaqiangban gaolujie yagao", "desc" : "gaoxiao meibai", "price" : 30, "producer" : "gaolujie producer", "tags": [ "meibai", "fangzhu" ] }
注意:
1、document的全量替換
(1)語法與創建文檔是一樣的,如果document id不存在,那么就是創建;如果document id已經存在,那么就是全量替換操作,替換document的json串內容
(2)document是不可變的,如果要修改document的內容,第一種方式就是全量替換,直接對document重新建立索引,替換里面所有的內容
(3)es會將老的document標記為deleted,然后新增我們給定的一個document,當我們創建越來越多的document的時候,es會在適當的時機在后台啟動線程刪除標記為deleted的document,釋放空間。
2、partial update 指定修改內容修改(推薦該方式)
POST /ecommerce/product/1/_update { "doc": { "name": "jiaqiangban gaolujie yagao" } }
該方式發生的步驟與全量替換基本一樣,不過該方式將這些流程放在es內部,這樣減少了放了請求,減少並發沖突發的概率。他同樣會生成兩份document,老的document標記為deleted,partial update 修改的數據更新到新的document中。
retry_on_conflict(重策略):當執行索引和更新的時候,有可能另一個進程正在執行更新。這個時候就會造成沖突,這個參數就是用於定義當遇到沖突時,再過多長時間執行操作,通過retry_on_conflict
參數設置重試次數來自動完成,這樣update
操作將會在發生錯誤前重試——這個值默認為0。
例如:
POST /test_index/test_type/1/_update?retry_on_conflict=5 { "doc": { "test_name": "update book re", "test_id":11122 } }
三、刪除
根據類型的Id直接刪除
DELETE /ecommerce/product/1
注意: 不是物理刪除,只會將其標記為deleted,當數據越來越多的時候,在后台啟動線程自動刪除,釋放空間。
四、查詢
(一)、query string search
什么是query string search:search參數都是以http請求的query string來附帶的
語法:
/_search 在所有的索引中搜索所有的類型 /gb/_search 在 gb 索引中搜索所有的類型 /gb,us/_search 在 gb 和 us 索引中搜索所有的文檔 /g*,u*/_search 在任何以 g 或者 u 開頭的索引中搜索所有的類型 /gb/user/_search 在 gb 索引中搜索 user 類型 /gb,us/user,tweet/_search 在 gb 和 us 索引中搜索 user 和 tweet 類型 /_all/user,tweet/_search 在所有的索引中搜索 user 和 tweet 類型
當在單一的索引下進行搜索的時候,Elasticsearch 轉發請求到索引的每個分片中,可以是主分片也可以是副本分片,然后從每個分片中收集結果。多索引搜索恰好也是用相同的方式工作的--只是會涉及到更多的分片。
demo:
1、搜索全部商品:
GET /ecommerce/product/_search
{
}
結果分析:
took:耗費了幾毫秒
timed_out:是否超時,這里是沒有
_shards:數據拆成了5個分片,所以對於搜索請求,會打到所有的primary shard(或者是它的某個replica shard也可以)
hits.total:查詢結果的數量,3個document
hits.max_score:score的含義,就是document對於一個search的相關度的匹配分數,越相關,就越匹配,分數也高
hits.hits:包含了匹配搜索的document的詳細數據
2、搜索商品名稱中包含yagao的商品,並按照價格倒序:
GET /ecommerce/product/_search?q=name:yagao&sort=price:desc
{
}
(二)、query DSL
DSL:Domain Specified Language,特定領域的語言
http request body:請求體,可以用json的格式來構建查詢語法,比較方便,可以構建各種復雜的語法。
1、分頁查詢所有商品並按照價格倒序
GET /ecommerce/product/_search { "query": { "match_all": {} }, "sort": [ { "price": "desc" } ], "from": 0, "size": 3 }
query:包裝查詢條件
sort:包裝排序條件
form:第幾頁
size:每頁幾條
2、根據名稱查詢,並按照價格倒序
GET /ecommerce/product/_search { "query": { "match": { "name":"yagao" } }, "sort": [ { "price": "desc" } ] }
3、指定返回結果的列(field)
GET /ecommerce/product/_search { "query": { "match_all": {} }, "_source": ["name", "price"] }
_source元數據:就是說,我們在創建一個document的時候,使用的那個放在request body中的json串,默認情況下,在get的時候,會原封不動的給我們返回回來。我們可以指定_source中,返回哪些field。
在分布式系統中深度分頁
理解為什么深度分頁是有問題的,我們可以假設在一個有 5 個主分片的索引中搜索。 當我們請求結果的第一頁(結果從 1 到 10 ),每一個分片產生前 10 的結果,並且返回給 協調節點 ,協調節點對 50 個結果排序得到全部結果的前 10 個。
現在假設我們請求第 1000 頁--結果從 10001 到 10010 。所有都以相同的方式工作除了每個分片不得不產生前10010個結果以外。 然后協調節點對全部 50050 個結果排序最后丟棄掉這些結果中的 50040 個結果。
可以看到,在分布式系統中,對結果排序的成本隨分頁的深度成指數上升。這就是 web 搜索引擎對任何查詢都不要返回超過 1000 個結果的原因。
(三)、query filter
1、搜索商品名稱包含yagao,而且售價大於25元的商品
GET /ecommerce/product/_search { "query" : { "bool" : { "must" : { "match" : { "name" : "yagao" } }, "filter" : { "range" : { "price" : { "gt" : 25 } } } } } }
(四)full-text search (全文檢索)
全文檢索會將輸入的搜索串拆解開來,去倒排索引里面去一一匹配,只要能匹配上任意一個拆解后的單詞,就可以作為結果返回。
1、根據一段文字進行查找
GET /ecommerce/product/_search { "query" : { "match" : { "producer" : "yagao producer" } } }
producer這個字段,會先被拆解,建立倒排索引。根據es中這個字段的所有數據分詞后有 jiajieshi、gaolujie、zhonghua、producer
查詢條件producer 也會被分詞 ,分詞為 yagao 和 producer
結果分析: "max_score": 0.2876821 匹配度
(五)、phrase search (短語查找)
輸入的搜索串,必須在指定的字段文本中,完全包含一模一樣的,才可以算匹配,作為結果返回。
GET /ecommerce/product/_search { "query" : { "match_phrase" : { "producer" : "yagao producer" } } }
結果為空
(六)、highlight search (高亮搜索結果)
GET /ecommerce/product/_search { "query" : { "match" : { "producer" : "producer" } }, "highlight": { "fields" : { "producer" : {} } } }