当前位置: 首页 > news >正文

SpringBoot集成Elasticsearch | Java High Level Rest Client(HLRC)方式

SpringBoot集成Elasticsearch | Java High Level Rest Client(HLRC)方式

    • 前言
    • 1. 环境说明与Maven依赖
      • 1.1 环境版本
      • 1.2 添加依赖
    • 2. YML配置
    • 3. ElasticsearchConfig(连接配置类)
    • 4. EsUtil(工具类)
    • 5. 测试代码(实体类+Controller)
      • 5.1 实体类(EmployeeInfo2)
      • 5.2 Controller(EmployeeElasticController)
    • 6. 测试验证与注意事项
      • 6.1 测试步骤
      • 6.2 注意事项
    • 参考文章

前言

Java High Level Rest Client(HLRC)是Elasticsearch官方提供的高级REST客户端

  • 若项目使用7.x版本ES服务器,HLRC是官方唯一推荐的高级客户端,稳定性与兼容性最优;
  • 若项目使用8.x及以上版本ES服务器,需选择Elasticsearch Java Client(官方全新推荐,功能持续迭代)。
  • 兼容性硬性限制:Elasticsearch Java Client为8.x版本专属客户端,仅支持8.x及以上ES服务器,完全不兼容8.x以下所有版本(含7.x全系列);HLRC虽可临时用于8.x服务器,但已被官方标记为弃用。

1. 环境说明与Maven依赖

1.1 环境版本

  • SpringBoot版本:2.6.13(默认管理的HLRC版本为7.15.2);
  • ES服务器版本:建议与HLRC版本一致(本文以7.15.2为例);
  • JDK版本:1.8及以上。

1.2 添加依赖

<!-- 手动引入HLRC依赖(版本需与ES服务器一致) -->
<dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.15.2</version> <!-- 与ES服务器版本保持一致 -->
</dependency>
<!-- 低级REST客户端(HLRC的底层依赖,必须引入) -->
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>7.15.2</version>
</dependency>
<!-- 高级REST客户端(核心依赖) -->
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.15.2</version>
</dependency>

2. YML配置

配置ES连接信息,其中clusterName非必需(HLRC基于REST协议,不依赖集群名称发现),仅作标识用。

注意
(1)需要 判断 Elasticsearch 是否 开启安全认证.

  • 7.x 环境:默认未开认证 → userName 和 password 不校验的。
  • 8.x 环境:默认开启认证 → 必须正确配置 userName(默认 elastic)和实际密码(8.x 首次启动会自动生成初始密码,需记录),否则连接失败。
# Elasticsearch配置
elasticsearch:# 集群名称(非必需,仅标识)clusterName: single-node-cluster# 用户名(ES 7.x默认用户为elastic,若未开启安全认证可删除)userName: elastic# 密码(ES默认密码需在安装时设置,若未开启安全认证可删除)password: elastic# 服务器地址(集群用逗号分隔,如127.0.0.1:9200,127.0.0.1:9201)hosts: 127.0.0.1:9200# 协议(http/https,未开启SSL用http)scheme: http# 连接超时时间(毫秒)connectTimeOut: 1000#  socket超时时间(毫秒,处理请求的超时时间)socketTimeOut: 30000# 请求排队超时时间(毫秒)connectionRequestTimeOut: 500# 最大连接数(连接池总大小)maxConnectNum: 100# 每个路由的最大连接数(单个ES节点的最大连接数)maxConnectNumPerRoute: 100

3. ElasticsearchConfig(连接配置类)

