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());
        }
 
    }
}
相关推荐
RainbowSea1 小时前
问题:后端由于字符内容过长,前端展示精度丢失修复
java·spring boot·后端
风象南2 小时前
SpringBoot 控制器的动态注册与卸载
java·spring boot·后端
我是一只代码狗2 小时前
springboot中使用线程池
java·spring boot·后端
hello早上好2 小时前
JDK 代理原理
java·spring boot·spring
PanZonghui2 小时前
Centos项目部署之运行SpringBoot打包后的jar文件
linux·spring boot
沉着的码农3 小时前
【设计模式】基于责任链模式的参数校验
java·spring boot·分布式
zyxzyx6663 小时前
Flyway 介绍以及与 Spring Boot 集成指南
spring boot·笔记
会又不会5 小时前
Jenkins-Publish HTML reports插件
运维·jenkins
一头生产的驴5 小时前
java整合itext pdf实现自定义PDF文件格式导出
java·spring boot·pdf·itextpdf
程序员张37 小时前
SpringBoot计时一次请求耗时
java·spring boot·后端