当前位置:   article > 正文

谷粒商城-商城业务-检索服务_谷粒商场 面包屑导航

谷粒商场 面包屑导航

目录

商城业务-检索服务-搭建页面环境

商城业务-检索服务-调整页面跳转

商城业务-检索服务-检索查询参数模型分析抽取

商城业务-检索服务-检索返回结果模型分析抽取

商城业务-检索服务-检索DSL测试-查询部分

商城业务-检索服务-检索DSL测试-聚合部分

商城业务-检索服务-SearchRequest构建-检索

商城业务-检索服务-SearchRequest构建-排序、分页、高亮&测试

商城业务-检索服务-SearchRequest构建-聚合

商城业务-检索服务-SearchRequest构建-分析&封装

商城业务-检索服务-验证结果封装正确性

商城业务-检索服务-页面数基本数据渲染

商城业务-检索服务-页面筛选条件渲染

商城业务-检索服务-页面分页数据渲染

商城业务-检索服务-页面排序功能

商城业务-检索服务-页面排序字段回显

商城业务-检索服务-页面价格区间搜索

商城业务-检索服务-面包屑导航

商城业务-检索服务-条件删除与URL编码问题

商城业务-检索服务-添加筛选联动


商城业务-检索服务-搭建页面环境

Nginx动静分离,将搜索页中的静态资源上传至/static/search文件夹下,将index.html搜索首页存放在gulimall-search服务的templates下

  1. cd /mydata/nginx/html/static
  2. mkdir search

 

 

使用thymeleaf模板引擎:

①导入thymeleaf的依赖

  1. <!--导入thymeleaf依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  5. </dependency>

② 导入thymeleaf的命名空间

xmlns:th="http://www.thymeleaf.org"

③ 修改静态资源的请求路径,使用CTRL+R进行全部替换

所有动态请求search.gulimall.com的请求由Nginx转发给网关

①配置域名转发

②配置Nginx配置文件

  1. cd /mydata/nginx/conf.d
  2. vi gulimall.conf

重启nginx服务 

docker restart  nginx

③配置网关

  1. - id: gulimall_host_route
  2. uri: lb://gulimall-product # lb:负载均衡
  3. predicates:
  4. - Host=gulimall.com # **.xxx 子域名
  5. - id: gulimall_search_route
  6. uri: lb://gulimall-search # lb:负载均衡
  7. predicates:
  8. - Host=search.gulimall.com # **.xxx 子域名

商城业务-检索服务-调整页面跳转

配置热部署

①导入依赖

  1. <!--导入热部署依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-devtools</artifactId>
  5. <optional>true</optional>
  6. </dependency>

② 开发期间默认关闭缓存

点击这几处要跳转到检索首页

鼠标右击,点击检查

修改请求路径

CTRL+F9重新编译 

出现错误:访问到80端口

出现问题的原因:nginx配置出错不能正确路由跳转 

解决方案:修改nginx配置文件

  1. cd /mydata/nginx/conf/conf.d
  2. vi gulimall.conf

重启nginx 

docker restart nginx

关闭Product服务的缓存,重启服务

  

首页,点击搜索按钮要来到搜索页

 点击手机1111111要来到搜索页

 

请求路径为http://search.gmall.com/list.html?catalog3Id=225,这是一个错误请求路径,缺少了gulimall而不是gumall

①将index.html修改为list.html

②编写控制类

③首页搜索栏修改为

④ 修改js并上传nginx,重启nginx

 结果:

商城业务-检索服务-检索查询参数模型分析抽取

①通过首页搜索栏进行检索,传递keyword

 ②通过分类进行检索。传递catalog3Id

 ③复杂查询

排序:①综合排序②销量③价格 ,例如:通过销量降序排序或者升序排序,sort=saleCount_desc/saleCount_asc

过滤:①库存,例如:有库存->hasStock=1,无库存 -> hasStock=0 ②价格区间 ,例如: 价格位于 400 -900 -> skuPrice=400_900,价格低于900 -> skuPrice= _900,价格高于900 -> skuPrice=900_  ③品牌: 可以按照多个品牌进行筛选

聚合:属性:多个属性以:分割,1号属性网络可以是4G也可以是5G -> attrs=1_4G:5G 

分页:页码

