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
  • 1.部署歷史 猿友們好,作為初來實習的我,已經遭受社會的“毒打”,所以請容許我在下麵環節適當吐槽,3Q! 傳統部署 ​ 回顧以往在伺服器部署webapi項目(非獨立發佈),dotnet環境、守護進程兩個逃都逃不掉,正常情況下還得來個nginx代理。不僅僅這仨,可能牽扯到yum或npm。node等都要 ...
  • 隨著技術的進步,跨平臺開發已經成為了標配,在此大背景下,ASP.NET Core也應運而生。本文主要基於ASP.NET Core+Element+Sql Server開發一個校園圖書管理系統為例,簡述基於MVC三層架構開發的常見知識點,前一篇文章,已經簡單介紹瞭如何搭建開發框架,和登錄功能實現,本篇... ...
  • 這道題只要會自定義cmp恰當地進行排序,其他部分沒有什麼大問題。 上代碼: 1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,s,h1,h2,cnt; 4 struct apple{ 5 int height,ns;//height為蘋 ...
  • 這篇文章主要描述RPC的路由策略,包括為什麼需要請求隔離,為什麼不在註冊中心中實現請求隔離以及不同粒度的路由策略。 ...
  • 簡介: 中介者模式,屬於行為型的設計模式。用一個中介對象來封裝一系列的對象交互。中介者是各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變他們之間的交互。 適用場景: 如果平行對象間的依賴複雜,可以使用中介者解耦。 優點: 符合迪米特法則,減少成員間的依賴。 缺點: 不適用於系統出現對 ...
  • 【前置內容】Spring 學習筆記全系列傳送門: Spring學習筆記 - 第一章 - IoC(控制反轉)、IoC容器、Bean的實例化與生命周期、DI(依賴註入) Spring學習筆記 - 第二章 - 註解開發、配置管理第三方Bean、註解管理第三方Bean、Spring 整合 MyBatis 和 ...
  • 簡介: 享元模式,屬於結構型的設計模式。運用共用技術有效地支持大量細粒度的對象。 適用場景: 具有相同抽象但是細節不同的場景中。 優點: 把公共的部分分離為抽象,細節依賴於抽象,符合依賴倒轉原則。 缺點: 增加複雜性。 代碼: //用戶類 class User { private $name; fu ...
  • 這次設計一個通用的多位元組SPI介面模塊,特點如下: 可以設置為1-128位元組的SPI通信模塊 可以修改CPOL、CPHA來進行不同的通信模式 可以設置輸出的時鐘 狀態轉移圖和思路與多位元組串口發送模塊一樣,這裡就不給出了,具體可看該隨筆。 一、模塊代碼 1、需要的模塊 通用8位SPI介面模塊 `tim ...
  • AOP-03 7.AOP-切入表達式 7.1切入表達式的具體使用 1.切入表達式的作用: 通過表達式的方式定義一個或多個具體的連接點。 2.語法細節: (1)切入表達式的語法格式: execution([許可權修飾符] [返回值類型] [簡單類名/全類名] [方法名]([參數列表]) 若目標類、介面與 ...
  • 測試一、虛繼承與繼承的區別 1.1 單個繼承,不帶虛函數 1>class B size(8): 1> + 1> 0 | + (base class A) 1> 0 | | _ia //4B 1> | + 1> 4 | _ib //4B 有兩個int類型數據成員,占8B,基類邏輯存在前面 1.2、單個 ...