您好,登錄后才能下訂單哦!
本篇內容介紹了“什么是Elasticsearch組件庫”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在講解之前,我在這里再次提下全文檢索是什么:
全文檢索: 指以全部文本信息作為檢索對象的一種信息檢索技術。而我們使用的數據庫,如 Mysql,MongoDB 對文本信息檢索能力特別是中文檢索并沒有 ES 強大。所以我們來看下 ES 在項目中是如何來代替 SQL 來工作的。
我使用的 Elasticsearch 服務是 7.4.2 的版本,然后采用官方提供的 Elastiscsearch-Rest-Client 庫來操作 ES,而且官方庫的 API 上手簡單。
該組件庫的官方文檔地址:
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html
另外這個組件庫是支持多種語言的:
支持多語言
注意:Elasticsearch Clients
就是指如何用 API 操作 ES 服務的組件庫。
可能有同學會提問,Elasticsearch 的組件庫中寫著 JavaScript API,是不是可以直接在前端訪問 ES 服務?可以是可以,但是會暴露 ES 服務的端口和 IP 地址,會非常不安全。所以我們還是用后端服務來訪問 ES 服務。
我們這個項目是 Java 項目,自然就是用上面的兩種:Java Rest Client
或者 Java API
。我們先看下 Java API,但是會發現已經廢棄了。如下圖所示:
Java API 已經廢棄了
所以我們只能用 Java REST Client 了。而它又分成兩種:高級和低級的。高級包含更多的功能,如果把高級比作MyBatis的話,那么低級就相當于JDBC。所以我們用高級的 Client。
高級和低級 Client
我們把檢索服務單獨作為一個服務。就稱作 passjava-search 模塊吧。
創建 passjava-search 模塊。
首先我們在 PassJava-Platform 模塊創建一個 搜索服務模塊 passjava-search。然后勾選 spring web 服務。如下圖所示。
第一步:選擇 Spring Initializr,然后點擊 Next。
選擇 Spring Initializr
第二步:填寫模塊信息,然后點擊 Next。
passjava-search 服務模塊
第三步:選擇 Web->Spring Web 依賴,然后點擊 Next。
參照 ES 官網配置。
進入到 ES 官方網站,可以看到有低級和高級的 Rest Client,我們選擇高階的(High Level Rest Client)。然后進入到高階 Rest Client 的 Maven 倉庫。官網地址如下所示:
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.9/index.html
Rest Client 官方文檔
加上 Maven 依賴。
對應文件路徑:\passjava-search\pom.xml
<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.4.2</version></dependency>
配置 elasticsearch 的版本為7.4.2
因加上 Maven 依賴后,elasticsearch 版本為 7.6.2,所以遇到這種版本不一致的情況時,需要手動改掉。
對應文件路徑:\passjava-search\pom.xml
<properties> <elasticsearch.version>7.4.2</elasticsearch.version> </properties>
刷新 Maven Project 后,可以看到引入的 elasticsearch 都是 7.4.2 版本了,如下圖所示:
設置版本為 7.4.2
引入 PassJava 的 Common 模塊依賴。
Common 模塊是 PassJava 項目獨立的出來的公共模塊,引入了很多公共組件依賴,其他模塊引入 Common 模塊依賴后,就不需要單獨引入這些公共組件了,非常方便。
對應文件路徑:\passjava-search\pom.xml
<dependency> <groupId>com.jackson0714.passjava</groupId> <artifactId>passjava-common</artifactId> <version>0.0.1-SNAPSHOT</version></dependency>
添加完依賴后,我們就可以將搜索服務注冊到 Nacos
注冊中心了。Nacos 注冊中心的用法在前面幾篇文章中也詳細講解過,這里需要注意的是要先啟動 Nacos 注冊中心,才能正常注冊 passjava-search 服務。
修改配置文件:src/main/resources/application.properties。配置應用程序名、注冊中心地址、注冊中心的命名中間。
spring.application.name=passjava-search spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.namespace=passjava-search
給啟動類
添加服務發現注解:@EnableDiscoveryClient
。這樣 passjava-search 服務就可以被注冊中心發現了。
因 Common 模塊依賴數據源,但 search 模塊不依賴數據源,所以 search 模塊需要移除數據源依賴:
exclude = DataSourceAutoConfiguration.class
以上的兩個注解如下所示:
@EnableDiscoveryClient@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)public class PassjavaSearchApplication { public static void main(String[] args) { SpringApplication.run(PassjavaSearchApplication.class, args); } }
接下來我們添加一個 ES 服務的專屬配置類,主要目的是自動加載一個 ES Client 來供后續 ES API 使用,不用每次都 new 一個 ES Client。
配置類:PassJavaElasticsearchConfig.java
核心方法就是 RestClient.builder 方法,設置好 ES 服務的 IP 地址、端口號、傳輸協議就可以了。最后自動加載了 RestHighLevelClient。
package com.jackson0714.passjava.search.config;import org.apache.http.HttpHost;import org.elasticsearch.client.RestClient;import org.elasticsearch.client.RestHighLevelClient;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * @Author: 公眾號 | 悟空聊架構 * @Date: 2020/10/8 17:02 * @Site: www.passjava.cn * @Github: https://github.com/Jackson0714/PassJava-Platform */@Configurationpublic class PassJavaElasticsearchConfig { @Bean // 給容器注冊一個 RestHighLevelClient,用來操作 ES // 參考官方文檔:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.9/java-rest-high-getting-started-initialization.html public RestHighLevelClient restHighLevelClient() { return new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.56.10", 9200, "http"))); } }
接下來我們測試下 ES Client 是否自動加載成功。
在測試類 PassjavaSearchApplicationTests 中編寫測試方法,打印出自動加載的 ES Client。期望結果是一個 RestHighLevelClient 對象。
package com.jackson0714.passjava.search;import org.elasticsearch.client.RestHighLevelClient;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTestclass PassjavaSearchApplicationTests { @Qualifier("restHighLevelClient") @Autowired private RestHighLevelClient client; @Test public void contextLoads() { System.out.println(client); } }
運行結果如下所示,打印出了 RestHighLevelClient。說明自定義的 ES Client 自動裝載成功。
ES 測試結果
測試方法 testIndexData,省略 User 類。users 索引在我的 ES 中是沒有記錄的,所以期望結果是 ES 中新增了一條 users 數據。
/** * 測試存儲數據到 ES。 * */@Testpublic void testIndexData() throws IOException { IndexRequest request = new IndexRequest("users"); request.id("1"); // 文檔的 id //構造 User 對象 User user = new User(); user.setUserName("PassJava"); user.setAge("18"); user.setGender("Man"); //User 對象轉為 JSON 數據 String jsonString = JSON.toJSONString(user); // JSON 數據放入 request 中 request.source(jsonString, XContentType.JSON); // 執行插入操作 IndexResponse response = client.index(request, RequestOptions.DEFAULT); System.out.println(response); }
執行 test 方法,我們可以看到控制臺輸出以下結果,說明數據插入到 ES 成功。另外需要注意的是結果中的 result 字段為 updated,是因為我本地為了截圖,多執行了幾次插入操作,但因為 id = 1,所以做的都是 updated 操作,而不是 created 操作。
控制臺輸出結果
我們再來到 ES 中看下 users 索引中數據。查詢 users 索引:
GET users/_search
結果如下所示:
查詢 users 索引結果
可以從圖中看到有一條記錄被查詢出來,查詢出來的數據的 _id = 1,和插入的文檔 id 一致。另外幾個字段的值也是一致的。說明插入的數據沒有問題。
"age" : "18","gender" : "Man","userName" : "PassJava"
示例:搜索 bank 索引,address 字段中包含 big 的所有人的年齡分布 ( 前 10 條 ) 以及平均年齡,以及平均薪資。
我們可以參照官方文檔給出的示例來創建一個 SearchRequest 對象,指定要查詢的索引為 bank,然后創建一個 SearchSourceBuilder 來組裝查詢條件。總共有三種條件需要組裝:
address 中包含 road 的所有人。
按照年齡分布進行聚合。
計算平均薪資。
代碼如下所示,需要源碼請到我的 Github/PassJava 上下載。
查詢復雜語句示例
將打印出來的檢索參數復制出來,然后放到 JSON 格式化工具中格式化一下,再粘貼到 ES 控制臺執行,發現執行結果是正確的。
打印出檢索參數
用在線工具格式化 JSON 字符串,結果如下所示:
然后我們去掉其中的一些默認參數,最后簡化后的檢索參數放到 Kibana 中執行。
Kibana Dev Tools 控制臺中執行檢索語句如下圖所示,檢索結果如下圖所示:
控制臺中執行檢索語句
找到總記錄數:29 條。
第一條命中記錄的詳情如下:
平均 balance:13136。
平均年齡:26。
地址中包含 Road 的:263 Aviation Road。
和 IDEA 中執行的測試結果一致,說明復雜檢索的功能已經成功實現。
而獲取命中記錄的詳情數據,則需要通過兩次 getHists() 方法拿到,如下所示:
// 3.1)獲取查到的數據。 SearchHits hits = response.getHits(); // 3.2)獲取真正命中的結果 SearchHit[] searchHits = hits.getHits();
我們可以通過遍歷 searchHits 的方式打印出所有命中結果的詳情。
// 3.3)、遍歷命中結果for (SearchHit hit: searchHits) { String hitStr = hit.getSourceAsString(); BankMember bankMember = JSON.parseObject(hitStr, BankMember.class); }
拿到每條記錄的 hitStr 是個 JSON 數據,如下所示:
{ "account_number": 431, "balance": 13136, "firstname": "Laurie", "lastname": "Shaw", "age": 26, "gender": "F", "address": "263 Aviation Road", "employer": "Zillanet", "email": "laurieshaw@zillanet.com", "city": "Harmon", "state": "WV"}
而 BankMember 是根據返回的結果詳情定義的的 JavaBean。可以通過工具自動生成。在線生成 JavaBean 的網站如下:
https://www.bejson.com/json2javapojo/new/
把這個 JavaBean 加到 PassjavaSearchApplicationTests 類中:
@ToString@Datastatic class BankMember { private int account_number; private int balance; private String firstname; private String lastname; private int age; private String gender; private String address; private String employer; private String email; private String city; private String state; }
然后將 bankMember 打印出來:
System.out.println(bankMember);
bankMember
得到的結果確實是我們封裝的 BankMember 對象,而且里面的屬性值也都拿到了。
ES 返回的 response 中,年齡分布的數據是按照 ES 的格式返回的,如果想按照我們自己的格式來返回,就需要將 response 進行處理。
如下圖所示,這個是查詢到的年齡分布結果,我們需要將其中某些字段取出來,比如 buckets,它代表了分布在 21 歲的有 4 個。
ES 返回的年齡分布信息
下面是代碼實現:
Aggregations aggregations = response.getAggregations(); Terms ageAgg1 = aggregations.get("ageAgg");for (Terms.Bucket bucket : ageAgg1.getBuckets()) { String keyAsString = bucket.getKeyAsString(); System.out.println("用戶年齡: " + keyAsString + " 人數:" + bucket.getDocCount()); }
最后打印的結果如下,21 歲的有 4 人,26 歲的有 4 人,等等。
打印結果:用戶年齡分布
現在來看看平均薪資如何按照所需的格式返回,ES 返回的結果如下圖所示,我們需要獲取 balanceAvg 字段的 value 值。
ES 返回的平均薪資信息
代碼實現:
Avg balanceAvg1 = aggregations.get("balanceAvg"); System.out.println("平均薪資:" + balanceAvg1.getValue());
打印結果如下,平均薪資 28578 元。
打印結果:平均薪資
PassJava 這個項目可以用來配置題庫,如果我們想通過關鍵字來搜索題庫,該怎么做呢?
類似于百度搜索,輸入幾個關鍵字就可以搜到關聯的結果,我們這個功能也是類似,通過 Elasticsearch 做檢索引擎,后臺管理界面和小程序作為搜索入口,只需要在小程序上輸入關鍵字,就可以檢索相關的題目和答案。
首先我們需要把題目和答案保存到 ES 中,在存之前,第一步是定義索引的模型,如下所示,模型中有 title
和 answer
字段,表示題目和答案。
"id": { "type": "long"},"title": { "type": "text", "analyzer": "ik_smart"},"answer": { "type": "text", "analyzer": "ik_smart"},"typeName": { "type": "keyword"}
上面我們已經定義了索引結構,接著就是在 ES 中創建索引。
在 Kibana 控制臺中執行以下語句:
PUT question { "mappings" : { "properties": { "id": { "type": "long" }, "title": { "type": "text", "analyzer": "ik_smart" }, "answer": { "type": "text", "analyzer": "ik_smart" }, "typeName": { "type": "keyword" } } } }
執行結果如下所示:
創建 question 索引
我們可以通過以下命令來查看 question 索引是否在 ES 中:
GET _cat/indices
執行結果如下圖所示:
查看 ES 中所有的索引
上面我們定義 ES 的索引,接著就是定義索引對應的模型,將數據存到這個模型中,然后再存到 ES 中。
ES 模型如下,共四個字段:id、title、answer、typeName。和 ES 索引是相互對應的。
@Datapublic class QuestionEsModel { private Long id; private String title; private String answer; private String typeName; }
當我們在后臺創建題目或保存題目時,先將數據保存到 mysql 數據庫,然后再保存到 ES 中。
如下圖所示,在管理后臺創建題目時,觸發保存數據到 ES 。
第一步,保存數據到 mysql 中,項目中已經包含此功能,就不再講解了,直接進入第二步:保存數據到 ES 中。
而保存數據到 ES 中,需要將數據組裝成 ES 索引對應的數據,所以我用了一個 ES model,先將數據保存到 ES model 中。
這里的關鍵代碼時 copyProperties
,可以將 question
對象的數據取出,然后賦值到 ES model 中。不過 ES model 中還有些字段是 question 中沒有的,所以需要單獨拎出來賦值,比如 typeName 字段,question 對象中沒有這個字段,它對應的字段是 question.type,所以我們把 type 取出來賦值到 ES model 的 typeName 字段上。如下圖所示:
用 model 來組裝數據
我在 passjava-search 微服務中寫了一個保存題目的 api 用來保存數據到 ES 中。
保存數據到 ES
然后在 passjava-question 微服務中調用 search 微服務的保存 ES 的方法就可以了。
// 調用 passjava-search 服務,將數據發送到 ES 中保存。searchFeignService.saveQuestion(esModel);
我們可以通過 kibana 的控制臺來查看 question 索引中的文檔。通過以下命令來查看:
GET question/_search
執行結果如下圖所示,有一條記錄:
另外大家有沒有疑問:可以重復更新題目嗎?
答案是可以的,保存到 ES 的數據是冪等的,因為保存的時候帶了一個類似數據庫主鍵的 id。
我們已經將數據同步到了 ES 中,現在就是前端怎么去查詢 ES 數據中,這里我們還是使用 Postman 來模擬前端查詢請求。
請求參數我定義了三個:
keyword:用來匹配問題或者答案。
id:用來匹配題目 id。
pageNum:用來分頁查詢數據。
這里我將這三個參數定義為一個類:
@Datapublic class SearchParam { private String keyword; // 全文匹配的關鍵字 private String id; // 題目 id private Integer pageNum; // 查詢第幾頁數據}
返回的 response 我也定義了四個字段:
questionList:查詢到的題目列表。
pageNum:第幾頁數據。
total:查詢到的總條數。
totalPages:總頁數。
定義的類如下所示:
@Datapublic class SearchQuestionResponse { private List<QuestionEsModel> questionList; // 題目列表 private Integer pageNum; // 查詢第幾頁數據 private Long total; // 總條數 private Integer totalPages; // 總頁數}
調用 ES 的查詢 API 時,需要構建查詢參數。
組裝查詢參數的核心代碼如下所示:
組裝查詢參數
第一步:創建檢索請求。
第二步:設置哪些字段需要模糊匹配。這里有三個字段:title,answer,typeName。
第三步:設置如何分頁。這里分頁大小是 5 個。
第四步:調用查詢 api。
ES 返回的數據是 ES 定義的格式,真正的數據被嵌套在 ES 的 response 中,所以需要格式化返回的數據。
核心代碼如下圖所示:
格式化 ES 返回結果
第一步:獲取查到的數據。
第二步:獲取真正命中的結果。
第三步:格式化返回的數據。
第四步:組裝分頁參數。
我們現在想要驗證 title 字段是否能匹配到,傳的請求參數 keyword = 111,匹配到了 title = 111 的數據,且只有一條。頁碼 pageNum 我傳的 1,表示返回第一頁數據。如下圖所示:
測試匹配 title
我們現在想要驗證 answer 字段是否能匹配到,傳的請求參數 keyword = 測試答案,匹配到了 title = 測試答案的數據,且只有一條,說明查詢成功。如下圖所示:
測試匹配 answer
我們現在想要匹配題目 id 的話,需要傳請求參數 id,而且 id 是精確匹配。另外 id 和 keyword 是取并集,所以不能傳 keyword 字段。
請求參數 id = 5,返回結果也是 id =5 的數據,說明查詢成功。如下圖所示:
測試 id 匹配
“什么是Elasticsearch組件庫”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。