SpringBoot集成ES-6.8


Elasticsearch客戶端

客戶端介紹

在elasticsearch官網中提供了各種語言的客戶端(也就是用來連接ES,用來操作ES的)

官方地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html

我們接下來要學習的是JavaRestClient的客戶端。注意點擊進入后,選擇版本到6.8,和我們的es版本對應

  • Low Level Rest Client是低級別封裝,提供一些基礎功能,但更靈活
  • High Level Rest Client,是在Low Level Rest Client基礎上進行的高級別封裝,功能更豐富和完善,而且API會變的簡單

創建Demo工程

初始化項目

創建springboot es項目

修改pom.xml

<!-- fastjson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.58</version>
</dependency>
<!-- High-level-Rest-Client-->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>6.8.3</version>
</dependency>
<!-- es 依賴 -->
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>6.8.3</version>
</dependency>

索引庫及映射

創建索引庫的同時,我們也會創建type及其映射關系,但是這些操作不建議使用Java客戶端完成,原因如下

  • 索引庫和映射往往是初始化時完成,不需要頻繁操作,不如提前配置好
  • 官方提供的創建索引庫及映射API非常繁瑣,需要通過字符串拼接Json結構

因此,這些操作建議還是使用Rest風格API去實現,我們接下來以這樣一個商品數據為例來創建索引庫

/**
 * @author BNTang
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    
    private Long id;
    
    /**
     * 標題
     */
    private String title;
    
    /**
     * 分類
     */
    private String category;
    
    /**
     * 品牌
     */
    private String brand;
    
    /**
     * 價格
     */
    private Double price;
    
    /**
     * 圖片地址
     */
    private String images;
   
}

分析一下數據結構

  • id:可以認為是主鍵,將來判斷數據是否重復的標示,不分詞,可以使用keyword類型
  • title:商品標題,搜索字段,需要分詞,可以用text類型
  • category:商品分類,這個是整體,不分詞,可以使用keyword類型
  • brand:品牌,與分類類似,不分詞,可以使用keyword類型
  • price:價格,這個是double類型
  • images:圖片,用來展示的字段,不搜索,index為false,不分詞,可以使用keyword類型

我們可以編寫這樣的映射配置

PUT /bntang
{
    "mappings": {
        "product": {
            "properties": {
                "id": {
                    "type": "keyword"
                },
                "title": {
                    "type": "text",
                    "analyzer": "ik_max_word"
                },
                "category": {
                    "type": "keyword"
                },
                "brand": {
                    "type": "keyword"
                },
                "images": {
                    "type": "keyword",
                    "index":  false
                },
                "price": {
                    "type": "double"
                }
            }
        }
    }
}

文檔操作

有了索引庫,我們接下來看看如何新增索引數據

配置客戶端

@Configuration
public class ESConfig {
    @Bean
    public RestHighLevelClient restHighLevelClient(){
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("127.0.0.1", 9200, "http")));
    }
}

新增文檔

