【业务功能篇90】微服务-springcloud-检索服务-ElasticSearch实战运用-DSL语句

商城检索服务

image.png

1.检索页面的搭建

  商品检索页面我们放在search服务中处理,首页我们需要在mall-search服务中支持Thymeleaf。添加对应的依赖

        <!-- 添加Thymeleaf的依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>

然后我们拷贝模板文件到template目录下,然后不要忘记添加Thymeleaf的名称空间

image.png

需要把相关的静态资源文件拷贝到Nginx服务中。目录结构是:/mydata/nginx/html/static/search/

image.png

我们需要修改index.html页面中的资源的路径

image.png

然后我们要通过 msb.search.com 来访问我们的检索服务,那么就需要设置对应的host文件

image.png

然后我们就需要修改Nginx的配置

image.png

这时我需要在修改网关的服务,根据我们的域名访问,那么需要网关路由到我们的检索服务中

image.png

然后我们就可以重启相关的服务 ,来测试了

image.png

2.检索服务

2.1 创建对应VO

  我们需要检索数据库中的相关的商品信息,那么我们就需要提交相关的检索条件,为了统一的管理提交的数据,我们需要创建一个VO来封装信息。

/*** 封装页面所有可能提交的查询条件*/
@Data
public class SearchParam {private String keyword; // 页面传递的查询全文匹配的关键字private Long catalog3Id;// 需要根据分类查询的编号/*** sort=salaCount_asc/desc* sort=skuPrice_asc/desc* sort=hotScore_asc/desc*/private String sort; // 排序条件// 查询的筛选条件  hasStock=0/1;private Integer hasStock ; // 是否只显示有货// brandId=1&brandId=2private List<Long> brandId; // 按照品牌来查询,可以多选// skuPrice=200_300// skuPrice=_300// skuPrice=200_private String skuPrice; // 价格区间查询// 不同的属性  attrs:1_苹果:6.5寸private List<String> attrs; // 按照属性信息进行筛选private Integer pageNum; // 页码}

  然后就是检索后的数据我们需要封装的VO对象,定义如下:

package com.msb.mall.mallsearch.vo;import com.msb.common.dto.es.SkuESModel;
import lombok.Data;import java.util.List;/*** 封装检索后的响应信息*/
@Data
public class SearchResult {private List<SkuESModel> products; // 查询到的所有的商品信息 满足条件// 分页信息private Integer pageNum; // 当前页private Long total;  // 总的记录数private Integer totalPages; // 总页数// 当前查询的所有的商品涉及到的所有的品牌信息private List<BrandVO> brands;// 当前查询的所有的商品涉及到的所有的属性信息private List<AttrVo> attrs;// 当前查询的所有商品涉及到的所有的类别信息private List<CatalogVO> catalogs;@Datapublic static class CatalogVO{private Long catalogId;private String catalogName;}/*** 品牌的相关信息*/@Datapublic static class BrandVO{private Long brandId; // 品牌的编号private String brandName; // 品牌的名称private String brandImg; // 品牌的图片}@Datapublic static class AttrVo{private Long attrId; // 属性的编号private String attrName; // 属性的名称private List<String> attrValue; // 属性的值}}

2.2 构建查询DSL语句

  我们需要根据基本的检索条件来封装对应的DSL语句

  • 查询关键字 模糊匹配
  • 过滤(分类,品牌,属性,价格区间,库存…)
  • 排序
  • 分页
  • 高亮
GET /product/_search
{"query": {"bool": {"must": [{"match": {"subTitle": "华为"}}],"filter": [{"term": {"catalogId": "225"}},{"terms": {"brandId": ["13","16","14"]}},{"range": {"skuPrice": {"gte": 10,"lte": 12000}}},{"nested": {"path": "attrs","query": {"bool": {"must": [{"term": {"attrs.attrId": {"value": "9"}}},{"terms": {"attrs.attrValue": ["12","08","11"]}}]}}}}]}},"sort": [{"skuPrice": {"order": "desc"}}],"from": 0,"size": 20,"highlight": {"fields": {"subTitle": {}},"pre_tags": "<b style='color:red'>","post_tags": "<b>"}
}

2.3 构建SearchRequest对象

