Elasticsearch(六)--ES文档的操作(中)---修改文档

news2024/11/17 0:26:34

一、前言

上篇文章我们了解了ES的插入和批量插入文档的操作,分别通过ES的kibana客户端以及Java高级Rest客户端进行学习,那么本篇则进入到对文档的修改操作,同新增文档,也有更新单条文档和批量更新文档操作,但还多出一个根据条件更新文档,我们本篇均会涉及到。

二、更新文档

2.1、更新单条文档

在ES中更新索引的请求类型是POST,其请求形式如下:

POST /${index_name}/_update/${_id}
{
....  //需要更新的数据,在URL中指定文档_id
}

上面的_id就是将要修改的ES文档中的_Id,修改后的字段和值将会填写到大括号中,其格式是JSON形式。例如把_id为017的文档中的city修改成下面的数据:

POST /hotel/_update/017
{
  "doc": {
    "city":"南昌"
  }
}

ES返回结果如下图:
在这里插入图片描述
通过结果可知,已经成功更新文档信息,并且本次修改后文档的版本变为2.下面根据_id搜索文档的命令进行验证:

GET /hotel/_doc/017

ES返回内容如下:
在这里插入图片描述
通过返回结果可知,文档017的对应字段已经被修改为目标数据。并且要注意的是,我只修改了city,其他的没有修改的数据并没有改变,仍然保持原值。
那么在这里需要提一下,虽然上篇文章也提到过,就是另一个命令也可以起到修改的作用,它就是写入文档的命令:

POST /${index_name}/_doc/${_id}
{
    //需要修改的文档数据
}

我们如果需要通过该命令修改指定索引的文档,只需要将_id改成该索引中需要修改的文档的id即可,那么执行该命令后,结果不再是created,而是updated,并且使version+1.
例如,我将文档018的内容修改如下,我也只修改一个city,原文档18的内容如下
在这里插入图片描述
然后执行如下命令,只修改city:

POST /hotel/_doc/018
{
    "city":"厦门"
}

在这里插入图片描述
但是此时查看文档,发现只剩下city属性了,意味着其他没有变动的属性,该命令会视为改为null,这是和前面update命令的区别所在,update命令不会影响没有变动的属性,仍然保持原值
在这里插入图片描述
除了普通的update功能,ES还提供了upsert.upsert即是update和insert的合体字,表示更新/插入数据。如果目标文档存在,则执行更新逻辑;否则执行插入逻辑。以下DSL演示了upsert的应用:

POST /hotel/_update/030
{
  "doc": {
    "city":"南昌"
  },
  "upsert": {
    "city":"厦门"
  }
}

那么文档030不存在,所以执行后会新增该文档:
在这里插入图片描述

在Java高级REST客户端中,更新单条文档需要创建UpdateRequest对象并设置对应的索引和_id字段名称,执行时,调用客户端的update()方法并把UpdateRequest对象传入即可。update()方法返回UpdateResponse对象,通过该对象可以获取当前请求的索引名称,文档_Id和版本号等。以下代码演示了向索引中添加单条文档的方法:
那么我们首先在service层建立ESUpdateDocService类,注入client后,写入以下代码,需要说明的是,如果需要使用upsert功能需要在调用update()方法之前将可能需要插入的map对象传入upsert方法即可:

public Map<String, Object> singleUpsert(String indexName, String docIdKey, Map<String, Object> recordMap) {
		String docId = recordMap.get(docIdKey).toString();
		//将ID字段从map中移除,这步可有可无
		recordMap.remove(docIdKey);
		UpdateRequest updateRequest = new UpdateRequest(indexName, docId);
		//如果有则进行修改,没有该文档则插入,可以支持链式编程
		updateRequest.doc(recordMap).upsert(recordMap);
		try {
			UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
			HashMap<String, Object> resultMap = new HashMap<>();
			String id = updateResponse.getId(); //文档ID
			String index = updateResponse.getIndex(); //索引名称
			long version = updateResponse.getVersion(); //文档版本
			resultMap.put("id", id);
			resultMap.put("index", index);
			resultMap.put("version", version);
			return resultMap;
		} catch (IOException e) {
			log.warn(e.getMessage());
			throw new SearchException("搜索错误,原因:" + e.getMessage());
		}
	}