接下來我們看看如何通過代碼往es服務器新增一個文檔。新增時,如果傳遞的id是已經存在的,則會完成修改操作,如果不存在,則是新增

  • 准備需要保存到索引庫的Json文檔數據
  • 創建IndexRequest請求對象,指定索引庫、類型、id(可選)
  • 調用source方法將請求數據封裝到IndexRequest請求對象中
  • 調用方法進行數據通信
  • 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {
    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;
    /**
     * 新增文檔
     */
    @Test
    public void addDoc() {
        // 1.准備需要保存到索引庫的Json文檔數據
        Product product = new Product(1L, "小米手機", "手機", "小米", 2899.00, "http://www.baidu.com");

        // 2.將對象轉為Json字符串
        String jsonString = JSON.toJSONString(product);

        // 3.創建請求對象,指定索引庫、類型、id(可選)
        IndexRequest indexRequest = new IndexRequest("bntang", "product", "1");

        // 4.調用source方法將請求數據封裝到IndexRequest請求對象中
        indexRequest.source(jsonString, XContentType.JSON);

        try {
            // 5.調用方法進行數據通信
            IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
            // 6.解析輸出結果
            System.out.println("結果:" + JSON.toJSONString(indexResponse));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

查看文檔

剛剛我們保存了一條數據進去,接下來我們根據REST風格,查看應該是根據id進行get查詢,難點是對結果的解析

相關類

  • GetRquest:封裝get請求參數
  • GetResponse:封裝get數據響應
  1. 構建GetRequest請求對象,指定索引庫、類型、id
  2. 調用方法進行數據通信
  3. 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 根據id查看文檔
     */
    @Test
    public void getDocById() {
        // 1.構建GetRequest請求對象,指定索引庫、類型、id
        GetRequest getRequest = new GetRequest("bntang", "product", "1");

        try {
            // 2.調用方法進行數據通信
            GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
            // 3.解析輸出結果
            System.out.println("結果:" + JSON.toJSONString(getResponse));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

批量新增

當需要新增的數據較多時,單個新增比較耗費性能,所以這時候批量新增功能就比較好用了

  1. 構建批量新增BulkRequest請求對象
  2. 准備需要保存到索引庫的Json文檔數據封裝到IndexRequest請求對象中
  3. 添加IndexRequest請求對象至批量新增BulkRequest請求對象
  4. 調用方法進行數據通信
@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 批量新增文檔
     */
    @Test
    public void bulkAddDoc() {
        // 1.構建批量新增BulkRequest請求對象
        BulkRequest bulkRequest = new BulkRequest();

        // 2.准備需要保存到索引庫的Json文檔數據封裝到IndexRequest請求對象中
        for (long i = 2; i < 9; i++) {
            // 2.1准備需要保存到索引庫的Json文檔數據
            Product product = new Product(i, "小米手機" + i, "手機", "小米", 2899.00 + i, "http://www.baidu.com");
            String jsonString = JSON.toJSONString(product);
            // 2.2創建請求對象,指定索引庫、類型、id(可選)
            IndexRequest indexRequest = new IndexRequest("bntang", "product", "" + i);
            // 2.3將請求數據封裝到IndexRequest請求對象中
            indexRequest.source(jsonString, XContentType.JSON);
            // 3.添加IndexRequest請求對象至批量新增BulkRequest請求對象
            bulkRequest.add(indexRequest);
        }

        try {
            // 4.調用方法進行數據通信
            client.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

通過Kibana查詢所有

關鍵點

  • BulkRequest:批量請求,可以添加多個IndexRequest對象,完成批處理

修改文檔

restAPI只提供了按文檔id進行修改的操作

  1. 構建修改請求對象,指定索引庫、類型、id
  2. 准備需要修改的Json文檔數據
  3. 將需要修改的Json文檔數據封裝到UpdateRequest請求對象中
  4. 調用方法進行數據通信
  5. 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 根據id修改文檔
     */
    @Test
    public void bulkAddDoc() {
        // 1.構建修改請求對象,指定索引庫、類型、id
        UpdateRequest updateRequest = new UpdateRequest("bntang", "product", "1");

        // 2.准備需要修改的Json文檔數據
        Product product = new Product(1L, "大米手機", "手機", "大米", 2899.00, "http://www.baidu.com");
        String jsonString = JSON.toJSONString(product);

        // 3.將需要修改的Json文檔數據封裝到UpdateRequest請求對象中
        updateRequest.doc(jsonString, XContentType.JSON);

        try {
            // 4.調用方法進行數據通信
            UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
            // 5.解析輸出結果
            System.out.println("結果:" + JSON.toJSONString(updateResponse));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

刪除文檔

resetApi只提供了根據id刪除

  1. 構建刪除請求對象,指定索引庫、類型、id
  2. 調用方法進行數據通信
  3. 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 根據id刪除文檔
     */
    @Test
    public void delDocById() {
        try{
            // 1.構建刪除請求對象,指定索引庫、類型、id
            DeleteRequest deleteRequest = new DeleteRequest("bntang", "product", "2");

            // 2.調用方法進行數據通信
            DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);

            // 3.解析輸出結果
            System.out.println("結果:" + JSON.toJSONString(deleteResponse));
        }catch (IOException e){
            e.printStackTrace();
        }
    }

}

搜索數據

關鍵字搜索match

  • SearchRequest:封裝搜索請求
  • SearchSourceBuilder:指定查詢類型、排序、高亮等,后面幾乎所有的操作都需要該類參與
  • QueryBuilders:用來構建各種查詢類型和查詢條件

步驟

  1. 構建SearchRequest請求對象,指定索引庫
  2. 構建SearchSourceBuilder查詢對象
  3. 構建QueryBuilder對象指定查詢方式和查詢條件
  4. 將QueryBuilder對象設置到SearchSourceBuilder中
  5. 將SearchSourceBuilder查詢對象封裝到請求對象SearchRequest中
  6. 調用方法進行數據通信
  7. 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 詞匹配搜索
     */
    @Test
    public void matchDoc() {
        // 1.構建SearchRequest請求對象,指定索引庫
        SearchRequest searchRequest = new SearchRequest("bntang");
        
        // 2.構建SearchSourceBuilder查詢對象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 3.構建QueryBuilder對象指定查詢方式和查詢條件
        QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "大米");

        // 4.將QueryBuilder對象設置到SearchSourceBuilder中
        sourceBuilder.query(queryBuilder);

        // 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
        searchRequest.source(sourceBuilder);

        try {
            // 6.調用方法進行數據通信
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 7.解析輸出結果
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                System.out.println("結果: " + sourceAsString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

注意,上面的代碼中,搜索條件是通過sourceBuilder.query(queryBuilder)來添加的。這個query()方法接受的參數是:QueryBuilder接口類型

這個接口提供了很多實現類,分別對應我們在之前不同類型的查詢,例如:term查詢、match查詢、range查詢、boolean查詢等,我們如果要使用各種不同查詢,其實僅僅是傳遞給sourceBuilder.query()方法的參數不同而已。而這些實現類不需要我們去new,官方提供了QueryBuilders工廠幫我們構建各種實現類

查詢所有match_all

  1. 構建SearchRequest請求對象,指定索引庫
  2. 構建SearchSourceBuilder查詢對象
  3. 構建QueryBuilder對象指定查詢方式
  4. 將QueryBuilder對象設置到SearchSourceBuilder中
  5. 將SearchSourceBuilder查詢對象封裝到請求對象SearchRequest中
  6. 調用方法進行數據通信
  7. 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 查詢所有
     */
    @Test
    public void matchDoc() {
        // 1.構建SearchRequest請求對象,指定索引庫
        SearchRequest searchRequest = new SearchRequest("bntang");
        
        // 2.構建SearchSourceBuilder查詢對象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 3.構建QueryBuilder對象指定查詢方式和查詢條件
        QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();

        // 4.將QueryBuilder對象設置到SearchSourceBuilder中
        sourceBuilder.query(queryBuilder);

        // 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
        searchRequest.source(sourceBuilder);

        try {
            // 6.調用方法進行數據通信
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 7.解析輸出結果
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                System.out.println("結果: " + sourceAsString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

其實搜索類型的變化,僅僅是利用QueryBuilders構建的查詢對象不同而已,其他代碼基本一致。因此,我們可以把這段代碼封裝,然后把查詢條件作為參數傳遞

@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 公共查詢方法
     */
    @Test
    public void commonSearch(QueryBuilder queryBuilder) {
        // 1.構建SearchRequest請求對象,指定索引庫
        SearchRequest searchRequest = new SearchRequest("bntang");
        
        // 2.構建SearchSourceBuilder查詢對象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 4.將QueryBuilder對象設置到SearchSourceBuilder中
        sourceBuilder.query(queryBuilder);

        // 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
        searchRequest.source(sourceBuilder);

        try {
            // 6.調用方法進行數據通信
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 7.解析輸出結果
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                System.out.println("結果: " + sourceAsString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

source過濾

默認情況下,索引庫中所有字段都會返回,如果我們想只返回部分字段,可以通過source filter來控制

@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;


    /**
     * source過濾查詢,過濾掉不需要的字段
     */
    @Test
    public void testSourceSearch(){
        commonSearch(QueryBuilders.matchAllQuery());
    }

    /**
     * 公共查詢方法
     */
    @Test
    public void commonSearch(QueryBuilder queryBuilder) {
        // 1.構建SearchRequest請求對象,指定索引庫
        SearchRequest searchRequest = new SearchRequest("bntang");
        
        // 2.構建SearchSourceBuilder查詢對象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 4.將QueryBuilder對象設置到SearchSourceBuilder中
        sourceBuilder.query(queryBuilder);

        // 使用fetchSource實現過濾
        sourceBuilder.fetchSource(new String[]{"id", "title"}, null);

        // 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
        searchRequest.source(sourceBuilder);

        try {
            // 6.調用方法進行數據通信
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 7.解析輸出結果
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                System.out.println("結果: " + sourceAsString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

排序

依然是通過sourceBuilder來配置

@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;


    /**
     * source過濾查詢,過濾掉不需要的字段
     */
    @Test
    public void testSourceSearch(){
        commonSearch(QueryBuilders.matchAllQuery());
    }

    /**
     * 公共查詢方法
     */
    @Test
    public void commonSearch(QueryBuilder queryBuilder) {
        // 1.構建SearchRequest請求對象,指定索引庫
        SearchRequest searchRequest = new SearchRequest("bntang");
        
        // 2.構建SearchSourceBuilder查詢對象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 4.將QueryBuilder對象設置到SearchSourceBuilder中
        sourceBuilder.query(queryBuilder);

        /*
         * 通過sort方法指定排序規則
         * 第一個參數:排序字段
         * 第二個參數:升序還是降序(可以不填寫,默認升序)
         */
        sourceBuilder.sort("id", SortOrder.DESC);
        sourceBuilder.sort("price",SortOrder.DESC);

        // 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
        searchRequest.source(sourceBuilder);

        try {
            // 6.調用方法進行數據通信
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 7.解析輸出結果
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                System.out.println("結果: " + sourceAsString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

備注:默認不能使用text類型的字段進行排序。不然會報錯

官網說明

解決方法:將需要進行排序的text類型的字段,設置fielddata=true即可

分頁

  • from:當前頁起始索引, int from= (pageNum - 1) * size
  • size:每頁顯示多少條
@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;


    /**
     * source過濾查詢,過濾掉不需要的字段
     */
    @Test
    public void testSourceSearch(){
        commonSearch(QueryBuilders.matchAllQuery());
    }

    /**
     * 公共查詢方法
     */
    @Test
    public void commonSearch(QueryBuilder queryBuilder) {
        // 1.構建SearchRequest請求對象,指定索引庫
        SearchRequest searchRequest = new SearchRequest("bntang");
        
        // 2.構建SearchSourceBuilder查詢對象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 4.將QueryBuilder對象設置到SearchSourceBuilder中
        sourceBuilder.query(queryBuilder);

        /*
         * 通過sort方法指定排序規則
         * 第一個參數:排序字段
         * 第二個參數:升序還是降序(可以不填寫,默認升序)
         */
        sourceBuilder.sort("id", SortOrder.DESC);
        sourceBuilder.sort("price",SortOrder.DESC);

        /*
            from:當前頁起始索引   int from = (pageNum - 1) * size;
            size:每頁顯示多少條
         */
        sourceBuilder.from(0);
        sourceBuilder.size(4);

        // 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
        searchRequest.source(sourceBuilder);

        try {
            // 6.調用方法進行數據通信
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 7.解析輸出結果
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                System.out.println("結果: " + sourceAsString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

高亮

高亮就是對匹配的內容里的關鍵詞通過html+css進行加顏色處理顯示

  1. 構建HighlightBuilder高亮對象
  2. 設置要高亮的字段
  3. 設置高亮樣式
  4. 高亮對象highlightBuilder設置到sourceBuilder中
@SpringBootTest
class EsJavaClientApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 高亮查詢
     */
    @Test
    public void highLightMatch(){
        // 1.構建QueryBuilder對象指定查詢方式
        QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "小米");

        // 2.構建SearchRequest請求對象,指定索引庫
        SearchRequest searchRequest = new SearchRequest("bntang");

        // 3.構建SearchSourceBuilder查詢對象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 4.將QueryBuilder對象設置到SearchSourceBuilder中
        sourceBuilder.query(queryBuilder);

        // 5構建HighlightBuilder高亮對象
        HighlightBuilder highlightBuilder = new HighlightBuilder();

        // 5.1設置要高亮的字段
        highlightBuilder.field("title");

        // 5.2設置高亮樣式
        highlightBuilder.preTags("<font color='pink' >");
        highlightBuilder.postTags("</font>");

        // 6.將高亮對象highlightBuilder設置到sourceBuilder中
        sourceBuilder.highlighter(highlightBuilder);

        // 7.將SearchSourceBuilder查詢對象封裝到請求對象SearchRequest中
        searchRequest.source(sourceBuilder);
        try {
            // 8.調用方法進行數據通信
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 9.解析輸出結果
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                System.out.println("結果:" + sourceAsString);
                // 10.獲取高亮結果
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField title = highlightFields.get("title");
                Text[] fragments = title.getFragments();
                for (Text fragment : fragments) {
                    System.err.println("高亮結果: " + fragment.toString());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

關鍵代碼

  • 查詢條件中添加高亮字段

  • new HighlightBuilder():創建高亮構建器
  • .field("title"):指定高亮字段
  • .preTags("").postTags(""):指定高亮的前置和后置標簽

解析高亮結果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM