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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...