通过@ConfigurationProperties绑定YML配置,构建RestHighLevelClient实例(单例,由Spring管理)。

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.ArrayList;
import java.util.List;/*** ES HLRC客户端配置类(构建单例RestHighLevelClient)*/
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticsearchConfig {// ES服务器地址(集群用逗号分隔)private String hosts;// 用户名(未开启安全认证可忽略)private String userName;// 密码(未开启安全认证可忽略)private String password;// 协议(http/https)private String scheme;// 连接超时时间(毫秒)private int connectTimeOut;// Socket超时时间(毫秒)private int socketTimeOut;// 请求排队超时时间(毫秒)private int connectionRequestTimeOut;// 最大连接数private int maxConnectNum;// 每个路由的最大连接数private int maxConnectNumPerRoute;/*** 构建RestHighLevelClient实例(Bean名称为restHighLevelClient)*/@Bean(name = "restHighLevelClient")public RestHighLevelClient restHighLevelClient() {// 1. 解析ES服务器地址List<HttpHost> httpHostList = new ArrayList<>();String[] hostArray = hosts.split(",");for (String host : hostArray) {String[] hostPort = host.split(":");// 校验地址格式(必须为host:port)if (hostPort.length != 2) {throw new RuntimeException("ES地址格式错误:" + host + "(正确格式:host:port)");}httpHostList.add(new HttpHost(hostPort[0], Integer.parseInt(hostPort[1]), scheme));}// 2. 构建RestClientBuilderRestClientBuilder builder = RestClient.builder(httpHostList.toArray(new HttpHost[0]));// 3. 配置安全认证(若未开启,可删除此段)CredentialsProvider credentialsProvider = new BasicCredentialsProvider();credentialsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials(userName, password));// 4. 配置请求超时(连接、Socket、排队)builder.setRequestConfigCallback(requestConfigBuilder -> {requestConfigBuilder.setConnectTimeout(connectTimeOut);requestConfigBuilder.setSocketTimeout(socketTimeOut);requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeOut);return requestConfigBuilder;});// 5. 配置连接池(最大连接数、单路由连接数)+ 安全认证builder.setHttpClientConfigCallback(httpClientBuilder -> {httpClientBuilder.setMaxConnTotal(maxConnectNum);httpClientBuilder.setMaxConnPerRoute(maxConnectNumPerRoute);httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);return httpClientBuilder;});// 6. 构建并返回客户端实例log.info("ES HLRC客户端初始化完成,服务器地址:{}", hosts);return new RestHighLevelClient(builder);}
}

4. EsUtil(工具类)

封装ES核心操作(索引CRUD、文档CRUD、高亮查询)

说明:工具类中的index对应ES的“索引”,可理解为关系型数据库中的“表”;id对应文档的唯一标识(类似数据库主键)。

package com.example.springbootfull.elasticsearchclient.util;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;/*** ES HLRC工具类(封装索引、文档、查询操作)*/
@Slf4j
@Component
public class EsUtil {@Autowiredprivate RestHighLevelClient restHighLevelClient;// 关键字后缀(用于精确匹配text类型字段,如"name.keyword")public static final String KEYWORD_SUFFIX = ".keyword";// 默认超时时间(1秒)private static final TimeValue DEFAULT_TIMEOUT = TimeValue.timeValueSeconds(1);/*** 1. 创建索引(若索引已存在,返回false)* @param index 索引名称(需符合ES命名规范:小写、无特殊字符)*/public boolean createIndex(String index) throws IOException {if (isIndexExist(index)) {log.warn("索引[{}]已存在,无需重复创建", index);return false;}// 创建索引请求CreateIndexRequest request = new CreateIndexRequest(index);// 执行请求(RequestOptions.DEFAULT为默认请求配置)CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);log.info("索引[{}]创建成功,响应状态:{}", index, response.isAcknowledged());return response.isAcknowledged();}/*** 2. 删除索引(若索引不存在,返回false)* @param index 索引名称*/public boolean deleteIndex(String index) throws IOException {if (!isIndexExist(index)) {log.warn("索引[{}]不存在,无需删除", index);return false;}DeleteIndexRequest request = new DeleteIndexRequest(index);AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);log.info("索引[{}]删除成功,响应状态:{}", index, response.isAcknowledged());return response.isAcknowledged();}/*** 3. 判断索引是否存在* @param index 索引名称*/public boolean isIndexExist(String index) throws IOException {GetIndexRequest request = new GetIndexRequest(index);boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);log.debug("索引[{}]存在状态:{}", index, exists);return exists;}/*** 4. 添加文档(自定义ID,若ID已存在则覆盖)* @param jsonObject 文档数据(JSON格式)* @param index 索引名称* @param id 文档ID(为null时ES自动生成)* @return 文档ID(便于后续操作)*/public String addData(JSONObject jsonObject, String index, String id) throws IOException {IndexRequest request = new IndexRequest(index);// 设置文档ID(null则ES自动生成)if (StringUtils.isNotBlank(id)) {request.id(id);}// 设置超时时间request.timeout(DEFAULT_TIMEOUT);// 设置文档内容(XContentType.JSON指定数据格式)request.source(jsonObject, XContentType.JSON);// 执行请求IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);log.info("文档添加成功:索引[{}],ID[{}],响应状态[{}]", index, response.getId(), response.status().getStatus());return response.getId();}/*** 4. 重载:添加文档(自动生成UUID作为ID)*/public String addData(JSONObject jsonObject, String index) throws IOException {// 生成UUID(去除横杠并转大写)String autoId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();return addData(jsonObject, index, autoId);}/*** 5. 根据ID删除文档* @param index 索引名称* @param id 文档ID*/public void deleteDataById(String index, String id) throws IOException {if (!existsById(index, id)) {log.warn("文档不存在:索引[{}],ID[{}]", index, id);return;}DeleteRequest request = new DeleteRequest(index, id);DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);log.info("文档删除成功:索引[{}],ID[{}],响应状态[{}]", index, id, response.status().getStatus());}/*** 6. 根据ID更新文档(部分更新,仅覆盖传入字段)* @param data 更新数据(POJO/Map均可)* @param index 索引名称* @param id 文档ID* @param isRealTime 是否实时刷新(true:更新后立即可查,会影响性能;false:默认1秒后刷新)*/public void updateDataById(Object data, String index, String id, boolean isRealTime) throws IOException {if (!existsById(index, id)) {log.warn("文档不存在:索引[{}],ID[{}],无法更新", index, id);return;}UpdateRequest request = new UpdateRequest(index, id);// 设置超时时间request.timeout(DEFAULT_TIMEOUT);// 设置实时刷新(wait_for:等待刷新完成后返回)if (isRealTime) {request.setRefreshPolicy("wait_for");}// 转换数据为JSON字符串(FastJSON序列化)String jsonData = JSON.toJSONString(data);request.doc(jsonData, XContentType.JSON);// 执行更新UpdateResponse response = restHighLevelClient.update(request, RequestOptions.DEFAULT);log.info("文档更新成功:索引[{}],ID[{}],响应状态[{}]", index, id, response.status().getStatus());}/*** 6. 重载:默认非实时更新(平衡性能与查询时效性)*/public void updateDataById(Object data, String index, String id) throws IOException {updateDataById(data, index, id, false);}/*** 7. 根据ID查询文档* @param index 索引名称* @param id 文档ID* @param fields 需返回的字段(逗号分隔,如"name,age";为null则返回所有字段)* @return 文档数据(Map格式,包含ID字段)*/public Map<String, Object> searchDataById(String index, String id, String fields) throws IOException {if (!existsById(index, id)) {log.warn("文档不存在:索引[{}],ID[{}]", index, id);return null;}GetRequest request = new GetRequest(index, id);// 筛选返回字段(仅查询需要的字段,减少数据传输)if (StringUtils.isNotBlank(fields)) {request.fetchSourceContext(new FetchSourceContext(true, fields.split(","),  // 需返回的字段Strings.EMPTY_ARRAY // 需排除的字段(空数组表示不排除)));}GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);Map<String, Object> dataMap = response.getSource();// 补充文档ID到返回结果中if (dataMap != null) {dataMap.put("id", response.getId());}return dataMap;}/*** 8. 判断文档是否存在(仅判断存在性,不返回数据,性能更高)* @param index 索引名称* @param id 文档ID*/public boolean existsById(String index, String id) throws IOException {GetRequest request = new GetRequest(index, id);// 不获取文档内容,仅判断存在性request.fetchSourceContext(new FetchSourceContext(false));request.storedFields("_none_");boolean exists = restHighLevelClient.exists(request, RequestOptions.DEFAULT);log.debug("文档存在状态:索引[{}],ID[{}],存在[{}]", index, id, exists);return exists;}/*** 9. 高亮查询(支持分页、排序、字段筛选)* @param index 索引名称* @param queryBuilder 查询条件(SearchSourceBuilder,需外部构建)* @param pageNum 页码(从1开始)* @param pageSize 每页条数* @param fields 需返回的字段(逗号分隔,null则返回所有)* @param sortField 排序字段(null则不排序)* @param highlightField 需高亮的字段(如"name",高亮标签为<span style='color:red'>)* @return 高亮后的文档列表(Map格式,包含ID和高亮字段)*/public List<Map<String, Object>> searchHighlightData(String index,SearchSourceBuilder queryBuilder,Integer pageNum,Integer pageSize,String fields,String sortField,String highlightField) throws IOException {// 1. 构建搜索请求SearchRequest request = new SearchRequest(index);// 2. 配置查询参数(分页、排序、字段筛选、高亮)// 分页:from = (页码-1)*每页条数int from = (pageNum - 1) * pageSize;queryBuilder.from(from).size(pageSize);// 排序:若字段为text类型,需拼接.keyword(精确排序)if (StringUtils.isNotBlank(sortField)) {queryBuilder.sort(sortField + KEYWORD_SUFFIX, SortOrder.ASC);}// 字段筛选if (StringUtils.isNotBlank(fields)) {queryBuilder.fetchSource(new FetchSourceContext(true,fields.split(","),Strings.EMPTY_ARRAY));}// 高亮配置(红色span标签,关闭字段匹配限制)if (StringUtils.isNotBlank(highlightField)) {HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field(highlightField).requireFieldMatch(false) // 允许高亮多个字段.preTags("<span style='color:red'>").postTags("</span>");queryBuilder.highlighter(highlightBuilder);}// 3. 执行查询request.source(queryBuilder);SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);log.info("高亮查询完成:索引[{}],匹配总数[{}],分页[{}页/{}条]",index, response.getHits().getTotalHits().value, pageNum, pageSize);// 4. 解析结果(处理高亮字段,替换原字段值)return parseHighlightResponse(response, highlightField);}/*** 解析高亮查询结果(私有工具方法)*/private List<Map<String, Object>> parseHighlightResponse(SearchResponse response, String highlightField) {List<Map<String, Object>> resultList = new ArrayList<>();for (SearchHit hit : response.getHits().getHits()) {// 获取原始文档数据Map<String, Object> sourceMap = hit.getSourceAsMap();// 补充文档IDsourceMap.put("id", hit.getId());// 处理高亮字段(若存在,替换原字段值)if (StringUtils.isNotBlank(highlightField)) {HighlightField highlight = hit.getHighlightFields().get(highlightField);if (highlight != null && highlight.getFragments() != null) {// 拼接高亮片段(多片段时合并)StringBuilder highlightValue = new StringBuilder();for (Text fragment : highlight.getFragments()) {highlightValue.append(fragment.string());}// 用高亮值替换原字段值sourceMap.put(highlightField, highlightValue.toString());}}resultList.add(sourceMap);}return resultList;}/*** 获取低级REST客户端(非必需,仅用于特殊场景)*/public RestClient getLowLevelClient() {return restHighLevelClient.getLowLevelClient();}
}

