javascript
SpringCloud(8)— 使用ElasticSearch(RestClient)
SpringCloud(8)— 使用ElasticSearch(RestClient)
一 認識RestClient
ES 官方提供了各種語言的客戶端用來操作ES,這些客戶端的本質就是組創DSL語句,通過 Http 請求發送給ES
官方文檔地址:Elasticsearch Clients | Elastic
提示1:ES 中支持兩種地理坐標數據類型
- geo_point:ES中的 mapping 類型中利用經度和緯度確定一個點
- geo_shape:由多個 geo_point 組成的復雜幾何圖形
提示2:字段拷貝可以使用 copy_to 屬性將當前字段的屬性值拷貝到指定的屬性上,方便以后搜索,且被指定的字段在查詢數據時對外不可見
例如:以下示例中的 all 字段,在后邊查詢數據時不會返回
{"all":{"type":"text","analyzer":"ik_max_word"},"brand":{"type":"keyword","copy_to":"all"} }二 RestClient操作索引庫
1.初始化RestClient
在項目中引入 RestClient 的 mavne 依賴坐標,版本需與 ElasticSearch 的版本一致。這里使用的是 7.12.1
<properties><elasticsearch.version>7.12.1</elasticsearch.version> </properties> <dependencies><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>${elasticsearch.version}</version></dependency> </dependencies>由于 SpringBoot 項目中已經對 elasticsearch 進行管理,所以需要手動定義相同的參數值去覆蓋默認的版本號,從而使用我們想使用的版本
編寫測試代碼,測試 RestHighLevelClient 是否初始化成功
private RestHighLevelClient restHighLevelClient;//初始化 RestHighLevelClient 對象 @BeforeEach void setUp(){this.restHighLevelClient=new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.119.101:9200"))); }//銷毀 RestHighLevelClient 對象 @AfterEach void tearDown() throws IOException {this.restHighLevelClient.close(); }@Test void testInit(){System.out.println(restHighLevelClient); }或者可以使用 Ioc 思想,創建一個 Bean 到內存中,然后使用它即可
@Bean public RestHighLevelClient restHighLevelClient(){return new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.119.101:9200"))); }2.創建索引庫
編寫一個測試方法,用來測試創建索引庫是否成功
@Test void testCreateIndex() throws IOException {//1.創建一個 CreateIndexRequest 對象,指定 indexName(索引庫名稱)CreateIndexRequest request = new CreateIndexRequest("hotel");// 這是一段很惡心的 DSL 語句的拼接,為了方便可以將這一塊代碼提了出來,這里為了方便演示不做優化處理String mapping ="{\n" +" \"mappings\": {\n" +" \"properties\": {\n" +" \"all\":{\n" +" \"type\": \"text\",\n" +" \"analyzer\": \"ik_max_word\"\n" +" },\n" +" \"id\":{\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"name\":{\n" +" \"type\": \"text\",\n" +" \"copy_to\": \"all\", \n" +" \"analyzer\": \"ik_max_word\"\n" +" },\n" +" \"address\":{\n" +" \"type\": \"keyword\",\n" +" \"index\": false\n" +" },\n" +" \"price\":{\n" +" \"type\": \"double\"\n" +" },\n" +" \"score\":{\n" +" \"type\": \"integer\"\n" +" },\n" +" \"brand\":{\n" +" \"type\": \"keyword\",\n" +" \"copy_to\": \"all\"\n" +" },\n" +" \"city\":{\n" +" \"type\": \"keyword\",\n" +" \"copy_to\": \"all\"\n" +" },\n" +" \"starName\":{\n" +" \"type\": \"keyword\",\n" +" \"copy_to\": \"all\"\n" +" },\n" +" \"business\":{\n" +" \"type\": \"keyword\",\n" +" \"copy_to\": \"all\"\n" +" },\n" +" \"location\":{\n" +" \"type\": \"geo_point\"\n" +" },\n" +" \"pic\":{\n" +" \"type\": \"keyword\",\n" +" \"index\": false\n" +" },\n" +" \"isAD\":{\n" +" \"type\": \"boolean\"\n" +" }\n" +" }\n" +" }\n" +"} ";//2.定義 DSLrequest.source(mapping, XContentType.JSON);//3.創建索引庫restHighLevelClient.indices().create(request, RequestOptions.DEFAULT); }運行完成后,可以去瀏覽器通過 GET 方法訪問,如果返回對應的JSON格式信息,則說明創建索引庫成功。
# 瀏覽器訪問 http://192.168.119.101:9200/hotel # kibana 中的 dev tools 中測試 GET /hotelCreateIndexRequest 對象中的 indices() 對象里,包含了操作索引庫的所有方法
3.刪除索引庫
@Test void testDeleteIndex() throws IOException {// 定義 DeleteIndexRequest 對象DeleteIndexRequest request=new DeleteIndexRequest("hotel");// 執行刪除操作restHighLevelClient.indices().delete(request,RequestOptions.DEFAULT); }4.判斷指定索引庫是否存在
@Test void testDeleteExist() throws IOException {// 定義 GetIndexRequest 對象GetIndexRequest request=new GetIndexRequest("hotel");// 發送請求restHighLevelClient.indices().exists(request,RequestOptions.DEFAULT); }三 RestClient操作文檔
1.添加文檔
編寫測試方法,從數據庫查詢到指定數據,并且格式化為 Json,然后將數據存儲到 特定的索引庫中
Hotel 實體對象示例:
@Data public class Hotel implements Serializable {private final static Long serialVersionUID=1L;private Long id;private String name;private String address;private Integer price;private Integer score;private String city;private String starName;private String brand;private String business;private String latitude;private String longitude;private String pic;/*** 是否廣告,1-是,0-否*/@TableField("isAD")private Integer isAD; }HotelDoc 文檔實體對象:
注意:文檔的實體應該與創建時的 mapping 字段完全一致
@Data public class HotelDoc implements Serializable {public HotelDoc() {}public HotelDoc(Hotel hotel) {this.id = hotel.getId();this.name = hotel.getName();this.address = hotel.getAddress();this.price = hotel.getPrice();this.score = hotel.getScore();this.brand = hotel.getBrand();this.city = hotel.getCity();this.starName = hotel.getStarName();this.business = hotel.getBusiness();// 使用字符串格式的 geo_point 時,要求“緯度在前,經度在后”this.location = hotel.getLatitude() + "," + hotel.getLongitude();this.pic = hotel.getPic();//處理廣告字段(isAD)格式if (hotel.getIsAD() == 1) {this.isAD = true;} else {this.isAD = false;}}private final static Long serialVersionUID = 1L;private Long id;private String name;private String address;private Integer price;private Integer score;private String city;private String starName;private String brand;private String business;private String pic;private String location;/*** 廣告*/public Boolean isAD;}添加一條文檔到索引庫中:
@Test void testAddDocument() throws IOException {Long id = 36934L;Hotel hotel = hotelService.getById(id);if (Objects.nonNull(hotel)) {// 1.創建 IndexRequest 對象,指定文檔要存入的【索引庫】及【文檔Id】,此處的【文檔Id】使用數據庫的【主鍵Id】IndexRequest request = new IndexRequest("hotel").id(id.toString());// 2.準備 JSON 數據HotelDoc hotelDoc = new HotelDoc(hotel);String jsonString = JSON.toJSONString(hotelDoc);request.source(jsonString, XContentType.JSON);// 3.發送請求restHighLevelClient.index(request, RequestOptions.DEFAULT);return;}System.out.println("id=" + id + "的信息不存在"); }踩坑底:對于 mapping 類型為 geo_point 的經緯度,使用字符串格式時,需要“緯度在前經度在后
運行成功后,數據將被存入到 ES 中
2.查看文檔
編寫查看文檔的示例代碼,重點API 【GetRequest】
@Test void testGetDocument() throws IOException {Long id = 36934L;// 1.創建 GetRequest 對象,指定【索引庫】和【文檔ID】GetRequest request = new GetRequest("hotel", id.toString());// 2.發送請求,讀取到數據GetResponse documentFields = restHighLevelClient.get(request, RequestOptions.DEFAULT);String sourceStr = documentFields.getSourceAsString();//3. 使用fastJson轉為實體對象HotelDoc hotelDoc = JSON.parseObject(sourceStr, HotelDoc.class);System.out.println(hotelDoc); }3.修改文檔
編寫修改文檔的示例代碼,重點API 【UpdateRequest】
@Test void testUpdateDocument() throws IOException {Long id=36934L;// 1.創建 UpdateRequest 對象,指定【索引庫】和 【文檔Id】UpdateRequest request=new UpdateRequest("hotel",id.toString());// 2.準備要更新的參數和值,每兩個參數為一對 key valuerequest.doc("price",200,"name","8天假日酒店");// 3.發送請求restHighLevelClient.update(request,RequestOptions.DEFAULT); }4.刪除文檔
編寫刪除文檔的示例代碼,重點API 【DeleteRequest】
@Test void testDeleteDocument() throws IOException {Long id=36934L;// 1.創建 DeleteRequest 對象,指定【索引庫】和 【文檔Id】DeleteRequest deleteRequest=new DeleteRequest("hotel",id.toString());// 2.發送請求restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT); }5.批量導入文檔
編寫批量導入測試方法,重點API 【BulkRequest】
@Test void tesBulkDocument() throws IOException {// 1.定義 BulkRequest 對象BulkRequest request=new BulkRequest();List<Hotel> list = hotelService.list();// 2.查詢所有數據并且轉換為 List<HotelDoc>List<HotelDoc> listDoc=list.stream().map(t->{HotelDoc doc=new HotelDoc(t);return doc;}).collect(Collectors.toList());// 3.遍歷 List<HotelDoc> 對象,創建 IndexRequest 并且通過 add() 添加到 BulkRequest 對象中for (HotelDoc doc: listDoc) {IndexRequest indexRequest=new IndexRequest("hotel").id(doc.getId().toString());indexRequest.source(JSON.toJSONString(doc),XContentType.JSON);request.add(indexRequest);}// 4.發送 ES 請求restHighLevelClient.bulk(request,RequestOptions.DEFAULT); }在 kibana 控制臺通過查詢命令可以查詢到已經導入數據
#1.hotel為索引庫名稱 GET /hotel/_search四 RestClient查詢
RestClient 所有解析的歸根結底是逐層解析 Json 結構
1.快速入門(match_all)
通過 match_all 演示 RestClient 的基本API
編寫以下測試代碼,實現 match_all 的使用
@Test void testMatchAll() throws IOException {//1.創建 SearchRequest 對象,指定索引庫名稱SearchRequest request=new SearchRequest("hotel");//2.組織 DSL 參數request.source().query(QueryBuilders.matchAllQuery()).from(0).size(3); //3.發送請求,得到響應結果SearchResponse response=restHighLevelClient.search(request, RequestOptions.DEFAULT);//4. 通過 SearchResponse 對象的 getHits 拿到數據結果集,然后進行處理SearchHits hits = response.getHits();//5.獲取數據總條數Long totalHits = hits.getTotalHits().value;System.out.println(totalHits);//6.處理結果for (SearchHit hit : hits) {HotelDoc doc = JSON.parseObject(hit.getSourceAsString(), HotelDoc.class);System.out.println(doc.toString());} }RestClient 與 DSL 語法對比:
- 使用 QueryBuilders 實現各種查詢方式
- 通過 SearchRequest 對象的 source() 對象實現鏈式編程,從而設置分頁,排序,高亮,復合查詢等操作
- 通過 RestHighLevelClient 對象的 search() 方法發送查詢請求,實現查詢
- 通過 SearchResponse 對象接受查詢結果并進行處理
- 通過 SearchHits 對象拿到結果集和數據總行數
主要通過解析 DSL 語法返回結果集中的 hits 對象,獲取到總條數及數據內容,從而對數據進行處理
2.全文檢索
全文檢索 與 match_all 的API基本一致,差別在于 match 有了查詢條件,即 DSL 語句中的 query 部分。
1.match(特定字段)
//1.創建 SearchRequest 對象,指定索引庫名稱 SearchRequest request = new SearchRequest("hotel");//2.組織 DSL 參數 request.source().query(QueryBuilders.matchQuery("all", "如家"));//3.發送請求,得到響應結果 SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);2.multi_match(多字段)
//1.創建 SearchRequest 對象,指定索引庫名稱 SearchRequest request = new SearchRequest("hotel");//2.組織 DSL 參數 request.source().query(QueryBuilders.multiMatchQuery("如家","name","brand"))//3.發送請求,得到響應結果 SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);match 和 multi_match 的區別主要在于 QueryBuilders 類的調用方法不同
match 中 QueryBuilders 使用了 matchQuery()
multi_match 中 QueryBuilders 使用了 multiMatchQuery()
3.term(精準查詢)
與 前面的相比,唯一的區別是使用了 termQuery()
@Test void testMatch() throws IOException {//1.創建 SearchRequest 對象,指定索引庫名稱SearchRequest request = new SearchRequest("hotel");//2.組織 DSL 參數request.source().query(QueryBuilders.termQuery("brand","如家"));//3.發送請求,得到響應結果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); }3.boolean query(組合)
boolean query 查詢 與 前面的查詢略有不同,需要先創建 BoolQueryBuilder 對象,然后通過 BoolQueryBuilder 對象添加過濾條件
@Test void testBooleanQuery() throws IOException {//1.創建 SearchRequest 對象,指定索引庫名稱SearchRequest request = new SearchRequest("hotel");//2.創建 BoolQueryBuilder 對象,通過鏈式編程方式添加查詢條件BoolQueryBuilder queryBuilder = new BoolQueryBuilder();queryBuilder.should(QueryBuilders.matchQuery("city", "上海")).must(QueryBuilders.termQuery("brand", "如家")).filter(QueryBuilders.rangeQuery("price").gte(200).lte(300));//3.組織 DSL 參數,填充 BoolQueryBuilder對象到 query中request.source().query(queryBuilder);//4.發送請求,得到響應結果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); }或者可以直接一步到位
@Test void testBooleanQuery() throws IOException {//1.創建 SearchRequest 對象,指定索引庫名稱SearchRequest request = new SearchRequest("hotel");//2.組織 DSL 參數request.source().query(new BoolQueryBuilder().should(QueryBuilders.matchQuery("city", "上海")).must(QueryBuilders.termQuery("brand", "如家")).filter(QueryBuilders.rangeQuery("price").gte(200).lte(300)));//3.發送請求,得到響應結果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); }4.排序和分頁
DSL 語句中,排序和分頁 在 query 同級,RestClient 中與 DSL 一樣,也是在 query() 之后
@Test void testPageAndOrder() throws IOException {//1.創建 SearchRequest 對象,指定索引庫名稱SearchRequest request = new SearchRequest("hotel");//2.組織 DSL 參數request.source().query(new BoolQueryBuilder().should(QueryBuilders.matchQuery("city", "上海")).must(QueryBuilders.termQuery("brand", "如家")).filter(QueryBuilders.rangeQuery("price").gte(200).lte(300))).from(0).size(3).sort("score", SortOrder.DESC).sort("price",SortOrder.ASC);//3.發送請求,得到響應結果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); }- from:開始位置,默認為0
- size:獲取的數據數量,默認為10
- sort:使用 sort() 方法來進行排序,傳入 字段名 和 排序方式
5.highlight(高亮顯示)
高亮API包括 請求DSL構建 和 結果解析 兩部分
測試代碼實現如下:
@Test void testHighLight() throws IOException {//1.創建 SearchRequest 對象,指定索引庫名稱SearchRequest request = new SearchRequest("hotel");//2.組織 DSL 參數request.source().query(QueryBuilders.matchQuery("all", "如家")).highlighter(//高亮字段是否和查詢字段匹配new HighlightBuilder().field("name").requireFieldMatch(false).preTags("<em>").postTags("</em>"));//3.發送請求,得到響應結果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);//4. 通過 SearchResponse 對象的 getHits 拿到數據結果集,然后進行處理SearchHits hits = response.getHits();//5.獲取數據總條數Long totalHits = hits.getTotalHits().value;System.out.println(totalHits);for (SearchHit hit : hits) {HotelDoc doc = JSON.parseObject(hit.getSourceAsString(), HotelDoc.class);// 獲取高亮區域的值Map<String, HighlightField> fields = hit.getHighlightFields();if (fields != null) {// 獲取指定字段的高亮內容對象HighlightField highlightField = fields.get("name");String highLightName = highlightField.getFragments()[0].toString();// 替換原有的值doc.setName(highLightName);}System.out.println(doc);} }6.距離排序
按照距離排序,需要提供一組經緯度作為中心點,然后按照距離遠近進行排序
距離排序中使用到一個新的對象,即 SortBuilders,以下是測試示例代碼:
@Test void testDistanceSort() throws IOException {//1.創建 SearchRequest 對象,指定索引庫名稱SearchRequest request = new SearchRequest("hotel");//2.組織 DSL 參數request.source().query(QueryBuilders.matchQuery("all", "如家")).sort(SortBuilders.geoDistanceSort("location", new GeoPoint("31.251433,121.47522")).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));//3.發送請求,得到響應結果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); }7.相關性算分
使用 RestClient 實現相關性算分時代碼較為復雜,先看對照示例:
實現方式是基于 FunctionScoreQueryBuilder 對象
以下是測試代碼,用于過濾 isAD=true 時,增加權重分值為10,且與原始分值相乘
@Test void testFunctionScore() throws IOException {//1.創建 SearchRequest 對象,指定索引庫名稱SearchRequest request = new SearchRequest("hotel");// 原始查詢,可以是一般查詢,也可以是復合查詢 BooleanQueryQueryBuilder queryBuilder = QueryBuilders.matchQuery("all", "如家");// 構建 FunctionScoreQueryBuilder 對象FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(//方式查詢queryBuilder,//functionScore 數組new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{// 其中一個 function scorenew FunctionScoreQueryBuilder.FilterFunctionBuilder(//過濾條件QueryBuilders.termQuery("isAD", true),//算分函數,這里使用加權重ScoreFunctionBuilders.weightFactorFunction(20))})// 加權模式.boostMode(CombineFunction.MULTIPLY);;//2.組織 DSL 參數request.source().query(functionScoreQueryBuilder);//3.發送請求,得到響應結果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); }注意:后邊的 “isAD” 字段 是為了做相關性算分增加的新字段,前邊的 索引庫 和 實體類 中沒有,需要手動增加。
2022-12-21,修改 “創建索引庫”時未設置分詞器的問題,Hotel 和 HotelDoc 實體類中添加 “isAD” 字段
本結知識點梳理完成,完結撒花。
總結
以上是生活随笔為你收集整理的SpringCloud(8)— 使用ElasticSearch(RestClient)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Linux同步系统时间
- 下一篇: macbook不能进系统 备份数据_用数
