当前位置:   article > 正文

es+springboot实现简单的数据增删改查,全字段检索功能_springboot项目像es中增删改查

springboot项目像es中增删改查

需求:实现在对数据库查询时,同时更新ES服务中指定索引的数据。若用户重建数据库,则需删除旧索引,查询数据库新数据,而后插入指定新索引中。

创建索引之前,进行数据操作部分(操作数据过程中同时更新当前索引数据):

点击重建索引按钮之后,进行对数据检索部分:

第一步,首先了解,安装Elasticsearch,注意各个版本的对应,否则会运行失败

elasticsearch:7.16.3

spring-boot-starter-parent:2.3.0.RELEASE

当运行之后输入http://localhost:9200/

如果有返回值则说明安装成功

第二步,引用ES(这里运用的RestHighLevelClient中封装好的方法)

  1. <!--全文检索-->
  2. <!--添加springboot-elasticsearch依赖-->
  3. <!--es客户端,不使用springboot封装的客户端-->
  4. <dependency>
  5. <groupId>org.elasticsearch.client</groupId>
  6. <artifactId>elasticsearch-rest-high-level-client</artifactId>
  7. <version>7.5.2</version>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.elasticsearch</groupId>
  11. <artifactId>elasticsearch</artifactId>
  12. <version>7.5.2</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.elasticsearch.client</groupId>
  20. <artifactId>elasticsearch-rest-client</artifactId>
  21. <version>7.6.2</version>
  22. </dependency>

application.yml中的配置如下:
 

  1. elasticsearch:
  2. schema: http
  3. address: 127.0.0.1:9200
  4. connectTimeout: 5000
  5. socketTimeout: 5000
  6. connectionRequestTimeout: 5000
  7. maxConnectNum: 100
  8. maxConnectPerRoute: 100

全文检索模块后端接口结构:

 

 其中config配置类如下(必须使用)

  1. /**
  2. * @Deacription ElasticSearch 配置
  3. * @Author hmf
  4. * @Date 2022/8/29
  5. * @Version 1.0
  6. **/
  7. @Configuration
  8. public class ElasticSearchConfiguration {
  9. /** 协议 */
  10. @Value("${elasticsearch.schema:http}")
  11. private String schema;
  12. /** 集群地址,如果有多个用“,”隔开 */
  13. @Value("${elasticsearch.address}")
  14. private String address;
  15. /** 连接超时时间 */
  16. @Value("${elasticsearch.connectTimeout}")
  17. private int connectTimeout;
  18. /** Socket 连接超时时间 */
  19. @Value("${elasticsearch.socketTimeout}")
  20. private int socketTimeout;
  21. /** 获取连接的超时时间 */
  22. @Value("${elasticsearch.connectionRequestTimeout}")
  23. private int connectionRequestTimeout;
  24. /** 最大连接数 */
  25. @Value("${elasticsearch.maxConnectNum}")
  26. private int maxConnectNum;
  27. /** 最大路由连接数 */
  28. @Value("${elasticsearch.maxConnectPerRoute}")
  29. private int maxConnectPerRoute;
  30. @Bean(name = "restHighLevelClient")
  31. public RestHighLevelClient restHighLevelClient() {
  32. // 拆分地址
  33. List<HttpHost> hostLists = new ArrayList<>();
  34. String[] hostList = address.split(",");
  35. for (String addr : hostList) {
  36. String host = addr.split(":")[0];
  37. String port = addr.split(":")[1];
  38. hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
  39. }
  40. // 转换成 HttpHost 数组
  41. HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});
  42. // 构建连接对象
  43. RestClientBuilder builder = RestClient.builder(httpHost);
  44. // 异步连接延时配置
  45. builder.setRequestConfigCallback(requestConfigBuilder -> {
  46. requestConfigBuilder.setConnectTimeout(connectTimeout);
  47. requestConfigBuilder.setSocketTimeout(socketTimeout);
  48. requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
  49. return requestConfigBuilder;
  50. });
  51. // 异步连接数配置
  52. builder.setHttpClientConfigCallback(httpClientBuilder -> {
  53. httpClientBuilder.setMaxConnTotal(maxConnectNum);
  54. httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
  55. return httpClientBuilder;
  56. });
  57. return new RestHighLevelClient(builder);
  58. }
  59. }

 ENTITY中的实体类(返回前端,存入ES的数据格式)

