javascript
springboot 单测加入参数_Spring Boot集成Elasticsearch实战分享
作者|java夢想口服液|簡書
最近有讀者問我能不能寫下如何使用 Spring Boot 開發(fā) Elasticsearch(以下簡稱 ES) 相關(guān)應(yīng)用,今天就講解下如何使用 Spring Boot 結(jié)合 ES。
可以在 ES 官方文檔中發(fā)現(xiàn),ES 為 Java REST Client 提供了兩種方式的 Client: Java Low Level Client 和 Java High Level REST Client 。
低級別客戶端,它允許通過 HTTP 請求與 ES 集群進(jìn)行通信,API 本身不負(fù)責(zé)數(shù)據(jù)的編碼解碼,由用戶去編碼解碼,它與所有的 ES 版本兼容。
高級客戶端基于低級客戶端,是從 6.0 才開始加入的,主要目標(biāo)是為了暴露各 API 特定的方法,高版本客戶端依賴于 ES 核心項(xiàng)目,將 Request 對象作為參數(shù),返回一個 Response 對象,所有 API 都可以同步或異步調(diào)用。
本文就通過 Spring Boot 結(jié)合 Java High Level REST Client 來進(jìn)行一些演示。
Spring Boot 集成 ES
Spring Boot 集成 ES 主要分為以下三步:
加入依賴
首先創(chuàng)建一個項(xiàng)目,在項(xiàng)目中加入 ES 相關(guān)依賴,具體依賴如下所示:
org.elasticsearch elasticsearch 7.1.0org.elasticsearch.client elasticsearch-rest-high-level-client 7.1.0創(chuàng)建 ES 配置
在配置文件 application.properties 中配置 ES 的相關(guān)參數(shù),具體內(nèi)容如下:
elasticsearch.host=localhostelasticsearch.port=9200elasticsearch.connTimeout=3000elasticsearch.socketTimeout=5000elasticsearch.connectionRequestTimeout=500其中指定了 ES 的 host 和端口以及超時時間的設(shè)置,另外我們的 ES 沒有添加任何的安全認(rèn)證,因此 username 和 password 就沒有設(shè)置。
然后在 config 包下創(chuàng)建 ElasticsearchConfiguration 類,會從配置文件中讀取到對應(yīng)的參數(shù),接著申明一個 initRestClient 方法,返回的是一個 RestHighLevelClient ,同時為它添加 @Bean(destroyMethod = "close") 注解,當(dāng) destroy 的時候做一個關(guān)閉,這個方法主要是如何初始化并創(chuàng)建一個 RestHighLevelClient 。
@Configurationpublic class ElasticsearchConfiguration { @Value("${elasticsearch.host}") private String host; @Value("${elasticsearch.port}") private int port; @Value("${elasticsearch.connTimeout}") private int connTimeout; @Value("${elasticsearch.socketTimeout}") private int socketTimeout; @Value("${elasticsearch.connectionRequestTimeout}") private int connectionRequestTimeout; @Bean(destroyMethod = "close", name = "client") public RestHighLevelClient initRestClient() { RestClientBuilder builder = RestClient.builder(new HttpHost(host, port)) .setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder .setConnectTimeout(connTimeout) .setSocketTimeout(socketTimeout) .setConnectionRequestTimeout(connectionRequestTimeout)); return new RestHighLevelClient(builder); }}定義文檔實(shí)體類
首先在 constant 包下定義常量接口,在接口中定義索引的名字為 user :
public interface Constant { String INDEX = "user";}然后在 `document` 包下創(chuàng)建一個文檔實(shí)體類:public class UserDocument { private String id; private String name; private String sex; private Integer age; private String city; // 省略 getter/setter}ES 基本操作
在這里主要介紹 ES 的索引、文檔、搜索相關(guān)的簡單操作,在 service 包下創(chuàng)建 UserService類。
索引操作
在這里演示創(chuàng)建索引和刪除索引:
創(chuàng)建索引
在創(chuàng)建索引的時候可以在 CreateIndexRequest 中設(shè)置索引名稱、分片數(shù)、副本數(shù)以及 mappings,在這里索引名稱為 user ,分片數(shù) number_of_shards 為 1,副本數(shù) number_of_replicas 為 0,具體代碼如下所示:
public boolean createUserIndex(String index) throws IOException { CreateIndexRequest createIndexRequest = new CreateIndexRequest(index); createIndexRequest.settings(Settings.builder() .put("index.number_of_shards", 1) .put("index.number_of_replicas", 0) ); createIndexRequest.mapping("{" + " "properties": {" + " "city": {" + " "type": "keyword"" + " }," + " "sex": {" + " "type": "keyword"" + " }," + " "name": {" + " "type": "keyword"" + " }," + " "id": {" + " "type": "keyword"" + " }," + " "age": {" + " "type": "integer"" + " }" + " }" + "}", XContentType.JSON); CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT); return createIndexResponse.isAcknowledged();}通過調(diào)用該方法,就可以創(chuàng)建一個索引 user ,索引信息如下:
關(guān)于 ES 的 Mapping 可以看下這篇文章: 一文搞懂 Elasticsearch 之 Mapping
刪除索引
在 DeleteIndexRequest 中傳入索引名稱就可以刪除索引,具體代碼如下所示:
public Boolean deleteUserIndex(String index) throws IOException { DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index); AcknowledgedResponse deleteIndexResponse = client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT); return deleteIndexResponse.isAcknowledged();}介紹完索引的基本操作,下面介紹文檔的相關(guān)操作:
文檔操作
在這里演示下創(chuàng)建文檔、批量創(chuàng)建文檔、查看文檔、更新文檔以及刪除文檔:
創(chuàng)建文檔
創(chuàng)建文檔的時候需要在 IndexRequest 中指定索引名稱, id 如果不傳的話會由 ES 自動生成,然后傳入 source,具體代碼如下:
public Boolean createUserDocument(UserDocument document) throws Exception { UUID uuid = UUID.randomUUID(); document.setId(uuid.toString()); IndexRequest indexRequest = new IndexRequest(Constant.INDEX) .id(document.getId()) .source(JSON.toJSONString(document), XContentType.JSON); IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT); return indexResponse.status().equals(RestStatus.OK);}下面通過調(diào)用這個方法,創(chuàng)建兩個文檔,具體內(nèi)容如下:
批量創(chuàng)建文檔
在一個 REST 請求中,重新建立網(wǎng)絡(luò)開銷是十分損耗性能的,因此 ES 提供 Bulk API, 支持在一次 API 調(diào)用中,對不同的索引進(jìn)行操作 ,從而減少網(wǎng)絡(luò)傳輸開銷,提升寫入速率。
下面方法是批量創(chuàng)建文檔,一個 BulkRequest 里可以添加多個 Request,具體代碼如下:
public Boolean bulkCreateUserDocument(List documents) throws IOException { BulkRequest bulkRequest = new BulkRequest(); for (UserDocument document : documents) { String id = UUID.randomUUID().toString(); document.setId(id); IndexRequest indexRequest = new IndexRequest(Constant.INDEX) .id(id) .source(JSON.toJSONString(document), XContentType.JSON); bulkRequest.add(indexRequest); } BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT); return bulkResponse.status().equals(RestStatus.OK);}下面通過該方法創(chuàng)建些文檔,便于下面的搜索演示。
查看文檔
查看文檔需要在 GetRequest 中傳入索引名稱和文檔 id,具體代碼如下所示:
public UserDocument getUserDocument(String id) throws IOException { GetRequest getRequest = new GetRequest(Constant.INDEX, id); GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT); UserDocument result = new UserDocument(); if (getResponse.isExists()) { String sourceAsString = getResponse.getSourceAsString(); result = JSON.parseObject(sourceAsString, UserDocument.class); } else { logger.error("沒有找到該 id 的文檔"); } return result;}下面?zhèn)魅胛臋n id 調(diào)用該方法,結(jié)果如下所示:
更新文檔
更新文檔則是先給 UpdateRequest 傳入索引名稱和文檔 id,然后通過傳入新的 doc 來進(jìn)行更新,具體代碼如下:
public Boolean updateUserDocument(UserDocument document) throws Exception { UserDocument resultDocument = getUserDocument(document.getId()); UpdateRequest updateRequest = new UpdateRequest(Constant.INDEX, resultDocument.getId()); updateRequest.doc(JSON.toJSONString(document), XContentType.JSON); UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT); return updateResponse.status().equals(RestStatus.OK);}下面將文檔 id 為 9b8d9897-3352-4ef3-9636-afc6fce43b20 的文檔的城市信息改為 handan ,調(diào)用方法結(jié)果如下:
刪除文檔
刪除文檔只需要在 DeleteRequest 中傳入索引名稱和文檔 id,然后執(zhí)行 delete 方法就可以完成文檔的刪除,具體代碼如下:
public String deleteUserDocument(String id) throws Exception { DeleteRequest deleteRequest = new DeleteRequest(Constant.INDEX, id); DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT); return response.getResult().name();}介紹完文檔的基本操作,接下來對搜索進(jìn)行簡單介紹:
搜索操作
對 ES 的 DSL 語法還不是很熟悉的可以先看下這篇文章: 看完這篇還不會 Elasticsearch 搜索,那我就哭了!
簡單的搜索操作需要在 SearchRequest 中設(shè)置將要搜索的索引名稱(可以設(shè)置多個索引名稱),然后通過 SearchSourceBuilder 構(gòu)造搜索源,下面將 TermQueryBuilder 搜索查詢傳給 searchSourceBuilder ,最后將 searchRequest 的搜索源設(shè)置為 searchSourceBuilder ,執(zhí)行 search 方法實(shí)現(xiàn)通過城市進(jìn)行搜索,具體代碼如下所示:
public List searchUserByCity(String city) throws Exception { SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(Constant.INDEX); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("city", city); searchSourceBuilder.query(termQueryBuilder); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); return getSearchResult(searchResponse);}該方法的執(zhí)行結(jié)果如圖所示:
聚合搜索
ES 聚合搜索相關(guān)知識可以看下這篇文章: Elasticsearch 之聚合分析入門
聚合搜索就是給 searchSourceBuilder 添加聚合搜索,下面方法是通過 TermsAggregationBuilder 構(gòu)造一個先通過城市就行分類聚合,其中還包括一個子聚合,是對年齡求平均值,然后在獲取聚合結(jié)果的時候,可以使用通過在構(gòu)建聚合時的聚合名稱獲取到聚合結(jié)果,具體代碼如下所示:
public List aggregationsSearchUser() throws Exception { SearchRequest searchRequest = new SearchRequest(Constant.INDEX); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); TermsAggregationBuilder aggregation = AggregationBuilders.terms("by_city") .field("city") .subAggregation(AggregationBuilders .avg("average_age") .field("age")); searchSourceBuilder.aggregation(aggregation); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); Aggregations aggregations = searchResponse.getAggregations(); Terms byCityAggregation = aggregations.get("by_city"); List userCityList = new ArrayList<>(); for (Terms.Bucket buck : byCityAggregation.getBuckets()) { UserCityDTO userCityDTO = new UserCityDTO(); userCityDTO.setCity(buck.getKeyAsString()); userCityDTO.setCount(buck.getDocCount()); // 獲取子聚合 Avg averageBalance = buck.getAggregations().get("average_age"); userCityDTO.setAvgAge(averageBalance.getValue()); userCityList.add(userCityDTO); } return userCityList;}下面是執(zhí)行該方法的結(jié)果:
到此為止,ES 的基本操作就簡單介紹完了,大家可以多動手試試,不會的可以看下官方文檔。
總結(jié)
本文的完整代碼在 https://github.com/wupeixuan/SpringBoot-Learn 的 elasticsearch 目錄下。
Spring Boot 結(jié)合 ES 還是比較簡單的,大家可以下載項(xiàng)目源碼,自己在本地運(yùn)行調(diào)試這個項(xiàng)目,更好地理解如何在 Spring Boot 中構(gòu)建基于 ES 的應(yīng)用。
總結(jié)
以上是生活随笔為你收集整理的springboot 单测加入参数_Spring Boot集成Elasticsearch实战分享的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iPhone 14 Pro烧屏怎么办iP
- 下一篇: 电脑工作组打不开(电脑工作组打不开怎么办