5. 测试代码(实体类+Controller)

5.1 实体类(EmployeeInfo2)

定义业务实体,与ES文档字段对应(支持驼峰转下划线,需在Controller中配置)。

package com.example.springbootfull.elasticsearchclient.bean;import java.math.BigDecimal;
import java.util.Date;/*** 员工实体类(对应ES索引employee_info_2)*/
public class EmployeeInfo2 {// 主键ID(与ES文档ID可不一致)private Long id;// 工号private String jobNo;// 姓名(可用于高亮查询)private String name;// 英文名private String englishName;// 岗位private String job;// 性别(1:男,2:女)private Integer sex;// 年龄private Integer age;// 薪资private BigDecimal salary;// 入职时间private Date jobDay;// 备注private String remark;// 无参构造public EmployeeInfo2() {}// 全参构造public EmployeeInfo2(Long id, String jobNo, String name, String englishName, String job, Integer sex, Integer age, BigDecimal salary, Date jobDay, String remark) {this.id = id;this.jobNo = jobNo;this.name = name;this.englishName = englishName;this.job = job;this.sex = sex;this.age = age;this.salary = salary;this.jobDay = jobDay;this.remark = remark;}// Getter + Setter(省略,需自行生成)public Long getId() { return id; }public void setId(Long id) { this.id = id; }public String getJobNo() { return jobNo; }public void setJobNo(String jobNo) { this.jobNo = jobNo; }public String getName() { return name; }public void setName(String name) { this.name = name; }public String getEnglishName() { return englishName; }public void setEnglishName(String englishName) { this.englishName = englishName; }public String getJob() { return job; }public void setJob(String job) { this.job = job; }public Integer getSex() { return sex; }public void setSex(Integer sex) { this.sex = sex; }public Integer getAge() { return age; }public void setAge(Integer age) { this.age = age; }public BigDecimal getSalary() { return salary; }public void setSalary(BigDecimal salary) { this.salary = salary; }public Date getJobDay() { return jobDay; }public void setJobDay(Date jobDay) { this.jobDay = jobDay; }public String getRemark() { return remark; }public void setRemark(String remark) { this.remark = remark; }// toString方法(便于日志打印)@Overridepublic String toString() {return "EmployeeInfo2{" +"id=" + id +", jobNo='" + jobNo + '\'' +", name='" + name + '\'' +", englishName='" + englishName + '\'' +", job='" + job + '\'' +", sex=" + sex +", age=" + age +", salary=" + salary +", jobDay=" + jobDay +", remark='" + remark + '\'' +'}';}
}

