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());
        }
 
    }
}
相关推荐
Elasticsearch11 小时前
需要知道某个同义词是否实际匹配了你的 Elasticsearch 查询吗?
elasticsearch
用户908324602732 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
洛森唛3 天前
ElasticSearch查询语句Query String详解:从入门到精通
后端·elasticsearch
用户8307196840823 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解3 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解3 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记3 天前
Spring Boot Web MVC配置详解
spring boot·后端
洛森唛4 天前
Elasticsearch DSL 查询语法大全:从入门到精通
后端·elasticsearch
初次攀爬者4 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840824 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp