SpringBoot整合ES(springboot整合es常用查询)

网友投稿 754 2022-08-01


目录概述添加Maven依赖配置application.yml创建索引对象SpringBoot操作ES数据的三种方式实现索引对应的Repository文档操作文档保存、查询、删除分页查询与滚动查询ES深度分页 vs 滚动查询SpringBoot集成ES基本使用在test中测试

概述

本文介绍 Spring Boot 项目中整合 ElasticSearch 并实现 CRUD 操作,包括分页、滚动等功能。

添加Maven依赖

org.springframework.boot

spring-boot-starter-data-elasticsearch

配置application.yml

spring:

elasticsearch:

rest:

uris: 192.168.1.81:9200

创建索引对象

package com.practice.elkstudy.entity;

import cn.hutool.core.date.DateTime;

import lombok.Data;

import org.springframework.data.annotation.Id;

import org.springframework.data.elasticsearch.annotations.Document;

import java.util.Date;

/**

* @Description : 文档模型

* @Version : V1.0.0

* @Date : 2021/12/22 14:08

*/

@Document(indexName = "article")

@Data

public class ArticleEntity {

@Id

private String id;

private String title;

private String content;

private Integer userId;

private Date createTime = DateTime.now();

}

SpringBoot操作ES数据的三种方式

实现ElasticsearchRepository接口引入ElasticsearchRestTemplate引入ElasticsearchOperations

实现索引对应的Repository

package com.practice.elkstudy.repository;

import com.practice.elkstudy.entity.ArticleEntity;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**

* @Description : article数据操作接口

* @Version : V1.0.0

* @Date : 2021/12/22 14:18

*/

public interface ArticleRepository extends ElasticsearchRepository {

}

文档操作

下面可以使用这个 ArticleRepository 来操作 ES 中的 Article 数据。

我们这里没有手动创建这个 Article 对应的索引,由 elasticsearch 默认生成。

下面的接口,实现了 spring boot 中对 es 数据进行插入、更新、分页查询、滚动查询、删除等操作。可以作为一个参考。

其中,使用了 Repository 来获取、保存、删除 ES 数据;使用 ElasticsearchRestTemplate 或 ElasticsearchOperations 来进行分页/滚动查询。

文档保存、查询、删除

package com.practice.elkstudy.controller.controller;

import com.practice.elkstudy.entity.ArticleEntity;

import com.practice.elkstudy.repository.ArticleRepository;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

import java.util.Optional;

/**

* @Description : article控制类

* @Version : V1.0.0

* @Date : 2021/12/22 14:11

*/

@RestController

@RequestMapping("/elk")

public class ArticleController {

@Resource

private ArticleRepository articleRepository;

/**

* 根据文档id查询数据

*

* @param id 文档id

* @return 文档详情

*/

@GetMapping("/byId")

public String findById(@RequestParam String id) {

Optional record = articleRepository.findById(id);

return record.toString();

}

/**

* 保存文档信息

*

* @param article 文档详情

* @return 保存的文档信息

*/

@PostMapping("/saveArticle")

public String saveArticle(@RequestBody ArticleEntity article) {

ArticleEntity result = articleRepository.save(article);

return result.toString();

}

@DeleteMapping("/deleteById")

public String deleteArticle(@RequestParam String id) {

articleRepository.deleteById(id);

return "success";

}

}

分页查询与滚动查询

package com.practice.elkstudy.controller.controller;

import com.practice.elkstudy.entity.ArticleEntity;

import org.elasticsearch.index.query.BoolQueryBuilder;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.PageRequest;

import org.springframework.data.elasticsearch.core.ElasticsearchOperations;

import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;

import org.springframework.data.elasticsearch.core.SearchHit;

import org.springframework.data.elasticsearch.core.SearchHits;

import org.springframework.data.elasticsearch.core.SearchHitsImpl;

import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;

import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;

import org.springframework.util.StringUtils;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Objects;

import java.util.stream.Collectors;

/**

* @Description : article高级查询

* @Version : V1.0.0

* @Date : 2021/12/22 15:10

*/

@RestController

@RequestMapping("/elk")

public class ArticleAdvanceController {

@Autowired

private ElasticsearchRestTemplate restTemplate;

@Autowired

private ElasticsearchOperations operations;

/**

* 分页查询

*

* @param pageNum 页码,从0开始

* @param pageSize 分页大小

* @return 查询结果

*/

@GetMapping("/queryPage")

public String queryPage(@RequestParam int pageNum, @RequestParam int pageSize) {

NativeSearchQuery query = new NativeSearchQuery(new BoolQueryBuilder());

query.setPageable(PageRequest.of(pageNum, pageSize));

// 方法1

SearchHits search = restTemplate.search(query, ArticleEntity.class);

// 方法2

// SearchHits search = operations.search(query, ArticleEntity.class);

List articles = search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());

return articles.toString();

}

