ElasticSearch是性能優(yōu)化的分布式全文搜索引擎,存儲數(shù)據(jù)的載體是文檔(Document),它的優(yōu)勢在于搜索速度快和支持聚合操作,在更新文檔時(shí),基本上能夠達(dá)到實(shí)時(shí)搜索。ElasticSearch引擎總是按照文檔標(biāo)識來更新數(shù)據(jù),并發(fā)控制是通過順序的版本ID(version)實(shí)現(xiàn)的,控制寫-寫、寫-讀沖突,實(shí)現(xiàn)數(shù)據(jù)弱一致性。

在ElasticSearch引擎中,索引定義了文檔的邏輯存儲,索引是由段(Segment)組成的,段不是實(shí)時(shí)更新的,這意味著,在建立索引時(shí),一個段寫入磁盤后,就不再被更新。被刪除文檔的信息存儲在一個單獨(dú)的文件中,在搜索數(shù)據(jù)時(shí),ElasticSearch首先從段中查詢,再從查詢結(jié)果中過濾被刪除的文檔,這意味著,段中存儲”未被刪除文檔“的密度降低。多個段可以通過段合并(Segment Merge)操作把“已刪除”的文檔將從段中物理刪除,將未刪除的文檔合并成一個新段,新段中沒有”已刪除文檔“,因此,段合并操作能夠提高索引的查找速度,但段合并是IO密集型的,需要消耗大量的IO操作。

一旦數(shù)據(jù)存儲在倒排索引中,就不能被修改,因此,更新文檔是一項(xiàng)復(fù)雜的任務(wù)。在內(nèi)部,ElasticSearch引擎必須首先獲取文檔(從_source屬性中獲得數(shù)據(jù)),刪除舊的文檔,更新_source屬性,然后重新索引該文檔,使之可被搜索到,就是說,文檔更新的流程,實(shí)際上是先標(biāo)記文檔被刪除,后插入新的文檔,最后將新文檔編入索引。

數(shù)據(jù)的更新,主要是通過_update端點(diǎn),編寫內(nèi)嵌腳本(inline script)來實(shí)現(xiàn)。默認(rèn)的腳本語言是Groovy,Groovy是內(nèi)置的腳本語言,不需要安裝,默認(rèn)是禁用的,在未啟用動態(tài)腳本的結(jié)點(diǎn)上執(zhí)行腳本更新,ElasticSearch引擎將會拋出異常消息:

scripts of type [inline], operation [update] and lang [groovy] are disabled

要啟用腳本更新,必須修改每個節(jié)點(diǎn)(node)的全局配置文件 config/elasticsearch.yml,添加配置選項(xiàng):

script.inline: truescript.indexed: true

一,編入索引(Index Data)

索引API用于將一個類型化的JSON結(jié)構(gòu)添加到一個索引中,或者更新索引中的一個文檔,使之能夠被搜索到。

1,使用文檔標(biāo)識編入索引

在把文檔編入索引時(shí),如果在API中顯式提供文檔的標(biāo)識(_id),那么ElasticSearch引擎使用Upsert(更新或增加)方式更新索引,這意味著,如果索引中已經(jīng)存在相同ID的文檔,那么ElasticSearch更新該文檔(實(shí)際上是先刪除,后添加);如果索引中不存在相同ID的文檔,那么把文檔添加索引中。

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

PUT host:port/twitter/tweet/1 -d 
{    "user" : "kimchy",    "post_date" : "2009-11-15T14:12:12",    "message" : "trying out Elasticsearch"}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

2,指定操作類型

在編入索引時(shí),索引操作支持參數(shù)op_type,用于指定索引數(shù)據(jù)的操作類型是create,當(dāng)文檔ID不存在時(shí),將文檔添加到索引中;當(dāng)顯式指定操作類型是create時(shí),如果創(chuàng)建的文檔ID已經(jīng)存在于索引中,那么創(chuàng)建操作將失敗。

PUT 'http://localhost:9200/twitter/tweet/1?op_type=create' -d
PUT 'http://localhost:9200/twitter/tweet/1/_create' -d

3,自動生成文檔標(biāo)識

