● 酒店搜索和分页
● 酒店结果过滤
● 我周边的酒店
● 酒店竞价排名
资源链接
hoteld-demo工程:https://pan.baidu.com/s/1YlKz6vxcm7VWXAWPlUiBqg
提取码:GY66
进入hoteld-demo工程,启动服务,打开浏览器进入http://localhost:8089/即可成功访问页面
案例一: 实现黑马旅游的酒店搜索功能,完成关键字搜索和分页
1、定义实体类,接收前端请求
import lombok.Data;
@Data
public class RequestParams {
// 搜索换关键字
private String key;
// 当前页码
private Integer page;
// 分页大小
private Integer size;
// 排序字段
private String sortBy;
}
# 返回分页查询结果
import lombok.Data;
import java.util.List;
@Data
public class PageResult {
private Long total;
private List<HotelDoc> hotelDocs;
}
2、定义controller接口,接收页面请求,调用IHotelService的search方法
① controller接口
public interface IHotelService extends IService<Hotel> {
PageResult search(RequestParams params) throws IOException;
}
② web层
@RestController
@RequestMapping("/hotel")
public class HotelController {
@Autowired
private IHotelService service;
@PostMapping("/list")
public PageResult search(@RequestBody RequestParams params) throws IOException {
return service.search(params);
}
}
3、定义IHotelService中的search方法,利用match查询实现根据关键字搜索酒店信息
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
@Override
public PageResult search(RequestParams params) {
try {
// 1、创建request对象
SearchRequest request = new SearchRequest("hotel");
// 2、准备DSL
// 2.1、 query
String key = params.getKey();
if (key == null || "".equals(key)) {
request.source().query(QueryBuilders.matchAllQuery());
} else {
request.source().query(QueryBuilders.matchQuery("all", key));
}
// 2.2、 分页(利用参数中的page、size实现)
int page = params.getPage();
int size = params.getSize();
request.source().from((page - 1) * size).size(size);
// 3、发送请求
SearchResponse response = null;
response = client.search(request, RequestOptions.DEFAULT);
// 解析响应
return handleResonse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private PageResult handleResonse(SearchResponse response) {
// 4、解析响应
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
// 遍历
List<HotelDoc> hotelDocs = new ArrayList<>();
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
hotelDocs.add(hotelDoc);
}
// 封装返回
return new PageResult(total,hotelDocs);
}
}
启动服务,进行测试
案例二: 添加品牌、城市、星级、价格等过滤功能
1、修改RequestParams类(前端所传的数据),添加brand、city、starName、minPrice、maxPrice等参数
package com.GY.hotel.pojo;
import lombok.Data;
@Data
public class RequestParams {
// 搜索换关键字
private String key;
// 当前页码
private Integer page;
// 分页大小
private Integer size;
// 排序字段
private String sortBy;
// 品牌
private String brand;
// 城市
private String city;
// 星级
private String starName;
// 最低价格
private int minPrice;
// 最高价格
private int maxPrice;
}
2、修改search方法的实现,在match查询基础上添加过滤条件,在关键字搜索时,在如果brand等参数存在,对其做过滤
过滤条件包括:
● city精确匹配 (term查询)
● brand精确匹配 (term查询)
● starName精确匹配 (term查询)
● price范围过滤 (range查询)
注意事项:
● 多个条件之间是AND关系,组合多条件用BooleanQuery
● 参数存在才需要过滤,做好非空判断
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
@Override
public PageResult search(RequestParams params) {
// 查询分页
try {
// 1、创建request对象
SearchRequest request = new SearchRequest("hotel");
// 2、准备DSL
// 2.1、 query
// 构建BooleanQuery
buildBasicQuery(params, request);
// 2.2、 分页
int page = params.getPage();
int size = params.getSize();
request.source().from((page - 1) * size).size(size);
// 3、发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 解析响应
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void buildBasicQuery(RequestParams params, SearchRequest request) {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 关键字搜索
String key = params.getKey();
if (key == null || "".equals(key)) {
boolQuery.must(QueryBuilders.matchAllQuery());
} else {
boolQuery.must(QueryBuilders.matchQuery("name", key));
}
// 城市条件 (term属于过滤条件放在must中会影响得分进而影响查询性能)
if(params.getCity()!=null && !params.getCity().equals("")){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
// 品牌条件
if(params.getBrand()!=null && !params.getBrand().equals("")){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
// 星级条件
if(params.getStarName()!=null && !params.getStarName().equals("")){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
// 价格
if(params.getMinPrice()!=null && params.getMaxPrice()!=null){
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
}
request.source().query(boolQuery);
}
private PageResult handleResponse(SearchResponse response) {
// 4、解析响应
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
// 遍历
List<HotelDoc> hotels = new ArrayList<>();
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
hotels.add(hotelDoc);
}
// 封装返回
return new PageResult(total,hotels);
}
}
重新启动服务进行测试(过滤成功!!!)
案例三: 我附近的酒店
前端页面点击定位后,会将你所在的位置发送到后台。我们要根据这个坐标,将酒店结果按照到这个点的距离升序排序。
1、修改RequestParams参数,接收location字段
// 位置
private String location;
2、修改search方法业务逻辑,如果location有值,添加根据geo_distance排序的功能
// 2.3、 排序
String location = params.getLocation();
if (location!=null && !location.equals("")){
request.source().sort(SortBuilders
.geoDistanceSort("location",new GeoPoint(location))
// 排序方式
.order(SortOrder.ASC)
// 指定单位
.unit(DistanceUnit.KILOMETERS));
}
按照距离排序后,还需要显示具体的距离值:
① HotelDoc实体类中添加distance属性
② 修改结果解析响应
private PageResult handleResponse(SearchResponse response) {
// 4、解析响应
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
// 遍历
List<HotelDoc> hotels = new ArrayList<>();
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
// 获取排序值
Object[] sortValues = hit.getSortValues();
if (sortValues.length>0){
Object value = sortValues[0];
hotelDoc.setDistance(value);
}
hotels.add(hotelDoc);
}
// 封装返回
return new PageResult(total,hotels);
}
}
重新启动服务进行测试(成功显示数据且排序成功!!!)
案例四:让指定的酒店在搜索结果中排名置顶
给需要置顶的酒店文档添加一个标记。然后利用function score给带有标记的文档增加权重。效果如下所示:
1、给HotelDoc类添加isAD字段,Boolean类型
2、挑选几个你喜欢的酒店,给它的文档数据添加isAD字段,值为true
3、修改search方法,添加function score功能,给isAD值为true的酒店增加权重
private void buildBasicQuery(RequestParams params, SearchRequest request) {
// 1、构建BooleanQuery
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 关键字搜索
String key = params.getKey();
if (key == null || "".equals(key)) {
boolQuery.must(QueryBuilders.matchAllQuery());
} else {
boolQuery.must(QueryBuilders.matchQuery("name", key));
}
// 城市条件 (term属于过滤条件放在must中会影响得分进而影响查询性能)
if(params.getCity()!=null && !params.getCity().equals("")){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
// 品牌条件
if(params.getBrand()!=null && !params.getBrand().equals("")){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
// 星级条件
if(params.getStarName()!=null && !params.getStarName().equals("")){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
// 价格
if(params.getMinPrice()!=null && params.getMaxPrice()!=null){
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
}
// 2、算分控制
FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
// 原始查询,相关性算分的查询
boolQuery,
// function score的数组
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
// 其中一个 function score 元素
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
// 过滤条件
QueryBuilders.termQuery("isAD",true),
// 算分函数(分值*10)
ScoreFunctionBuilders.weightFactorFunction(10)
)
});
request.source().query(functionScoreQuery);
}
广告置顶成功