本节书摘来自华章计算机《深入理解ElasticSearch》一书中的第2章,第2.6节,作者:[美] 拉斐尔·酷奇(Rafa Ku) 马雷克·罗戈任斯基(Marek Rogoziński)更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.6 数据更新API
在索引一个新文档的时候,Lucene会对每个字段进行分析并产生词条流,词条流中的词条可能会经过滤器的额外处理,而没有过滤掉的词条会写入倒排索引中。索引过程中,一些不需要的信息可能会被抛弃,这些信息包括:某些特殊词条的位置(当词项向量没有存储时),特定词汇(停用词或同义词),词条的变形(如词干还原)。因此,我们无法更新索引中的文档,并且在每次修改文档时不得不向索引发送文档所有字段的数据。但ElasticSearch可以通过使用_source伪字段存储和检索文档的原始数据来解决这个问题。当用户需要更改文档时,ElasticSearch会获取_source字段中的值,做相应的修改,然后向索引提交一个新文档。当然,为了使这个特性生效,_source字段必须是可用的。更新命令一个很大的局限性就是它只能更新单个的文档,目前还不支持通过查询实现批量更新。
如果你不熟悉Apache Lucene的文本分析处理机制或任何前面提到的术语,请参考1.1节(Apache Lucene简介)。
从API的角度来看,文档更新可以通过执行发送至端点的更新请求来实现,也可以通过在更新请求的url中添加_update参数来更新某个特定的文档,如/library/book/1/_update。现在,我们来看看ElasticSearch提供了哪些更新功能。
作为示例,本节的其余部分都将使用下面命令所索引的文档:
https://yqfile.alicdn.com/71ee79d45b3220ecab1d84119db6fa6364fc18b7.png" >
2.6.1 简单字段更新
在本节的第一个案例里,我们尝试更新指定文档的字段。例如,使用下面的命令:
其中,我们更新了文档中的两个字段,title字段与year字段。作为响应,ElasticSearch将返回一个与建索引操作类似的回复:
https://yqfile.alicdn.com/99e820f53f663b64f3eb1ab8ecf47f2b80dcf9a9.png" >
现在,如果我们想从索引中获取刚才修改的文档,查看那两个字段是否真得修改了,可执行下面的命令:
该命令的响应如下所示:
正如我们所见,_source字段中,title字段与year字段的值已经被修改过了。接下来,我们查看下一个范例,该范例通过脚本来更新文档。
2.6.2 使用脚本按条件更新
有些时候,在修改文档的时候添加一些额外的逻辑是很有好处的,基于这点考虑,ElasticSearch允许用户结合脚本使用更新API。例如,我们发送下面这样的请求:
正如你所见,script字段定义了要对文档进行的操作,这可以是任何脚本。在范例中我们指派了ctx变量来引用源文档,但一般来说,脚本中会定义多个变量。通过使用ctx._source,我们可以修改当前字段或创建新字段(如果引用不存在的字段,ElasticSearch会自动创建这个字段),这正是范例中ctx._source.year = new_date语句产生的动作。此外,也可以使用remove()方法来移除某些字段,例如:
2.6.3 使用更新 API创建或删除文档
更新API不仅仅可以用来修改字段,也可以用来操作整个文档。upsert属性允许用户在当URL中地址不存在时创建一个新的文档。请查看下面这个命令:
该命令修改了某个已有文档的year字段(该文档位于索引library中,book类型,文档ID为1)。如果该文档不存在,将会创建一个新文档,并且该文档会创建一个新字段title,如请求命令中upset部分定义的那样。此外,前面的命令也可以使用脚本重写为以下形式:
https://yqfile.alicdn.com/d0960e7238fe7938c44126482228e4095211e3dc.png" >
最后一个有趣的特性是有条件地移除整个文档,具体可以通过设置ctx.op的值为delete来实现。例如,可以通过下面的命令从索引中删除文档:
https://yqfile.alicdn.com/5854c569f0e9259deab0eca73a4754a1046a81a7.png" >
当然,我们可以使用脚本实现更复杂的逻辑来删除满足特定规则的文档。