创建Vo,用于封装查询条件

商城业务-检索服务-检索返回结果模型分析抽取

以京东为例,搜索小米

默认:查询所有商品信息

1.小米所属的品牌 2.小米所属的分类 3.小米所属的属性

编写返回结果的Vo

商城业务-检索服务-检索DSL测试-查询部分

首先,这是一个复合查询即bool查询,将需要评分的检索条件写在must中,不需要评分的检索条件写在filter中。

①keyword的全文检索,例如:keyword=iphone

② 手机分类的检索,例如: catalogId=225 ,非文本字段检索用term

③ 品牌检索

④根据属性检索,属性未防止扁平化处理声明为nested,因此,需要使用nested查询

nested query文档地址:Nested query | Elasticsearch Guide [8.2] | Elastic

 

 ⑤是否有库存

 ⑥价格区间检索

⑦排序

⑧ 页码

⑨ 高亮,标题内容含有搜索内容则标题中含有的搜索内容标红

 

DSL语句:

  1. GET /product/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "skuTitle": "iphone"
  9. }
  10. }
  11. ],
  12. "filter": [
  13. {
  14. "term": {
  15. "catalogId": {
  16. "value": "225"
  17. }
  18. }
  19. },
  20. {
  21. "terms": {
  22. "brandId": [
  23. "8",
  24. "9"
  25. ]
  26. }
  27. },
  28. {
  29. "nested": {
  30. "path": "attrs",
  31. "query": {
  32. "bool": {
  33. "must": [
  34. {
  35. "term": {
  36. "attrs.attrId": {
  37. "value": "1"
  38. }
  39. }
  40. },
  41. {
  42. "terms": {
  43. "attrs.attrValue": [
  44. "5G",
  45. "4G"
  46. ]
  47. }
  48. }
  49. ]
  50. }
  51. }
  52. }
  53. },
  54. {
  55. "term": {
  56. "hasStock": {
  57. "value": "false"
  58. }
  59. }
  60. },
  61. {
  62. "range": {
  63. "skuPrice": {
  64. "gte": 4999,
  65. "lte": 5400
  66. }
  67. }
  68. }
  69. ]
  70. }
  71. },
  72. "sort": [
  73. {
  74. "skuPrice": {
  75. "order": "desc"
  76. }
  77. }
  78. ],
  79. "from": 0,
  80. "size": 10,
  81. "highlight": {
  82. "fields": {"skuTitle":{}},
  83. "pre_tags": "<b style='color:red'>",
  84. "post_tags": "</b>"
  85. }
  86. }

商城业务-检索服务-检索DSL测试-聚合部分

①product映射有些数据类型不允许索引,因此,创建新的映射,允许索引

  1. PUT /gulimall_product
  2. {
  3. "mappings": {
  4. "properties": {
  5. "skuId":{
  6. "type": "long"
  7. },
  8. "spuId":{
  9. "type": "keyword"
  10. },
  11. "skuTitle":{
  12. "type": "text",
  13. "analyzer": "ik_smart"
  14. },
  15. "skuPrice":{
  16. "type": "keyword"
  17. },
  18. "skuImg":{
  19. "type": "keyword"
  20. },
  21. "saleCount":{
  22. "type": "long"
  23. },
  24. "hasStock":{
  25. "type": "boolean"
  26. },
  27. "hotScore":{
  28. "type": "long"
  29. },
  30. "brandId":{
  31. "type": "long"
  32. },
  33. "catelogId":{
  34. "type": "long"
  35. },
  36. "brandName":{
  37. "type": "keyword"
  38. },
  39. "brandImg":{
  40. "type": "keyword"
  41. },
  42. "catelogName":{
  43. "type": "keyword"
  44. },
  45. "attrs":{
  46. "type": "nested",
  47. "properties": {
  48. "attrId":{
  49. "type":"long"
  50. },
  51. "attrName":{
  52. "type": "keyword"
  53. },
  54. "attrValue":{
  55. "type":"keyword"
  56. }
  57. }
  58. }
  59. }
  60. }
  61. }

 ②数据迁移

③修改索引常量

④品牌聚合

⑤分类聚合

⑥属性聚合,应使用嵌入式聚合

nested aggregations文档地址:Nested Aggregations | Elasticsearch: The Definitive Guide [2.x] | Elastic

