【业务功能篇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.hqwc.cn/news/90695.html

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

相关文章

原生微信小程序 动态(横向,纵向)公告(广告)栏

先看一下动态效果 Y轴滚动公告的原理是swiper组件在页面中的Y轴滚动&#xff0c;属性vertical&#xff0c;其余属性也设置一下autoplay circular interval"3000" X轴滚动的原理是&#xff0c;利用动画效果&#xff0c;将内容从右往左过渡过去 wxml&#xff1a; &l…

力扣:73. 矩阵置零(Python3)

题目&#xff1a; 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚…

【Flutter】下载安装Flutter并使用学习dart语言

前言 安装flutter, 并使用flutter内置的dartSDK学习使用dart语言。 编辑器&#xff1a; Android Studio fluuter 版本 : flutter_windows_3.13.1 内置dartSDK : 3.1.0 dart路径路径&#xff1a; flutter安装路径\bin\cache\dart-sdk 安装Flutter 下载安装包 flutter下载地址…

whisper 语音识别项目部署

1.安装anaconda软件 在如下网盘免费获取软件&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1zOZCQOeiDhx6ebHh5zNasA 提取码&#xff1a;hfnd 2.使用conda命令创建python3.8环境 conda create -n whisper python3.83.进入whisper虚拟环境 conda activate whisper4.…

【Navicat Premium 16】使用Navicat将excel的数据进行单表的导入,详细操作

业务场景&#xff1a;经常与数据打交道嘛&#xff0c;有的时候会需要将excel的数据导入到数据库中&#xff0c;后面发现对于单表的数据导入&#xff0c;使用Navicat还是非常方便的&#xff0c;仅仅需要将字段关系映射好就可以了 一、开始操作 前提条件&#xff1a;已经成功连接…

【FPGA零基础学习之旅#11】数码管动态扫描

&#x1f389;欢迎来到FPGA专栏~数码管动态扫描 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能指正…

【OpenCV实战】3.OpenCV颜色空间实战

OpenCV颜色空间实战 〇、Coding实战内容一、imread1.1 函数介绍1.2 Flags1.3 Code 二. 色彩空间2.1 获取单色空间2.2. HSV、YUV、RGB2.3. 不同颜色空间应用场景 〇、Coding实战内容 OpenCV imread()方法不同的flags差异性获取单色通道【R通道、G通道、B通道】HSV、YUV、RGB 一…

JVM ZGC垃圾收集器

ZGC垃圾收集器 ZGC&#xff08;“Z”并非什么专业名词的缩写&#xff0c;这款收集器的名字就叫作Z Garbage Collector&#xff09;是一款在JDK 11中新加入的具有实验性质[1]的低延迟垃圾收集器&#xff0c;是由Oracle公司研发的。 ZGC收集器是一款基于Region内存布局的&#…

微服务学习资料

文章目录 参考资料一. 微服务概述1. CAP理论2. BASE理论3. SpringBoot 与 SpringCloud对比 二. 服务注册&#xff1a;Zookeeper,Eureka,Nacos,Consul1. Nacos两种健康检查方式&#xff1f;2. nacos中负责负载均衡底层是如何实现的3. Nacos原理4. 临时实例和持久化(非临时)实例 …

JVM第二篇 类加载子系统

JVM主要包含两个模块&#xff0c;类加载子系统和执行引擎&#xff0c;本篇博客将类加载子系统做一下梳理总结。 目录 1. 类加载子系统功能 2. 类加载子系统执行过程 2.1 加载 2.2 链接 2.3 初始化 3. 类加载器分类 3.1 引导类加载器 3.2 自定义加载器 3.2.1 自定义加载器实…

YOLOv5:解读general.py

YOLOv5&#xff1a;解读general.py 前言前提条件相关介绍general.pyclip_boxesscale_boxes ★ \bigstar ★xywh2xyxynon_max_suppression ★ ★ ★ \bigstar\bigstar\bigstar ★★★未完待续 参考 前言 记录一下自己阅读general.py代码的一些重要点&#xff0c;方便自己查阅。…

plumelog介绍与应用-一个简单易用的java分布式日志系统

官方文档&#xff1a;http://www.plumelog.com/zh-cn/docs/FASTSTART.html 简介 无代码入侵的分布式日志系统&#xff0c;基于log4j、log4j2、logback搜集日志&#xff0c;设置链路ID&#xff0c;方便查询关联日志基于elasticsearch作为查询引擎高吞吐&#xff0c;查询效率高全…