06-ElasticSearch搜索結果處理

来源:https://www.cnblogs.com/OnlyOnYourself-lzw/archive/2022/08/10/16570857.html
-Advertisement-
Play Games

3、ElasticSearch搜索結果處理 3.1、排序 Elasticsearch預設是根據相關度算分(_score)來排序,但是也支持自定義方式對搜索結果排序,可以排序的欄位類型有如下幾種 keyword類型 數值類型 地理坐標類型 日期類型 ... 3.1.1、普通欄位排序 keyword、數 ...


3、ElasticSearch搜索結果處理

3.1、排序

  • Elasticsearch預設是根據相關度算分(_score)來排序,但是也支持自定義方式對搜索結果排序,可以排序的欄位類型有如下幾種
    • keyword類型
    • 數值類型
    • 地理坐標類型
    • 日期類型
    • ...

3.1.1、普通欄位排序

  • keyword、數值、日期類型排序的語法基本一致;DSL語法如下所示

    • GET /indexName/_search
      {
        "query": {
          "match_all": {}
        },
        "sort": [
          {
            "FIELD": "desc"  // 排序欄位、排序方式ASC、DESC
          }
        ]
      }
      
  • 排序條件是一個數組,也就是可以寫多個排序條件;按照聲明的排序,當第一個條件相等的時候,再按照第二個條件排序,依次類推

  • 示例

    • 酒店數據按照用戶評價(score)降序排序,評價相同的按照價格(price)升序排序

    • DSL語句如下所示(match處是自定義的,也可以直接使用match_all

      • GET hotel/_search
        {
          "query": {
            "match": {
              "name": "酒店"
            }
          },
          "sort": [
            {
              "score": {
                "order": "desc"
              }
            },
            {
              "price": {
                "order": "asc"
              }
            }
          ],
          "size": 100
        }
        
    • 運行結果如下所示

3.1.2、地理坐標排序

  • 地理坐標排序跟上面的普通欄位排序略有不同,DSL語法格式如下所示

    • GET /indexName/_search
      {
        "query": {
          "match_all": {}
        },
        "sort": [
          {
            "_geo_distance" : {
                "FIELD" : "緯度,經度", // 文檔中geo_point類型的欄位名、目標坐標點
                "order" : "asc", // 排序方式
                "unit" : "km" // 排序的距離單位
            }
          }
        ]
      }
      
  • 這個查詢的含義是

    • ①、指定一個坐標,作為目標點
    • ②、計算每一個文檔中,指定欄位(必須是geo_point類型)的坐標,到目標點的距離是多少
    • ③、根據距離排序
  • 示例

    • 實現對酒店數據按照自己的位置坐標的距離升序排序

    • 假設我的位置坐標是:113.266022,22.995959,尋找周圍距離最近的酒店的DSL語句如下所示(location欄位也可以不使用大括弧,直接使用字元串)

      • GET hotel/_search
        {
          "query": {
            "match_all": {}
          },
          "sort": [
            {
              "_geo_distance": {
                "location": {
                  "lat": 22.995959,
                  "lon": 113.266022
                },
                "order": "asc",
                "unit": "km"
              }
            }
          ]
        }
        
    • 運行結果如下所示

3.1.3、普通欄位排序RestAPI

  • 代碼如下所示

    •     /**
           * 普通欄位排序查詢
           */
          @Test
          public void testCommonFieldSortQuery() throws IOException {
              // 1. 創建查詢請求對象
              SearchRequest searchRequest = new SearchRequest("hotel");
              // 2. 添加查詢請求體
              searchRequest.source().query(
                      QueryBuilders.matchAllQuery()
              ).sort(
                      SortBuilders.fieldSort("score").order(SortOrder.DESC)
              ).sort(
                      SortBuilders.fieldSort("price").order(SortOrder.ASC)
              );
      
              // 3. 執行查詢,獲取響應結果
              SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
      
              // 4. 處理響應數據
              handlerCommonResponse(response);
          }
      
      
          /**
           * 用來處理響應數據(相當於解析返回的JSON數據)
           * @param response
           */
          private void handlerCommonResponse(SearchResponse response) throws JsonProcessingException {
              // 1. 得到命中的數量(即總記錄數量)
              SearchHits hits = response.getHits();
              long totalCount = hits.getTotalHits().value;// 總記錄數
              System.out.println("總記錄數量為:" + totalCount);
      
              // 2. 獲取本次查詢出來的列表數據
              SearchHit[] hitsArray = hits.getHits();
              for (SearchHit hit : hitsArray) {
                  Object[] sortValues = hit.getSortValues();
                  if (sortValues.length > 0) {
                      System.out.println("當前酒店得分為【" + sortValues[0] + "】");
                      System.out.println("當前酒店價格為【" + sortValues[1] + "】");
                  }
      
                  // 得到json字元串
                  String json = hit.getSourceAsString();
                  // 將json字元串轉換為實體類對象
                  HotelDoc hotelDoc = objectMapper.readValue(json, HotelDoc.class);
                  System.out.println(hotelDoc);
              }
          }
      

3.1.4、地理坐標排序RestAPI

  • 代碼如下所示

    •     /**
           * 地理坐標排序測試
           */
          @Test
          public void testGeoDistanceSortQuery() throws IOException {
              // 1. 創建查詢請求體
              SearchRequest searchRequest = new SearchRequest("hotel");
      
              // 2. 添加查詢請求體
              searchRequest.source().query(
                      QueryBuilders.matchAllQuery()
              ).sort(
                      SortBuilders.geoDistanceSort(
                              "location",
                              new GeoPoint(22.995959,113.266022)
                      ).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS)
              );
      
              // 3. 執行查詢,獲取響應數據
              SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
      
              // 4. 處理響應結果
              handlerGeoResponse(response);
      
          }
      
      
          /**
           * 用來處理響應數據(相當於解析返回的JSON數據)
           * @param response
           */
          private void handlerGeoResponse(SearchResponse response) throws JsonProcessingException {
              // 1. 得到命中的數量(即總記錄數量)
              SearchHits hits = response.getHits();
              long totalCount = hits.getTotalHits().value;// 總記錄數
              System.out.println("總記錄數量為:" + totalCount);
      
              // 2. 獲取本次查詢出來的列表數據
              SearchHit[] hitsArray = hits.getHits();
              for (SearchHit hit : hitsArray) {
                  Object[] sortValues = hit.getSortValues();
                  if (sortValues.length > 0) {
                      System.out.println("距離當前位置【" + sortValues[0] + "】公裡");
                  }
      
                  // 得到json字元串
                  String json = hit.getSourceAsString();
                  // 將json字元串轉換為實體類對象
                  HotelDoc hotelDoc = objectMapper.readValue(json, HotelDoc.class);
                  System.out.println(hotelDoc);
              }
          }
      