⑦完整DSL

  1. GET /gulimall_product/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "skuTitle": "iphone"
  9. }
  10. }
  11. ],
  12. "filter": [
  13. {
  14. "term": {
  15. "catalogId": {
  16. "value": "225"
  17. }
  18. }
  19. },
  20. {
  21. "terms": {
  22. "brandId": [
  23. "8",
  24. "9"
  25. ]
  26. }
  27. },
  28. {
  29. "nested": {
  30. "path": "attrs",
  31. "query": {
  32. "bool": {
  33. "must": [
  34. {
  35. "term": {
  36. "attrs.attrId": {
  37. "value": "1"
  38. }
  39. }
  40. },
  41. {
  42. "terms": {
  43. "attrs.attrValue": [
  44. "5G",
  45. "4G"
  46. ]
  47. }
  48. }
  49. ]
  50. }
  51. }
  52. }
  53. },
  54. {
  55. "term": {
  56. "hasStock": {
  57. "value": "false"
  58. }
  59. }
  60. },
  61. {
  62. "range": {
  63. "skuPrice": {
  64. "gte": 4999,
  65. "lte": 5400
  66. }
  67. }
  68. }
  69. ]
  70. }
  71. },
  72. "sort": [
  73. {
  74. "skuPrice": {
  75. "order": "desc"
  76. }
  77. }
  78. ],
  79. "from": 0,
  80. "size": 10,
  81. "highlight": {
  82. "fields": {"skuTitle":{}},
  83. "pre_tags": "<b style='color:red'>",
  84. "post_tags": "</b>"
  85. },
  86. "aggs": {
  87. "brand_agg": {
  88. "terms": {
  89. "field": "brandId",
  90. "size": 10
  91. },
  92. "aggs": {
  93. "brand_name_agg": {
  94. "terms": {
  95. "field": "brandName",
  96. "size": 10
  97. }
  98. },
  99. "brand_img-agg": {
  100. "terms": {
  101. "field": "brandImg",
  102. "size": 10
  103. }
  104. }
  105. }
  106. },
  107. "catalog_agg":{
  108. "terms": {
  109. "field": "catalogId",
  110. "size": 10
  111. },
  112. "aggs": {
  113. "catalog_name_agg": {
  114. "terms": {
  115. "field": "catelogName",
  116. "size": 10
  117. }
  118. }
  119. }
  120. },
  121. "attr_agg":{
  122. "nested": {
  123. "path": "attrs"
  124. },
  125. "aggs": {
  126. "attr_id_agg": {
  127. "terms": {
  128. "field": "attrs.attrId",
  129. "size": 10
  130. },
  131. "aggs": {
  132. "attr_name_agg": {
  133. "terms": {
  134. "field": "attrs.attrName",
  135. "size": 10
  136. }
  137. },
  138. "attr_value_agg":{
  139. "terms": {
  140. "field": "attrs.attrValue",
  141. "size": 10
  142. }
  143. }
  144. }
  145. }
  146. }
  147. }
  148. }
  149. }

⑧将gulimall_product映射和DSL进行保存

商城业务-检索服务-SearchRequest构建-检索

编写接口

导入ES客户端对象

整体逻辑:1.封装DSL 2.查询 3.封装响应结果

开始封装DSL 

1.构建bool查询

2. 构建must查询

3. 构建filter中三级分类查询

4.构建filter中的品牌查询

5.构建filter中的库存查询

6.构建filter中的价格区间查询

7. 构建filter中的属性查询

商城业务-检索服务-SearchRequest构建-排序、分页、高亮&测试

1.构建排序

2. 构建分页

默认页码为1,页码大小为2方便测试

3.构建高亮

进行简单的测试,将打印的DSL复制到Kibana中看是否正确

商城业务-检索服务-SearchRequest构建-聚合

1.品牌聚合

2.分类聚合

3.属性聚合

商城业务-检索服务-SearchRequest构建-分析&封装

打上断点,根据返回的response封装响应结果

 1对应于2

 

1. 封装所有记录

2. 封装品牌,聚合返回的类型可以从断点返回结果查看

3.封装分类

4.封装属性

5.封装页码

6.封装总记录数

7.封装总页数

商城业务-检索服务-验证结果封装正确性

