首頁 > 科技

如何在自己的項目中引入ElasticSearch搜尋引擎?

2021-06-01 23:54:11

(一)介紹

在大多數系統中,都需要支援搜尋的功能,以簡單部落格系統為例,雖然說Mysql也可以通過模糊查詢匹配到對應的資料,但是效率實在太低。這個時候就需要拿出分散式搜尋引擎ElasticSearch了。本部落格重點在於ES的整合使用,因此前端採用最簡單的方式呈現,大家只需要關注後端邏輯即可。(本部落格基於ES7.6.1,和ES6.X版本有較大差異)

(二)項目搭建

2.1 依賴引入

依賴主要就是web、es以及thymleaf相關:

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></dependency><dependency>    <groupId>org.projectlombok</groupId>    <artifactId>lombok</artifactId>    <optional>true</optional></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope></dependency><dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson</artifactId>    <version>1.2.76</version></dependency><!--thymleaf相關--><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency>    <groupId>org.thymeleaf</groupId>    <artifactId>thymeleaf-spring5</artifactId></dependency><dependency>    <groupId>org.thymeleaf.extras</groupId>    <artifactId>thymeleaf-extras-java8time</artifactId></dependency>

2.2 編寫ES的配置類

編寫ES的配置類,編寫連線資訊,之後直接通過Autowired連線即可:

@Configurationpublic class ElasticSearchConfig {        @Bean    public RestHighLevelClient restHighLevelClient(){        RestHighLevelClient client=new RestHighLevelClient(            RestClient.builder(new HttpHost("192.168.78.128",9200,"http"))        );                return client;    }}

2.3 編寫Blog實體類

編寫一個類用來儲存要儲存的資料,我這裡為了演示只在es中插入標題和作者的資訊

@Data@AllArgsConstructorpublic class BlogDO {        private String title;        private String author;}

2.4 準備controller和service

最後新建一個IndexController和IndexService以及IndexServiceImpl,接下來會使用。最終的目錄結構如下:

(三)資料準備

要做資料的搜尋,首先第一步就是資料的匯入。在真實的業務場景中,資料的匯入有很多方式。一種是當新增資料時在程式碼邏輯中做增量的匯入操作,或者是由數倉團隊負責資料的增量匯入。我接觸到的業務中,後端程式設計師不需要去關注匯入的操作,這個步驟是數倉團隊做的。

在我們個人的部落格系統中,可以在新增部落格後立刻同步資料到ES,也可以先通過訊息中介軟體傳送一條訊息,消費者定期去讀取訊息新增資料。

這裡演示就直接匯入了:

@Controllerpublic class IndexController {        @Autowired    private IndexService indexService;            @ResponseBody    @GetMapping("/prepareData")    public String prepareData(){        String result=indexService.prepareData();               return result;    }}

具體的service實現如下:

@Servicepublic class IndexServiceImpl implements IndexService {    @Autowired        private RestHighLevelClient restHighLevelClient;        @Override        public String prepareData() {        List<BlogDO> blogDOS = new ArrayList<>();        blogDOS.add(new BlogDO("ElasticSearch究竟是個什麼東西", "Java魚仔"));        blogDOS.add(new BlogDO("SpringBoot+SpringSecurity實現基於真實資料的授權認證", "Java魚仔"));        blogDOS.add(new BlogDO("Dubbo兩小時快速上手教程(直接程式碼、Spring、SpringBoot)", "Java魚仔"));        blogDOS.add(new BlogDO("淺析五種最常用的Java加密演算法", "Java魚仔"));        blogDOS.add(new BlogDO("Java程式設計師需要知道的作業系統知識彙總", "Java魚仔"));        blogDOS.add(new BlogDO("一步步教你如何在SpringBoot項目中引入支付功能", "Java魚仔"));        blogDOS.add(new BlogDO("Zookeeper實現分散式鎖的原理是什麼?", "Java魚仔"));        blogDOS.add(new BlogDO("一個成熟的Java項目如何優雅地處理異常", "Java魚仔"));        blogDOS.add(new BlogDO("基於SpringBoot實現檔案的上傳下載", "Java魚仔"));        blogDOS.add(new BlogDO("如何用Java寫一個規範的http介面?", "Java魚仔"));        BulkRequest bulkRequest = new BulkRequest();        bulkRequest.timeout("10s");        blogDOS.stream().forEach(x -> {            bulkRequest.add(new IndexRequest("blog_index").source(JSON.toJSONString(x), XContentType.JSON));        });        BulkResponse responses=null;                try {            responses = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);        } catch (IOException e) {            e.printStackTrace();        }                        return String.valueOf(responses.status());    }}

我選取了自己的幾篇部落格文章,多執行幾次介面,保證ES中有幾十條資料供測試使用即可。

(四)部落格搜尋

接下來就是搜尋的過程了,搜尋的邏輯其實比較簡單,具體的程式碼就按照上一篇部落格中的方式來編寫,在真實業務場景中,每個公司可能會有自己的封裝搜尋方法:

