目标:
我们实现六部分功能:
由此可以知道,我们这个请求的信息如下:
- 请求方式:POST
- 请求路径:/hotel/list
- 请求参数:JSON对象,包含4个字段:
- key:搜索关键字
- page:页码
- size:每页大小
- sortBy:排序,目前暂不实现
- 返回值:分页查询,需要返回分页结果PageResult,包含两个属性:
1)请求参数
package cn.itcast.hotel.pojo;
import lombok.Data;
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
}
2)返回值
package cn.itcast.hotel.pojo;
import lombok.Data;
import java.util.List;
@Data
public class PageResult {
private Long total;
private List<HotelDoc> hotels;
public PageResult() {
}
public PageResult(Long total, List<HotelDoc> hotels) {
this.total = total;
this.hotels = hotels;
}
}
定义controller
@RestController
@RequestMapping("/hotel")
public class HotelController {
@Autowired
private IHotelService hotelService;
// 搜索酒店数据
@PostMapping("/list")
public PageResult search(@RequestBody RequestParams params){
return hotelService.search(params);
}
}
实现业务
1)定义方法
/**
* 根据关键字搜索酒店信息
* @param params 请求参数对象,包含用户输入的关键字
* @return 酒店文档列表
*/
PageResult search(RequestParams params);
2) 注册bean
@Bean
public RestHighLevelClient client(){
return new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
3)实现search方法
@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.分页
int page = params.getPage();
int size = params.getSize();
request.source().from((page - 1) * size).size(size);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 结果解析
private PageResult handleResponse(SearchResponse response) {
// 4.解析响应
SearchHits searchHits = response.getHits();
// 4.1.获取总条数
long total = searchHits.getTotalHits().value;
// 4.2.文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
List<HotelDoc> hotels = new ArrayList<>();
for (SearchHit hit : hits) {
// 获取文档source
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
// 放入集合
hotels.add(hotelDoc);
}
// 4.4.封装返回
return new PageResult(total, hotels);
}
二.酒店结果过滤
1.需求分析
包含的过滤条件有:
- brand:品牌值
- city:城市
- minPrice~maxPrice:价格范围
- starName:星级
我们需要做两件事情:
- 修改请求参数的对象RequestParams,接收上述参数
-
2.修改实体类
@Data public class RequestParams { private String key; private Integer page; private Integer size; private String sortBy; // 下面是新增的过滤条件参数 private String city; private String brand; private String starName; private Integer minPrice; private Integer maxPrice; }
3.修改搜索业务
private void buildBasicQuery(RequestParams params, SearchRequest request) { // 1.构建BooleanQuery BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 2.关键字搜索 String key = params.getKey(); if (key == null || "".equals(key)) { boolQuery.must(QueryBuilders.matchAllQuery()); } else { boolQuery.must(QueryBuilders.matchQuery("all", key)); } // 3.城市条件 if (params.getCity() != null && !params.getCity().equals("")) { boolQuery.filter(QueryBuilders.termQuery("city", params.getCity())); } // 4.品牌条件 if (params.getBrand() != null && !params.getBrand().equals("")) { boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand())); } // 5.星级条件 if (params.getStarName() != null && !params.getStarName().equals("")) { boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName())); } // 6.价格 if (params.getMinPrice() != null && params.getMaxPrice() != null) { boolQuery.filter(QueryBuilders .rangeQuery("price") .gte(params.getMinPrice()) .lte(params.getMaxPrice()) ); } // 7.放入source request.source().query(boolQuery); }
三.我周边的酒店
1.需求分析
```java 我们要做的事情就是基于这个location坐标,然后按照距离对周围酒店排序。实现思路如下:
修改RequestParams参数,接收location字段
- 修改search方法业务逻辑,如果location有值,添加根据geo_distance排序的功能
<a name="sIFpo"></a>
### 2.修改实体类
```java
package cn.itcast.hotel.pojo;
import lombok.Data;
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
private String city;
private String brand;
private String starName;
private Integer minPrice;
private Integer maxPrice;
// 我当前的地理坐标
private String location;
}
3.功能实现
//TODO:距离排序功能
String location = params.getLocation();
if (StringUtils.isNotBlank(location)) {
//SortBuilders.geoDistanceSort构建距离排序 new GeoPoint(location) 当前自己的坐标
source.sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location))
//升序排序
.order(SortOrder.ASC)
//单位是km
.unit(DistanceUnit.KILOMETERS)
);
}
4.排序距离显示
1)修改HotelDoc类,添加距离字段
// 排序时的 距离值
private Object distance;
2)修改解析响应结果
//TODO:获取距离排序值
Object[] sortValues = hit.getSortValues();
if (sortValues.length > 0) {
hotelDoc.setDistance(sortValues[0]);
}
list.add(hotelDoc);
}
四.酒店竞价排名
1.需求分析
2.修改HotelDoc实体
//是否广告
private Boolean isAD;
3.添加广告标记
挑几个酒店,添加isAD字段,设置为true:
POST /hotel/_update/1902197537
{
"doc": {
"isAD": true
}
}
POST /hotel/_update/2056126831
{
"doc": {
"isAD": true
}
}
4.添加算分函数查询
//TODO: 算分控制
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
boolQuery, //原始的查询条件
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
//算分函数
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
//过滤条件
QueryBuilders.termQuery("isAD", true),
//计算得分的方法 weight常量分值
ScoreFunctionBuilders.weightFactorFunction(10)
)
}
//加权模式
).boostMode(CombineFunction.MULTIPLY); //默认相乘
source.query(functionScoreQueryBuilder);
五.给黑马旅游添加排序功能
1.需求分析
2.功能实现
//TODO:排序功能
if (StringUtils.isNotBlank(params.getSortBy())) {
//根据score字段 降序
FieldSortBuilder sortBuilder = SortBuilders.fieldSort("score");
sortBuilder.order(SortOrder.DESC);
//根据price排序,升序
FieldSortBuilder priceBuilder = SortBuilders.fieldSort("price");
priceBuilder.order(SortOrder.ASC);
source.sort(sortBuilder);
source.sort(priceBuilder);
}
六.给黑马旅游添加搜索关键字效果
1.需求分析
2.代码实现
//TODO:高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("name"); //那个字段高亮
highlightBuilder.preTags("<em>"); //前置标签
highlightBuilder.postTags("</em>"); //后置标签
highlightBuilder.requireFieldMatch(false); //关闭默认设置 默认高亮处理不允许搜索字段和高亮字段不一致
source.highlighter(highlightBuilder);
3.解析响应结果
//TODO:单独获取高亮处理结果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//判断
if (!CollectionUtils.isEmpty(highlightFields)) {
// 文档name字段的高亮处理结果
HighlightField highlightField = highlightFields.get("name");
if (highlightField != null) {
//拼接高亮字段
String highlightResultStr = StringUtils.join(highlightField.getFragments());
//用高亮处理结果替换之前的name字段内容
hotelDoc.setName(highlightResultStr);
}
}