《ElasticSearch查询》目录导航:

ElasticSearch是性能优化的分布式全文搜索引擎,存储数据的载体是文档(Document),它的优势在于搜索速度快和支持聚合操作,在更新文档时,基本上能够达到实时搜索。ElasticSearch引擎总是按照文档标识来更新数据,并发控制是通过顺序的版本ID(version)实现的,控制写-写、写-读冲突,实现数据弱一致性。

在ElasticSearch引擎中,索引定义了文档的逻辑存储,索引是由段(Segment)组成的,段不是实时更新的,这意味着,在建立索引时,一个段写入磁盘后,就不再被更新。被删除文档的信息存储在一个单独的文件中,在搜索数据时,ElasticSearch首先从段中查询,再从查询结果中过滤被删除的文档,这意味着,段中存储”未被删除文档“的密度降低。多个段可以通过段合并(Segment Merge)操作把“已删除”的文档将从段中物理删除,将未删除的文档合并成一个新段,新段中没有”已删除文档“,因此,段合并操作能够提高索引的查找速度,但段合并是IO密集型的,需要消耗大量的IO操作。

一旦数据存储在倒排索引中,就不能被修改,因此,更新文档是一项复杂的任务。在内部,ElasticSearch引擎必须首先获取文档(从_source属性中获得数据),删除旧的文档,更新_source属性,然后重新索引该文档,使之可被搜索到,就是说,文档更新的流程,实际上是先标记文档被删除,后插入新的文档,最后将新文档编入索引。

数据的更新,主要是通过_update端点,编写内嵌脚本(inline script)来实现。默认的脚本语言是Groovy,Groovy是内置的脚本语言,不需要安装,默认是禁用的,在未启用动态脚本的结点上执行脚本更新,ElasticSearch引擎将会抛出异常消息:

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

要启用脚本更新,必须修改每个节点(node)的全局配置文件 config/elasticsearch.yml,添加配置选项:

script.inline: true
script.indexed: true

一,编入索引(Index Data)

索引API用于将一个类型化的JSON结构添加到一个索引中,或者更新索引中的一个文档,使之能够被搜索到。

1,使用文档标识编入索引

在把文档编入索引时,如果在API中显式提供文档的标识(_id),那么ElasticSearch引擎使用Upsert(更新或增加)方式更新索引,这意味着,如果索引中已经存在相同ID的文档,那么ElasticSearch更新该文档(实际上是先删除,后添加);如果索引中不存在相同ID的文档,那么把文档添加索引中。

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

2,指定操作类型

在编入索引时,索引操作支持参数op_type,用于指定索引数据的操作类型是create,当文档ID不存在时,将文档添加到索引中;当显式指定操作类型是create时,如果创建的文档ID已经存在于索引中,那么创建操作将失败。

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

3,自动生成文档标识

在索引文档时,如果没有指定文档标识,那么ElasticSearch将会自动生成文档标识,并自动把操作类型(op_type)设置为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 引擎执行段合并操作时,才会真正从物理上删除文档。

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

三,在更新端点(_update)更新文档

ElasticSearch引擎在更新端点(_update)上更新文档,更新操作首先从索引中查询到文档,执行更新逻辑,并将更新之后的文档重新索引,使之能够被搜索到。在更新文档时,ElasticSearch使用版本控制并发操作可能产生的冲突。更新端点(_update)主要是基于脚本的文档更新,ElasticSearch引擎从索引中获取文档,使用脚本和可选的参数执行更新操作,并将文档重新编入索引。在更新时,即使只修改文档的部分字段,ElasticSearch也会重新索引整个文档,并使用文档版本避免读-写冲突。使用端点(_update)和内嵌脚本对文档执行更新操作,必须启用_source 字段。

1,根据参数值,更新指定文档的字段

ctx 是单词context的缩写,表示文档的上下文,在script节中,使用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参数,当指定的文档不存在时,upsert参数包含的内容将会被插入到索引中,作为一个新文档;如果指定的文档存在,ElasticSearch引擎将会执行指定的更新逻辑。

例如以下脚本,当文档存在时,把文档的counter字段设置为1;当文档不存在时,插入一个新的文档,文档的counter字段的值是2。

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

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,根据提供的文档片段更新数据