IndexController中增加一個方法:

@GetMapping("/search")public String search(@RequestParam("keywords")String keywords,                      @RequestParam("pageNum")String pageNum,                      @RequestParam("pageSize")String pageSize,                      Model model){    List<Map<String,Object>> list=indexService.searchByKeywords(keywords,pageNum,pageSize);    model.addAttribute("datas",list);        return "search";}

具體實現類中增加方法:

@Overridepublic List<Map<String, Object>> searchByKeywords(String keywords, String pageNum, String pageSize) {        return this.searchData(keywords,Integer.parseInt(pageNum),Integer.parseInt(pageSize));}public List<Map<String,Object>> searchData(String keywords, int pageNum, int pageSize){        if (pageNum<1){        pageNum=1;    }        //生成搜尋物件    SearchRequest request = new SearchRequest("blog_index");    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();        //設定分頁參數    searchSourceBuilder.from(pageNum);    searchSourceBuilder.size(pageSize);        //設定搜尋的欄位    MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", keywords);    searchSourceBuilder.query(matchQueryBuilder);    searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));    request.source(searchSourceBuilder);    SearchResponse search=null;        try {        search = restHighLevelClient.search(request, RequestOptions.DEFAULT);    } catch (IOException e) {        e.printStackTrace();    }        //將結果返回    List<Map<String,Object>> result=new ArrayList();    SearchHit[] hits = search.getHits().getHits();        for (SearchHit searchHit:hits){        result.add(searchHit.getSourceAsMap());    }            return result;}

簡單寫一個前端頁面

<!DOCTYPE html><!--引入thymeleaf--><html xmlns:th="     <head>        <meta charset="UTF-8">        <title>Title</title>    </head>        <body>        <div>            <div th:each="datas:${datas}">                <span th:text="${datas.author}"/>                <span th:utext="${datas.title}"/>                <hr/>            </div>        </div>    </body></html>

跑起來看一下,訪問

http://localhost:8080/search?keywords=Java&pageNum=1&pageSize=10

在連結中,我關鍵詞填了Java,pageNum是1,每頁展示10行,可以看到和Java相關的資料就被查出來了。

(五)實現高亮查詢

在百度搜索Java時,可以看到查詢出來的Java被高亮顯示了,之前在講ES語法的時候,我們也知道了ES支援高亮查詢,下面就通過程式碼來實現。

稍微修改一下搜尋的程式碼,增加高亮配置,在返回值中用高亮字元串替換原來的字元串。

public List<Map<String,Object>> searchHighLightData(String keywords, int pageNum, int pageSize){        if (pageNum<1){        pageNum=1;    }    SearchRequest request = new SearchRequest("blog_index");    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();    searchSourceBuilder.from(pageNum);    searchSourceBuilder.size(pageSize);            //高亮構造器    HighlightBuilder highlightBuilder=new HighlightBuilder();        //高亮查詢欄位    highlightBuilder.field("title");        //是否將所有匹配到的欄位高亮顯示,false表示只顯示一個    highlightBuilder.requireFieldMatch(false);        //高亮的標籤    highlightBuilder.preTags("<span style='color:red'>");    highlightBuilder.postTags("</span>");    searchSourceBuilder.highlighter(highlightBuilder);        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", keywords);    searchSourceBuilder.query(matchQueryBuilder);    searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));    request.source(searchSourceBuilder);    SearchResponse search=null;    try {        search = restHighLevelClient.search(request, RequestOptions.DEFAULT);    } catch (IOException e) {        e.printStackTrace();    }        List<Map<String,Object>> result=new ArrayList();    SearchHit[] hits = search.getHits().getHits();        //遍歷結果,將高亮返回值title替換到原來的title中    for (SearchHit searchHit:hits){                Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();        HighlightField title = highlightFields.get("title");                if (title!=null){            StringBuilder highLightTitle=new StringBuilder();            Text[] texts = title.fragments();                        for(Text text:texts){                highLightTitle.append(text);            }            sourceAsMap.put("title",highLightTitle);        }        result.add(sourceAsMap);    }            return result;}

繼續訪問

http://localhost:8080/search?keywords=Java&pageNum=1&pageSize=10,

通過斷點可以看到,搜尋的關鍵詞已經被我們設定的span標籤包住了。

在前端thymeaf中,我是用了th:utext,這個標籤可以將Html解析,最終的高亮顯示如下:

(六)總結

ES的應用到這裡就結束了,ES可以很方便地嵌入到真實的項目中,對於應用來講,瞭解到這一步已經足夠,對於想要提高的人來說,還遠遠不夠。作為最流行的分散式搜尋引擎,ES還有許多值得學的地方,任重而道遠。我是魚仔,我們下期再見!

想了解更多精彩內容,快來關注計算機java程式設計


IT145.com E-mail:sddin#qq.com