其中Document必须提前设置好指定存入的index(可以理解成数据库的某个表) 

  1. /**
  2. * @author hmf
  3. * @create 2022-08-29 10:28
  4. */
  5. @Data
  6. @Document(indexName = "gt_office_doc", type = "_doc")
  7. public class OfficeDocVO {
  8. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  9. private String officeDocFileId;
  10. @JSONField(serialize = false)
  11. private MultipartFile docFileContent;
  12. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  13. private String docFileName;
  14. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  15. private String docFileKeyword;
  16. @Id
  17. private String officeDocId;
  18. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  19. private String docTitle;
  20. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  21. private String dispatchnoId;
  22. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  23. private String issuingAuthority;
  24. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  25. private String issuingAuthorityYear;
  26. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  27. private String postingSequenceNumber;
  28. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  29. private String orgUuid;
  30. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  31. private String orgName;
  32. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  33. private String drafterId;
  34. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  35. private String drafterName;
  36. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  37. private String uploaderName;
  38. @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
  39. private Date draftDate;
  40. @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
  41. private Date uploadTime;
  42. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  43. private String previewUrl;
  44. }

 Serivce中封装的主要方法(增删改查)

OFFICE_INDEX为设置好的全局常量,内容为:gt_office_doc (也就是index)

其中,初始化数据时候,是从数据库中查询LIST而后插入ES(批量操作)

@Async为异步请求,考虑当数据库中的数据较多,反应时间较长,加载批量操作数据的方法中

其中,全字段检索使用了multiMatchQuery,将需要匹配的字段写入,则可按照查询。

  1. import static com.icss.audit.gt.constant.CommonConst.OFFICE_INDEX;
  2. /**
  3. * @author hmf
  4. * @create 2022-08-29
  5. */
  6. @Slf4j
  7. @Service
  8. public class EsIndexService {
  9. @Resource
  10. ElasticsearchRestTemplate elasticsearchRestTemplate;
  11. @Autowired
  12. private RestHighLevelClient restHighLevelClient;
  13. @Resource
  14. OfficeDocumentDao officeDocumentDao;
  15. /**
  16. * 创建索引
  17. */
  18. public void createIndex() throws Exception {
  19. if (elasticsearchRestTemplate.indexOps(IndexCoordinates.of(OFFICE_INDEX)).exists()){
  20. deleteIndex();
  21. }
  22. // 创建索引配置信息,配置
  23. Settings settings = Settings.builder()
  24. .put("index.number_of_shards", 1)
  25. .put("index.number_of_replicas", 0)
  26. .build();
  27. // 新建创建索引请求对象,然后设置索引类型(ES 7.0 将不存在索引类型)和 mapping 与 index 配置
  28. CreateIndexRequest request = new CreateIndexRequest(OFFICE_INDEX, settings);
  29. // RestHighLevelClient 执行创建索引
  30. CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
  31. // 判断是否创建成功
  32. boolean isCreated = createIndexResponse.isAcknowledged();
  33. if (isCreated){
  34. //初始化数据
  35. initEsData();
  36. }
  37. }
  38. /**
  39. * 删除索引
  40. */
  41. public void deleteIndex()throws Exception {
  42. // 新建删除索引请求对象
  43. DeleteIndexRequest request = new DeleteIndexRequest(OFFICE_INDEX);
  44. // 执行删除索引
  45. AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
  46. // 判断是否删除成功
  47. log.info("索引是否删除成功:" + acknowledgedResponse.isAcknowledged());
  48. }
  49. /**
  50. * 初始化索引数据
  51. */
  52. @Async
  53. public void initEsData() throws Exception{
  54. List<OfficeDocVO> resultlist = officeDocumentDao.initIndexData();
  55. //增加预览参数
  56. for(OfficeDocVO temp : resultlist){
  57. if (temp.getDocFileName().endsWith(".pdf")){
  58. temp.setPreviewUrl("/officeDocument/PreviewOfficeDoc?id=");
  59. }
  60. }
  61. // 创建索引请求对象
  62. BulkRequest bulkRequest = new BulkRequest();
  63. // 准备批量插入的数据
  64. resultlist.forEach(user -> {
  65. // 设置请求对象
  66. IndexRequest request = new IndexRequest(OFFICE_INDEX);
  67. // 文档id
  68. request.id(user.getOfficeDocId());
  69. // 将json格式字符串放在请求中
  70. // 下面这种写法也可以写成:request.source(XContentType.JSON, "name", "张三", "age", "男", "age", 22);,其中"name"、"age"、 "age"是User对象中的字段名,而这些字段名称后面的值就是对应的值
  71. System.out.println("插入的数据为:"+user.toString());
  72. request.source(JSONObject.toJSONString(user), XContentType.JSON);
  73. // 将request添加到批量处理请求中
  74. bulkRequest.add(request);
  75. });
  76. // 3、发送请求到ES
  77. BulkResponse response = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
  78. // 4、处理响应结果
  79. log.info("批量插入是否失败:" + response.hasFailures());
  80. }
  81. /**
  82. * 获取所有文档信息
  83. */
  84. public SearchResponse getDocument(PageData page) throws IOException {
  85. // 获取请求对象
  86. SearchRequest getRequest = new SearchRequest(OFFICE_INDEX, "", "");
  87. // 指定检索条件
  88. SearchSourceBuilder builder = new SearchSourceBuilder();
  89. Map<String, Object> condition = page.getCondition();
  90. String AuthorityYear = "";
  91. String Authority = "";
  92. String ownerUnit = "";
  93. int currentPage = (int) page.getCurrent();
  94. int pageSize = 10;
  95. int from = (currentPage - 1) * pageSize;
  96. builder.from(from);
  97. builder.size(pageSize);
  98. // 若无参则用来查询索引中全部的数据
  99. builder.query(QueryBuilders.matchAllQuery());
  100. getRequest.source(builder);
  101. if (condition != null) {
  102. if (condition.get("AuthorityYear") != "") {
  103. AuthorityYear = (String) condition.get("AuthorityYear");
  104. builder.query(QueryBuilders.termQuery("issuingAuthorityYear.keyword", AuthorityYear));
  105. getRequest.source(builder);
  106. }
  107. if (condition.get("Authority") != "") {
  108. Authority = condition.get("Authority").toString();
  109. builder.query(QueryBuilders.termQuery("dispatchnoId.keyword", Authority));
  110. getRequest.source(builder);
  111. }
  112. /*全文检索*/
  113. if (condition.get("ownerUnit") != ""){
  114. ownerUnit = condition.get("ownerUnit").toString();
  115. //多个字段条件匹配查询(multiMatchQuery)
  116. builder.query(QueryBuilders.multiMatchQuery(ownerUnit,"docTitle","issuingAuthority","issuingAuthorityYear","postingSequenceNumber","orgName","drafterName","uploaderName"));
  117. getRequest.source(builder);
  118. }
  119. }
  120. // 3、发送请求到ES
  121. return restHighLevelClient.search(getRequest, RequestOptions.DEFAULT);
  122. // // 4、处理响应结果
  123. // for (SearchHit hit : response.getHits().getHits()) {
  124. // OfficeDocVO officeInfo = JSON.parseObject(hit.getSourceAsString(), OfficeDocVO.class);
  125. // log.info("所有信息:{}", officeInfo);
  126. // }
  127. }
  128. /**
  129. * 新增索引数据
  130. */
  131. public void addEsDoc(OfficeDocVO officeDocVO)throws IOException{
  132. // 定义请求对象
  133. IndexRequest request = new IndexRequest(OFFICE_INDEX);
  134. // 设置文档id
  135. request.id(officeDocVO.getOfficeDocId());
  136. // 将json格式字符串放在请求中
  137. request.source(JSONObject.toJSONString(officeDocVO), XContentType.JSON);
  138. // 3、发送请求到ES
  139. IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
  140. }
  141. /**
  142. * 更新索引数据
  143. */
  144. public void updateEsDoc(OfficeDocVO officeDocVO)throws IOException{
  145. UpdateRequest request = new UpdateRequest();
  146. request.index(OFFICE_INDEX).id(officeDocVO.getOfficeDocId());
  147. // 拓展:局部更新也可以这样写:request.doc(XContentType.JSON, "name", "李四", "age", 25);,其中"name"和"age"是User对象中的字段名称,而"小美"和20是对应的字段值
  148. request.doc(JSONObject.toJSONString(officeDocVO), XContentType.JSON);
  149. // 3、发送请求到ES
  150. UpdateResponse response = restHighLevelClient.update(request, RequestOptions.DEFAULT);
  151. }
  152. /**
  153. * 删除索引数据
  154. * @param OfficeDocId
  155. * @throws Exception
  156. */
  157. public void deleteEsDoc(String OfficeDocId)throws Exception{
  158. // 2、定义请求对象
  159. DeleteRequest request = new DeleteRequest(OFFICE_INDEX);
  160. request.id(OfficeDocId);
  161. // 3、发送请求到ES
  162. DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
  163. }
  164. }

EsSearchContoller

  1. /**
  2. * @author hmf
  3. * @create 2022-08-26 10:44
  4. */
  5. @Api(tags = "ES全文检索")
  6. @RestController
  7. @CrossOrigin
  8. @RequestMapping("/EsSearch")
  9. public class EsSearchContoller {
  10. @Resource
  11. RestHighLevelClient client;
  12. @Resource
  13. EsIndexService esIndexService;
  14. @ApiOperation("初始化ES数据以及index")
  15. @RequestMapping("/initIndex")
  16. public void createIndex()throws Exception{
  17. esIndexService.createIndex();
  18. }
  19. @ApiOperation("ES数据查询以及全文检索")
  20. @RequestMapping("/getEsData")
  21. public SearchResponse getEsData(@RequestBody PageData page)throws IOException{
  22. return esIndexService.getDocument(page);
  23. }
  24. }

第三步,前端获取数据 

需要注意从ES查询出来的返回值存储在res.hits.hits中

由于传输中,将数据库里的数据时间类型转入ES后会转成时间戳,返回前端后展示数据会变成时间戳。

在后端加注解也是方法之一,

但这里是用前端进行转换。

  1. // 表格初始化
  2. Initialization(data){
  3. let condition = {
  4. AuthorityYear:data.parentid,
  5. Authority:data.dispatchnoId,
  6. ownerUnit:this.formInline.ownerUnit,
  7. };
  8. let size = this.page.size;
  9. let current=this.page.currentPage;
  10. let Params = {condition,size,current}
  11. this.request('/EsSearch/getEsData',Params,'post').then((res) =>{
  12. if (res.hits.hits.length > 0){
  13. res.hits.hits.forEach(element => {
  14. element.sourceAsMap.draftDate = this.Time(element.sourceAsMap.draftDate);
  15. element.sourceAsMap.uploadTime = this.Time(element.sourceAsMap.uploadTime);
  16. });
  17. this.tableData = res.hits.hits;
  18. }else{
  19. //表格重置为空
  20. this.tableData=[];
  21. }
  22. //重置显示总数
  23. this.page.total=res.hits.totalHits.value;
  24. })
  25. },
  1. Time(time) { //处理时间
  2. // return moment(parseInt(e)).format('YYYY-MM-DD');
  3. //将13位时间戳转换成时间格式 输出为2018-10-09
  4. let date = new Date(time);
  5. let year = date.getFullYear();
  6. let month = date.getMonth() + 1;
  7. let day = date.getDate();
  8. month = month < 10 ? "0" + month : month;
  9. day = day < 10 ? "0" + day : day;
  10. var myDate = ''
  11. myDate = year + '-' + month + '-' + day;
  12. return myDate
  13. },

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/67987
推荐阅读
相关标签
  

闽ICP备14008679号