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 微服務框架,幫助我們輕鬆構建和管理微服務應用。 本框架不僅支持 Consul 服務註 ...
  • 先看一下效果吧: 如果不會寫動畫或者懶得寫動畫,就直接交給Blend來做吧; 其實Blend操作起來很簡單,有點類似於在操作PS,我們只需要設置關鍵幀,滑鼠點來點去就可以了,Blend會自動幫我們生成我們想要的動畫效果. 第一步:要創建一個空的WPF項目 第二步:右鍵我們的項目,在最下方有一個,在B ...
  • Prism:框架介紹與安裝 什麼是Prism? Prism是一個用於在 WPF、Xamarin Form、Uno 平臺和 WinUI 中構建鬆散耦合、可維護和可測試的 XAML 應用程式框架 Github https://github.com/PrismLibrary/Prism NuGet htt ...
  • 在WPF中,屏幕上的所有內容,都是通過畫筆(Brush)畫上去的。如按鈕的背景色,邊框,文本框的前景和形狀填充。藉助畫筆,可以繪製頁面上的所有UI對象。不同畫筆具有不同類型的輸出( 如:某些畫筆使用純色繪製區域,其他畫筆使用漸變、圖案、圖像或繪圖)。 ...
  • 前言 嗨,大家好!推薦一個基於 .NET 8 的高併發微服務電商系統,涵蓋了商品、訂單、會員、服務、財務等50多種實用功能。 項目不僅使用了 .NET 8 的最新特性,還集成了AutoFac、DotLiquid、HangFire、Nlog、Jwt、LayUIAdmin、SqlSugar、MySQL、 ...
  • 本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...