【Elasticsearch】学习笔记-黑马旅游网实践

这篇具有很好参考价值的文章主要介绍了【Elasticsearch】学习笔记-黑马旅游网实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

HotelServicesearch方法中,只有一个地方需要修改:requet.source().query( ... )其中的查询条件。

在之前的业务中,只有match查询,根据关键字搜索,现在要添加条件过滤,包括:

  • 品牌过滤:是keyword类型,用term查询

  • 星级过滤:是keyword类型,用term查询

  • 价格过滤:是数值类型,用range查询

  • 城市过滤:是keyword类型,用term查询

多个查询条件组合,肯定是boolean查询来组合:

  • 关键字搜索放到must中,参与算分

  • 其它过滤条件放到filter中,不参与算分

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

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 需求分析

需求:查询我附近的酒店

在酒店列表页的右侧,有一个小地图,点击地图的定位按钮,地图会找到你所在的位置:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

并且,在前端会发起查询请求,将你的坐标发送到服务端:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

我们要做的事情就是基于这个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代码示例:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

3.4 添加距离排序

cn.itcast.hotel.service.implHotelServicesearch方法中,添加一个排序功能:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

代码如下:

@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 排序距离显示

重启服务后,测试我的酒店功能:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

发现确实可以实现对我附近酒店的排序,不过并没有看到酒店到底距离我多远

排序完成后,页面还要获取我附近每个酒店的具体距离值,这个值在响应结果中是独立的:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

因此,我们在结果解析阶段,除了解析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方法:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

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 hotels = new ArrayList<>(hits.length);

for (SearchHit hit : hits) {

// 4.4.获取source

String json = hit.getSourceAsString();

// 4.5.反序列化,非高亮的

HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);

// 4.6.排序距离信息

Object[] sortValues = hit.getSortValues();

if (sortValues.length > 0) {

hotelDoc.setDistance(sortValues[0]);

}

// 4.7.放入集合

hotels.add(hotelDoc);

}

return new PageResult(total, hotels);

}

重启后测试,发现页面能成功显示距离了:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

4.酒店竞价排名


4.1 需求分析

需求:让指定的酒店(打了广告的)在搜索结果中排名置顶

要让指定酒店在搜索结果中排名置顶,效果如图:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

那怎样才能让指定的酒店排名置顶呢?

我们之前学习过的function_score查询可以影响算分,算分高了,自然排名也就高了。而function_score包含3个要素:

  • 过滤条件:哪些文档要加分

  • 算分函数:如何计算function score

  • 加权方式:function score 与 query score如何运算

解决办法:让指定酒店排名靠前。因此我们需要给这些酒店添加一个标记,这样在过滤条件中就可以根据这个标记来判断,是否要提高算分

比如,我们给酒店添加一个字段:isADBoolean类型:

  • true:是广告

  • false:不是广告

这样function_score包含3个要素就很好确定了:

  • 过滤条件:判断isAD 是否为true

  • 算分函数:我们可以用最简单暴力的weight,固定加权值

  • 加权方式:可以用默认的相乘,大大提高算分

因此,业务的实现步骤包括:

  1. HotelDoc类添加isAD字段,Boolean类型

  2. 挑选几个你喜欢的酒店,给它的文档数据添加isAD字段,值为true

  3. 修改search方法,添加function score功能,给isAD值为true的酒店增加权重

4.2 修改HotelDoc实体

cn.itcast.hotel.pojo包下的HotelDoc类添加isAD字段:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

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

}

}

4.4 添加算分函数查询

接下来我们就要修改查询条件了。之前是用的boolean 查询,现在要改成function_socre查询。

function_score查询结构如下:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

对应的JavaAPI如下:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

我们可以将之前写的boolean查询作为原始查询条件放到query中,接下来就是添加过滤条件算分函数加权模式了。所以原来的代码依然可以沿用。

修改cn.itcast.hotel.service.impl包下的HotelService类中的buildBasicQuery方法,添加算分函数查询:

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(“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);

}

5.酒店实现聚合


5.1 需求分析

需求:搜索页面的品牌、城市等信息不应该是在页面写死,而是通过聚合索引库中的酒店数据得来的:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

分析

首先我们看看为什么需要实现聚合?

目前,页面的城市列表、星级列表、品牌列表都是写死的,并不会随着搜索结果的变化而变化。但是用户搜索条件改变时,搜索结果会跟着变化。

例如:用户搜索“东方明珠”,那搜索的酒店肯定是在上海东方明珠附近,因此,城市只能是上海,此时城市列表中就不应该显示北京、深圳、杭州这些信息了。也就是说,搜索结果中包含哪些城市,页面就应该列出哪些城市;搜索结果中包含哪些品牌,页面就应该列出哪些品牌。

如何得知搜索结果中包含哪些品牌?如何得知搜索结果中包含哪些城市?

解决办法:

使用聚合功能,利用Bucket聚合,对搜索结果中的文档基于品牌分组、基于城市分组,就能得知包含哪些品牌、哪些城市了。

因为是对搜索结果聚合,因此聚合是限定范围的聚合,也就是说聚合的限定条件跟搜索文档的条件一致。

查看浏览器可以发现,前端其实已经发出了这样的一个请求:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

请求参数与搜索文档的参数完全一致

返回值类型就是页面要展示的最终结果:

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

结果是一个Map结构:

  • key是字符串,城市、星级、品牌、价格

  • value是集合,例如多个城市的名称

5.2 业务实现

cn.itcast.hotel.web包的HotelController中添加一个方法,遵循下面的要求:

  • 请求方式:POST

  • 请求路径:/hotel/filters

  • 请求参数:RequestParams,与搜索文档的参数一致

  • 返回值类型:Map<String, List<String>>