3.2、分頁

  • Elasticsearch預設情況下只返回top10的數據。而如果要查詢更多數據就需要修改分頁參數
  • Elasticsearch中通過修改fromsize參數來控制要返回的分頁結果
    • from:從第幾個文檔開始,從0開始
    • size:總共查詢幾個文檔
  • 類似於MySQL中的limit ?, ?
  • DSL格式比較簡單,這裡就不多說了;只需要添加上面說的兩個參數即可

3.2.1、分頁查詢

  • DSL語句如下圖所示

3.2.2、RestAPI

  • 代碼如下所示

    • package com.coolman.hotel.test;
      
      import com.coolman.hotel.pojo.HotelDoc;
      import com.fasterxml.jackson.core.JsonProcessingException;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import org.elasticsearch.action.search.SearchRequest;
      import org.elasticsearch.action.search.SearchResponse;
      import org.elasticsearch.client.RequestOptions;
      import org.elasticsearch.client.RestHighLevelClient;
      import org.elasticsearch.index.query.QueryBuilder;
      import org.elasticsearch.index.query.QueryBuilders;
      import org.elasticsearch.search.SearchHit;
      import org.elasticsearch.search.SearchHits;
      import org.junit.jupiter.api.Test;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.test.context.SpringBootTest;
      
      import java.io.IOException;
      
      /**
       * 分頁
       */
      @SpringBootTest
      public class PageQueryTest {
      
          @Autowired
          private RestHighLevelClient restHighLevelClient;
      
          //jackson
          private final ObjectMapper objectMapper = new ObjectMapper();
      
          /**
           * 分頁查詢測試
           * @throws IOException
           */
          @Test
          public void testPage() throws IOException {
              int from = 0;
              int size = 2;
              // 1. 創建查詢請求體
              SearchRequest searchRequest = new SearchRequest("hotel");
      
              // 2. 添加查詢請求體
              searchRequest.source().query(
                      QueryBuilders.matchAllQuery()
              ).from(from).size(size);
      
              // 3. 執行操作,獲取響應數據
              SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
      
              // 4. 處理響應數據
              handlerResponse(response);
          }
      
      
          /**
           * 用來處理響應數據(相當於解析返回的JSON數據)
           * @param response
           */
          private void handlerResponse(SearchResponse response) throws JsonProcessingException {
              // 1. 得到命中的數量(即總記錄數量)
              SearchHits hits = response.getHits();
              long totalCount = hits.getTotalHits().value;// 總記錄數
              System.out.println("總記錄數量為:" + totalCount);
      
              // 2. 獲取本次查詢出來的列表數據
              SearchHit[] hitsArray = hits.getHits();
              for (SearchHit hit : hitsArray) {
                  // 得到json字元串
                  String json = hit.getSourceAsString();
                  // 將json字元串轉換為實體類對象
                  HotelDoc hotelDoc = objectMapper.readValue(json, HotelDoc.class);
                  System.out.println(hotelDoc);
              }
          }
      }
      
      