使用"doc"字段传递文档片段(Partial Document),doc字段包含完整文档的一部分字段,ElasticSearch引擎对已经存在的文档进行归并(Merge)更新,这就意味着,如果文档中存在doc节指定的字段,那么替换文档中的字段值;如果文档中部存在doc节指定的字段,那么向文档中增加新的字段,例如,对文档标识为1的文档,将该文档中的name字段更新为“new_name”:

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

detect_noop参数,在更新部分文档时,文档值被归并到_source字段,默认值是true,这意味着,当ElasticSearch引擎会检测_source字段的数据发生变化时,ElasticSearch引擎将重新索引该文档;如果设置设置为False时,ElasticSearch引擎不管_source字段的数据是否变化,都会更新文档。

5,更新操作的参数

retry_on_conflict参数:指定更新操作在发生版本冲突时重试的次数。

对于文档的更新操作,ElasticSearch引擎需要顺序执行三个阶段:获取文档(Get),更新文档(Update)和索引文档(Index)。在更新文档时,其他进程可能已经把相同的文档修改了。在默认情况下,更新操作由于检测到版本冲突而就立即失败,抛出异常。参数retry_on_conflict控制在ElasticSearch引擎真正抛出异常之前,更新操作重新执行的次数。

fields 参数:从已更新的文档中,返回有关字段(Relevant Fields)的数据,如果将fields设置为_source,将返回整个文档的所有数据。

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

四,批量操作(_bulk)

批量端点(_bulk)用于在一个请求(Request)中封装多个操作,请求格式是/index_name/type_name/_bulk。在请求主体中,包含多个操作请求,单个请求的格式相同,不同之处在于,每个请求包含两行JSON对象:信息行和数据行,由于批量端点必须识别换行,因此,发送的请求格式 使用--data-binary 代替 -d:

POST /_bulk?pretty  --data-binary  request_body

请求主动体,有四种类型,分别是index、update、create和delete,实现数据的索引分析,文档更新,文档创建和文档删除。

1,在索引中增加或替换现有文档,使用index节

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

2,从索引中移除文档,使用delete节

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

3,当索引中不存在文档定义时,在索引中增加新文档,使用create节

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

4,当更新文档时,使用update节

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

参考文档:

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

最新文章

  1. cx_oracle 执行cur.execute(sql)提交数据出现 UnicodeEncodeError: 'ascii' codec can't encode character u'\u2122' in position 170
  2. sharebutton
  3. [Java拾遗五]使用Session防止表单重复提交
  4. 使用CuteSlider做网站炫酷的幻灯片
  5. 【Hibernate 9】悲观锁和乐观锁
  6. 为什么要用Hibernate框架? 把SessionFactory,Session,Transcational封装成包含crud的工具类并且处理了事务,那不是用不着spring了?
  7. Git使用完全解析(一)
  8. YII中面包屑制作(当前位置:网站首页 >> 会员登陆)
  9. C++构造函数的自动调用(调用一个父类的构造函数,有显性调用最好,否则就默认调用无参数的构造函数)——哲学思想:不调用怎么初始化父类的成员数据和VMT?
  10. 【转】调用getActionBar()报Call requires API level 11 (current min is 8): android.app.Activity#getActionBar
  11. selenium结合docker构建分布式测试环境
  12. 201521123072《Java程序设计》第1周学习总结
  13. pulltorefresh 设置刷新文字提示颜色
  14. memcached源码分析-----slab内存分配器
  15. django框架使用mysql报错,及两种解决方法
  16. 内联函数:static inline 和 extern inline 的含义
  17. codeforces742B
  18. OPTIMIZE TABLE linked list 表优化原理 链表数据结构 空间再利用
  19. centos 编译lantrn
  20. c#读excel 不用office组件

热门文章

  1. php notes
  2. orcl 如何快速删除表中百万或千万数据
  3. css基础内容
  4. Java重要类详解之ArrayList类
  5. 读高性能JavaScript编程 第一章
  6. mysql host'XXX' is not allowed to connect to this mysql server
  7. php linux 环境搭建
  8. 处理AsyncTask的内存泄漏问题
  9. 自己写的开源MVC-easyMVC分享
  10. FreeChart柱状图中如何取消柱子的倒影