恕我直言,我也是才知道ElasticSearch条件更新是这么玩的
背景
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条件更新是这么玩的的更多相关文章
- SQL-根据多个条件更新数据
根据多个条件更新数据 UPDATE sphwph SET BKXSHL=t2.BKXSHL FROM sphwph t1,sphwph_170420 t2 --(SELECT a.* FROM dbo ...
- elasticsearch【更新】操作
基于上一篇博文基础上,进行es的操作,document的新增比较简单,就不说了,这里主要说说更新操作. 更新操作,有两大类,一个是Replace,一个是Update,就是说一个是替换,一个是更新. 替 ...
- update关联其他表批量更新数据-跨数据库-跨服务器Update时关联表条件更新
1.有时在做项目时会有些期初数据更新,从老系统更新到新系统.如果用程序循环从老系统付给新系统. 2.有时在项目中需要同步程序,或者自动同步程序时会有大量数据更新就可能用到如下方法了. 3.为了做分析, ...
- 【数据库】同一字段根据不同条件更新的sql语句的写法
语法: update test set 字段1=case when 条件1 then 值1 when 条件2 then 值2 end 示例: update PMS_ProjectInfo set Pr ...
- .net core Elasticsearch 查询更新
记录一下: 数据结构如下: public class ESUserTransaction { public long AccountId { get; set; } public string Var ...
- Elasticsearch之更新(全部更新和局部更新)
前面的基础, Elasticsearch之curl创建索引库 Elasticsearch之curl创建索引 Elasticsearch之curl创建索引库和索引时注意事项 Elasticsearch之 ...
- FMDB条件更新
更新操作返回一个BOOL值,YES表示操作成功,NO表示执行过程遇到错误,可以通过-lastErrorMessage和-lastErrorCode查看错误信息.使用executeUpdate:方法执行 ...
- Elasticsearch 追加更新
追加更新,学名不知道叫啥,我这里指在历史数据的基础上,追加数据更新.比如 价格数据,我在价格字段里面保存了一个每天价格的数组,追加更新的时候在数组的后面直接add,而不是像一般情况那样覆盖. ES追加 ...
- mysql if case条件更新
在mysql中,如果你要根据某个字段的值不一样,来更新另一个字段的值,可以用如下sql语句: 如果仅仅是两个分支,if语句就可以了 update tm set page_name=if(q_aswer ...
随机推荐
- 浅谈tkinter模块
目录 tkinter模块 tkinter模块简单使用 主窗口 Button按钮 Label标签 Text编辑框 Entry输入框 ListBox列表 RadioButton单选框 CheckButto ...
- spring boot 整合Thymeleaf模板
SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖 ...
- int c, int ndigit[10]; ++ndigit[c-'0'];
for example c-'0' is an integer expression with a value between 0and 9 corresponding to the characte ...
- python编程从入门到实践笔记
我的第一个hello world 程序 print("hello python world") print("hello python world"*3) 打印 ...
- (三)ansible playbook
一,YAML语法 YAML的语法和其他高阶语言类似并且可以简单表达清单.散列表.标量等数据结构.(列表用横杆表示,键值对用冒号分割,键值对里又可以嵌套另外的键值对) YAML文件扩展名通常为.yaml ...
- 【线型DP模板】最上上升子序列(LIS),最长公共子序列(LCS),最长公共上升子序列(LCIS)
BEGIN LIS: 一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的.对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序 ...
- Vue动态修改class
#####对象方法-最简单的绑定(这里的active加不加单引号都可以,以下也一样都能渲染) :class="{ 'active': isActive }"1判断是否绑定一个act ...
- 2020最新的Spring Boot 分布式锁的具体实现(内附代码)
前言 面试总是会被问到有没有用过分布式锁.redis 锁,大部分读者平时很少接触到,所以只能很无奈的回答 "没有".本文通过 Spring Boot 整合 redisson 来实现 ...
- JVM 专题二十一:垃圾回收(五)垃圾回收器 (二)
3. 回收器 3.1 Serial回收器:串行回收 3.1.1 概述 Serial收集器是最基本.历史最悠久的垃圾收集器了.JDK1.3之前回收新生代唯一的选择. Serial收集器作为Hotspot ...
- 隐藏Layer中的Iframe内部元素
隐藏Layer中的Iframe内部元素 1.Layer:一个web弹出层/窗解决方案 2.隐藏Layer弹出层中Iframe内部的元素,例子如下: 这个就是在Layer弹出层内的内容,加载完毕之后,获 ...