3.3、高亮顯示

3.3.1、高亮原理

  • 高亮查詢的概念
    • 我們在百度,京東等網站搜索的時候,關鍵字會變紅色,比較醒目,這就叫做高亮顯示
    • 使用檢查工具查看源代碼可以發現
  • 高亮顯示的實現分為兩步
    • 1)給文檔中的所有關鍵字都添加一個標簽,例如<em>標簽
    • 2)頁面給<em>標簽編寫CSS樣式

3.3.2、高亮顯示查詢DSL

  • 語法格式

    • GET /indexName/_search
      {
        "query": {
          "match": {
            "FIELD": "TEXT" // 查詢條件,高亮一定要使用全文檢索查詢
          }
        },
        "highlight": {
          "fields": { // 指定要高亮的欄位
            "FIELD": {
              "pre_tags": "<em>",  // 用來標記高亮欄位的前置標簽 ,預設使用<em>標簽
              "post_tags": "</em>" // 用來標記高亮欄位的後置標簽
            }
          }
        }
      }
      
    • 註意

      • 高亮是對關鍵字高亮,因此搜索條件必須帶有關鍵字,而不能是範圍這樣的查詢。
      • 預設情況下,高亮的欄位,必須與搜索指定的欄位一致,否則無法高亮
      • 如果要對非搜索欄位高亮,則需要添加一個屬性:required_field_match=false
  • 示例

    • 需求:讓酒店搜索結果的name欄位高亮顯示關鍵詞

    • DSL語句如下所示

      • # 高亮顯示
        
        # 需求:讓酒店搜索結果的name欄位高亮顯示關鍵詞
        
        # fields: 指定需要高亮顯示的欄位名
        # pre_tags: 樣式首碼	不指定的話,就預設是<em>標簽
        # post_tags:樣式尾碼
        # require_field_match: 
        # true 代表高亮欄位必須出現在條件中,才可以高亮
        # false代表高亮欄位不一定要出現在條件,也可以高亮
        
        GET hotel/_search
        {
          "query": {
            "match": {
              "name": "如家"
            }
          },
          "highlight": {
            "fields": {
              "brand": {},
              "name": {}
            },
            "require_field_match": "false", 
            "pre_tags": "<front color='red'>",
            "post_tags": "</front>"
          }
        }
        
    • 查詢結果如下所示

      • PS:這裡的DSL語句可以有些不同,比如fields可以是數組,然後require_field_matchpre_tagspost_tags都可以放在欄位中
      • 變化有點多,這裡就不演示了,上面的是相當於全局配置的意思

