springboot 整合 ElasticSearch 方法 (二)

依赖

在pom.xml文件中需要引入3个依赖, 三个都必须有并且三个依赖的版本要一致, 不然会报错.

不一定是 7.6.1 这个版本 , 只需要保证这三个依赖的版本一致就可以了.

xml 复制代码
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.6.1</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>7.6.1</version>
</dependency>
 
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.6.1</version>
</dependency>

配置类

用配置类的方法来配置 es, 配置文件中就 只需要写一下用户名和密码之类的就可以了, 我们用 @Value 拿到它 .

java 复制代码
package com.es.config;
 
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class ElasticSearchClientConfig {
	@Value("${elasticsearch.username}")
	private String username;

	@Value("${elasticsearch.password}")
	private String password;
 
    @Bean
    public RestHighLevelClient restHighLevelClient(){
    	RestClientBuilder builder = RestClient.builder(new HttpHost("elastic-bingo.cld", 9200)); // host和ip也是一样可以写在配置文件中的
    	
    	// 设置用户名和密码
    	CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    	credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password);
    	builder.setHttpClientConfigCallback(f -> f.setDefaultCredentialsProvider(credentialsProvider));
    	
    	RestHighLevelClient client = new RestHighLevelClient(builder);
        return client;
    }
}

设置用户名和密码的部分参考这篇文章: ElasticSearch设置用户名和密码

如果是本地可以写 http://localhost, 拼接上端口, 可以访问这个页面就可以了.

实体类

实体类和普通的 mysql 的实体类是一样的.

这里没有加 id, es 会自动生成的 (不需要设置自增非空什么的, 它自己就会生成的)

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Log{
    private String username;
    private String content;
    private Long startTime;
    private Long endTime;
}

@Data, @NoArgsConstructor, @AllArgsConstructor 这三个注解是使用了 lombok 工具, 如果没有用这个依赖, 还是要生成一下get, set方法以及构造函数的哦.

其他类

因为我的项目中既用了 es, 又用了 mysql, es只是用来存储日志的 .

所以为了统一, 我还是创建了 LogServiceLogServiceImpl , 没有 mapper, 怎删改查的逻辑我写在 LogServiceImpl 里了.

LogService

java 复制代码
public interface LogService {
	// 插入logs
	void insertLogs(List<Log> logs);
	
	// 查找logs
	List<Log> searchLogs(String username, Long startTime, Long endTime);
}

LogServiceImpl

实现类中用到了 JSON.toJSONString, 别忘记引入 fastjson 依赖:

xml 复制代码
		<!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
