ElasticSearch查詢 第四篇:匹配查詢(Match)
ElasticSearch是性能優(yōu)化的分布式全文搜索引擎,存儲數(shù)據(jù)的載體是文檔(Document),它的優(yōu)勢在于搜索速度快和支持聚合操作,在更新文檔時,基本上能夠達到實時搜索。ElasticSearch引擎總是按照文檔標識來更新數(shù)據(jù),并發(fā)控制是通過順序的版本ID(version)實現(xiàn)的,控制寫-寫、寫-讀沖突,實現(xiàn)數(shù)據(jù)弱一致性。
在ElasticSearch引擎中,索引定義了文檔的邏輯存儲,索引是由段(Segment)組成的,段不是實時更新的,這意味著,在建立索引時,一個段寫入磁盤后,就不再被更新。被刪除文檔的信息存儲在一個單獨的文件中,在搜索數(shù)據(jù)時,ElasticSearch首先從段中查詢,再從查詢結(jié)果中過濾被刪除的文檔,這意味著,段中存儲”未被刪除文檔“的密度降低。多個段可以通過段合并(Segment Merge)操作把“已刪除”的文檔將從段中物理刪除,將未刪除的文檔合并成一個新段,新段中沒有”已刪除文檔“,因此,段合并操作能夠提高索引的查找速度,但段合并是IO密集型的,需要消耗大量的IO操作。
一旦數(shù)據(jù)存儲在倒排索引中,就不能被修改,因此,更新文檔是一項復(fù)雜的任務(wù)。在內(nèi)部,ElasticSearch引擎必須首先獲取文檔(從_source屬性中獲得數(shù)據(jù)),刪除舊的文檔,更新_source屬性,然后重新索引該文檔,使之可被搜索到,就是說,文檔更新的流程,實際上是先標記文檔被刪除,后插入新的文檔,最后將新文檔編入索引。
數(shù)據(jù)的更新,主要是通過_update端點,編寫內(nèi)嵌腳本(inline script)來實現(xiàn)。默認的腳本語言是Groovy,Groovy是內(nèi)置的腳本語言,不需要安裝,默認是禁用的,在未啟用動態(tài)腳本的結(jié)點上執(zhí)行腳本更新,ElasticSearch引擎將會拋出異常消息:
scripts of type [inline], operation [update] and lang [groovy] are disabled
要啟用腳本更新,必須修改每個節(jié)點(node)的全局配置文件 config/elasticsearch.yml,添加配置選項:
script.inline:truescript.indexed: true
一,編入索引(Index Data)
索引API用于將一個類型化的JSON結(jié)構(gòu)添加到一個索引中,或者更新索引中的一個文檔,使之能夠被搜索到。
1,使用文檔標識編入索引
在把文檔編入索引時,如果在API中顯式提供文檔的標識(_id),那么ElasticSearch引擎使用Upsert(更新或增加)方式更新索引,這意味著,如果索引中已經(jīng)存在相同ID的文檔,那么ElasticSearch更新該文檔(實際上是先刪除,后添加);如果索引中不存在相同ID的文檔,那么把文檔添加索引中。
PUT host:port/twitter/tweet/1-d
{
? ? "user":"kimchy",
? ? "post_date":"2009-11-15T14:12:12",
? ? "message":"trying out Elasticsearch"}
2,指定操作類型
在編入索引時,索引操作支持參數(shù)op_type,用于指定索引數(shù)據(jù)的操作類型是create,當文檔ID不存在時,將文檔添加到索引中;當顯式指定操作類型是create時,如果創(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,自動生成文檔標識
在索引文檔時,如果沒有指定文檔標識,那么ElasticSearch將會自動生成文檔標識,并自動把操作類型(op_type)設(shè)置為create,注意,自動生成文檔標識是更新操作,修改索引中的文檔,而不是新建一個新的文檔,因此使用POST動詞,而不是PUT動詞。
POST'http://localhost:9200/twitter/tweet/'-d'{"user":"kimchy",
? ? "post_date":"2009-11-15T14:12:12",
? ? "message":"trying out Elasticsearch"}'
二,刪除文檔
在ElasticSearch引擎中刪除文檔非常簡單,通過文檔標識刪除文檔,實際上,該文檔并沒有從索引中物理刪除,只是在其他文件中被標記刪除,只要ElasticSerach 引擎執(zhí)行段合并操作時,才會真正從物理上刪除文檔。
DELETE'http://localhost:9200/twitter/tweet/1'
三,在更新端點(_update)更新文檔
ElasticSearch引擎在更新端點(_update)上更新文檔,更新操作首先從索引中查詢到文檔,執(zhí)行更新邏輯,并將更新之后的文檔重新索引,使之能夠被搜索到。在更新文檔時,ElasticSearch使用版本控制并發(fā)操作可能產(chǎn)生的沖突。更新端點(_update)主要是基于腳本的文檔更新,ElasticSearch引擎從索引中獲取文檔,使用腳本和可選的參數(shù)執(zhí)行更新操作,并將文檔重新編入索引。在更新時,即使只修改文檔的部分字段,ElasticSearch也會重新索引整個文檔,并使用文檔版本避免讀-寫沖突。使用端點(_update)和內(nèi)嵌腳本對文檔執(zhí)行更新操作,必須啟用_source 字段。
1,根據(jù)參數(shù)值,更新指定文檔的字段
ctx 是單詞context的縮寫,表示文檔的上下文,在script節(jié)中,使用ctx引用文檔。
POST'localhost:9200/test/type1/1/_update'-d'{"script" : {
? ? ? ? "inline":"ctx._source.counter += count",
? ? ? ? "params" : { "count":4 }
? ? },
? ? "upsert" : { "counter":1? }
}'
示例,腳本更新文檔的字段counter,把ID為1的文檔的counter字段增加4。當文檔中沒有該字段時,例如,想要增加文檔中的counter字段值,而該字段不存在,在請求中使用upsert字段,提供counter字段的默認值。
upsert參數(shù),當指定的文檔不存在時,upsert參數(shù)包含的內(nèi)容將會被插入到索引中,作為一個新文檔;如果指定的文檔存在,ElasticSearch引擎將會執(zhí)行指定的更新邏輯。
例如以下腳本,當文檔存在時,把文檔的counter字段設(shè)置為1;當文檔不存在時,插入一個新的文檔,文檔的counter字段的值是2。
{?
? "script":{?
? ? ? "inline":"ctx._source.counter= 1"? },
? "upsert":{"counter":2}
}
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)存在的文檔進行歸并(Merge)更新,這就意味著,如果文檔中存在doc節(jié)指定的字段,那么替換文檔中的字段值;如果文檔中部存在doc節(jié)指定的字段,那么向文檔中增加新的字段,例如,對文檔標識為1的文檔,將該文檔中的name字段更新為“new_name”:
POST'localhost:9200/test/type1/1/_update'-d'{"doc" : {
? ? ? ? "name":"new_name"? ? },
? "detect_noop":false}'
detect_noop參數(shù),在更新部分文檔時,文檔值被歸并到_source字段,默認值是true,這意味著,當ElasticSearch引擎會檢測_source字段的數(shù)據(jù)發(fā)生變化時,ElasticSearch引擎將重新索引該文檔;如果設(shè)置設(shè)置為False時,ElasticSearch引擎不管_source字段的數(shù)據(jù)是否變化,都會更新文檔。
5,更新操作的參數(shù)
retry_on_conflict參數(shù):指定更新操作在發(fā)生版本沖突時重試的次數(shù)。
對于文檔的更新操作,ElasticSearch引擎需要順序執(zhí)行三個階段:獲取文檔(Get),更新文檔(Update)和索引文檔(Index)。在更新文檔時,其他進程可能已經(jīng)把相同的文檔修改了。在默認情況下,更新操作由于檢測到版本沖突而就立即失敗,拋出異常。參數(shù)retry_on_conflict控制在ElasticSearch引擎真正拋出異常之前,更新操作重新執(zhí)行的次數(shù)。
fields 參數(shù):從已更新的文檔中,返回有關(guān)字段(Relevant Fields)的數(shù)據(jù),如果將fields設(shè)置為_source,將返回整個文檔的所有數(shù)據(jù)。
{?
? "doc":{?
? ? ? "counter":3? },
? "upsert":{"counter":2},
? "fields":["counter"],
? "detect_noop":true}
四,批量操作(_bulk)
批量端點(_bulk)用于在一個請求(Request)中封裝多個操作,請求格式是/index_name/type_name/_bulk。在請求主體中,包含多個操作請求,單個請求的格式相同,不同之處在于,每個請求包含兩行JSON對象:信息行和數(shù)據(jù)行,由于批量端點必須識別換行,因此,發(fā)送的請求格式 使用--data-binary 代替 -d:
POST /_bulk?pretty? --data-binary? request_body
請求主動體,有四種類型,分別是index、update、create和delete,實現(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,當索引中不存在文檔定義時,在索引中增加新文檔,使用create節(jié)
{"create":{"_index":"index_name","_type":"type_name","_id":####}}
{"doc_field1":"xx","doc_field2":"yy"}
4,當更新文檔時,使用update節(jié)
{"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"]}