5.2 Controller(EmployeeElasticController)

提供HTTP接口测试ES操作

package com.example.springbootfull.elasticsearchclient.controller;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.example.springbootfull.elasticsearchclient.bean.EmployeeInfo2;
import com.example.springbootfull.elasticsearchclient.util.EsUtil;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;/*** ES员工接口(测试索引CRUD、文档CRUD)*/
@RestController
@RequestMapping("/api/employee")
public class EmployeeElasticController {@Autowiredprivate EsUtil esUtil;// 索引名称(与实体类对应)private static final String EMPLOYEE_INDEX = "employee_info_2";// FastJSON配置:驼峰转下划线(适配ES字段命名规范,如jobNo→job_no)private static final SerializeConfig SNAKE_CASE_CONFIG;// 日志打印(需自行注入或使用@Slf4j)private static final Logger log = LoggerFactory.getLogger(EmployeeElasticController.class);static {SNAKE_CASE_CONFIG = new SerializeConfig();SNAKE_CASE_CONFIG.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;}/*** 1. 创建员工索引*/@GetMapping("/index/create")public String createEmployeeIndex() {try {boolean result = esUtil.createIndex(EMPLOYEE_INDEX);return result ? "索引创建成功" : "索引已存在";} catch (Exception e) {log.error("创建索引失败", e);return "索引创建失败:" + e.getMessage();}}/*** 2. 删除员工索引*/@GetMapping("/index/delete")public String deleteEmployeeIndex() {try {boolean result = esUtil.deleteIndex(EMPLOYEE_INDEX);return result ? "索引删除成功" : "索引不存在";} catch (Exception e) {log.error("删除索引失败", e);return "索引删除失败:" + e.getMessage();}}/*** 3. 添加员工文档(支持动态传参)* @param employee 员工信息(JSON格式)*/@PostMapping("/save")public String saveEmployee(@RequestBody EmployeeInfo2 employee) {try {// 驼峰转下划线(如jobNo→job_no)String json = JSON.toJSONString(employee, SNAKE_CASE_CONFIG);JSONObject jsonObject = JSONObject.parseObject(json);// 添加文档(自动生成ID)String docId = esUtil.addData(jsonObject, EMPLOYEE_INDEX);return "员工添加成功,文档ID:" + docId;} catch (Exception e) {log.error("添加员工失败", e);return "添加员工失败:" + e.getMessage();}}/*** 4. 根据文档ID查询员工* @param docId ES文档ID*/@GetMapping("/get/{docId}")public Map<String, Object> getEmployeeByDocId(@PathVariable String docId) {try {// 仅返回name、job、salary、job_day字段(减少数据传输)return esUtil.searchDataById(EMPLOYEE_INDEX, docId, "name,job,salary,job_day");} catch (Exception e) {log.error("查询员工失败(ID:{})", docId, e);return null;}}/*** 5. 根据文档ID更新员工薪资* @param docId 文档ID* @param salary 新薪资*/@PostMapping("/update/salary/{docId}")public String updateEmployeeSalary(@PathVariable String docId, @RequestParam BigDecimal salary) {try {// 仅更新salary字段(部分更新)EmployeeInfo2 updateData = new EmployeeInfo2();updateData.setSalary(salary);// 非实时更新(平衡性能)esUtil.updateDataById(updateData, EMPLOYEE_INDEX, docId);return "员工薪资更新成功(文档ID:" + docId + ")";} catch (Exception e) {log.error("更新员工薪资失败(ID:{})", docId, e);return "更新员工薪资失败:" + e.getMessage();}}/*** 6. 高亮查询员工(按姓名模糊匹配,分页)* @param name 姓名关键词(模糊匹配)* @param pageNum 页码(默认1)* @param pageSize 每页条数(默认10)*/@GetMapping("/search/highlight")public List<Map<String, Object>> searchEmployeeHighlight(@RequestParam String name,@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "10") Integer pageSize) {try {// 构建查询条件:姓名模糊匹配(matchQuery支持分词,如"张三"可匹配"张三三")SearchSourceBuilder queryBuilder = new SearchSourceBuilder();queryBuilder.query(QueryBuilders.matchQuery("name", name));// 高亮查询(高亮name字段,按job_no升序排序)return esUtil.searchHighlightData(EMPLOYEE_INDEX,queryBuilder,pageNum,pageSize,"name,job_no,age,salary", // 返回字段"job_no", // 排序字段"name" // 高亮字段);} catch (Exception e) {log.error("高亮查询员工失败(关键词:{})", name, e);return null;}}

6. 测试验证与注意事项

6.1 测试步骤

