背景

ElasticSearch 的使用度越来越普及了,很多公司都在使用。有做日志搜索的,有做商品搜索的,有做订单搜索的。

大部分使用场景都是通过程序定期去导入数据到 ElasticSearch 中,或者通过 CDC 的方式来构建索引。在这种场景下,更新数据都是单条更新,比如 ID=1 的数据发生了修改操作,那么就会把 ElasticSearch 中 ID=1 的这条数据更新下。

但有些场景下需要根据条件同时更新多条数据,就像 Mysql 中我们使用 Update Table Set Name=XXX where Age=18 去更新一批数据一样。

正好有同学微信问我怎么批量更新,接下来就看看在 ElasticSearch 中是如何去进行按条件更新的操作。

单条更新

ElasticSearch 的客户端官方推荐使用 elasticsearch-rest-high-level-client。所以本文也是基于 elasticsearch-rest-high-level-client 来构建代码。

首先来回顾下单条数据的更新是怎么做的,代码如下:

UpdateRequest updateRequest = new UpdateRequest(index, type, id);
updateRequest.doc(documentJson, XContentType.JSON);
restHighLevelClient.update(updateRequest, options);

构建 UpdateRequest 的时候就指定了索引,类型,ID 三个字段,也就精确到了某一条数据,所以更新的自然也是这一条数据。

条件更新

首先我们准备几条测试数据,如下:

{
    id: 1,
    title: "Java怎么学",
    type: 1,
    userId: 1,
    tags: [
        "java"
    ],
    textContent: "我要学Java",
    status: 1,
    heat: 100
}
{
    id: 2,
    title: "Java怎么学",
    type: 1,
    userId: 1,
    tags: [
        "java"
    ],
    textContent: "我要学Java",
    status: 1,
    heat: 100
}

假如我们的需求是将 userId=1 的所有文档数据改成无效,也就是 status=0。如果不用按条件更新,你就得查询出 userId=1 的所有数据,然后一条条更新,这就太慢了。

下面看看按条件更新是如何使用的,如下:

POST http://47.105.66.210:9200/article_v1/doc/_update_by_query
{
    "script": {
        "source":"ctx._source['status']=0;"
    },
    "query": {
        "term": {
            "userId": 1
        }
    } 
}

按条件更新需要使用_update_by_query 来进行,query 用于指定更新数据的匹配条件,script 用于更新的逻辑。

详细使用文档:

https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html

https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html

在 Java 代码中如何实现条件更新呢?

UpdateByQueryRequest request = new UpdateByQueryRequest("article_v1");
request.setQuery(new TermQueryBuilder("userId", 1));
request.setScript(new Script("ctx._source['status']=0;"));
restHighLevelClient.updateByQuery(request, RequestOptions.DEFAULT);

是不是也很简单,跟单条数据更新差不多,使用 UpdateByQueryRequest 构建更新对象,然后设置 Query 和 Script 就可以了。

条件更新数组

比如我们的需求是要移除 tags 中的 java,如下:

POST http://47.105.66.210:9200/article_v1/doc/_update_by_query
{
    "script": {
        "source":"ctx._source['tags'].removeIf(item -> item == 'java');"
    },
    "query": {
        "term": {
            "userId": 1
        }
    } 
}

新增的话只需要将 removeIf 改成 add 就可以了。

ctx._source['tags'].add('java');

如果有特殊的业务逻辑,Script 中还可以写判断来判断是否需要修改。

POST http://47.105.66.210:9200/article_v1/doc/_update_by_query
{
    "script": {
        "source":"if(ctx._source.type == 11) {ctx._source['tags'].add('java');}"
    },
    "query": {
        "term": {
            "userId": 1
        }
    } 
}

封装通用的条件更新

大部分场景下的更新都比较简单,根据某个字段去更新某个值,或者去更新多个值。在 Java 中如果每个地方都去写脚本,就重复了,最好是抽一个比较通用的方法来更新。

下面是简单的示列,其中还有很多需要考虑的点,像数据类型我只处理了数字,字符串,和 List,其他的大家需要自己去扩展。

public BulkByScrollResponse updateByQuery(String index, QueryBuilder query, Map<String, Object> document) {
UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(index);
updateByQueryRequest.setQuery(query);
StringBuilder script = new StringBuilder();
Set<String> keys = document.keySet();
for (String key : keys) {
String appendValue = "";
Object value = document.get(key);
if (value instanceof Number) {
appendValue = value.toString();
} else if (value instanceof String) {
appendValue = "'" + value.toString() + "'";
} else if (value instanceof List){
appendValue = JsonUtils.toJson(value);
} else {
appendValue = value.toString();
}
script.append("ctx._source.").append(key).append("=").append(appendValue).append(";");
}
updateByQueryRequest.setScript(new Script(script.toString()));
return updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT);
}
public BulkByScrollResponse updateByQuery(UpdateByQueryRequest updateByQueryRequest, RequestOptions options) {
Map<String, Object> catData = new HashMap<>(1);
catData.put(ElasticSearchConstant.UPDATE_BY_QUERY_REQUEST, updateByQueryRequest.toString());
return CatTransactionManager.newTransaction(() -> {
try {
return restHighLevelClient.updateByQuery(updateByQueryRequest, options);
}catch (IOException e) {
throw new RuntimeException(e);
}
}, ElasticSearchConstant.ES_CAT_TYPE, ElasticSearchConstant.UPDATE, catData);
}

如果有了这么一个方法,那么使用方式如下:

