基础排序功能,包括两种:
-
普通字段排序
-
地理坐标排序
然后我们要修改实体类中 添加实体类的字段 (我的是location 这个字段是坐标)
package cn.itcast.hotel.service.impl;
import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Resource
private HotelMapper hotelMapper;
@Resource
private RestHighLevelClient client;
@Override
public PageResult search(RequestParams params) throws IOException {
//准备Request
SearchRequest request = new SearchRequest("hotel");//操作的数组
//构建boolQuery组合条件
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//boolQuery才是搜索条件 其他查询只是boolQuery中的一部分
request.source().query(boolQuery);
String key = params.getKey();
if (key != null && key != "") {
//match查询,会将搜索词分词,再与目标查询字段进行匹配,若分词中的任意一个词与目标字段匹配上,则可查询到。
boolQuery.must(QueryBuilders.matchQuery("all", key));
} else {
//must 中的语句是必须满足的,会影响最终文档的得分
//filter 中的语句是必须满足的,但是不会影响最终的得分
//must_not 中的语句是必须不满足的, 不影响最终的得分
//should中的语句是可以满足的,影响得分,minimum_should_match就是为这兄弟设置的
boolQuery.must(QueryBuilders.matchAllQuery());
}
//城市条件(精确匹配QueryBuilders.termQuery)
if (params.getCity() != null && !"".equals(params.getCity())) {
//termQuery输入的查询内容是什么,就会按照什么去查询,并不会解析查询内容,对它分词。
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}//品牌条件(精确匹配QueryBuilders.termQuery)
if (params.getBrand() != null && !"".equals(params.getBrand())) {
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}//星级匹配
if (params.getStarName() != null && !"".equals(params.getStarName())) {
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
//价格条件 (范围匹配QueryBuilders.rangeQuery)
if (params.getMinPrice() != null & params.getMaxPrice() != null) {
boolQuery.filter(QueryBuilders.rangeQuery("price")
.gte(params.getMinPrice()).lt(params.getMaxPrice()));
//gte大于等于 gt大于 lte小于等于 lt小于
}
//分页内容
int page = params.getPage();
int size = params.getSize();
// 偏移量 (page-1)*size
request.source().from((page - 1) * size).size(size);
//排序 按地理坐标排序
if (params.getLocation()!= null&&!"".equals(params.getLocation())) {
request.source().sort(SortBuilders
//参数1 地理位置的字段 参数2 地理坐标
.geoDistanceSort("location",new GeoPoint(params.getLocation()))
.order(SortOrder.ASC) //排序(升序)
.unit(DistanceUnit.KILOMETERS) //设置单位km
);
}
//发送请求
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
return getPageResult(search);
}
//对结果进行处理
private PageResult getPageResult(SearchResponse search) {
SearchHits searchHits = search.getHits();
PageResult pageResult = new PageResult();
pageResult.setTotal(searchHits.getTotalHits().value);
SearchHit[] hits = searchHits.getHits();
ArrayList<HotelDoc> list = new ArrayList<>();
for (SearchHit hit : hits) {
// 获取文档source
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
//获取排序值
Object[] sortValues = hit.getSortValues();
if (sortValues.length>0){
Object sortValue = sortValues[0];
hotelDoc.setDistance(sortValue);
}
// 放入集合
list.add(hotelDoc);
}
pageResult.setHotels(list);
// 4.4.封装返回
return pageResult;
}
}
注意事项1: 三个参数的设置
//排序 按地理坐标排序
if (params.getLocation()!= null&&!"".equals(params.getLocation())) {
request.source().sort(SortBuilders
//参数1 地理位置的字段 参数2 地理坐标
.geoDistanceSort("location",new GeoPoint(params.getLocation()))
.order(SortOrder.ASC) //排序(升序)
.unit(DistanceUnit.KILOMETERS) //设置单位km
);
}
因为我接受的String类型的坐标参数 所以 new GeoPoint 来操作
注意事项2:
Object[] sortValues = hit.getSortValues();
if (sortValues.length>0){
Object sortValue = sortValues[0];
hotelDoc.setDistance(sortValue);
}
判断地址有没有传值过来 (因为查询第一次是没值的)
通过hit.getSortValues()获取坐标集合 然后取下标为0的坐标set方法设置到hotelDoc中返回
其实可以将判断的语句封装为一个方法 这样看起来不会那么臃肿