  1. 启动ES服务器(确保版本为7.15.2,与HLRC版本一致);
  2. 启动SpringBoot项目,访问http://localhost:8080/api/employee/index/create创建索引;
  3. 用Postman发送POST请求http://localhost:8080/api/employee/save,传入员工数据(示例JSON如下);
{"id": 6001,"jobNo": "2001","name": "张三","englishName": "zhangsan","job": "Java开发","sex": 1,"age": 25,"salary": 15000.00,"jobDay": "2023-01-10","remark": "技术骨干"
}
  1. 访问http://localhost:8080/api/employee/get/{docId}(替换docId为步骤3返回的ID),验证查询结果;
  2. 访问http://localhost:8080/api/employee/search/highlight?name=张&pageNum=1,验证高亮效果(姓名“张三”会被红色span标签包裹)。

6.2 注意事项

  1. 版本兼容:HLRC版本必须与ES服务器版本一致(7.x HLRC ↔ 7.x服务器,8.x HLRC ↔ 8.x服务器);
  2. 依赖冲突:若项目中存在Jackson和FastJSON共存,需注意JSON序列化一致性(避免字段类型不匹配);
  3. 性能优化
    • 查询时尽量筛选返回字段(减少数据传输);
    • 非核心场景避免使用实时刷新(setRefreshPolicy("wait_for"));
    • 连接池参数(maxConnectNummaxConnectNumPerRoute)需根据并发量调整;
  4. HLRC弃用提示:若未来升级ES 8.x,需迁移至Elasticsearch Java Client(API差异较大,需重新适配)。

参考文章

【1】SpringBoot整合ElasticSearch的两种方式
【2】elasticsearch7.9 java工具类restHighLevelClient精华整理
【3】三种方式实现Java对象转json下划线格式

http://www.dtcms.com/a/528137.html

相关文章:

  • 《神领物流》day07-线路规划之线路管理_完整代码【简单易懂注释版】
  • 使用Ansys Polyflow对泡沫聚合物挤出进行建模
  • 【组成原理·硬件】6总线
  • Spring Boot3零基础教程,整合 SSM,笔记52
  • 序列化详解
  • 网站设计制作电影福建网站建设公司
  • 记录一次Oracle日志listener.log文件大小超过4G后出现Tomcat服务启动一直报错的原因【ORACLE】
  • Docker Desktop快速搭建本地k8s集群
  • LabVIEW超高分辨显微成像系统
  • 东莞建网站的公破解付费wordpress主题
  • 国产数据库破局:金仓数据库如何无缝替代MongoDB支撑2TB政务数据
  • Switch 20.5.0系统最新PSP模拟器懒人包
  • 怎么做网上直营店网站php素材网站源码免费下载
  • 巡检机器人户外视觉识别困境剖析与自动优化模式构建
  • C++ - 异常
  • C++笔记(面向对象)深赋值 浅赋值
  • 数据库在什么情况下会发生数据库死锁
  • 如何将Word文档精确转换为图片型PDF?
  • Auto CAD二次开发——封装事务处理函数并绘制直线函数
  • C4D域力场重要概念之点对象、粒子对象和通道转换
  • 车载诊断架构 ---关于Service 29证书认证与整车时间同步的问题带来的深思
  • 做蛋糕需要建议网站不wordpress跳转移动端模板
  • html5培训网站模板兴国电商网站建设
  • 【JUnit实战3_13】第八章:mock 对象模拟技术在细粒度测试中的应用(上)
  • STM32项目分享:简易自动门设计
  • 小白怎样建设公司网站奔奔网站建设
  • YouTube评论情感分析项目84%正确率:基于BERT的实战复现与原理解析
  • 【Shell】Shell变量
  • 华为OD机考:计算正方形数量(Python C/C++ JAVA JS GO)
  • 基于 STM32 的语音识别智能垃圾桶设计与实现