在索引文檔時(shí),如果沒有指定文檔標(biāo)識,那么ElasticSearch將會自動生成文檔標(biāo)識,并自動把操作類型(op_type)設(shè)置為create,注意,自動生成文檔標(biāo)識是更新操作,修改索引中的文檔,而不是新建一個新的文檔,因此使用POST動詞,而不是PUT動詞。

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

POST 'http://localhost:9200/twitter/tweet/' -d'{
    "user" : "kimchy",    "post_date" : "2009-11-15T14:12:12",    "message" : "trying out Elasticsearch"}'

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

二,刪除文檔

在ElasticSearch引擎中刪除文檔非常簡單,通過文檔標(biāo)識刪除文檔,實(shí)際上,該文檔并沒有從索引中物理刪除,只是在其他文件中被標(biāo)記刪除,只要ElasticSerach 引擎執(zhí)行段合并操作時(shí),才會真正從物理上刪除文檔。

DELETE 'http://localhost:9200/twitter/tweet/1'

三,在更新端點(diǎn)(_update)更新文檔

ElasticSearch引擎在更新端點(diǎn)(_update)上更新文檔,更新操作首先從索引中查詢到文檔,執(zhí)行更新邏輯,并將更新之后的文檔重新索引,使之能夠被搜索到。在更新文檔時(shí),ElasticSearch使用版本控制并發(fā)操作可能產(chǎn)生的沖突。更新端點(diǎn)(_update)主要是基于腳本的文檔更新,ElasticSearch引擎從索引中獲取文檔,使用腳本和可選的參數(shù)執(zhí)行更新操作,并將文檔重新編入索引。在更新時(shí),即使只修改文檔的部分字段,ElasticSearch也會重新索引整個文檔,并使用文檔版本避免讀-寫沖突。使用端點(diǎn)(_update)和內(nèi)嵌腳本對文檔執(zhí)行更新操作,必須啟用_source 字段。

1,根據(jù)參數(shù)值,更新指定文檔的字段

ctx 是單詞context的縮寫,表示文檔的上下文,在script節(jié)中,使用ctx引用文檔。

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

POST 'localhost:9200/test/type1/1/_update' -d '{
    "script" : {        "inline": "ctx._source.counter += count",        "params" : { "count" : 4 }
    },    "upsert" : { "counter" : 1  }
}'

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

示例,腳本更新文檔的字段counter,把ID為1的文檔的counter字段增加4。當(dāng)文檔中沒有該字段時(shí),例如,想要增加文檔中的counter字段值,而該字段不存在,在請求中使用upsert字段,提供counter字段的默認(rèn)值。

upsert參數(shù),當(dāng)指定的文檔不存在時(shí),upsert參數(shù)包含的內(nèi)容將會被插入到索引中,作為一個新文檔;如果指定的文檔存在,ElasticSearch引擎將會執(zhí)行指定的更新邏輯。

例如以下腳本,當(dāng)文檔存在時(shí),把文檔的counter字段設(shè)置為1;當(dāng)文檔不存在時(shí),插入一個新的文檔,文檔的counter字段的值是2。

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

{  
   "script":{  
      "inline":"ctx._source.counter= 1"
   },   "upsert":{"counter":2}
}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

2,向_source字段,增加一個字段

POST 'localhost:9200/test/type1/1/_update' -d '{
    "script" : "ctx._source.name_of_new_field = \"value_of_new_field\""}'

3,從_source字段中,刪除一個字段

POST 'localhost:9200/test/type1/1/_update' -d '{
    "script" : "ctx._source.remove(\"name_of_field\")"}'

4,根據(jù)提供的文檔片段更新數(shù)據(jù)

使用"doc"字段傳遞文檔片段(Partial Document),doc字段包含完整文檔的一部分字段,ElasticSearch引擎對已經(jīng)存在的文檔進(jìn)行歸并(Merge)更新,這就意味著,如果文檔中存在doc節(jié)指定的字段,那么替換文檔中的字段值;如果文檔中部存在doc節(jié)指定的字段,那么向文檔中增加新的字段,例如,對文檔標(biāo)識為1的文檔,將該文檔中的name字段更新為“new_name”:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

POST 'localhost:9200/test/type1/1/_update' -d '{
    "doc" : {        "name" : "new_name"
    },   "detect_noop": false}'

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

