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型別)的坐標,到目標點的距離是多少 - ③、根據距離排序
-
示例
-
實作對酒店資料按照自己的位置坐標的距離升序排序
- 獲取自己位置的經緯度方式可以訪問這個網站:https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/
-
假設我的位置坐標是: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中通過修改
from、size引數來控制要回傳的分頁結果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樣式
- 1)給檔案中的所有關鍵字都添加一個標簽,例如
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_match、pre_tags、post_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); } } }
-
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/501336.html
標籤:其他
上一篇:@Autowired注解 --required a single bean, but 2 were found出現的原因以及解決方法
下一篇:(有圖說人話)解決MyBatis:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)





