(一)介紹在大多數系統中,都需要支援搜尋的功能,以簡單部落格系統為例,雖然說Mysql也可以通過模糊查詢匹配到對應的資料,但是效率實在太低。這個時候就需要拿出分散式搜尋引擎Elast
2021-06-01 23:54:11
在大多數系統中,都需要支援搜尋的功能,以簡單部落格系統為例,雖然說Mysql也可以通過模糊查詢匹配到對應的資料,但是效率實在太低。這個時候就需要拿出分散式搜尋引擎ElasticSearch了。本部落格重點在於ES的整合使用,因此前端採用最簡單的方式呈現,大家只需要關注後端邏輯即可。(本部落格基於ES7.6.1,和ES6.X版本有較大差異)
依賴主要就是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>
編寫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; }}
編寫一個類用來儲存要儲存的資料,我這裡為了演示只在es中插入標題和作者的資訊
@Data@AllArgsConstructorpublic class BlogDO { private String title; private String author;}
最後新建一個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還有許多值得學的地方,任重而道遠。我是魚仔,我們下期再見!
相關文章
(一)介紹在大多數系統中,都需要支援搜尋的功能,以簡單部落格系統為例,雖然說Mysql也可以通過模糊查詢匹配到對應的資料,但是效率實在太低。這個時候就需要拿出分散式搜尋引擎Elast
2021-06-01 23:54:11
相信前幾次的開發者內測申請活動中,已經有不少小夥伴順利升級了鴻蒙系統,當然開發者內測資格稽核比較嚴格,需要提交一大堆資訊不說,還需要驗證申請人的開發者身份。所以,絕大多數
2021-06-01 23:54:00
黑貓好物|618電商京東618電商節首日銷量榜已經公佈,iPhone 12毫無懸念拿下第一,紅米Note10 Pro憑藉67W快充+天璣1100拿下第三,第二名誰都沒想到,居然是搭載驍龍750G的realme Q3。
2021-06-01 23:53:49
安全更新旨在確保Android裝置儘可能的安全,一加承諾會對其智慧手機進行至少兩年的定期更新,那麼來關注下2021年5月的安全更新。#OnePlus#OnePlus 2021年5月安全更新-有什麼新
2021-06-01 23:53:37
雖然還未到6.18,但作為開門紅的6.1,各大手機廠商的戰報已然出爐。根據我所知道的各大電商平臺以及手機廠商公佈的戰報來看,realme的「黑馬」之姿已成。如果說去年的realme還只
2021-06-01 23:53:27
說起安卓平板電腦,華為的相關產品可以說是有口皆碑,其在生態的建設上面做得相當不錯,不過因為晶片短缺等問題,導致華為的平板電腦也出現了緊缺,而今天有博主曝光了新款華為MatePa
2021-06-01 23:53:18