Elasticsearch学习笔记-黑马旅游网实践
Posted LL.LEBRON
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elasticsearch学习笔记-黑马旅游网实践相关的知识,希望对你有一定的参考价值。
文章目录
视频指路👉 B站黑马微服务超级推荐!!
黑马旅游网实践
1.酒店搜索和分页
1.1 需求分析
需求:实现黑马旅游的酒店搜索功能,完成关键字搜索和分页
点击搜索按钮,可以看到浏览器控制台发出了请求:
由此可以知道,我们这个请求的信息如下:
- 请求方式:
POST
- 请求路径:
/hotel/list
- 请求参数:JSON对象,包含4个字段:
key
:搜索关键字page
:页码size
:每页大小sortBy
:排序,目前暂不实现
- 返回值:分页查询,需要返回分页结果
PageResult
,包含两个属性:total
:总条数List<HotelDoc>
:当前页的数据
因此,我们实现业务的流程如下:
- 步骤一:定义实体类
RequestParams
,接收请求参数的JSON对象 - 步骤二:编写
controller
,接收页面的请求 - 步骤三:编写业务实现,利用
RestHighLevelClient
实现搜索、分页
1.2 定义实体类
实体类有两个:
- 一个是前端的请求参数实体
RequestParams
- 一个是服务端应该返回的响应结果实体
PageResult
(1)请求参数
前端请求的json结构如下:
"key": "搜索关键字",
"page": 1,
"size": 3,
"sortBy": "default"
因此,我们在cn.itcast.hotel.pojo
包下定义一个实体类RequestParams
:
@Data
public class RequestParams
private String key;
private Integer page;
private Integer size;
private String sortBy;
(2)返回值
分页查询,需要返回分页结果PageResult
,包含两个属性:
total
:总条数List<HotelDoc>
:当前页的数据
因此,我们在cn.itcast.hotel.pojo
中定义返回结果类PageResult
:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult
private Long total;
private List<HotelDoc> hotels;
1.3 定义controller
定义一个HotelController
,声明查询接口,满足下列要求:
- 请求方式:
Post
- 请求路径:
/hotel/list
- 请求参数:对象,类型为
RequestParam
- 返回值:
PageResult
,包含两个属性Long total
:总条数List<HotelDoc> hotels
:酒店数据
因此,我们在cn.itcast.hotel.web
中定义HotelController
:
@RestController
@RequestMapping("/hotel")
public class HotelController
@Autowired
private IHotelService hotelService;
// 搜索酒店数据
@PostMapping("/list")
public PageResult search(@RequestBody RequestParams params)
return hotelService.search(params);
1.4 实现搜索业务
(1)在cn.itcast.hotel.service
中的IHotelService
接口中定义一个方法:
/**
* 根据关键字搜索酒店信息
* @param params 请求参数对象,包含用户输入的关键字
* @return 酒店文档列表
*/
PageResult search(RequestParams params);
(2)实现搜索业务,肯定离不开RestHighLevelClient
,我们需要把它注册到Spring中作为一个Bean
。在cn.itcast.hotel
中的HotelDemoApplication
中声明这个Bean:
@MapperScan("cn.itcast.hotel.mapper")
@SpringBootApplication
public class HotelDemoApplication
public static void main(String[] args)
SpringApplication.run(HotelDemoApplication.class, args);
@Bean
public RestHighLevelClient client()
return new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.75.130:9200")
));
(3)在cn.itcast.hotel.service.impl
中的HotelService
中实现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))
boolQuery.must(QueryBuilders.matchAllQuery());
else
boolQuery.must(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);
解析相响应方法handleResponse
:
// 结果解析
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);
2.酒店结果过滤
2.1 需求分析
需求:添加品牌、城市、星级、价格等过滤功能
如图:在页面搜索框下面,会有一些过滤项:
传递的参数如图:
包含的过滤条件有:
brand
:品牌值city
:城市minPrice~maxPrice
:价格范围starName
:星级
我们需要做两件事情:
- 修改请求参数的对象
RequestParams
,接收上述参数 - 修改业务逻辑,在搜索条件之外,添加一些过滤条件
2.2 修改实体类RequestParams
修改在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;
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.构建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);
3.查询周边的酒店
3.1 需求分析
需求:查询我附近的酒店
在酒店列表页的右侧,有一个小地图,点击地图的定位按钮,地图会找到你所在的位置:
并且,在前端会发起查询请求,将你的坐标发送到服务端:
我们要做的事情就是基于这个location
坐标,然后按照距离对周围酒店排序。实现思路如下:
- 修改
RequestParams
参数,接收location
字段 - 修改
search
方法业务逻辑,如果location
有值,添加根据geo_distance
排序的功能
3.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;
// 我当前的地理坐标
private String location;
3.3 距离排序API
地理坐标排序的DSL语法,如下:
GET /indexName/_search
"query":
"match_all":
,
"sort": [
"price": "asc"
,
"_geo_distance" :
"FIELD" : "纬度,经度",
"order" : "asc",
"unit" : "km"
]
对应的java代码示例:
3.4 添加距离排序
在cn.itcast.hotel.service.impl
的HotelService
的search
方法中,添加一个排序功能:
代码如下:
@Override
public PageResult search(RequestParams params)
try
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
buildBasicQuery(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);
3.5 排序距离显示
重启服务后,测试我的酒店功能:
发现确实可以实现对我附近酒店的排序,不过并没有看到酒店到底距离我多远
排序完成后,页面还要获取我附近每个酒店的具体距离值,这个值在响应结果中是独立的:
因此,我们在结果解析阶段,除了解析source
部分以外,还要得到sort
部分,也就是排序的距离,然后放到响应结果中。
我们要做两件事:
- 修改
HotelDoc
,添加排序距离字段,用于页面显示 - 修改
HotelService
类中的handleResponse
方法,添加对sort
值的获取
(1)修改HotelDoc
类,添加距离字段:
@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
方法:
private PageResult handleResponse(SearchResponse response)
SearchHits searchHits = response.getHits();
// 4.1.总条数
long total = searchHits.getTotalHits().value;
// 4.2.获取文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
List<HotelDoc> hotels = new ArrayList<>以上是关于Elasticsearch学习笔记-黑马旅游网实践的主要内容,如果未能解决你的问题,请参考以下文章
黑马Java笔记+踩坑汇总JavaSE+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud/SpringCloudAlibaba+黑马旅游+谷粒商城
ElasticSearch-学习笔记04Java客户端操作索引库