腾讯云 门户网站建设,怎么做国外的网站推广,网站开发成本分析,wordpress怎么转移黑马旅游案例记录 11.9.黑马旅游案例11.9.1.酒店搜索和分页11.9.1.1.需求分析11.9.1.2.定义实体类11.9.1.3.定义controller11.9.1.4.实现搜索业务 11.9.2.酒店结果过滤11.9.2.1.需求分析11.9.2.2.修改实体类11.9.2.3.修改搜索业务 11.9.3.我周边的酒店11.9.3.1.需求分析11.9.3.… 黑马旅游案例记录 11.9.黑马旅游案例11.9.1.酒店搜索和分页11.9.1.1.需求分析11.9.1.2.定义实体类11.9.1.3.定义controller11.9.1.4.实现搜索业务 11.9.2.酒店结果过滤11.9.2.1.需求分析11.9.2.2.修改实体类11.9.2.3.修改搜索业务 11.9.3.我周边的酒店11.9.3.1.需求分析11.9.3.2.修改实体类11.9.3.3.距离排序API11.9.3.4.添加距离排序11.9.3.5.排序距离显示 11.9.4.酒店竞价排名11.9.4.1.需求分析11.9.4.2.修改HotelDoc实体11.9.4.3.添加广告标记11.9.4.4.添加算分函数查询 11.9.黑马旅游案例
下面我们通过黑马旅游的案例来实战演练下之前学习的知识。
我们实现四部分功能
酒店搜索和分页酒店结果过滤我周边的酒店酒店竞价排名
启动我们提供的hotel-demo项目其默认端口是8089访问http://localhost:8089就能看到项目页面了 11.9.1.酒店搜索和分页
案例需求实现黑马旅游的酒店搜索功能完成关键字搜索和分页
11.9.1.1.需求分析
在项目的首页有一个大大的搜索框还有分页按钮 点击搜索按钮可以看到浏览器控制台发出了请求 请求参数如下 由此可以知道我们这个请求的信息如下
请求方式POST请求路径/hotel/list请求参数JSON对象包含4个字段 key搜索关键字page页码size每页大小sortBy排序目前暂不实现 返回值分页查询需要返回分页结果PageResult包含两个属性 total总条数ListHotelDoc当前页的数据
因此我们实现业务的流程如下
步骤一定义实体类接收请求参数的JSON对象步骤二编写controller接收页面的请求步骤三编写业务实现利用RestHighLevelClient实现搜索、分页
11.9.1.2.定义实体类
实体类有两个一个是前端的请求参数实体一个是服务端应该返回的响应结果实体。
1请求参数
前端请求的json结构如下
{key: 搜索关键字,page: 1,size: 3,sortBy: default
}因此我们在cn.itcast.hotel.pojo包下定义一个实体类
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返回值
分页查询需要返回分页结果PageResult包含两个属性
total总条数ListHotelDoc当前页的数据
因此我们在cn.itcast.hotel.pojo中定义返回结果
package cn.itcast.hotel.pojo;import lombok.Data;import java.util.List;Data
public class PageResult {private Long total;private ListHotelDoc hotels;public PageResult() {}public PageResult(Long total, ListHotelDoc hotels) {this.total total;this.hotels hotels;}
}11.9.1.3.定义controller
定义一个HotelController声明查询接口满足下列要求
请求方式Post请求路径/hotel/list请求参数对象类型为RequestParam返回值PageResult包含两个属性 Long total总条数ListHotelDoc hotels酒店数据
因此我们在cn.itcast.hotel.web中定义HotelController
RestController
RequestMapping(/hotel)
public class HotelController {Autowiredprivate IHotelService hotelService;// 搜索酒店数据PostMapping(/list)public PageResult search(RequestBody RequestParams params){return hotelService.search(params);}
}11.9.1.4.实现搜索业务
我们在controller调用了IHotelService并没有实现该方法因此下面我们就在IHotelService中定义方法并且去实现业务逻辑。
1在cn.itcast.hotel.service中的IHotelService接口中定义一个方法
/*** 根据关键字搜索酒店信息* param params 请求参数对象包含用户输入的关键字 * return 酒店文档列表*/
PageResult search(RequestParams params);2实现搜索业务肯定离不开RestHighLevelClient我们需要把它注册到Spring中作为一个Bean。在cn.itcast.hotel中的HotelDemoApplication中声明这个Bean
Bean
public RestHighLevelClient client(){return new RestHighLevelClient(RestClient.builder(HttpHost.create(http://192.168.150.101:9200)));
}3在cn.itcast.hotel.service.impl中的HotelService中实现search方法
Override
public PageResult search(RequestParams params) {try {// 1.准备RequestSearchRequest request new SearchRequest(hotel);// 2.准备DSL// 2.1.queryString key params.getKey();BoolQueryBuilder boolQuery QueryBuilders.boolQuery();if (key null || .equals(key)) {boolQuery.must(QueryBuilders.matchAllQuery());} else {boolQuery.must(QueryBuilders.matchQuery(all, key));}
request.source().query(boolQuery);// 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.遍历ListHotelDoc hotels new ArrayList();for (SearchHit hit : hits) {// 获取文档sourceString json hit.getSourceAsString();// 反序列化HotelDoc hotelDoc JSON.parseObject(json, HotelDoc.class);// 放入集合hotels.add(hotelDoc);}// 4.4.封装返回return new PageResult(total, hotels);
}11.9.2.酒店结果过滤
需求添加品牌、城市、星级、价格等过滤功能
11.9.2.1.需求分析
在页面搜索框下面会有一些过滤项 传递的参数如图 包含的过滤条件有
brand品牌值city城市minPrice~maxPrice价格范围starName星级
我们需要做两件事情
修改请求参数的对象RequestParams接收上述参数修改业务逻辑在搜索条件之外添加一些过滤条件
11.9.2.2.修改实体类
修改在cn.itcast.hotel.pojo包下的实体类RequestParams
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;
}11.9.2.3.修改搜索业务
在HotelService的search方法中只有一个地方需要修改requet.source().query( … )其中的查询条件。
在之前的业务中只有match查询根据关键字搜索现在要添加条件过滤包括
品牌过滤是keyword类型用term查询星级过滤是keyword类型用term查询价格过滤是数值类型用range查询城市过滤是keyword类型用term查询
多个查询条件组合肯定是boolean查询来组合
关键字搜索放到must中参与算分其它过滤条件放到filter中不参与算分
因为条件构建的逻辑比较复杂这里先封装为一个函数 buildBasicQuery的代码如下
private void buildBasicQuery(RequestParams params, SearchRequest request) {// 1.构建BooleanQueryBoolQueryBuilder 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.放入sourcerequest.source().query(boolQuery);
}11.9.3.我周边的酒店
需求我附近的酒店
11.9.3.1.需求分析
在酒店列表页的右侧有一个小地图点击地图的定位按钮地图会找到你所在的位置 并且在前端会发起查询请求将你的坐标发送到服务端 我们要做的事情就是基于这个location坐标然后按照距离对周围酒店排序。实现思路如下
修改RequestParams参数接收location字段修改search方法业务逻辑如果location有值添加根据geo_distance排序的功能
11.9.3.2.修改实体类
修改在cn.itcast.hotel.pojo包下的实体类RequestParams
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;
}
11.9.3.3.距离排序API
我们以前学习过排序功能包括两种
普通字段排序地理坐标排序
我们只讲了普通字段排序对应的java写法。地理坐标排序只学过DSL语法如下
GET /indexName/_search
{query: {match_all: {}},sort: [{price: asc },{_geo_distance : {FIELD : 纬度经度,order : asc,unit : km}}]
}对应的java代码示例 11.9.3.4.添加距离排序
在cn.itcast.hotel.service.impl的HotelService的search方法中添加一个排序功能 完整代码
Override
public PageResult search(RequestParams params) {try {// 1.准备RequestSearchRequest request new SearchRequest(hotel);// 2.准备DSL// 2.1.querybuildBasicQuery(params, request);// 2.2.分页int page params.getPage();int size params.getSize();request.source().from((page - 1) * size).size(size);// 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));}// 3.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 4.解析响应return handleResponse(response);} catch (IOException e) {throw new RuntimeException(e);}
}11.9.3.5.排序距离显示
重启服务后测试我的酒店功能 发现确实可以实现对我附近酒店的排序不过并没有看到酒店到底距离我多远这该怎么办
排序完成后页面还要获取我附近每个酒店的具体距离值这个值在响应结果中是独立的 因此我们在结果解析阶段除了解析source部分以外还要得到sort部分也就是排序的距离然后放到响应结果中。
我们要做两件事
修改HotelDoc添加排序距离字段用于页面显示修改HotelService类中的handleResponse方法添加对sort值的获取
1修改HotelDoc类添加距离字段
package cn.itcast.hotel.pojo;import lombok.Data;
import lombok.NoArgsConstructor;Data
NoArgsConstructor
public class HotelDoc {private Long id;private String name;private String address;private Integer price;private Integer score;private String brand;private String city;private String starName;private String business;private String location;private String pic;// 排序时的 距离值private Object distance;public HotelDoc(Hotel hotel) {this.id hotel.getId();this.name hotel.getName();this.address hotel.getAddress();this.price hotel.getPrice();this.score hotel.getScore();this.brand hotel.getBrand();this.city hotel.getCity();this.starName hotel.getStarName();this.business hotel.getBusiness();this.location hotel.getLatitude() , hotel.getLongitude();this.pic hotel.getPic();}
}
2修改HotelService中的handleResponse方法 重启后测试发现页面能成功显示距离了 11.9.4.酒店竞价排名
需求让指定的酒店在搜索结果中排名置顶
11.9.4.1.需求分析
要让指定酒店在搜索结果中排名置顶效果如图 页面会给指定的酒店添加广告标记。
那怎样才能让指定的酒店排名置顶呢
我们之前学习过的function_score查询可以影响算分算分高了自然排名也就高了。而function_score包含3个要素
过滤条件哪些文档要加分算分函数如何计算function score加权方式function score 与 query score如何运算
这里的需求是让指定酒店排名靠前。因此我们需要给这些酒店添加一个标记这样在过滤条件中就可以根据这个标记来判断是否要提高算分。
比如我们给酒店添加一个字段isADBoolean类型
true是广告false不是广告
这样function_score包含3个要素就很好确定了
过滤条件判断isAD 是否为true算分函数我们可以用最简单暴力的weight固定加权值加权方式可以用默认的相乘大大提高算分
因此业务的实现步骤包括 给HotelDoc类添加isAD字段Boolean类型 挑选几个你喜欢的酒店给它的文档数据添加isAD字段值为true 修改search方法添加function score功能给isAD值为true的酒店增加权重
11.9.4.2.修改HotelDoc实体
给cn.itcast.hotel.pojo包下的HotelDoc类添加isAD字段 11.9.4.3.添加广告标记
接下来我们挑几个酒店添加isAD字段设置为true
POST /hotel/_update/1902197537
{doc: {isAD: true}
}
POST /hotel/_update/2056126831
{doc: {isAD: true}
}
POST /hotel/_update/1989806195
{doc: {isAD: true}
}
POST /hotel/_update/2056105938
{doc: {isAD: true}
}11.9.4.4.添加算分函数查询
接下来我们就要修改查询条件了。之前是用的boolean 查询现在要改成function_socre查询。
function_score查询结构如下 对应的JavaAPI如下 我们可以将之前写的boolean查询作为原始查询条件放到query中接下来就是添加过滤条件、算分函数、加权模式了。所以原来的代码依然可以沿用。
修改cn.itcast.hotel.service.impl包下的HotelService类中的buildBasicQuery方法添加算分函数查询
private void buildBasicQuery(RequestParams params, SearchRequest request) {// 1.构建BooleanQueryBoolQueryBuilder boolQuery QueryBuilders.boolQuery();// 关键字搜索String key params.getKey();if (key null || .equals(key)) {boolQuery.must(QueryBuilders.matchAllQuery());} else {boolQuery.must(QueryBuilders.matchQuery(all, key));}// 城市条件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),// 算分函数ScoreFunctionBuilders.weightFactorFunction(10))});request.source().query(functionScoreQuery);
}