商城检索服务

1.检索页面的搭建

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

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

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

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

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

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

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

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

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

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;}

【第十三篇】商城系统-商城检索服务相关推荐

  1. 2023最新谷粒商城笔记之检索服务篇(全文总共13万字,超详细)

    检索服务 这里有关于检索的功能我们基于微服务架构应该抽出来单独作为一个服务运行,所以新建gulimall-search服务并注册到nacos中,以便网关服务可以监控到检索服务并将检索的请求转发给检索服 ...

  2. JAVA b2b2c多用户商城系统源码-服务发现服务端EurekaServer微服务

    一.大致介绍 1.众所周知,在现在互联网开发中,访问地址的IP和端口号是动态的,一个服务停掉再重新启用后IP和端口就可能发生了改变,所以用硬编码是肯定不行了.于是我们尝试使用新的技术来解决这一难题.需 ...

  3. 【第九篇】商城系统-商城首页功能

    一.商品上架功能 ElasticSearch实现商城系统中全文检索的流程. 1.商品ES模型 商品的映射关系 PUT product {"mappings": {"pro ...

  4. java B2B2C springmvc mybatis多租户电子商城系统(三):服务提供与调用

    上一篇文章我们介绍了eureka服务注册中心的搭建,这篇文章介绍一下如何使用eureka服务注册中心,搭建一个简单的服务端注册服务,客户端去调用服务使用的案例. 案例中有三个角色:服务注册中心.服务提 ...

  5. 计算机毕业设计 SSM+Vue保健品商城系统 商城购物平台 健康品商城系统 药房商城网购平台

  6. java基于springboot+vue的家具用品销售商城系统 前后端分离nodejs含商家用户

    在21世纪的今天.社会的不断发展和进步当中,人民对于计算机信息科学化认识的程度越来越高.由以前我们的感性认识.到现在理性认识提高了,管理员的工作重要性也越来越高,科学化现代管理手段,使现代化信息存储. ...

  7. 判断 小程序_社区团购小程序商城系统,可以从哪些方面判断?

    原标题:社区团购小程序商城系统,可以从哪些方面判断? 社区商城系统属于电子商务行业品种,有数据表明发现,有72%的客户是通过网络渠道了解服务商.了解产品.了解行业的,那么你要做的就是在网络中辨识真伪. ...

  8. 多商户商城系统开发方案

    ​传统企业在电商互联网的冲击下,纷纷开始融合当前市场线上线下主流渠道,大多数传统企业基本认同可以通过搭建多用户商城系统实现企业营销模式多样化,这里[数 商 云]小编也将提供全方位的B2B2C多用户商城 ...

  9. 大型商城系统(永久免费的商城系统)

    厦门哪里有大型的图书商城? 买书的话到莲坂外图,但是只借书或看书的话可以去文化艺术中心的图书馆,那有很多种类的书供你免费阅读. 为什么大型网购商城?为什么大型网购商城-万象网购内的商品可以确 只要有发 ...

最新文章

  1. 别把个人信息“玩”丢了
  2. Java开发面试问题,牛逼轰轰!
  3. java笔试题递归,递归(网上搜的一些笔试题)
  4. 让我们将包变成模块系统!
  5. linux 进程代码,怎样从Linux终端管理进程:10个你必须知道的命令
  6. 深入理解 Docker 网络原理
  7. 前端如何玩转虚拟机_想运行虚拟机,用win10自带的Hyper-V即可,不用安装其它软件...
  8. 黑马程序员——面向对象篇之封装
  9. Prometheus自动发现Exporter实现方案(一看就懂)
  10. Web 应用程序测试工具
  11. SQLyog详细使用教程
  12. 计算机基础知识精品课程,计算机应用基础精品课程课件第1课计算机基础知识第课计算机基础.PDF...
  13. OpenCV阈值分割
  14. 接口测试(测试用例)
  15. 用jQuery制作对联广告
  16. 使用容联云通讯实现短信验证登入
  17. 微信公众号申请流程整理
  18. aviary android sdk,Make Photo Magic 使用Aviary SDK 进行图片编辑开发,功能非常强大!!!!(Aviary SDK安装说明)...
  19. ipad协议835最新版
  20. [iWencai]问财-热门股票排名

热门文章

  1. Android 绘制原理
  2. List的contains方法使用
  3. ag-grid 中外部编辑数据同步修改table单元格数据,数据实时更新
  4. 今天,最新程序媛图解出炉
  5. 华为鸿蒙内置即时通讯,内置鸿蒙系统的四款华为手机,实力都很强,可惜都有一点瑕疵!...
  6. Android 自定义View修炼-高仿猎豹清理大师自定义内存开口圆环比例进度View
  7. 深度学习入门——前馈神经网络
  8. java interface和impl,被测单元:Impl还是接口?
  9. AndroidStudio 模拟器root方法
  10. 无限火力更新服务器公告,2021无限火力时间表公告 lol无限火力什么时候开放