3.3.3、高亮顯示查詢RestAPI

  • 代碼如下所示,可以參考DSL語句的格式來寫

    • package com.coolman.hotel.test;
      
      import com.coolman.hotel.pojo.HotelDoc;
      import com.fasterxml.jackson.core.JsonProcessingException;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import org.elasticsearch.action.search.SearchRequest;
      import org.elasticsearch.action.search.SearchResponse;
      import org.elasticsearch.client.RequestOptions;
      import org.elasticsearch.client.RestHighLevelClient;
      import org.elasticsearch.common.text.Text;
      import org.elasticsearch.index.query.QueryBuilder;
      import org.elasticsearch.index.query.QueryBuilders;
      import org.elasticsearch.search.SearchHit;
      import org.elasticsearch.search.SearchHits;
      import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
      import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
      import org.junit.jupiter.api.Test;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.test.context.SpringBootTest;
      
      import java.io.IOException;
      import java.util.Collection;
      import java.util.Collections;
      import java.util.Map;
      
      /**
       * 高亮顯示
       */
      @SpringBootTest
      public class HighLightQueryTest {
      
          @Autowired
          private RestHighLevelClient restHighLevelClient;
      
          //jackson
          private final ObjectMapper objectMapper = new ObjectMapper();
      
          @Test
          public void testHighLightSearch() throws IOException {
              // 1. 創建查詢請求對象
              SearchRequest searchRequest = new SearchRequest("hotel");
      
              // 2. 添加查詢請求體
      
              HighlightBuilder highlightBuilder = new HighlightBuilder();
              // 添加高亮欄位方式1(暫時不知道什麼區別,瞭解就好)
      //        highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
      //        highlightBuilder.fields().add(new HighlightBuilder.Field("brand"));
              // 添加高亮欄位方式2
              highlightBuilder.field("name");
              highlightBuilder.field("brand");
              // 這裡相當於是全局的配置,也可以在上面添加配置,如  highlightBuilder.field("name").requireFieldMatch(false).postTags("...").preTags("...");
              highlightBuilder.requireFieldMatch(false).preTags("<front color='red'>").postTags("</front>");
      
              searchRequest.source().query(
                      QueryBuilders.matchQuery("name", "如家")
              ).highlighter(highlightBuilder);
      
              // 3. 執行操作,獲取響應數據
              SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
      
              // 4. 處理響應數據
              handlerResponse(response);
          }
      
      
          /**
           * 用來處理響應數據(相當於解析返回的JSON數據)
           * @param response
           */
          private void handlerResponse(SearchResponse response) throws JsonProcessingException {
              // 1. 得到命中的數量(即總記錄數量)
              SearchHits hits = response.getHits();
              long totalCount = hits.getTotalHits().value;// 總記錄數
              System.out.println("總記錄數量為:" + totalCount);
      
              // 2. 獲取本次查詢出來的列表數據
              SearchHit[] hitsArray = hits.getHits();
              for (SearchHit hit : hitsArray) {
      
                  // 得到json字元串
                  String json = hit.getSourceAsString();
                  // 將json字元串轉換為實體類對象
                  HotelDoc hotelDoc = objectMapper.readValue(json, HotelDoc.class);
      
                  // 處理高亮的情況
                  Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                  if (highlightFields.size() > 0) {
                      if (highlightFields.get("name") != null) {
                          Text nameHighLight = highlightFields.get("name").fragments()[0];
                          hotelDoc.setName(nameHighLight.toString());
                      }
                      if (highlightFields.get("brand") != null) {
                          Text brandHighLight = highlightFields.get("brand").fragments()[0];
                          hotelDoc.setBrand(brandHighLight.toString());
                      }
                  }
                  System.out.println(hotelDoc);
              }
          }
      }
      
      

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、一元運算符 註:()不能與++一起使用,因為優先順序一樣,控制台會報錯,如果寫一行,要分開寫 二、比較 1、字元與數值進行比較-->會自動轉換成number,若轉換不成功,就是NaN,結果就是false 2、只要看到NaN,就是false(NaN==NaN,結果就是false) 3、字元串和字元串 ...
  • 一、誕生時間 1995年 二、JavaScript包含內容 ECMAScript:基本語法及相關對象es(包括我們瞭解的es3,es5,es6.es7...) DOM:文檔對象模型,用來操作HTML BOM:瀏覽器對象模型,用來操作瀏覽器 三、書寫方式 1、書寫在script標簽里(比較常用) 2、 ...
  • margin:auto為什麼不垂直居中 margin:auto是具有強烈計算意味的關鍵字,用來計算元素對應方向上應該獲得的剩餘空間大小。 行內元素margin:auto; 不能水平居中在一行的中央位置(行內元素不獨占一行)。 position定位屬性大家都不會陌生,添加position屬性的元素可以 ...
  • vivo 互聯網前端團隊-Yang Kun 一、背景 在團隊中,我們因業務發展,需要用到桌面端技術,如離線可用、調用桌面系統能力。什麼是桌面端開發?一句話概括就是:以 Windows 、macOS 和 Linux 為操作系統的軟體開發。對此我們做了詳細的技術調研,桌面端的開發方式主要有 Native ...
  • 蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》 寫在開頭 作為一名Java Developer,我們都清楚地知道,主要從搭載Linux系統上的伺服器程式來說,使用Java編寫的是”單進程-多線程"程式,而用C++語言編寫的,可能是“單進程-多線程”程式,“多 ...
  • 技術 Leader 是一個對綜合素質要求非常高的崗位,不僅要有解具體技術問題的架構能力,還要具備團隊管理的能力,更需要引領方向帶領團隊/平臺穿越迷茫進階到下一個境界的能力。所以通常來說技術 Leader 的技能是虛實結合的居多,繁雜的工作偏多。為此我把自己在工作中經常用到的思考技巧也做了一個整理。 ...
  • 社交是一種永恆的需求,既有生存層面的必要,也有情感上的渴求。而隨著互聯網開始統治這個時代,社交被搬到了網上,並且越來越成為主流,社交也在發展成互聯網產品的一個重要賽道。本文將介紹Soul是如何破解Z世代社交密碼的。 文章目錄 01 年輕人的社交密碼 02 為什麼對年輕人來說,Soul是那個對的產品? ...
  • 統一術語(戰略設計) 我們將通過DDD完成業務與技術的完整落地 統一 領域模型術語 DDD模式名稱 技術 技術設計術語 技術術語 技術設計模式 業務 領域模型術語 DDD模式名稱 業務術語 設計無關的業務術語 清晰的事件流 DDD 領域驅動設計是一個有關軟體開發的方法論,它提出基於領域開發的開發模式 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...