高亮设置

商城业务-检索服务-页面数基本数据渲染

由于有库存的商品非常少,因此,不设置库存的默认值,前端传进来的参数不为空时再拼装上查询条件

将分页大小设置为16 

动态获取页面显示数据 

①商品显示

 注意细节:th:text 会进行转义 ,th:utext不会进行转义

如果使用th:text,带keyword高亮之后,则会出现下面的结果:

 ②品牌显示

③分类显示 

④ 属性显示

商城业务-检索服务-页面筛选条件渲染

1.按品牌条件筛选,&quot="

2.按分类条件筛选

3.按属性条件筛选

4. url拼接函数编写

商城业务-检索服务-页面分页数据渲染

1.搜索栏功能完成

 为input创建id,方便后续拿到input中的输入;编写跳转方法

 

 搜索框回显搜索内容,th:value 为属性设置值 ;param是指请求参数,param.keyword是指

 请求参数中的keyword值

2.分页功能的完善

① 当前页码>第一页才能显示上一页,当前页码<总页码才能显示下一页

② 自定义属性用于保存当前页码,作用:用于替换请求参数中的pageNum值

③遍历显示页码

 

④ 当前页码显示特定的样式

⑤ 请求参数的替换

将a标签中href全部删除,添加a标签的class,为其绑定事件,并编写回调函数

$(this)指当前被点击的元素,return false作用:禁用默认行为,a标签可能会跳转

替换方法 

  1. function replaceParamVal(url,paramName,replaceVal){
  2. var oUrl = url.toString();
  3. var re = eval('/('+paramName+'=)([^&]*)/gi');
  4. var nUrl = oUrl.replace(re,paramName+'='+replaceVal);
  5. return nUrl;
  6. }

商城业务-检索服务-页面排序功能

为a标签定义class 

为a标签绑定点击事件 

为选中的元素设置样式

 为选中的元素设置样式之前需要将所有元素的样式恢复成最初样式

使用toggleClass()为class加上desc,默认为降序排序 

添加升降符号 

$(this).text()获取当前点击元素的文本内容

添加升降符号之前需要清空元素的升降符号

将被选中元素的样式改变抽取成一个方法 

  1. function changeStyle(ele){
  2. $(".sort_a").css({"color":"#333","border-color":"#CCC","background":"#FFF"})
  3. $(ele).css({"color":"#FFF","border-color":"#e4393c","background":"#e4393c"})
  4. $(ele).toggleClass("desc");
  5. $(".sort_a").each(function (){
  6. var text = $(this).text().replace("↓","").replace("↑","");
  7. $(this).text(text);
  8. });
  9. if ($(ele).hasClass("desc")){
  10. var text = $(ele).text().replace("↓","").replace("↑","");
  11. text = text+"↓";
  12. $(ele).text(text);
  13. }else {
  14. var text = $(ele).text().replace("↓","").replace("↑","");
  15. text = text+"↑";
  16. $(ele).text(text);
  17. }
  18. }

自定义属性赋值为某种排序

改写替换方法

  1. function replaceOrAddParamVal(url,paramName,replaceVal){
  2. var oUrl = url.toString();
  3. if (oUrl.indexOf(paramName)!=-1){
  4. var re = eval('/('+paramName+'=)([^&]*)/gi');
  5. var nUrl = oUrl.replace(re,paramName+'='+replaceVal);
  6. return nUrl;
  7. }else {
  8. if (oUrl.indexOf("?")!=-1){
  9. var nUrl = oUrl+"&"+paramName+"="+replaceVal;
  10. return nUrl;
  11. }else {
  12. var nUrl = oUrl+"?"+paramName+"="+replaceVal;
  13. return nUrl;
  14. }
  15. }
  16. }

跳转指定路径

出现问题: 通过toggleClass()为class添加desc,刷新或者跳转之后会丢失

商城业务-检索服务-页面排序字段回显

页面跳转之后样式回显,th:with 用于声明变量,#strings即调用字符串工具类

根据URL动态添加class 

动态的添加升降符号 

商城业务-检索服务-页面价格区间搜索

编写价格区间搜索栏

为button按钮绑定单击事件 

价格回显 

①获取skuPirce的值

②价格区间回显 