/**

* 滚动查询

*

* @param scrollId 滚动id

* @param pageSize 分页大小

* @return 查询结果

*/

@GetMapping(value = "/scrollQuery")

public String scroll(String scrollId, Integer pageSize) {

if (pageSize == null || pageSize <= 0) {

return "please input query page num";

}

NativeSearchQuery query = new NativeSearchQuery(new BoolQueryBuilder());

query.setPageable(PageRequest.of(0, pageSize));

SearchHits searchHits;

if (StringUtils.isEmpty(scrollId) || scrollId.equals("0")) {

// 开启一个滚动查询,设置该scroll上下文存在60s

// 同一个scroll上下文,只需要设置一次query(查询条件)

searchHits = restTemplate.searchScrollStart(60000, query, ArticleEntity.class, IndexCoordinates.of("article"));

if (searchHits instanceof SearchHitsImpl) {

scrollId = ((SearchHitsImpl) searchHits).getScrollId();

}

} else {

// 继续滚动

searchHits = restTemplate.searchScrollContinue(scrollId, 60000, ArticleEntity.class, IndexCoordinates.of("article"));

}

List articles = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());

if (articles.size() == 0) {

// 结束滚动

restTemplate.searchScrollClear(Collections.singletonList(scrollId));

scrollId = null;

}

if (Objects.isNull(scrollId)) {

Map result = new HashMap<>(2);

result.put("articles", articles.toString());

result.put("message", "已到末尾");

return result.toString();

} else {

Map result = new HashMap<>();

result.put("count", String.valueOf(searchHits.getTotalHits()));

result.put("pageSize", String.valueOf(articles.size()));

result.put("articles", articles.toString());

result.put("scrollId", scrollId);

return result.toString();

}

}

}

ES深度分页 vs 滚动查询

之前遇到的一个问题,日志检索的接口太慢了。

开始使用的是深度分页,即1,2,3…10,这样的分页查询,查询条件较多(十多个参数)、查询数据量较大(单个日志索引约2亿条数据)。

分页查询速度慢的原因在于:ES的分页查询,如查询第100页数据,每页10条,是先从每个分区(shard,一个索引默认是5个shard)中把命中的前100*10条数据查出来,然后协调节点进行合并操作,最后给出100页的数据。也就是说,实际被加载到内存的数据远远超过理想情况。

这样,索引分片数越多,查询页数越多,查询速度就越慢。ES默认的max_result_window是10000条,也就是正常情况下,用分页查询到10000条数据时,就不会在返回下一页数据了。

如果不需要进行跳页,比如直接查询第100页数据,或者数据量非常大,那么可以考虑用scroll查询。在scroll查询下,第1次需要根据查询参数开启一个scroll上下文,设置上下文缓存时间。以后的滚动只需要根据第一次返回的scrollId来进行即可。

scroll只支持往下滚动,如果想要往前滚动,还可以根据scrollId缓存查询结果,这样就可以实现上下文滚动查询了一一就像大家经常使用的淘宝商品检索时上下滚动一样。

SpringBoot集成ES基本使用

#配置es

#Liunx 上的ip地址和配置端口号

spring.elasticsearch.rest.uris=192.168.113.129:9200

在test中测试

import com.alibaba.fastjson.JSON;

import com.hzx.pojo.User;

import com.hzx.utils.ESconst;

import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;

import org.elasticsearch.action.bulk.BulkRequest;

import org.elasticsearch.action.bulk.BulkResponse;

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.RestHighLevelClient;

import org.elasticsearch.client.indices.CreateIndexRequest;

import org.elasticsearch.client.indices.CreateIndexResponse;

import org.elasticsearch.client.indices.GetIndexRequest;

import org.elasticsearch.common.unit.TimeValue;

import org.elasticsearch.common.xcontent.XContentType;

import org.elasticsearch.index.query.QueryBuilders;

import org.elasticsearch.index.query.TermQueryBuilder;

import org.elasticsearch.search.SearchHit;

import org.elasticsearch.search.builder.SearchSourceBuilder;

import org.elasticsearch.search.fetch.subphase.FetchSourceContext;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;

import java.util.ArrayList;

import java.util.concurrent.TimeUnit;

@Autowired

private RestHighLevelClient client;

@Test