  根据客户端提交的检索的信息,我们需要封装为对应的SearchRequest对象,然后通过ES的API来检索数据。

    /*** 构建检索的请求* 模糊匹配,关键字匹配* 过滤(类别,品牌,属性,价格区间,库存)* 排序* 分页* 高亮* 聚合分析* @param param* @return*/private SearchRequest buildSearchRequest(SearchParam param) {SearchRequest searchRequest = new SearchRequest();searchRequest.indices(ESConstant.PRODUCT_INDEX);SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 构建具体的检索的条件// 1.构建bool查询BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 1.1 关键字的条件if(!StringUtils.isEmpty(param.getKeyword())){boolQuery.must(QueryBuilders.matchQuery("subTitle",param.getKeyword()));}// 1.2 类别的检索条件if(param.getCatalog3Id() != null){boolQuery.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));}// 1.3 品牌的检索条件if(param.getBrandId() != null && param.getBrandId().size() > 0){boolQuery.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));}// 1.4 是否有库存if(param.getHasStock() != null){boolQuery.filter(QueryBuilders.termQuery("hasStock",param.getHasStock() == 1));}// 1.5 根据价格区间来检索if(!StringUtils.isEmpty(param.getSkuPrice())){String[] msg = param.getSkuPrice().split("_");RangeQueryBuilder skuPrice = QueryBuilders.rangeQuery("skuPrice");if(msg.length == 2){// 说明是 200_300skuPrice.gte(msg[0]);skuPrice.lte(msg[1]);}else if(msg.length == 1){// 说明是 _300  200_if(param.getSkuPrice().endsWith("_")){// 说明是 200_skuPrice.gte(msg[0]);}if(param.getSkuPrice().startsWith("_")){// 说明是 _300skuPrice.lte(msg[0]);}}boolQuery.filter(skuPrice);}// 1.6 属性的检索条件 attrs=20_8英寸:10英寸&attrs=19_64GB:32GBif(param.getAttrs() != null && param.getAttrs().size() > 0){for (String attrStr : param.getAttrs()) {BoolQueryBuilder boolNestedQuery = QueryBuilders.boolQuery();// attrs=19_64GB:32GB 我们首先需要根据 _ 做分割String[] attrStrArray = attrStr.split("_");// 属性的编号String attrId = attrStrArray[0];// 64GB:32GB  获取属性的值String[] values = attrStrArray[1].split(":");// 拼接组合条件boolNestedQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));boolNestedQuery.must(QueryBuilders.termsQuery("attrs.attrValue",values));NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", boolNestedQuery, ScoreMode.None);boolQuery.filter(nestedQuery);}}sourceBuilder.query(boolQuery);// 2.排序if(!StringUtils.isEmpty(param.getSort())){// sort=salaCount_asc/descString[] s = param.getSort().split("_");SortOrder order = s[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;sourceBuilder.sort(s[0], order);}// 3.处理分页// Integer pageNum; // 页码if(param.getPageNum() != null){// 需要做分页处理 pageSize = 5// pageNum:1 from:0  [0,1,2,3,4]// pageNum:2 from:5 [5,6,7,8,9]// from = ( pageNum - 1 ) * pageSizesourceBuilder.from( (param.getPageNum() - 1 ) * ESConstant.PRODUCT_PAGESIZE);sourceBuilder.size(ESConstant.PRODUCT_PAGESIZE);}// 4. 设置高亮if(!StringUtils.isEmpty(param.getKeyword())){// 如果有根据关键字查询那么我们才需要高亮设置HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field("subTitle");highlightBuilder.preTags("<b style='color:red'>");highlightBuilder.postTags("</b>");sourceBuilder.highlighter(highlightBuilder);}// 5.聚合运算// 5.1 品牌的聚合TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");brand_agg.field("brandId");brand_agg.size(50);// 品牌的子聚合brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(10));brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(10));sourceBuilder.aggregation(brand_agg);// 5.2 类别的聚合TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg");catalog_agg.field("catalogId");catalog_agg.size(10);// 类别的子聚合catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(10));sourceBuilder.aggregation(catalog_agg);// 5.3 属性的聚合NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");// 属性id聚合TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg");attr_id_agg.field("attrs.attrId");attr_id_agg.size(10);// 属性id下的子聚合 属性名称和属性值attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(10));attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(10));attr_agg.subAggregation(attr_id_agg);sourceBuilder.aggregation(attr_agg);System.out.println(sourceBuilder.toString());searchRequest.source(sourceBuilder);return searchRequest;}

2.4 构建SearchResult对象

  当我们通过封装的SearchRequest对象从ES中检索出了相关的信息后,我们需要将返回的SearchResponse对象封装为前端接收的SearchResult对象。

  • 所有的满足条件的商品
  • 分页相关的信息
  • 当前商品涉及的品牌信息
  • 当前商品涉及的类别信息
  • 当前商品涉及的属性信息
   /*** 根据检索的结果解析封装为SearchResult对象* @param response* @return*/private SearchResult buildSearchResult(SearchResponse response,SearchParam param){SearchResult result = new SearchResult();SearchHits hits = response.getHits();// 1.检索的所有商品信息SearchHit[] products = hits.getHits();List<SkuESModel> esModels = new ArrayList<>();if(products != null && products.length > 0){for (SearchHit product : products) {String sourceAsString = product.getSourceAsString();// 把json格式的字符串通过fastjson转换为SkuESModel对象SkuESModel model = JSON.parseObject(sourceAsString, SkuESModel.class);if(!StringUtils.isEmpty(param.getKeyword())){// 我们需要设置高亮HighlightField subTitle = product.getHighlightFields().get("subTitle");String subTitleHighlight = subTitle.getFragments()[0].string();model.setSubTitle(subTitleHighlight); // 设置高亮}esModels.add(model);}}result.setProducts(esModels);Aggregations aggregations = response.getAggregations();// 2.当前商品所涉及到的所有的品牌ParsedLongTerms brand_agg = aggregations.get("brand_agg");List<? extends Terms.Bucket> buckets = brand_agg.getBuckets();// 存储所有品牌的容器List<SearchResult.BrandVO> brandVOS = new ArrayList<>();if(buckets!=null && buckets.size() > 0){for (Terms.Bucket bucket : buckets) {SearchResult.BrandVO brandVO = new SearchResult.BrandVO();// 获取品牌的keyString keyAsString = bucket.getKeyAsString();brandVO.setBrandId(Long.parseLong(keyAsString)); // 设置品牌的编号// 然后我们需要获取品牌的名称和图片的地址ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");List<? extends Terms.Bucket> bucketsImg = brand_img_agg.getBuckets();if(bucketsImg != null && bucketsImg.size() > 0){String img = bucketsImg.get(0).getKeyAsString();brandVO.setBrandImg(img);}// 获取品牌名称的信息ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");String breadName = brand_name_agg.getBuckets().get(0).getKeyAsString();brandVO.setBrandName(breadName);brandVOS.add(brandVO);}}result.setBrands(brandVOS);// 3.当前商品涉及到的所有的类别信息ParsedLongTerms catalog_agg = aggregations.get("catalog_agg");List<? extends Terms.Bucket> bucketsCatalogs = catalog_agg.getBuckets();// 创建一个保存所有类别的容器List<SearchResult.CatalogVO> catalogVOS = new ArrayList<>();if(bucketsCatalogs != null && bucketsCatalogs.size() > 0){for (Terms.Bucket bucket : bucketsCatalogs) {SearchResult.CatalogVO catalogVO = new SearchResult.CatalogVO();String keyAsString = bucket.getKeyAsString(); // 获取类别的编号catalogVO.setCatalogId(Long.parseLong(keyAsString));// 获取类别的名称ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");String catalogName = catalog_name_agg.getBuckets().get(0).getKeyAsString();catalogVO.setCatalogName(catalogName);catalogVOS.add(catalogVO);}}result.setCatalogs(catalogVOS);// 4.当前商品涉及到的所有的属性信息ParsedNested attr_agg = aggregations.get("attr_agg");ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");List<? extends Terms.Bucket> bucketsAttr = attr_id_agg.getBuckets();List<SearchResult.AttrVo > attrVos = new ArrayList<>();if(bucketsAttr != null && bucketsAttr.size() > 0){for (Terms.Bucket bucket : bucketsAttr) {SearchResult.AttrVo attrVo = new SearchResult.AttrVo();// 获取属性的编号String keyAsString = bucket.getKeyAsString();attrVo.setAttrId(Long.parseLong(keyAsString));// 又得分别获取 属性的名称 和 属性的值ParsedStringTerms attr_name_agg = bucket.getAggregations().get("attr_name_agg");String attrName = attr_name_agg.getBuckets().get(0).getKeyAsString(); // 属性的名称attrVo.setAttrName(attrName);ParsedStringTerms attr_value_agg = bucket.getAggregations().get("attr_value_agg");if(attr_value_agg.getBuckets() != null && attr_value_agg.getBuckets().size() > 0 ){List<String> values = attr_value_agg.getBuckets().stream().map(item -> {String keyAsString1 = item.getKeyAsString();return keyAsString1;}).collect(Collectors.toList());attrVo.setAttrValue(values);}attrVos.add(attrVo);}}result.setAttrs(attrVos);// 5. 分页信息  当前页 总的记录数  总页数long total = hits.getTotalHits().value;result.setTotal(total);// 设置总记录数  6 /5  1+1result.setPageNum(param.getPageNum()); // 设置当前页long totalPage = total % ESConstant.PRODUCT_PAGESIZE == 0 ? total / ESConstant.PRODUCT_PAGESIZE : (total / ESConstant.PRODUCT_PAGESIZE + 1);result.setTotalPages((int)totalPage); // 设置总的页数return result;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/60370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(笔记二)利用opencv调用鼠标事件在图像上绘制图形

目录 &#xff08;1&#xff09;查看cv2所支持的鼠标事件&#xff08;2&#xff09;通过鼠标事件在图像上做标记&#xff08;3&#xff09;高级操作&#xff1a;通过移动鼠标在图像绘制图形、曲线 该功能主要创建一个鼠标事件发生时执行的回调函数。鼠标事件可以是任何与鼠标有…

根据身高重建队列【贪心算法】

根据身高重建队列 假设有打乱顺序的一群人站成一个队列&#xff0c;数组 people 表示队列中一些人的属性&#xff08;不一定按顺序&#xff09;。每个 people[i] [hi, ki] 表示第 i 个人的身高为 hi &#xff0c;前面 正好 有 ki 个身高大于或等于 hi 的人。 请你重新构造并返…

SQL注入漏洞复现(CVE-2017-8917)

文章目录 搭建环境启动环境漏洞复现报错注入使用sqlmap 前提条件&#xff1a; 1.安装docker docker pull medicean/vulapps:j_joomla_22.安装docker-compose docker run -d -p 8000:80 medicean/vulapps:j_joomla_23.下载vulhub Docker Compose是 docker 提供的一个命令行工具&…

Mysql中九种索引失效场景分析

表数据&#xff1a; 索引情况&#xff1a; 其中a是主键&#xff0c;对应主键索引&#xff0c;bcd三个字段组成联合索引&#xff0c;e字段为一个索引 情况一&#xff1a;不符合最左匹配原则 去掉b1的条件后就不符合最左匹配原则了&#xff0c;导致索引失效 情况二&#xff…

go学习-指针 标识符

指针&#xff0c;以及标识符 1.指针 &#xff08;1&#xff09;.基本介绍 1&#xff09;基本数据类型&#xff0c;变量存的值&#xff0c;也叫值类型 2&#xff09;获取变量的地址用&&#xff0c;比如 var num int ,获取num的地址&#xff1a;&num 3)指针类型&…

小程序隐私保护授权处理方式之弹窗组件

欢迎点击关注-前端面试进阶指南&#xff1a;前端登顶之巅-最全面的前端知识点梳理总结 *分享一个使用比较久的&#x1fa9c; 小程序隐私保护授权弹窗组件 调用wx.getUserProfile进行授权时&#xff0c;返回错误信息&#xff1a;{errMsg: “getUserProfile:fail api scope is…

智慧排水监测系统:创新监测技术保障排水系统安全运行

城市排水系统作为城市基础设施的重要组成部分&#xff0c;其安全运行直接关系到环境卫生、居民生活和城市发展。为了确保排水系统的顺畅运行&#xff0c;传统的监测手段已经不能满足日益复杂的城市排水需求&#xff0c;物联网技术的快速发展为排水系统的监测带来了巨大的便利&a…

科普宣传片制作思路

科普宣传片可以针对不同的科学领域和主题&#xff0c;包括自然科学、生命科学、物理学、化学、天文学、地球科学、环境保护等&#xff0c;提供具体的案例、实验和研究成果&#xff0c;帮助观众更好地理解和应用科学知识。科普宣传片的制作思路可以根据具体的科普主题和目标观众…

基于ssm vue智慧城市实验室主页系统源码和论文

基于ssm vue智慧城市实验室主页系统源码和论文059 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方…

基于协同滤波推荐算法的图书管理系统

目录 一、项目概述 二、技术框架 三、功能设计 四、数据库设计 五、项目截图 六、技术文档 一、项目概述 Hi&#xff0c;大家好&#xff0c;今天分享的项目是《基于协同滤波推荐算法的图书管理系统》&#xff0c;对用户登录注册、图书推荐、图书管理、用户信息进行管理&…

Vue3响应式原理 私

响应式的本质&#xff1a;当数据变化后会自动执行某个函数映射到组件&#xff0c;自动触发组件的重新渲染。 响应式的实现方式就是劫持数据&#xff0c;Vue3的reactive就是通过Proxy劫持数据&#xff0c;由于劫持的是整个对象&#xff0c;所以可以检测到任何对象的修改&#xf…

版本控制 Git工具的使用

版本控制的概念&#xff1a; 版本控制&#xff08;Revision control&#xff09;是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史&#xff0c;方便查看更改历史记录&#xff0c;备份以便恢复以前的版本的软件工程技术。简单来说就是用于管理多人协同开发…

C语言每日一练------(Day3)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今天练习题的关键字&#xff1a; 尼科彻斯定理 等差数列 &#x1f493;博主csdn个人主页&#xff1a…

2023_Spark_实验一:Windows中基础环境安装

Ⅰ、WINDOWS中安装JDK1.8 一、下载安装包 链接&#xff1a;百度网盘 请输入提取码 所在文件夹&#xff1a;根目录或者大数据必备工具--》开发工具(前端后端)--》后端 下载文件名称&#xff1a;jdk-8u191-windows-x64.exe 二、安装JDK 1.现在转到下载的exe文件可用的文件夹&…

【Vue3+Ts】项目启动准备和配置项目代码规范和css样式的重置

项目启动准备 创建项目&#xff08; 使用Vite 构建工具创建项目模板&#xff09;目录介绍插件安装创建别名编译说明项目配置配置icon和标题配置项目别名配置ts.config.json检测vscode的插件是否配置 配置项目代码规范集成editorconfig配置prettier工具库ESLint检测配置 CSS样式…

transformer实现词性标注

1、self-attention 1.1、self-attention结构图 上图是 Self-Attention 的结构&#xff0c;在计算的时候需要用到矩阵 Q(查询), K(键值), V(值)。在实际中&#xff0c;Self-Attention 接收的是输入(单词的表示向量 x组成的矩阵 X) 或者上一个 Encoder block 的输出。而 Q, K, V…

Hive-启动与操作(2)

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

测试左移——代码审计SonarQube 平台搭建

一、sonarqube代码分析技术体系 1、代码分析工具 IDE 辅助功能 xcode、android studio阿里巴巴 java 开发手册 ide 插件支持 独立的静态分析工具 spotbugs、findbugs、androidlint、scan-build、Checkstyle、FindSecBugspmd 阿里巴巴 java 开发手册 pmd 插件 综合性的代码…

堆,堆排序和TOP—K问题(C语言版)

前言 堆是一种重要的数据结构&#xff0c;堆分为大根堆和小根堆&#xff0c;大根堆堆顶的数据是最大的&#xff0c;小根堆堆顶的数据是最小的&#xff0c;堆在逻辑结构上是一颗完全二叉树&#xff0c;这棵树中如果满足根节点大于左右子树&#xff0c;每个节点都满足这个条件就是…

Rabbitmq的Shovel

Federation 具备的数据转发功能类似&#xff0c; Shovel 够可靠、持续地从一个 Broker 中的队列 ( 作为源端&#xff0c;即source)拉取数据并转发至另一个 Broker 中的交换器 ( 作为目的端&#xff0c;即 destination) 。作为源端的队列和作为目的端的交换器可以同时位于…