1、搜索、分页
1、前端会传page和pageSize,即当前页码和页容量,可以通过(page - 1)*size计算偏移量以确定需要展示的数据
2、搜索可以直接找到创建索引库时特地额外增加的all字段进行匹配
2、条件过滤
1、城市、品牌、星级、价格范围,每个条件都要做健壮性判断,即只有在有需要的时候才运行。其中前三者是精确匹配,得用termQuery,价格是范围匹配,得用rangeQuery
3、距离排序
1、在前端传过来定位的时候,需要在查询中添加定位信息,es会根据定位返回与文档中的定位的距离值。在得到查询结果后,获取距离值放到返回对象中以便前端展示。
4、置顶
1、需要保证es文档中有标识置顶因素的索引,比如isAD:true
2、在后端向索引库查询数据的时候,可以判断其是否带有该置顶标识
3、如带有置顶标识,则执行相应的算分控制,以提高其在前端展示中的权重
全程代码如下:
java
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
@Override
public PageResult search(RequestParams pageParams) {
try {
//1.准备Request
SearchRequest request = new SearchRequest("hotel");
//2.准备dsl
//构建dsl
buildBasicQuery(pageParams,request);
//价格排序
// request.source().sort("price", SortOrder.ASC);
//距离排序
if(pageParams.getLocation()!=null && !pageParams.getLocation().equals("")){
request.source().sort(
SortBuilders.geoDistanceSort("location",new GeoPoint(pageParams.getLocation()))
.order(SortOrder.ASC)
.unit(DistanceUnit.KILOMETERS));
}
//构建分页
int page = pageParams.getPage();
int size = pageParams.getSize();
request.source().from((page - 1)*size).size(size);
//3.发送请求,得到响应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析响应
return extracted(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void buildBasicQuery(RequestParams pageParams,SearchRequest request) {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//关键字检索
String key = pageParams.getKey();
if (key == null || "".equals(key)) {
boolQuery.must(QueryBuilders.matchAllQuery());
}else{
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
//城市
if (pageParams.getCity() != null && !pageParams.getCity().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("city", pageParams.getCity()));
}
//品牌
if (pageParams.getBrand() != null && !pageParams.getBrand().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("brand", pageParams.getBrand()));
}
//星级
if (pageParams.getStarName() != null && !pageParams.getStarName().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("starName", pageParams.getStarName()));
}
//价格范围
if (pageParams.getMaxPrice() != null && pageParams.getMinPrice()!=null) {
boolQuery.filter(QueryBuilders.rangeQuery("price")
.gte(pageParams.getMinPrice()).lte(pageParams.getMaxPrice()));
}
//2、算分控制
FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
//原始查询,相关性算分的查询
boolQuery,
//function score的数组
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
//其中的一个function score 元素
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
//过滤条件
QueryBuilders.termQuery("isAD",true),
//算分函数
ScoreFunctionBuilders.weightFactorFunction(Integer.MAX_VALUE)
)
});
request.source().query(functionScoreQuery);
}
private static PageResult extracted(SearchResponse response) {
//获取返回结果的hites部分
SearchHits hits = response.getHits();
//获取hits中的value部分
long value = hits.getTotalHits().value;
System.out.println(value);
//获取hits中的value中的hits部分
SearchHit[] hits1 = hits.getHits();
//新建个返回对象的列表以返回对象集合
List<HotelDoc> list = new ArrayList<>();
for (SearchHit hit : hits1) {
String string = hit.getSourceAsString();
//反序列化
HotelDoc hotelDoc = JSON.parseObject(string, HotelDoc.class);
//获取距离值
Object[] sortValues = hit.getSortValues();
if (sortValues.length > 0) {
Object sortValue = sortValues[0];
hotelDoc.setDistance(sortValue);
}
System.out.println(hotelDoc);
list.add(hotelDoc);
}
return new PageResult(value,list);
}
}