void contextLoads() throws IOException {

//创建索引请求

CreateIndexRequest request = new CreateIndexRequest("hong_index");

//客户端执行请求 IndicesClient create创建请求 RequestOptions.DEFAULT默认请求参数

CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);

//获取返回的参数

System.out.println(createIndexResponse);

}

@Test

void test2() throws IOException {

//获取指定索引库

GetIndexRequest request = new GetIndexRequest("hong_index2");

//判断获取索引是否存在

boolean exists = client.indices().exists(request,RequestOptions.DEFAULT);

//如果索引存在就返回为true 或者 为false

System.out.println(exists);

}

@Test

void test3() throws IOException {

//删除指定索引库

DeleteIndexRequest request = new DeleteIndexRequest("hong_index");

//获取删除索引

AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);

//检查索引是否被删除

System.out.println(delete.isAcknowledged());

}

//测试添加文档

@Test

void test4() throws IOException {

//创建对象

User user = new User("枣信",18);

//创建索引库

IndexRequest request = new IndexRequest("hong_index");

//规则 为 put /hong_index/_doc/1

//创建的id

request.id("1");

//创建的时间

request.timeout(TimeValue.timeValueSeconds(1));

// request.timeout("1s");

//将数据放入到请求 JSON.toJSONString(user)将对象转换为json

request.source(JSON.toJSONString(user), XContentType.JSON);

//客户端发送请求 向索引中添加数据

IndexResponse indices = client.index(request, RequestOptions.DEFAULT);

//获取返回的json对象

System.out.println(indices.toString());

//获取发送请求的状态 添加为CREATED 更新为OK

System.out.println(indices.status());

}

//获取文档信息

@Test

void test6() throws IOException {

//根据索引传入的id获取

GetRequest getRequest = new GetRequest("hong_index","1");

//通过get获取信息

GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);

//根据指定的Source获取对应内容

System.out.println(getResponse.getSourceAsString());

//打印json对象

System.out.println(getResponse);

}

//更新 修改信息

@Test

void test7() throws IOException {

//根据索引库传入的id更新

UpdateRequest updateRequest = new UpdateRequest("hong_index","1");

//更新时间

updateRequest.timeout("1s");

//创建对象

User user = new User("李四", 26);

//更新 将对象转换为json

updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);

//客户端发送请求,进行更新

UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT);

//获取更新状态

System.out.println(update.status());

}

//删除文档信息

@Test

void test8() throws IOException {

//根据传入的索引id进行删除

DeleteRequest request = new DeleteRequest("hong_index","1");

//发送请求,删除

DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);

//获取删除的状态 没有删除成功为NOT_FOUND 删除成功为OK

System.out.println(delete.status());

}

//批量添加数据

@Test

void test9() throws IOException {

//创建批量添加

BulkRequest bulkRequest = new BulkRequest();

//添加时间

bulkRequest.timeout("8s");

//创建一个arraylist集合

ArrayList userList = new ArrayList<>(http://);

userList.add(new User("李四",19));

userList.add(new User("王五",25));

userList.add(new User("赵刚",30));

userList.add(new User("张三",21));

userList.add(new User("赵六",36));

userList.add(new User("小武",20));

//批量处理请求

for (int i = 0; i < userList.size(); i++) {

//批量更新和删除 在这修改对应的请求即可 不添加id(""+(i+1)) 会默认随机id,在大数据情况下,让他默认随机id

bulkRequest.add(new IndexRequest("hong_index").id(""+(i+1)).source(JSON.toJSONString(userList.get(i)),XContentType.JSON));

}

//批量添加发送请求

BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);

//获取批量添加的状态 返回false代表添加成功

System.out.println(bulk.hasFailures());

}

//查询索引信息

@Test

void test10() throws IOException {

//查询

SearchRequest searchRequest = new SearchRequest(ESconst.ES_INDEX);

//构建搜索条件

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

//查询条件,可以使用QueryBuilders工具来实现

// QueryBuilders.termQuery精确查询

// QueryBuilders.matchQuery()查询所有

TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "李四");

//查询的时间

sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

//将查询的sourceBuilder放入searchRequest中

searchRequest.source(sourceBuilder);

//发送请求

SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);

//获取信息

System.out.println(JSON.toJSONString(search.getHits()));

//循环变量出信息

for(SearchHit documentFields : search.getHits().getHits()){

//获取所有信息

System.out.println(documentFields.getSourceAsMap());

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Bean创建和循环依赖(springmvc的工作原理)
下一篇:Java Io File文件操作基础教程
相关文章

 发表评论

暂时没有评论,来抢沙发吧~