@Test
public void testUpdate5() {
Map<String, Object> document = new HashMap<>();
document.put("title", "Java");
document.put("status", 0);
document.put("tags", Lists.newArrayList("JS", "CSS"));
kittyRestHighLevelClient.updateByQuery(elasticSearchIndexConfig.getArticleSaveIndexName(), new TermQueryBuilder("userId", 1), document);
}

关于作者 :尹吉欢,简单的技术爱好者,《Spring Cloud 微服务-全栈技术与案例解析》, 《Spring Cloud 微服务 入门 实战与进阶》作者, 公众号 猿天地 发起人。

恕我直言,我也是才知道ElasticSearch条件更新是这么玩的的更多相关文章

  1. SQL-根据多个条件更新数据

    根据多个条件更新数据 UPDATE sphwph SET BKXSHL=t2.BKXSHL FROM sphwph t1,sphwph_170420 t2 --(SELECT a.* FROM dbo ...

  2. elasticsearch【更新】操作

    基于上一篇博文基础上,进行es的操作,document的新增比较简单,就不说了,这里主要说说更新操作. 更新操作,有两大类,一个是Replace,一个是Update,就是说一个是替换,一个是更新. 替 ...

  3. update关联其他表批量更新数据-跨数据库-跨服务器Update时关联表条件更新

    1.有时在做项目时会有些期初数据更新,从老系统更新到新系统.如果用程序循环从老系统付给新系统. 2.有时在项目中需要同步程序,或者自动同步程序时会有大量数据更新就可能用到如下方法了. 3.为了做分析, ...

  4. 【数据库】同一字段根据不同条件更新的sql语句的写法

    语法: update test set 字段1=case when 条件1 then 值1 when 条件2 then 值2 end 示例: update PMS_ProjectInfo set Pr ...

  5. .net core Elasticsearch 查询更新

    记录一下: 数据结构如下: public class ESUserTransaction { public long AccountId { get; set; } public string Var ...

  6. Elasticsearch之更新(全部更新和局部更新)

    前面的基础, Elasticsearch之curl创建索引库 Elasticsearch之curl创建索引 Elasticsearch之curl创建索引库和索引时注意事项 Elasticsearch之 ...

  7. FMDB条件更新

    更新操作返回一个BOOL值,YES表示操作成功,NO表示执行过程遇到错误,可以通过-lastErrorMessage和-lastErrorCode查看错误信息.使用executeUpdate:方法执行 ...

  8. Elasticsearch 追加更新

    追加更新,学名不知道叫啥,我这里指在历史数据的基础上,追加数据更新.比如 价格数据,我在价格字段里面保存了一个每天价格的数组,追加更新的时候在数组的后面直接add,而不是像一般情况那样覆盖. ES追加 ...

  9. mysql if case条件更新

    在mysql中,如果你要根据某个字段的值不一样,来更新另一个字段的值,可以用如下sql语句: 如果仅仅是两个分支,if语句就可以了 update tm set page_name=if(q_aswer ...

随机推荐

  1. Illustrate Java Access Levels

    https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html 官网教程,清晰明了. (完)

  2. Python3笔记009 - 2.6 输入和输出

    第2章 python语言基础 python语法特点 保留字与标识符 变量 数据类型 运算符 输入和输出 2.6 输入和输出 1.input()函数 name = input("请输入姓名:& ...

  3. 猿灯塔:最详细Dubbo相关面试题!

    1.Dubbo是什么? Dubbo是阿里巴巴开源的基于 Java 的高性能 RPC 分布式服务框架,现已成为 Apache 基金会孵化项目. 面试官问你如果这个都不清楚,那下面的就没必要问了. 官网: ...

  4. JS中同步和异步

    首先,我们要知道,JavaScript的本质是一门浏览器脚本语言,在执行的时候是一行一行的执行,只有前面的代码执行完了才会执行后面的代码.JS是单线程语言指的就是这个意思. 同步和异步其实在进行任务执 ...

  5. day45 如何完全删除mysql服务

    卸载mysql之后,mysql的服务无法删除 解决方案 在我们在卸载mysql后会有一些东西没有删除干净,当我们把这些内容清除干净后,服务自然就消失了 步骤一: 如果是默认安装的话 在这三个文件内都有 ...

  6. [网鼎杯 2020 青龙组]AreUSerialz

    题目分析 <?php include("flag.php"); highlight_file(FILE); class FileHandler { protected $op ...

  7. Django -MD5密码加密与登录

    直接贴代码 login_reg.py from django.shortcuts import render, redirect from web.forms.login_reg import Reg ...

  8. 数据可视化之DAX篇(一)Power BI时间智能函数如何处理2月29日的?

    https://zhuanlan.zhihu.com/p/109964336 ​今年是闰年,有星友问我,在Power BI中,2月29日的上年同期是怎么计算的? 这是个好问题,正好梳理一下,Power ...

  9. 数据可视化之PowerQuery篇(九)巧用Power Query,Excel也可以轻松管理文档

    https://zhuanlan.zhihu.com/p/111674088 ​来自知乎一个朋友的问题,如何在Excel中批量插入文件的超链接,以便在Excel中对文档进行有序的目录管理? 这个问题的 ...

  10. Python之爬虫(二十二) Scrapy分布式原理

    关于Scrapy工作流程回顾 Scrapy单机架构 上图的架构其实就是一种单机架构,只在本机维护一个爬取队列,Scheduler进行调度,而要实现多态服务器共同爬取数据关键就是共享爬取队列. 分布式架 ...