ES7學習筆記(十一)與SpringBoot結合

在前面的章節中,我們把ES的基本功能都給大家介紹完了,從ES的搭建、創建索引、分詞器、到數據的查詢,大家發現,我們都是通過ES的API去進行調用,那麼,我們在項目當中怎麼去使用ES呢?這一節,我們就看看ES如何與我們的SpringBoot項目結合。

版本依賴

SpringBoot默認是有ElasticSearch的Starter,但是它依賴的ES客戶端的版本比較低,跟不上ES的更新速度,所以我們在SpringBoot項目中要指定ES的最新版本,如下:

<properties>
    <elasticsearch.version>7.6.1</elasticsearch.version>
</properties>

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

我們在項目中指定ES客戶端的版本爲7.6.1。

配置文件

然後我們在SpringBoot的配置文件application.properties當中,配置ES集羣的地址,如下:

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索引中有哪些字段,

在索引中只有3個字段,id、title和desc。接下來我們在創建索引ik_index對應的實體類,內容也很簡單,具體如下:

@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索引中添加一條新的數據,如下:

@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當中,最後我們調用clientindex方法完成數據的插入。我們看看執行結果吧。

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插件查詢一下,結果如下:

數據插入成功,並且新添加的字段category也有了對應的值,這是我們期望的結果。下面我們再看看查詢怎麼使用。代碼如下:

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,再構建我們的查詢請求QueryBuilderQueryBuilder是一個接口,它的實現類有很多,對應着ES中的不同種類的查詢,比如咱們前面介紹的boolboosting查詢,都有對應的實現類。在這裏,咱們使用MatchQueryBuilder並查詢desc包含香蕉好吃的數據,這個查詢咱們在前面通過API的方式也查詢過。
  • 最後我們封裝好請求,並通過client.search方法進行查詢,返回的結構是SearchResponse
  • 在返回的結果中,我們獲取對應的數據,咦?這個爲什麼調用了兩次Hits方法?咱們可以從API的返回值看出端倪,如下:

  • 我們可以看到返回的結果中確實有兩個hits,第一個hits中包含了數據的條數,第二個hits中才是我們想要的查詢結果,所以在程序中,我們調用了兩次hits。

  • 在每一個hit當中,我們調用getSourceAsString方法,獲取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中包含好吃的都被查詢了出來,而我們新添加的足球數據並沒有查詢出來,這也是符合預期的。我們再來看看聚合查詢怎麼用,

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,然後再創建一個TermsAggregationBuilderTermsAggregationBuilder我們指定了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後,我們可以獲取裏邊的值了。運行一下,看看結果。
體育
1

key是體育,doc_count是1,說明分類體育的數據只有1條。完全符合我們的預期,這個聚合查詢的功能非常重要,在電商平臺中,商品搜索頁通常列出所有的商品類目,並且每個類目後面都有這個商品的數量,這個功能就是基於聚合查詢實現的。

好了,到這裏,ES已經結合到我們的SpringBoot項目中了,並且最基礎的功能也已經實現了,大家放心的使用吧~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章