代码:

@PostMapping(“filters”)

public Map<String, List> getFilters(@RequestBody RequestParams params){

return hotelService.getFilters(params);

}

cn.itcast.hotel.service.IHotelService中定义新方法:

Map<String, List> filters(RequestParams params);

cn.itcast.hotel.service.impl.HotelService中实现该方法:

@Override

public Map<String, List> filters(RequestParams params) {

try {

// 1.准备Request

SearchRequest request = new SearchRequest(“hotel”);

// 2.准备DSL

// 2.1.query

buildBasicQuery(params, request);

// 2.2.设置size

request.source().size(0);

// 2.3.聚合

buildAggregation(request);

// 3.发出请求

SearchResponse response = client.search(request, RequestOptions.DEFAULT);

// 4.解析结果

Map<String, List> result = new HashMap<>();

Aggregations aggregations = response.getAggregations();

// 4.1.根据品牌名称,获取品牌结果

List brandList = getAggByName(aggregations, “brandAgg”);

result.put(“品牌”, brandList);

// 4.2.根据品牌名称,获取品牌结果

List cityList = getAggByName(aggregations, “cityAgg”);

result.put(“城市”, cityList);

// 4.3.根据品牌名称,获取品牌结果

List starList = getAggByName(aggregations, “starAgg”);

result.put(“星级”, starList);

return result;

} catch (IOException e) {

throw new RuntimeException(e);

}

}

//把对需要字段的聚合抽取出来,这里是品牌,城市,星级

private void buildAggregation(SearchRequest request) {

request.source().aggregation(AggregationBuilders

.terms(“brandAgg”)

.field(“brand”)

.size(100)

);

request.source().aggregation(AggregationBuilders

.terms(“cityAgg”)

.field(“city”)

.size(100)

);

request.source().aggregation(AggregationBuilders

.terms(“starAgg”)

.field(“starName”)

.size(100)

);

}

//把通过聚合名称获取聚合结果封装成一个方法

private List getAggByName(Aggregations aggregations, String aggName) {

// 4.1.根据聚合名称获取聚合结果

Terms brandTerms = aggregations.get(aggName);

// 4.2.获取buckets

List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();

// 4.3.遍历

List brandList = new ArrayList<>();

for (Terms.Bucket bucket : buckets) {

// 4.4.获取key

String key = bucket.getKeyAsString();

brandList.add(key);

}

return brandList;

}

6.酒店数据自动补全


6.1 需求分析

现在,我们的hotel索引库还没有设置拼音分词器,需要修改索引库中的配置。但是我们知道索引库是无法修改的,只能删除然后重新创建。

另外,我们需要添加一个字段,用来做自动补全,将brand、suggestion等都放进去,作为自动补全的提示。

因此,总结一下,我们需要做的事情包括:

  1. 修改hotel索引库结构,设置自定义拼音分词器

  2. 修改索引库的nameall字段,使用自定义分词器

  3. 索引库添加一个新字段suggestion,类型为completion类型,使用自定义的分词器

  4. HotelDoc类添加suggestion字段,内容包含brandbusiness

  5. 重新导入数据到hotel

6.2 修改酒店映射结构

代码如下:

// 酒店数据索引库

PUT /hotel

{

“settings”: {

“analysis”: {

“analyzer”: {

“text_anlyzer”: {

“tokenizer”: “ik_max_word”,

“filter”: “py”

},

“completion_analyzer”: {

“tokenizer”: “keyword”,

“filter”: “py”

}

},

“filter”: {

“py”: {

“type”: “pinyin”,

“keep_full_pinyin”: false,

“keep_joined_full_pinyin”: true,

“keep_original”: true,

“limit_first_letter_length”: 16,

“remove_duplicated_term”: true,

“none_chinese_pinyin_tokenize”: false

}

}

}

},

“mappings”: {

“properties”: {

“id”:{

“type”: “keyword”

},

“name”:{

“type”: “text”,

“analyzer”: “text_anlyzer”,

“search_analyzer”: “ik_smart”,

“copy_to”: “all”

},

“address”:{

“type”: “keyword”,

“index”: false

},

“price”:{

“type”: “integer”

},

“score”:{

“type”: “integer”

},

“brand”:{

“type”: “keyword”,

“copy_to”: “all”

},

“city”:{

“type”: “keyword”

},

“starName”:{

“type”: “keyword”

},

“business”:{

“type”: “keyword”,

“copy_to”: “all”

},

“location”:{

“type”: “geo_point”

},

“pic”:{

“type”: “keyword”,

“index”: false

},

“all”:{

“type”: “text”,

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

最后

现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
_to": “all”

},

“city”:{

“type”: “keyword”

},

“starName”:{

“type”: “keyword”

},

“business”:{

“type”: “keyword”,

“copy_to”: “all”

},

“location”:{

“type”: “geo_point”

},

“pic”:{

“type”: “keyword”,

“index”: false

},

“all”:{

“type”: “text”,

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-ZMdtqdnm-1712702137995)]

[外链图片转存中…(img-01jfcgtB-1712702137996)]

[外链图片转存中…(img-FbCDT7yl-1712702137996)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

【Elasticsearch】学习笔记-黑马旅游网实践,程序员,elasticsearch,学习,笔记

最后

现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**

[外链图片转存中…(img-GChtUa1K-1712702137996)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!文章来源地址https://www.toymoban.com/news/detail-853007.html

到了这里,关于【Elasticsearch】学习笔记-黑马旅游网实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包