版本约定
本系列博客ES版本如下:
- Elasticsearch
7.17.X
- Spring Data Elasticsearch
4.4.X
Elasticsearch下载地址Spring Data Elasticsearch 下载地址
更新文档API
- 对于简单点的更新,可以在doc里写新的文档内容来更新文档
- 对于复杂点的更新,可以使用painless script脚本更新文档
初始化数据
PUT pigg_test/_doc/1
{
"name": "亚瑟王",
"age": 33
}
1 doc更新
把需要更新的字段放到doc里面,已有的字段会更新,新的字段会添加进文档。
POST pigg_test/_update/1
{
"doc": {
"age": 32,
"word": "向我祈求怜悯吧"
}
}
再次查询id=1的文档发现age的值改变了,并且添加了新的字段word。
{
"_source" : {
"name" : "亚瑟王",
"age" : 32,
"word" : "向我祈求怜悯吧"
}
}
当文档不存在时,更新会报错,可以设置"doc_as_upsert": true
就会创建文档:
当id=2的文档不存在时,会创建该文档
POST pigg_test/_update/2
{
"doc": {
"name": "死亡骑士",
"age": 32,
"word": "向我祈求怜悯吧"
},
"doc_as_upsert": true
}
2 painless script更新
painless
是Elasticsearch的默认脚本语言,它具有像 Groovy 那样的语法。painless
可以用在更新文档,也可以用来写查询文档的查询条件。
下面就先写些更新文档的例子,用painless
脚本查询文档在后期博客再做详细介绍。
修改字段的值
POST pigg_test/_update/1
{
"script": {
"source": "ctx._source.word = '我会狠狠地拒绝你'"
}
}
上面的脚步也可以简写为如下格式:
POST pigg_test/_update/1
{
"script": "ctx._source.word = '我会狠狠地拒绝你'"
}
对数值类型进行数学计算
POST pigg_test/_update/1
{
"script": "ctx._source.age += 1"
}
给文档添加一个新的字段act
POST pigg_test/_update/1
{
"script": "ctx._source.act = ['誓约之盾']"
}
给act数组再添加一项值
POST pigg_test/_update/1
{
"script": "ctx._source.act.add('回旋打击')"
}
当act数组中不存在“圣剑裁决”时,才添加“圣剑裁决”
POST pigg_test/_update/1
{
"script": {
"source": "if(!ctx._source.act.contains(params.act)) {ctx._source.act.add(params.act)}",
"lang": "painless",
"params": {
"act": "圣剑裁决"
}
}
}
当act数组中存在“圣剑裁决”时,才删除“圣剑裁决”
POST pigg_test/_update/1
{
"script":{
"source": "if(ctx._source.act.contains(params.act)) {ctx._source.act.remove(ctx._source.act.indexOf(params.act))}",
"lang": "painless",
"params": {
"act": "圣剑裁决"
}
}
}
添加一个新的字段newName
,它的值和name
相等
POST pigg_test/_update/1
{
"script": "ctx._source.newName = ctx._source.name"
}
删除字段newName
,但是不修改mapping
POST pigg_test/_update/1
{
"script": "ctx._source.remove('newName')"
}
3 Update By Query
当需要对查询条件匹配到的文档更新时,可以使用_update_by_query,在script
同级加上query
查询语句。
POST pigg_test/_update_by_query?conflicts=proceed
{
"script": {
"source": "ctx._source.age+=1",
"lang": "painless"
},
"query": {
"term": {
"name.keyword": {
"value": "亚瑟王"
}
}
}
}
Java API中使用painless script
1 Elasticsearch Java API
写一个方法,根据查询条件执行script更新文档:
- 第一个参数是索引名称
- 第二个参数是查询条件
- 第三个参数是要更新的script脚步
public static BulkByScrollResponse updateByQuery(String index, QueryBuilder query, String strScript) {
BulkByScrollResponse bulkResponse = null;
try {
UpdateByQueryRequest request = new UpdateByQueryRequest(index);
request.setConflicts("proceed");
request.setQuery(query);
Script script = new Script(
ScriptType.INLINE, "painless",
strScript,
Collections.emptyMap());
request.setScript(script);
bulkResponse =
restHighLevelClient.updateByQuery(request, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("ES更新异常", e.getMessage());
}
return bulkResponse;
}
调用上面方法的案例:将state更新为1
IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery().addIds(ids.stream().toArray(String[]::new));
RestfulElasticsearchUtils.updateByQuery(
"pigg_test",
idsQueryBuilder,
"ctx._source['state']='1'"
);
2 Spring Data Elasticsearch
这里用Spring Data Elasticsearch的UpdateQuery
举例:
根据查询条件,将文档的state
的值更新为INVALID
public long forceInvalidByQuery(QueryBuilder queryBuilder) {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(queryBuilder)
.build();
UpdateQuery updateQuery = UpdateQuery.builder(query)
.withScriptType(ScriptType.INLINE)
.withScript("ctx._source['state'] = params['newState']").withLang("painless")
.withParams(Collections.singletonMap("newState", StateEnum.INVALID.getValue()))
.withAbortOnVersionConflict(false)
.build();
ByQueryResponse byQueryResponse = operations.updateByQuery(updateQuery, getIndexCoordinates());
return byQueryResponse.getUpdated();
}