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中通过修改from、size参数来控制要返回的分页结果
- from:从第几个文档开始,从0开始
- size:总共查询几个文档
- 类似于MySQL中的limit ?, ?
- DSL格式比较简单,这里就不多说了;只需要添加上面说的两个参数即可
3.2.1、分页查询
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_match、pre_tags、post_tags都可以放在字段中
- 变化有点多,这里就不演示了,上面的是相当于全局配置的意思
3.3.3、高亮显示查询RestAPI
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |