zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【Elasticsearch教程2】 更新文档 painless script案例

2023-09-27 14:26:50 时间

版本约定

本系列博客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();
}