detect_noop參數(shù),在更新部分文檔時(shí),文檔值被歸并到_source字段,默認(rèn)值是true,這意味著,當(dāng)ElasticSearch引擎會檢測_source字段的數(shù)據(jù)發(fā)生變化時(shí),ElasticSearch引擎將重新索引該文檔;如果設(shè)置設(shè)置為False時(shí),ElasticSearch引擎不管_source字段的數(shù)據(jù)是否變化,都會更新文檔。

5,更新操作的參數(shù)

retry_on_conflict參數(shù):指定更新操作在發(fā)生版本沖突時(shí)重試的次數(shù)。

對于文檔的更新操作,ElasticSearch引擎需要順序執(zhí)行三個階段:獲取文檔(Get),更新文檔(Update)和索引文檔(Index)。在更新文檔時(shí),其他進(jìn)程可能已經(jīng)把相同的文檔修改了。在默認(rèn)情況下,更新操作由于檢測到版本沖突而就立即失敗,拋出異常。參數(shù)retry_on_conflict控制在ElasticSearch引擎真正拋出異常之前,更新操作重新執(zhí)行的次數(shù)。

fields 參數(shù):從已更新的文檔中,返回有關(guān)字段(Relevant Fields)的數(shù)據(jù),如果將fields設(shè)置為_source,將返回整個文檔的所有數(shù)據(jù)。

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

{  
   "doc":{  
      "counter":3
   },   "upsert":{"counter":2},   "fields":["counter"],   "detect_noop":true}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

四,批量操作(_bulk)

批量端點(diǎn)(_bulk)用于在一個請求(Request)中封裝多個操作,請求格式是/index_name/type_name/_bulk。在請求主體中,包含多個操作請求,單個請求的格式相同,不同之處在于,每個請求包含兩行JSON對象:信息行和數(shù)據(jù)行,由于批量端點(diǎn)必須識別換行,因此,發(fā)送的請求格式 使用--data-binary 代替 -d:

POST /_bulk?pretty  --data-binary  request_body

請求主動體,有四種類型,分別是index、update、create和delete,實(shí)現(xiàn)數(shù)據(jù)的索引分析,文檔更新,文檔創(chuàng)建和文檔刪除。

1,在索引中增加或替換現(xiàn)有文檔,使用index節(jié)

{"index":{"_index":"index_name","_type":"type_name","_id":####}}
{"doc_field1":"xx","doc_field2":"yy"}

2,從索引中移除文檔,使用delete節(jié)

{"delete":{"_index":"index_name","_type":"type_name","_id":####}}

3,當(dāng)索引中不存在文檔定義時(shí),在索引中增加新文檔,使用create節(jié)

{"create":{"_index":"index_name","_type":"type_name","_id":####}}
{"doc_field1":"xx","doc_field2":"yy"}

4,當(dāng)更新文檔時(shí),使用update節(jié)

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

{ "update" : {"_id" : "1", "_type" : "type1", "_index" : "index1", "_retry_on_conflict" : 3} }
{ "doc" : {"field" : "value"} }
{ "update" : { "_id" : "0", "_type" : "type1", "_index" : "index1", "_retry_on_conflict" : 3} }
{ "script" : { "inline": "ctx._source.counter += param1", "params" : {"param1" : 1}}, "upsert" : {"counter" : 1}}
{ "update" : {"_id" : "2", "_type" : "type1", "_index" : "index1", "_retry_on_conflict" : 3} }
{ "doc" : {"field" : "value"}, "upsert" : true }
{ "update" : {"_id" : "3", "_type" : "type1", "_index" : "index1", "fields" : ["_source"]} }
{ "doc" : {"field" : "value"} }
{ "update" : {"_id" : "4", "_type" : "type1", "_index" : "index1"} }
{ "doc" : {"field" : "value"}, "fields": ["_source"]}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

參考文檔:

Elasticsearch Reference [2.4] ? Document APIs

Elasticsearch Reference [2.4] ? Document APIs ? Bulk API

Elasticsearch Reference [2.4] ? Document APIs ? Update API

Elasticsearch Reference [2.4] ? Modules ? Scripting

--業(yè)精于勤而荒于嬉,行成于思而毀于隨--
--歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處--

分類: ElasticSearch