java 复制代码
@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public void insertLogs(List<Log> logs) {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");

        // es里的批量插入
        try {
            for (Log log:logs) {
                bulkRequest.add(
                        new IndexRequest("logs").source(JSON.toJSONString(log), XContentType.JSON)
                );
            }
            restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<Log> searchLogs(String username, Long startTime, Long endTime) {
        SearchRequest searchRequest = new SearchRequest("logs"); // logs是索引名
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 用来存放查到的所有log日志数据
        List<Log> logs = new ArrayList<>();

        // 查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 查找某个范围内的时间, time是es中的字段
        boolQuery.filter(QueryBuilders.rangeQuery("time").gte(startTime).lte(endTime));
        // 精确查找某个用户, 用must函数
        boolQuery.must(QueryBuilders.matchQuery("username",username));
        
        searchSourceBuilder.query(boolQuery);
        searchRequest.source(searchSourceBuilder);

        try {
            // 发送请求
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            // 处理返回的数据, es返回的数据结构跟mysql不一样, 返回的是一个 SearchHit[] 对象, 所以这里再处理一下
            for (SearchHit hit: response.getHits().getHits()) {
                Log log = JSON.parseObject(JSON.toJSONString(hit.getSourceAsMap()), Log.class);
                logs.add(log);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return logs;
    }
}

es返回的结构

关于 es 查询返回的结构, 可以再看一下:

我们拿到的 response 的结构是这样的 (数据是我伪造的可能有出入, 大概结构是这样的):

java 复制代码
{
	"took":1,
	"time_out":false,
	"_shards": {
		"total":1,
		"succesful":1,
		"skipped":0,
		"failed":0
	},
	"hits": {
		"total":{
			"value":45,
			"relation":"eq"
		},
		"max_score":1.0,
		"hits":[
			{
				"_index":"logs",
				"_type":"_doc",
				"_id":"jsOFp_jdfk_operR",
				"_score":1.0,
				"_source": {
					"username":"xiaoming",
					"content":"I am content",
					"startTime":20240101,
					"endTime":20240201
				}
			},
			{
				"_index":"logs",
				"_type":"_doc",
				"_id":"jswep_joerfk_opeoRR",
				"_score":1.0,
				"_source": {
					"username":"xiaohong",
					"content":"I am content2",
					"startTime":20240101,
					"endTime":20240201
				}
			}
		]
	}
}

response.getHits().getHits() 的结构是这样的:

java 复制代码
[
	{
		"_index":"logs",
		"_type":"_doc",
		"_id":"jsOFp_jdfk_operR",
		"_score":1.0,
		"_source": {
			"username":"xiaoming",
			"content":"I am content",
			"startTime":20240101,
			"endTime":20240201
		}
	},
	{
		"_index":"logs",
		"_type":"_doc",
		"_id":"jswep_joerfk_opeoRR",
		"_score":1.0,
		"_source": {
			"username":"xiaohong",
			"content":"I am content2",
			"startTime":20240101,
			"endTime":20240201
		}
	}
]

在循环里面 hit.getSourceAsMap() 拿到的结构是这样的, 只拿到了 _source 的部分:

java 复制代码
	{
			"username":"xiaohong",
			"content":"I am content2",
			"startTime":20240101,
			"endTime":20240201
		}

测试案例

下面是一些 es 的测试案例, 作为参考.

在建测试类的时候, 不要忘记加 @RunWith(SpringRunner.class) 注解(因为配置类是写在config文件夹下面的, 需要 @Autowired 注入一起启动)

java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class EsTest {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
 
    //索引的创建
    @Test
    public void testCreateIndex() throws IOException {
        //1.创建索引请求
        CreateIndexRequest request = new CreateIndexRequest("java_test");
        //2.执行创建请求,请求后获得响应
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        System.out.println(createIndexResponse.isAcknowledged());
    }
 
    //获取索引,只能判断存不存在
    @Test
    public void testExistIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest("java_test");
        boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        System.out.println(exists);
    }
 
    //删除索引
    @Test
    public void testDeleteIndex() throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest("java_test");
        AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
        System.out.println(response.isAcknowledged());
    }
 
    //添加文档
    @Test
    public void testAddDocument() throws IOException {
        //创建对象
        User user = new User("张三", 18);
        //创建请求
        IndexRequest request = new IndexRequest("java_test");
        //规则 put /java_test/_doc/1
        request.id("1");
        request.timeout(TimeValue.timeValueSeconds(1));
        //将数据放入请求 json
        request.source(JSON.toJSONString(user), XContentType.JSON);
        //客户端发送请求,获取响应结果
        IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
 
        System.out.println(indexResponse.toString());
        System.out.println(indexResponse.status());
    }
 
    //获取文档,判断是否存在
    @Test
    public void testExistDocument() throws IOException {
        GetRequest request = new GetRequest("java_test","1");
        //不获取返回的_source的上下文
        request.fetchSourceContext(new FetchSourceContext(false));
        request.storedFields("_none_");
 
        boolean exists = restHighLevelClient.exists(request, RequestOptions.DEFAULT);
        System.out.println(exists);
    }
 
    //获取文档的信息
    @Test
    public void testGetDocument() throws IOException {
        GetRequest request = new GetRequest("java_test","1");
        GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
        String sourceAsString = response.getSourceAsString();
        System.out.println(response);//返回全部内容和命令是一样的
        System.out.println(sourceAsString);//打印文档内容
    }
 
    //更新文档的信息
    @Test
    public void testUpdateDocument() throws IOException {
        UpdateRequest request = new UpdateRequest("java_test","1");
        User user = new User("李四", 22);
        request.doc(JSON.toJSONString(user),XContentType.JSON);
        UpdateResponse response = restHighLevelClient.update(request, RequestOptions.DEFAULT);
        System.out.println(response.status());
    }
 
    //删除文档记录
    @Test
    public void testDeleteDocument() throws IOException {
        DeleteRequest request = new DeleteRequest("java_test", "1");
        DeleteResponse deleteResponse = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        System.out.println(deleteResponse.status());
    }
 
    //真实项目一般都会批量插入数据
    @Test
    public void testBulkRequest() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        
        bulkRequest.timeout("10s");
 
        List<User> userList = new ArrayList<>();
        userList.add(new User("zhangan1", 18));
        userList.add(new User("zhangan2", 18));
        userList.add(new User("zhangan3", 18));
        userList.add(new User("lisi1", 18));
        userList.add(new User("lisi2", 18));
        userList.add(new User("lisi3", 18));
 
        //批处理请求
        for (int i = 0; i < userList.size(); i++) {
            //批量更新和批量删除,就在这里修改对应的请求就可以了
            bulkRequest.add(
                    new IndexRequest("java_test")
                            .id("" + (i+1))
                            .source(JSON.toJSONString(userList.get(i)), XContentType.JSON)
            );
        }
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(bulkResponse.hasFailures());//是否失败,返回false 代表成功
    }
 
    //查询
    @Test
    public void testSearch() throws IOException {
        SearchRequest searchRequest = new SearchRequest("java_test");
        //构建搜索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //查询条件,我们可以使用QueryBuilds工具类来实现
        //QueryBuilders.termQuery() 精确
        //QueryBuilders.matchAllQuery() 匹配所有
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "lisi1");
 
        sourceBuilder.query(termQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
 
        searchRequest.source(sourceBuilder);
 
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(JSON.toJSONString(searchResponse.getHits()));
        System.out.println("===============================================");
        for (SearchHit searchHit:searchResponse.getHits().getHits()) {
            System.out.println(searchHit.getSourceAsString());
        }
 
    }
}
相关推荐
Dxy12393102163 分钟前
Elasticsearch 8如何做好标题搜索
大数据·elasticsearch
king0076 分钟前
Spring-AI 结合自定义 mcp server 实现飞书智能机器人
spring boot·ai·ai编程
长不大的蜡笔小新14 分钟前
私人健身房管理系统
java·javascript·spring boot
斯普信云原生组1 小时前
Elasticsearch(ES) 内存 CPU 过高问题排查报告
大数据·elasticsearch·搜索引擎
弘毅 失败的 mian2 小时前
Git 分支管理
大数据·经验分享·笔记·git·elasticsearch
trayvontang2 小时前
Spring属性自动配置原理与自定义转换
spring boot·spring·spring属性自动配置·spring转换原理·spring自定义属性转换器
袁煦丞 cpolar内网穿透实验室2 小时前
无需公网 IP 也能全球访问本地服务?cpolar+Spring Boot+Vue应用实践!
vue.js·spring boot·tcp/ip·远程工作·内网穿透·cpolar
阿坤带你走近大数据2 小时前
Elasticsearch(ES)的基本概念、架构及基本使用介绍
大数据·elasticsearch
幽络源小助理2 小时前
SpringBoot+Vue雅苑小区管理系统源码 | Java物业项目免费下载 – 幽络源
java·vue.js·spring boot
记得记得就1513 小时前
【jenkins持续集成测试】
运维·jenkins·集成测试