在controller层建立ESUpdateController,然后建立单条修改文档的方法,代码如下:

    @PostMapping("/update/doc")
	public FoundationResponse<Map<String, Object>> singleUpdate(@RequestBody HotelDocRequest hotelDocRequest) {
		String indexName = hotelDocRequest.getIndexName();
		if (CharSequenceUtil.isBlank(indexName)) {
			return FoundationResponse.error(100, "索引名不能为空");
		}
		Hotel hotel = hotelDocRequest.getHotel();
		HashMap<String, Object> dataMap = new HashMap<>();
		//这里对比之前的插入单条文档,需要多加入一个id
		dataMap.put("id", hotel.getId());
		dataMap.put("title", hotel.getTitle());
		dataMap.put("city", hotel.getCity());
		dataMap.put("price", hotel.getPrice());
		try {
			Map<String, Object> resultMap = esUpdateDocService.singleUpsert(indexName, hotelDocRequest.getDocIdKey(), dataMap);
			return FoundationResponse.success(resultMap);
		} catch (SearchException e) {
			log.warn("搜索发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		} catch (Exception e) {
			log.error("服务发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		}
	}

postman中执行该接口,body内容如下:

{
    "hotel": {
        "id": "020",
        "title": "可莉酒店3",
        "city": "上海",
        "price": 648
    },
    "indexName":"hotel",
    "docIdKey":"id"
}

在这里插入图片描述

2.2、批量更新文档

与批量写入文档相似,批量更新文档的请求形式如下:

POST /_bulk
{"update":{"_index":"${index_name}","_id":"${_id}"}}
{"doc":{"修改的json数据"},"upsert":{"需要插入的json数据"}
{"update":{"_index":"${index_name}","_id":"${_id}"}}
{"doc":{"修改的json数据"},"upsert":{"需要插入的json数据"}

注意,与批量写入文档不同的是,批量更新文档必须在元数据中填写需要更新的文档_id.且与单条文档类似的是,同样也可以加入upsert功能。下面的DSL将批量更新_id为001和002的文档:

POST /_bulk
{"update":{"_index":"hotel_order","_id":"004"}}
{"doc":{"username":"Mike JorDan"},"upsert":{"username":"Mike JorDan"}}
{"update":{"_index":"hotel_order","_id":"002"}}
{"doc":{"username":"Tom JorDan"}}
{"update":{"_index":"hotel_order","_id":"003"}}
{"doc":{"username":"Kobi JorDan"}}

在java客户端接口中,批量更新文档需要创建BulkRequest对象并设置对应的索引名称,这一点和批量写入是相同的。对于多条需要更新的文档,可构建多个UpdateRequest对象并调用BulkRequest.add()方法添加这些UpdateRequest对象,执行时,调用客户端的bulk()方法并把BulkRequest对象传入即可。
首先在service层写批量更新的方法:

public String bulkUpdate(HotelDocRequest hotelDocRequest) {
		String indexName = hotelDocRequest.getIndexName();
		if (CharSequenceUtil.isBlank(indexName)) {
			throw new SearchException("索引名不能为空");
		}
		BulkRequest bulkRequest = new BulkRequest();
		List<Map<String, Object>> recordMapList = hotelDocRequest.getRecordMapList();
		for (Map<String, Object> dataMap : recordMapList) {
			String docIdKey = hotelDocRequest.getDocIdKey();
			String docId = dataMap.get(docIdKey).toString();
			//将ID字段从map中移除,这步可有可无,这个操作和单条修改的基本一致
			dataMap.remove(docIdKey);
			bulkRequest.add(new UpdateRequest(indexName, docId).doc(dataMap).upsert(dataMap));
		}
		BulkResponse bulkResponse;
		try {
			bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
			if (bulkResponse.hasFailures()) {
				return "失败,原因:" + bulkResponse.buildFailureMessage();
			} else {
				return "成功";
			}
		} catch (IOException e) {
			throw new SearchException("批量修改服务错误");
		}
	}

然后controller层调用service层,这里和之前一样,也是通过后端做一次转化,前台就无需输入复杂的map json串

    @PostMapping("/bulk/update/doc")
	public FoundationResponse<String> bulkUpdateDoc(@RequestBody HotelDocRequest hotelDocRequest) {
		List<Hotel> hotelList = hotelDocRequest.getHotelList();
		if (CollUtil.isEmpty(hotelList)) {
			return FoundationResponse.error(100, "无可修改的有效文档");
		}
		//这里之所以转化是因为json输入List<Map<k,v>>这个结构非常复杂,所以由后端这边做一次转化,这样前台只需要输入List<Hotel>的json
		ArrayList<Map<String, Object>> recordListMap = new ArrayList<>();
		hotelList.forEach(hotel -> {
			HashMap<String, Object> dataMap = new HashMap<>();
			//这里对比之前的插入单条文档,需要多加入一个id
			dataMap.put("id", hotel.getId());
			dataMap.put("title", hotel.getTitle());
			dataMap.put("city", hotel.getCity());
			dataMap.put("price", hotel.getPrice());
			recordListMap.add(dataMap);
		});
		hotelDocRequest.setRecordMapList(recordListMap);
		try {
			String s = esUpdateDocService.bulkUpdate(hotelDocRequest);
			return FoundationResponse.success(s);
		} catch (SearchException e) {
			log.warn("批量修改发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		} catch (Exception e) {
			log.error("服务发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		}
	}

postman中执行该接口,body中输入以下内容:

{
    "hotelList": [
        {
            "id": "021",
            "title": "可莉酒店4",
            "city": "上海",
            "price": 648
        },
        {
            "id": "018",
            "title": "可莉酒店5",
            "city": "上海",
            "price": 648
        }
    ],
    "docIdKey":"id",
    "indexName":"hotel"
}

执行成功:
在这里插入图片描述

2.3、根据条件更新文档

在索引数据的更新操作中,有些场景需要根据某些条件同时更新多条数据,类似于在关系型数据库中使用update table table_name set … where … 更新一批数据。为了满足这样的需求,ES为用户提供了_update_by_query功能,其请求形式如下:

POST /${index_name}/_update_by_query
{
  "query": {
   ... //条件更新的查询条件
  },
  "script": {
   ... //条件更新的具体更新脚本代码
  }
}

上面的query用于指定更新数据的匹配条件,相当于SQL中的where语句;script用于指定具体的更新操作,相当于SQL的set内容。script的知识点将在后面的章节中进行介绍,这里仅简单应用一下,请求的DSL如下:

POST /hotel/_update_by_query
{
  "query": {
    "term":{
      "city": {
        "value": "上海"    //更新文档的查询条件:城市为上海的文档
      }
    }
  },
  "script": {
    "source": "ctx._source['price']='6480'",   //条件更新的更新脚本,将price改为6480
    "lang": "painless"
  }
}

执行以上DSL后,ES将先搜素城市为“上海”的酒店,然后把这些酒店的价格改为6480.
在Java高级客户端,执行根据条件更新文档,需要创建UpdateByQueryRequest对象并设置对应的索引名称,类似于DSL中的query子句,通过调用UpdateByQueryRequest.setQuery()方法设置查询逻辑,script子句通过UpdateByQueryRequest.setScript()方法设置更新逻辑,然后执行客户端的updateByQuery()方法并把UpdateByQueryRequest对象传入即可。一下代码演示了根据城市字段查找文档然后更新价格字段的方法,首先在service层操作:

public String updatePriceByCity(String indexName, String oldCity, String newPrice) {
		UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(indexName);
		//设置按照城市查找文档的query
		updateByQueryRequest.setQuery(new TermQueryBuilder("city", oldCity));
		updateByQueryRequest.setScript(new Script("ctx._source['price']='" + newPrice + "';"));
		try {
			BulkByScrollResponse response = client.updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT);
			return response.toString();
		} catch (IOException e) {
			throw new SearchException("按照条件修改服务错误");
		}
	}

然后controller层调用service方法:

	@PostMapping("/update/byCity")
	public FoundationResponse<String> updatePriceByQueryCity(String indexName, String oldCity, String newPrice) {
		if (CharSequenceUtil.isBlank(indexName)) {
			throw new SearchException("索引名不能为空");
		}
		try {
			String result = esUpdateDocService.updatePriceByCity(indexName, oldCity, newPrice);
			return FoundationResponse.success(result);
		} catch (SearchException e) {
			log.warn("搜索发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		} catch (Exception e) {
			log.error("服务发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		}
	}

postman调用该接口
在这里插入图片描述
如果更新所有文档中的某个字段应该如何操作呢?其实,_update_by_query中的query子句可以不定义,这种情况下ES会选中所有的文档执行script中的内容。以下为修改所有酒店中城市为"上海"的DSL:

POST /hotel/_update_by_query
{
  "script": {
    "source": "ctx._source['city']='上海'",
    "lang": "painless"
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184539.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Day873.普通索引唯一索引的选择 -MySQL实战

普通索引&唯一索引的选择 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于普通索引&唯一索引的选择的内容。 假设你在维护一个市民系统&#xff0c;每个人都有一个唯一的身份证号&#xff0c;而且业务代码已经保证了不会写入两个重复的身份证号。 如果市民…

Java基础项目实战--大学生求职招聘信息网站系统

Java基础项目实战–大学生求职招聘信息网站系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系…

基于php的婚纱商城管理系统

摘要网络技术给生活带来了十分的便利。所以把婚纱商城管理系统与现在网络相结合。在婚纱商城发展的整个过程中&#xff0c;婚纱信息管理担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类管理系统程序也在不断改进。本课题所设计的婚纱商城管理系统&#xff0…

自动驾驶介绍、应用、前景

自动驾驶介绍、应用、前景1 介绍1.1 定义1.2 作用1.3 发展历程1.4 分类23年初竞争格局1.5 顾虑1.6 前景2 产业链现状2.1 芯片2.2 仿真3 技术路线3.1 是否交互3.1.1 单车智能3.1.2 车路协同3.2 主传感器区分3.2.1 纯视觉3.2.2 混合传感器3.3 前装还是后装3.3.1 前装3.3.2 后装4 …

基于java SSM医药住院管理系统设计和实现

基于java SSM医药住院管理系统设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 …

宇隆光电冲刺上交所上市:毛利率持续下滑,收入极其依赖京东方

近日&#xff0c;重庆宇隆光电科技股份有限公司&#xff08;下称“宇隆光电”&#xff09;预披露招股书&#xff0c;准备在上海证券交易所主板上市。 本次冲刺上市&#xff0c;宇隆光电计划募资15亿元&#xff0c;其中7亿元用于OLED控制板及液晶模组控制板和精密模切生产基地项…

Spring Cloud_服务监控hystrixDashboard

目录一、概述二、仪表盘90011.新建Module2.POM3.YML4.主函数5.Provider微服务提供类都需要监控依赖配置6.启动仪表盘三、断路器演示1.修改cloud-provider-hystrix-payment80012.监控测试代码链接 https://github.com/lidonglin-bit/cloud 一、概述 除了隔离依赖服务的调用以外…

背景图片大小设置 解决背景图多张铺满盒子 背景图和背景颜色混用

目录更多的样式透明度鼠标样式&#xff1a;cursor盒子隐藏背景图和img元素的区别涉及的css属性1. background-image:url(“”)2. background-repeat3. background-size解决图片多张铺满盒子的问题4. background-position5. background-attachment6. 背景图和背景颜色混用7. 速写…

Jackson使用详细介绍

Jackson使用详细介绍一 . Jackson 介绍二. Jackson Maven 依赖三. ObjectMapper 对象映射器四. Jackson JSON 基本操作1. Jackson JSON 序列化2. Jackson JSON 反序列化3. JSON 转 List4. JSON 转 Map5. Jackson 忽略字段6. Jackson 日期格式化Date 类型LocalDateTime 类型时间…

动态内存分配

目录 一、内存使用方式 &#xff08;一&#xff09;一个c/c编译的程序占用的内存分为以下几个部分 二、malloc &#xff08;一&#xff09;malloc 1. 举例&#xff1a;malloc(4) 2. 如何理解malloc(size(Var_T)*N) 3. 举例 &#xff08;二&#xff09;静态、全局指针…

【数据结构与算法】队列-模拟实现队列以及设计循环队列

文章目录队列的概念链表实现栈设计循环队列总结队列的概念 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;和栈一样&#xff0c;…

图的搜索(DFS、BFS)

图的搜索&#xff08;图的遍历&#xff09;是指从图的任一顶点出发&#xff0c;访问图的所有顶点&#xff0c;且每个顶点只访问一次。 深度优先搜索 DFS概念&#xff1a; 深度优先搜索 (Depth-First Search&#xff0c;DFS)是从某个顶点v1出发对图进行搜素&#xff0c;每一步…

第八章 面向对象编程(中级)

一、访问修饰符&#xff08;P279&#xff09; 1. 基本介绍 java提供四种访问控制修饰符号&#xff0c;用于控制方法和属性&#xff08;成员变量&#xff09;的访问权限&#xff08;范围&#xff09;&#xff1a; &#xff08;1&#xff09;公开级别&#xff1a;用 public 修饰…

2018-NIPS-owards Sparse Hierarchical Graph Classifiers

2018-NIPS-owards Sparse Hierarchical Graph Classifiers Paper: https://arxiv.org/abs/1811.01287 Code: 对稀疏分类分级图 作者提出以往的图分类方法中通常使用单个全局池化步骤来聚合节点特征或手动设计的固定启发式算法&#xff0c;这样做会丢失信息&#xff0c;所以将…

ABB机器人系统输入输出信号System Input和Output详解(二)

ABB机器人系统输入输出信号System Input和Output详解(二) 上一次和大家分享了系统输入信号System Input相关的内容,具体可参考以下链接中的内容: ABB机器人系统输入输出信号System Input和Output详解(一) 本次和大家分享系统输出信号的相关内容: System Output类型: 可…

数据挖掘,计算机网络、操作系统刷题笔记38

数据挖掘&#xff0c;计算机网络、操作系统刷题笔记38 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;orac…

91年的印度程序员开发博客网站每月已赚2500美元以及他的创业历程

他是谁 Sai Krishna &#xff0c;程序员、美食家、电影爱好者。 目前正在开发和维护的superblog.ai的收入情况&#xff08;截止2023年1月27日&#xff09;&#xff1a; 此前他是 SpotPlay 的联合创始人兼首席技术官。此外&#xff0c;他开发的应用程序和游戏的下载量已达数百…

CodePlus | C# 网页所有图片批量下载

C# 网页所有图片批量下载 文章目录C# 网页所有图片批量下载前言演示效果操作步骤第一步&#xff1a;安装CodePlus扩展库第二步&#xff1a;提取链接程序第三步&#xff1a;取网页源码第四步&#xff1a;设置前后缀第五步&#xff1a;执行下载更多演示结束语前言 今天想着换一个…

CANoe-添加自定义示例工程

CANoe自带的示例工程我们讲过很多次了。当安装CANoe软件时,会在安装界面让用户选择是否安装Sample Configurations。如果勾选了,则会在电脑的的C盘自带需要的示例工程,路径为:C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 15.3.89 当然你也可以在CANoe软…

Netty 之 DefaultPromise 源码解析

在解析Netty源码时&#xff0c;在解析NioEventLoop 创建过程中&#xff0c;有一段这样的代码。 protected MultithreadEventExecutorGroup(int nThreads, Executor executor,EventExecutorChooserFactory chooserFactory, Object... args) {if (nThreads < 0) {throw new I…