ES[7.6.x]學習筆記(十一)與SpringBoot結合
阿新 • • 發佈:2020-05-27
在前面的章節中,我們把ES的基本功能都給大家介紹完了,從ES的搭建、建立索引、分詞器、到資料的查詢,大家發現,我們都是通過ES的API去進行呼叫,那麼,我們在專案當中怎麼去使用ES呢?這一節,我們就看看ES如何與我們的SpringBoot專案結合。
## 版本依賴
SpringBoot預設是有ElasticSearch的Starter,但是它依賴的ES客戶端的版本比較低,跟不上ES的更新速度,所以我們在SpringBoot專案中要指定ES的最新版本,如下:
```xml
```
我們在專案中指定ES客戶端的版本為7.6.1。
## 配置檔案
然後我們在SpringBoot的配置檔案`application.properties`當中,配置ES叢集的地址,如下:
```properties
spring.elasticsearch.rest.uris=http://192.168.73.130:9200,http://192.168.73.131:9200,http://192.168.73.132:9200
```
多個地址之間我們使用`,`隔開即可。
## 與ES互動
所有配置的東西都準備好了,下面我們看看在程式當中如何互動,還記得前面咱們提到的**動態對映**嗎?這個東西是非常的好用的,簡化了我們不少的工作量。在這裡我們還用前面的索引`ik_index`舉例,我們先看看目前`ik_index`索引中有哪些欄位,
![](https://img2020.cnblogs.com/blog/1191201/202005/1191201-20200527152623675-873655342.png)
在索引中只有3個欄位,id、title和desc。接下來我們在建立索引`ik_index`對應的實體類,內容也很簡單,具體如下:
```java
@Setter@Getter
public class IkIndex {
private Long id;
private String title;
private String desc;
private String category;
}
```
在實體類中,我們新添加了一個欄位`category`表示分類,我們可以聯想一下,`category`欄位動態對映到ES當中會是什麼型別?對了,就是`text`型別,我們再深入想一步,`text`型別會用到全文索引,會用到分詞器,而在索引`ik_index`當中,我們配置了預設的分詞器是IK中文分詞器。能夠想到這裡,我覺得你對ES瞭解的比較深入了。
接下來,我們就要編寫`service`了,並向`ik_index`索引中新增一條新的資料,如下:
```java
@Service
public class EService {
@Autowired
private RestHighLevelClient client;
/**
* 新增索引資料
* @throws IOException
*/
public void insertIkIndex() throws IOException {
IkIndex ikIndex = new IkIndex();
ikIndex.setId(10l);
ikIndex.setTitle("足球");
ikIndex.setDesc("足球是世界第一運動");
ikIndex.setCategory("體育");
IndexRequest request = new IndexRequest("ik_index");
// request.id("1");
request.source(JSON.toJSONString(ikIndex), XContentType.JSON);
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
System.out.println(indexResponse.status());
System.out.println(indexResponse.toString());
}
}
```
首先,我們要引入ES的高等級的客戶端`RestHighLevelClient`,由於我們在配置檔案中配置了ES叢集的地址,所以SpringBoot自動為我們建立了`RestHighLevelClient`的例項,我們直接自動注入就可以了。然後在新增索引資料的方法中,我們先把索引對應的實體建立好,並設定對應的值。
接下來我們就要構建索引的請求了,在`IndexRequest`的建構函式中,我們指定了索引的名稱`ik_index`,索引的id被我們註釋掉了,ES會給我們預設生成id,當然自己指定也可以。大家需要注意的是,這個id和`IkIndex`類裡的id不是一個id,這個id是資料在ES索引裡的唯一標識,而`IkIndex`實體類中的id只是一個數據而已,大家一定要區分開。然後我們使用`request.source`方法將實體類轉化為JSON物件並封裝到`request`當中,最後我們呼叫`client`的`index`方法完成資料的插入。我們看看執行結果吧。
```shell
CREATED
IndexResponse[index=ik_index,type=_doc,id=f20EVHIBK8kOanEwfXbW,version=1,result=created,seqNo=9,primaryTerm=6,shards={"total":2,"successful":2,"failed":0}]
```
`status`返回的值是`CREATED`,說明資料新增成功,而後面的響應資訊中,包含了很多具體的資訊,像每個分片是否成功都已經返回了。我們再用`elasticsearch-head`外掛查詢一下,結果如下:
![](https://img2020.cnblogs.com/blog/1191201/202005/1191201-20200527152446485-1871061621.png)
資料插入成功,並且新新增的欄位`category`也有了對應的值,這是我們期望的結果。下面我們再看看查詢怎麼使用。程式碼如下:
```java
public void searchIndex() throws IOException {
SearchRequest searchRequest = new SearchRequest("ik_index");
SearchSourceBuilder ssb = new SearchSourceBuilder();
QueryBuilder qb = new MatchQueryBuilder("desc","香蕉好吃");
ssb.query(qb);
searchRequest.source(ssb);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
String record = hit.getSourceAsString();
System.out.println(record);
}
}
```
* 我們先建立一個查詢請求,並指定索引為`ik_index`;
* 然後我們建立一個請求體`SearchSourceBuilder`,再構建我們的查詢請求`QueryBuilder`,`QueryBuilder`是一個介面,它的實現類有很多,對應著ES中的不同種類的查詢,比如咱們前面介紹的`bool`和`boosting`查詢,都有對應的實現類。在這裡,咱們使用`MatchQueryBuilder`並查詢`desc`包含`香蕉好吃`的資料,這個查詢咱們在前面通過API的方式也查詢過。
* 最後我們封裝好請求,並通過`client.search`方法進行查詢,返回的結構是`SearchResponse`。
* 在返回的結果中,我們獲取對應的資料,咦?這個為什麼呼叫了兩次Hits方法?咱們可以從API的返回值看出端倪,如下:
![](https://img2020.cnblogs.com/blog/1191201/202005/1191201-20200527152541541-1057277549.png)
* 我們可以看到返回的結果中確實有兩個hits,第一個hits中包含了資料的條數,第二個hits中才是我們想要的查詢結果,所以在程式中,我們呼叫了兩次hits。
* 在每一個hit當中,我們呼叫`getSourceAsString`方法,獲取JSON格式的結果,我們可以用這個字串通過JSON工具對映為實體。
我們看看程式執行的結果吧,
```json
{"id":1,"title":"香蕉","desc":"香蕉真好吃"}
{"id":1,"title":"香蕉","desc":"香蕉真好吃"}
{"id":1,"title":"橘子","desc":"橘子真好吃"}
{"id":1,"title":"桃子","desc":"桃子真好吃"}
{"id":1,"title":"蘋果","desc":"蘋果真好吃"}
```
查詢出了5條資料,和我們的預期是一樣的,由於使用IK中文分詞器,所以`desc`中包含`好吃`的都被查詢了出來,而我們新新增的`足球`資料並沒有查詢出來,這也是符合預期的。我們再來看看聚合查詢怎麼用,
```java
public void searchAggregation() throws IOException {
SearchRequest searchRequest = new SearchRequest("ik_index");
SearchSourceBuilder ssb = new SearchSourceBuilder();
TermsAggregationBuilder category = AggregationBuilders.terms("category").field("category.keyword");
ssb.aggregation(category);
searchRequest.source(ssb);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
Terms terms = response.getAggregations().get("category");
for (Terms.Bucket bucket : terms.getBuckets()) {
System.out.println(bucket.getKey());
System.out.println(bucket.getDocCount());
}
}
```
* 同樣,我們建立一個`SearchRequest`,然後再建立一個`TermsAggregationBuilder`,`TermsAggregationBuilder`我們指定了`name`叫做`category`,這個`name`對應著上一節中的那個自定義的名稱,大家還有印象嗎?
* 後面的`field`是我們要聚合的欄位,注意這裡因為`category`欄位是`text`型別,預設是不能夠做聚合查詢的,我們指定的是`category.keyword`,還記得這個`keyword`型別嗎?它是不使用分詞器的,我們使用這個`keyword`型別是可以的。
* 最後把`AggregationBuilder`封裝到查詢請求中,進行查詢。
* 查詢後,我們怎麼去取這個`aggregation`呢?取查詢結果我們是通過`hits`,取聚合查詢,我們要使用`aggregation`了,然後再get我們的自定義名稱`response.getAggregations().get("category")`。至於前面的型別,它是和`AggregationBuilder`對應的,在咱們的例子中使用的是`TermsAggregationBuilder`,那麼我們在取結果時就要用`Terms`;如果查詢時使用的是`AvgAggregationBuilder`,取結果時就要用`Avg`。
* 在取得`Terms`後,我們可以獲取裡邊的值了。執行一下,看看結果。
```json
體育
1
```
`key`是體育,`doc_count`是1,說明分類`體育`的資料只有1條。完全符合我們的預期,這個聚合查詢的功能非常重要,在電商平臺中,商品搜尋頁通常列出所有的商品類目,並且每個類目後面都有這個商品的數量,這個功能就是基於聚合查詢實現的。
好了,到這裡,ES已經結合到我們的SpringBoot專案中了,並且最基礎的功能也已經實現了,大家放心的