#strings.substringAfter(name,prefix):获取prifix之后的字符串

#strings.substringBefore(name,suffix):获取suffix之前的字符串

拼接是否有货查询条件

为单选框绑定改变事件 

通过调用prop('check')获取是否被选中,选中为true否则false 

回显选中状态 

商城业务-检索服务-面包屑导航

 ①编写面包屑导航栏Vo

② 封装面包屑导航栏数据

属性名的获取要通过远程服务调用product服务进行查询 

①导入cloud的版本

<spring-cloud.version>Hoxton.SR9</spring-cloud.version>

② 导入cloud依赖管理

  1. <dependencyManagement>
  2. <dependencies>
  3. <dependency>
  4. <groupId>org.springframework.cloud</groupId>
  5. <artifactId>spring-cloud-dependencies</artifactId>
  6. <version>${spring-cloud.version}</version>
  7. <type>pom</type>
  8. <scope>import</scope>
  9. </dependency>
  10. </dependencies>
  11. </dependencyManagement>

③ 导入openfeign的依赖

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-openfeign</artifactId>
  4. </dependency>

④ 开启远程服务调用功能

⑤编写接口,配置调用的服务名 

⑥编写调用服务的接口,注意:全路径

⑦编写自己传key和返回值类型获取自己想要的数据类型方法,之前的只能获取data的数据

⑧编写返回类型的Vo,Vo和AttrRespVo属性一致

 

⑨封装属性名 

商城业务-检索服务-条件删除与URL编码问题

①封装原生的查询条件

HttpServletRequest的getQueryString()方法可以获取url的请求参数

②封装链接 

出现问题:路径替换失败

出现问题的原因:浏览器会将中文进行一个编码,而查询出来的属性值是中文

解决方案:将中文进行编码

注意:有些符号,浏览器的编码与java编码不一致

例如:'(':浏览器不进行编码,java会编码成%28;')':浏览器不进行编码,java会编码成%29;空格浏览器会编码成%20,java会编码成'+'

  1. // 8.封装面包屑导航栏的数据
  2. if (param.getAttrs()!=null && param.getAttrs().size()>0){
  3. List<SearchResVo.NavVo> navVoList = param.getAttrs().stream().map(item -> {
  4. SearchResVo.NavVo navVo = new SearchResVo.NavVo();
  5. String[] s = item.split("_");
  6. // 封装属性值
  7. navVo.setAttrValue(s[1]);
  8. //封装属性名
  9. R r = productFeignService.info(Long.parseLong(s[0]));
  10. if (r.getCode() == 0){
  11. AttrResponseVo responseVo = r.getData("attr", new TypeReference<AttrResponseVo>() {});
  12. navVo.setAttrName(responseVo.getAttrName());
  13. }else {
  14. // 出现异常则封装id
  15. navVo.setAttrName(s[0]);
  16. }
  17. //封装链接即去掉当前属性的查询的url封装
  18. String encode=null;
  19. try {
  20. encode = URLEncoder.encode(item,"UTF-8");
  21. encode=encode.replace("%28","(").replace("%29",")").replace("+","%20");
  22. } catch (UnsupportedEncodingException e) {
  23. e.printStackTrace();
  24. }
  25. String replace = param.get_queryString().replace("&attrs=" + encode, "");
  26. navVo.setLink("http://search.gulimall.com/list.html?"+replace);
  27. return navVo;
  28. }).collect(Collectors.toList());
  29. searchResVo.setNavs(navVoList);
  30. }

导航栏回显编写

①右击检测,找到元素

 

改写 replaceOrAddParamVal默认是对属性进行一个替换,forceAdd是否强制添加的标识

商城业务-检索服务-添加筛选联动

完善品牌面包屑导航栏功能,分类面包屑导航栏也类似,不同之处是不用剔除,设置url

①为面包屑vo设置一个默认值

② 远程调用product服务查询品牌名称

 

远程服务调用,查询很费时,可以将查询的结果保存进缓存中 ,例如:

value:分区名,key:用于标识第几号属性

③将封装替换url的方法抽取出来 

④编写面包屑导航栏功能

品牌面包屑导航栏,品牌筛选剔除

⑤创建一个list用于封装已经筛选的属性id

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/872855
推荐阅读
相关标签
  

闽ICP备14008679号