谷粒商城篇章5 ---- P173-P192 ---- 检索服务【分布式高级篇二】

目录

1 检索服务 

1.1 搭建页面环境

1.1.1 引入依赖

1.1.2 将检索页面放到gulimall-search的src/main/resources/templates/目录下

1.1.3 调整搜索页面

1.1.4 将静态资源放到linux的nginx相关映射目录下/root/docker/nginx/html/static/ search/

1.1.5 SwitchHosts配置域名转发

1.1.6 测试

1.1.7 nginx配置

1.1.8 网关配置

1.1.9 重启测试

1.2 调整页面跳转

1.2.1 引入spring-boot-devtools依赖

1.2.2 关闭thymeleaf缓存

1.2.3 修改页面能够跳转到商城首页

1.2.4 修改index.html文件名

1.3 检索查询参数模型分析抽取

1.3.1 检索条件分析

1.3.2 查询参数封装

1.4 检索返回结果模型分析抽取

1.5 检索DSL测试

1.5.1 DSL查询部分

1.5.1.1 查询部分DSL

1.5.1.2 查询部分+排序+分页+高亮DSL

1.5.2 聚合部分

1.5.2.1 聚合时出现的问题Use doc values instead

1.5.2.1.1 报错原因

1.5.2.1.2 解决方案

1.5.2.2 聚合部分DSL

1.5.3 总的DSL(查询+聚合)

1.6 SearchRequest构建

1.6.1 检索、排序、分页、高亮、聚合

1.6.1.1 controller层

1.6.1.2 service层

1.6.1.3 EsConstant.java(Es常量类)

1.6.2 测试

1.7 SearchResponse分析&封装

1.8 验证结果封装正确性

1.9 渲染检索页面【P184-192】

1.9.1 检索页面完整代码

1.9.2 检索服务后端相关代码

1.9.2.1 引入依赖

1.9.2.2 vo

1.9.2.3 controller

1.9.2.4 service

1.9.2.5 远程调用接口

1.9.3 远程服务相关接口

1.9.3.1 attrInfo接口

1.9.3.2 brandsInfo接口


1 检索服务 

1.1 搭建页面环境

1. 引入依赖
2. 将页面复制到gulimall-search的src/main/resources/templates/目录下
3. 调整搜索页面1)引入thymeleafxmlns:th="http://www.thymeleaf.org"2)修改页面静态资源的引入,加上/static/searcheg: 由<link rel="stylesheet" href="./css/index.css">改为:<link rel="stylesheet" href="/static/search/css/index.css">
4. 将静态资源放到linux的nginx相关映射目录下/root/docker/nginx/html/static/search/
5. SwitchHosts配置域名转发1)以管理员身份运行SwitchHosts;2)让所有的search.gulimall.com可以定位到linux的nginx服务
6. 测试1)浏览器访问http://localhost:120002)浏览器访问http://search.gulimall.com/
7. nginx配置
8. 网关配置
9. 测试 http://search.gulimall.com/

1.1.1 引入依赖

<!-- 模板引擎 :thymeleaf -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional>
</dependency>

1.1.2 将检索页面放到gulimall-search的src/main/resources/templates/目录下

1.1.3 调整搜索页面

        引入thymeleaf,修改引入静态资源的路径以/static/search开始。以下为了举例,list.html完整代码见 1.9.1 检索页面完整代码

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><link rel="stylesheet" href="/static/search/css/index.css"><link rel="stylesheet" type="text/css" href="/static/search/font/iconfont.css"><!--<script src="/static/search/js/jquery-3.2.1.min.js"></script>--><script src="/static/search/js/jquery-1.12.4.js"></script><title>Document</title>
</head>
...

1.1.4 将静态资源放到linux的nginx相关映射目录下/root/docker/nginx/html/static/ search/

1.1.5 SwitchHosts配置域名转发

    (1)以管理员身份运行SwitchHosts;
    (2)让所有的search.gulimall.com可以定位到linux的nginx服务

1.1.6 测试

    (1)浏览器访问http://localhost:12000


    (2)浏览器访问http://search.gulimall.com/

1.1.7 nginx配置

(1)修改gulimall.conf,将 server_name 由 gulimall.com 改为 *.gulimall.com
(2)保存gulimall.conf并重启nginx。
# 进入/root/docker/nginx/conf/conf.d/
cd /root/docker/nginx/conf/conf.d/# 进入gulimall.conf,修改server_name
vi gulimall.conf# 重启nginx
docker restart nginx# 查看nginx是否启动成功
docker ps -a

1.1.8 网关配置

- id: gulimall_host_routeuri: lb://gulimall-productpredicates:# 由以下的主机域名访问转发到商品服务- Host=gulimall.com- id: gulimall_search_routeuri: lb://gulimall-searchpredicates:# 由以下的主机域名访问转发到搜索服务- Host=search.gulimall.com

1.1.9 重启测试

http://search.gulimall.com/

 注意静态资源的路径是否正确;有些图片可能缺失,影响不大。

1.2 调整页面跳转

1.2.1 引入spring-boot-devtools依赖

        前面已经引入。

1.2.2 关闭thymeleaf缓存

页面修改后同过Ctrl+Shift+F9对html页面进行重新构建,无需重启项目

spring:thymeleaf:cache: false

1.2.3 修改页面能够跳转到商城首页

效果要求:点击搜索页面左上角的谷粒商城首页或谷粒商城都能跳转到商城首页。如下图: 

1. 修改页面代码:(跳转路径 http://gulimall.com)

2. 测试:

点击谷粒商城首页或谷粒商城

3. 解决商城首页显示问题

server_name gulimall.com *.gulimall.com;

相关命令:

# 进入gulimall.conf,修改server_name
vi gulimall.conf# 重启nginx
docker restart nginx

重启nginx,访问 gulimall.com:

1.2.4 修改index.html文件名

1. gulimall-search下的index.html重命名为list.html

原因:在首页点击搜索跳转的链接为list.html

2. 新增由/list.html能跳转到list.thml页面的方法gulimall-search/src/main/java/com/wen/gulimall/search/controller/SearchController.java

@Controller
public class SearchController {@GetMapping("/list.html")public String listPage(){return "list";}
}

3. 将gulimall-product的index.html页面中的search()方法的调用由img标签放到a标签

 4. 重启服务测试

1.3 检索查询参数模型分析抽取

1.3.1 检索条件分析

  • 全文检索:skuTitle-》keyword
  • 排序:saleCount(销量)、hotScore(热度分)、skuPrice(价格)
  • 过滤:hasStock、skuPrice区间、brandId、catalog3Id、attrs
  • 聚合:attrs

完整查询参数

keyword=小米&sort=saleCount_desc/asc&hasStock=0/1&skuPrice=400_1900&brandId=1&catalog3Id=1&at trs=1_3G:4G:5G&attrs=2_骁龙845&attrs=4_高清屏

1.3.2 查询参数封装

gulimall-search/src/main/java/com/wen/gulimall/search/vo/SearchParam.java

/*** @author W* @createDate 2023/7/17 16:10* @description 封装页面所有可能传递过来的查询条件*  catalog3Id=255&keyword=小米&sort=saleCount_asc&hasStock=0/1&bandId=1&bandId=2*/
@Data
public class SearchParam {private String keyword; // 页面穿过来的全文匹配关键字private Long catalog3Id; // 三级分类的id/*** sort=saleCount_asc/desc* sort=skuPrice_asc/desc* sort=hotScore_asc/desc*/private String sort; //排序条件/*** 好多的过滤条件*  hasStock=0/1*  skuPrice=1_500/_500/500_*  bandId=1*  attrs=2_5寸:6寸*/private Integer hasStock; // 是否只显示有货private String skuPrice;// 价格区间查询private List<Long> brandId;// 按照品牌进行查询,可以多选private List<String> attrs;// 按照属性进行筛选private Integer pageNum;// 页码}

1.4 检索返回结果模型分析抽取

gulimall-search/src/main/java/com/wen/gulimall/search/vo/SearchResult.java

@Data
public class SearchResult {// 查询到的所有商品信息private List<SkuEsModel> products;/*** 以下是分页信息*/private Integer pageNum; // 当前页码private Long total; // 总记录数private Integer totalPages; // 总页码private List<BrandVo> brands; // 当前查询到的结果,所有涉及到的品牌private List<CatalogVo> catalogs; // 当前查询到的结果,所有涉及到的所有分类private List<AttrVo> attrs; // 当前查询到的结果,所有涉及到的所有属性//========================以上是返回给页面的所有信息==========================@Datapublic static class BrandVo{private Long brandId;private String brandName;private String brandImg;}@Datapublic static class CatalogVo{private Long catalogId;private String catalogName;}@Datapublic static class AttrVo{private Long attrId;private String attrName;private List<String> attrValue;}}

1.5 检索DSL测试

1.5.1 DSL查询部分

  • 属性数据类型为嵌入式,即"type"="nested",查询、过滤也需要嵌入式。可参照官网:Nested query | Elasticsearch Guide [7.4] | Elastic
  • 价格区间使用range gte lte.

1.5.1.1 查询部分DSL

# 查询部分
GET product/_search
{"query": {"bool": {"must": [{"match": {"skuTitle": "华为"}}],"filter": [{"term": {"catalogId": 225}},{"terms": {"brandId": ["1","2","7"]}},{"nested": {"path": "attrs","query": {"bool": {"must": [{"term": {"attrs.attrId": {"value": "12"}}},{"terms": {"attrs.attrValue": ["HUAWEI Kirin 980","A13"]}}]}}}},{"term": {"hasStock": {"value": "false"}}},{"range": {"skuPrice": {"gte": 0,"lte": 6000}}}]}}
}

1.5.1.2 查询部分+排序+分页+高亮DSL

模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析

# 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析
GET product/_search
{"query": {"bool": {"must": [{"match": {"skuTitle": "华为"}}],"filter": [{"term": {"catalogId": 225}},{"terms": {"brandId": ["1","2","7"]}},{"nested": {"path": "attrs","query": {"bool": {"must": [{"term": {"attrs.attrId": {"value": "12"}}},{"terms": {"attrs.attrValue": ["HUAWEI Kirin 980","A13"]}}]}}}},{"term": {"hasStock": {"value": "false"}}},{"range": {"skuPrice": {"gte": 0,"lte": 6000}}}]}},"sort": [{"skuPrice": {"order": "desc"}}],"from": 0,"size": 1,"highlight": {"fields": {"skuTitle": {}},"pre_tags": "<b style='color:red'>","post_tags": "</b>"}
}

1.5.2 聚合部分

1.5.2.1 聚合时出现的问题Use doc values instead

1.5.2.1.1 报错原因

1. mapping映射参数index、doc_values使用说明: 

  •  index:index选项控制是否对字段值进行索引。它接受true或false,默认为true。如果为 false, 表示该字段不会被索引, 但是检索结果里面有, 但字段本身不能当做检索条件。
  • doc_values:默认为true。如果为false,表示字段不需要进行排序、聚合、或者使用脚本访问字段值,这样可以节省磁盘空间。还可以通过设定doc_values为true,index为false 来让字段不能被检索但是可以用于排序、聚合以及脚本操作。

        具体可以参照官网:mapping parameters

2. 错误原因:

        brandName属性的doc_values为false不可以进行聚合。

1.5.2.1.2 解决方案

        更新映射,删除映射中的index和doc_values参数。

(1)创建新的索引指定映射

PUT gulimall_product
{"mappings": {"properties": {"skuId": {"type": "long"},"spuId": {"type": "keyword"},"skuTitle": {"type": "text","analyzer": "ik_smart"},"skuPrice": {"type": "keyword"},"skuImg": {"type": "keyword"},"saleCount": {"type": "long"},"hasStock": {"type": "boolean"},"hotScore": {"type": "long"},"brandId": {"type": "long"},"catalogId": {"type": "long"},"brandName": {"type": "keyword"},"brandImg": {"type": "keyword"},"catalogName": {"type": "keyword"},"attrs": {"type": "nested","properties": {"attrId": {"type": "long"},"attrName": {"type": "keyword"},"attrValue": {"type": "keyword"}}}}}
}

(2)迁移数据

# 数据迁移
POST _reindex
{"source": {"index": "product"},"dest": {"index": "gulimall_product"}
}

迁移成功

 (3)修改索引常量名

由product改为gulimall_product

public class EsConstant {public static final String PRODUCT_INDEX = "gulimall_product";
}

1.5.2.2 聚合部分DSL

(1)DSL聚合部分

GET gulimall_product/_search
{"query": {"match_all": {}},"aggs": {"brand_agg": {"terms": {"field": "brandId","size": 10},"aggs": {"brand_name_agg": {"terms": {"field": "brandName","size": 10}},"brand_img_agg":{"terms": {"field": "brandImg","size": 10}}}},"catalog_agg":{"terms": {"field": "catalogId","size": 10},"aggs": {"catalog_name_agg": {"terms": {"field": "catalogName","size": 10}}}},"attr_agg":{"nested": {"path": "attrs"},"aggs": {"attr_id_agg": {"terms": {"field": "attrs.attrId","size": 10},"aggs": {"attr_name_agg": {"terms": {"field": "attrs.attrName","size": 10}},"attr_value_agg":{"terms": {"field": "attrs.attrValue","size": 10}}}}}}}
}

1.5.3 总的DSL(查询+聚合)

GET gulimall_product/_search
{"query": {"bool": {"must": [{"match": {"skuTitle": "华为"}}],"filter": [{"term": {"catalogId": 225}},{"terms": {"brandId": ["1","2","7"]}},{"nested": {"path": "attrs","query": {"bool": {"must": [{"term": {"attrs.attrId": {"value": "12"}}},{"terms": {"attrs.attrValue": ["HUAWEI Kirin 980","A13"]}}]}}}},{"term": {"hasStock": {"value": "false"}}},{"range": {"skuPrice": {"gte": 0,"lte": 6000}}}]}},"sort": [{"skuPrice": {"order": "desc"}}],"from": 0,"size": 1,"highlight": {"fields": {"skuTitle": {}},"pre_tags": "<b style='color:red'>","post_tags": "</b>"},"aggs": {"brand_agg": {"terms": {"field": "brandId","size": 10},"aggs": {"brand_name_agg": {"terms": {"field": "brandName","size": 10}},"brand_img_agg":{"terms": {"field": "brandImg","size": 10}}}},"catalog_agg":{"terms": {"field": "catalogId","size": 10},"aggs": {"catalog_name_agg": {"terms": {"field": "catalogName","size": 10}}}},"attr_agg":{"nested": {"path": "attrs"},"aggs": {"attr_id_agg": {"terms": {"field": "attrs.attrId","size": 10},"aggs": {"attr_name_agg": {"terms": {"field": "attrs.attrName","size": 10}},"attr_value_agg":{"terms": {"field": "attrs.attrValue","size": 10}}}}}}}
}

1.6 SearchRequest构建

1.6.1 检索、排序、分页、高亮、聚合

1.6.1.1 controller层

gulimall-search/src/main/java/com/wen/gulimall/search/controller/SearchController.java

@Controller
public class SearchController {@Resourceprivate MallSearchService mallSearchService;/*** 自动将页面提交过来的所有请求查询参数封装成指定的对象* @param searchParam* @return*/@GetMapping("/list.html")public String listPage(SearchParam searchParam, Model model){// 1. 根据传递来的页面的查询参数,去es中检索商品SearchResult result = mallSearchService.search(searchParam);model.addAttribute("result", result);return "list";}
}

1.6.1.2 service层

gulimall-search/src/main/java/com/wen/gulimall/search/service/MallSearchService.java

public interface MallSearchService {/**** @param searchParam 检索的所有参数* @return 返回检索的结果,里面包含页面需要的所有信息*/SearchResult search(SearchParam searchParam);
}

gulimall-search/src/main/java/com/wen/gulimall/search/service/impl/MallSearchServiceImpl.java

@Service
public class MallSearchServiceImpl implements MallSearchService {@Resourceprivate RestHighLevelClient restHighLevelClient;// 根据条件去es中检索@Overridepublic SearchResult search(SearchParam searchParam) {// 动态的构建出查询所需要的DSLSearchResult result = null;// 1. 准备检索请求SearchRequest searchRequest = buildSearchRequest(searchParam);try {// 2. 执行检索请求SearchResponse searchResponse = restHighLevelClient.search(searchRequest, GulimallElasticsearchConfig.COMMON_OPTIONS);// 3. 分析响应数据,封装成需要的格式result = buildSearchResponse(searchParam,searchResponse);} catch (IOException e) {e.printStackTrace();}return result;}/*** 准备检索请求* # 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析* @return*/private SearchRequest buildSearchRequest(SearchParam searchParam) {// 构建DSL语句对象SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();/*** 查询:模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)*/// 1. 构建bool - queryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 1.1 bool - must 模糊匹配if(StrUtil.isNotEmpty(searchParam.getKeyword())){boolQuery.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));}// 1.2 bool - filter - 按照三级分类id查询if(searchParam.getCatalog3Id() != null){boolQuery.filter(QueryBuilders.termQuery("catalogId",searchParam.getCatalog3Id()));}// 1.2 bool -filter - 按照品牌id查询if(CollectionUtil.isNotEmpty(searchParam.getBrandId())){boolQuery.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));}// 1.2 bool - filter - 按照所有指定的属性进行查询if(CollectionUtil.isNotEmpty(searchParam.getAttrs())){// attrs=1_5寸:6寸&attrs=2_8G:16Gfor (String attrStr : searchParam.getAttrs()) {String[] s = attrStr.split("_");String attrId = s[0]; // 检索的属性idString[] attrValues = s[1].split(":"); // 这个属性检索所需要的值BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));// ScoreMode.None 不参与评分// 每一个必须都得生成一个nested查询NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None);boolQuery.filter(nestedQuery);}}// 1.2 bool -filter - 按照库存是否有进行查询if(searchParam.getHasStock() != null) {boolQuery.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));}// 1.2 bool -filter - 按照价格区间if(StrUtil.isNotEmpty(searchParam.getSkuPrice())){String[] s = searchParam.getSkuPrice().split("_");RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");if(s.length == 2){rangeQuery.gte(s[0]).lte(s[1]);boolQuery.filter(rangeQuery);}else if(s.length == 1){if(searchParam.getSkuPrice().startsWith("_")){rangeQuery.lte(s[0]);boolQuery.filter(rangeQuery);}if(searchParam.getSkuPrice().endsWith("_")){rangeQuery.gte(s[0]);boolQuery.filter(rangeQuery);}}}// 把以上所有的条件都拿来进行封装sourceBuilder.query(boolQuery);/*** 排序,分页,高亮*/// 2.1 排序if(StrUtil.isNotEmpty(searchParam.getSort())){// sort=saleCount_asc/descString[] s = searchParam.getSort().split("_");SortOrder order = s[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;sourceBuilder.sort(s[0],order);}// 2.2 分页 pageSize = 5// pageNum:1   from:0 size:5// pageNum:2   form:5 size:5// from = (pageNum - 1)*sizesourceBuilder.from((searchParam.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);// 2.3 高亮if(StrUtil.isNotEmpty(searchParam.getKeyword())) {HighlightBuilder builder = new HighlightBuilder();builder.field("skuTitle");builder.preTags("<b style='color:red'>");builder.postTags("</b>");sourceBuilder.highlighter(builder);}/*** 聚合分析*/// 1. 品牌聚合TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");brand_agg.field("brandId").size(50);// 品牌聚合的子聚合brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));// TODO 1.聚合brandsourceBuilder.aggregation(brand_agg);// 2. 分类聚合 catalog_aggTermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);// 分类聚合的子聚合catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));// TODO 1.聚合catalogsourceBuilder.aggregation(catalog_agg);// 3. 属性聚合NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");// 聚合出当前所有的attrIdTermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(10);// 聚合出当前所有的attr_id对应的名字attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));// 聚合出当前所有的attr_id对应的所有可能的属性值attrValueattr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));attr_agg.subAggregation(attr_id_agg);// TODO 1.聚合attrsourceBuilder.aggregation(attr_agg);System.out.println("构建的DSL语句"+sourceBuilder.toString());SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX},sourceBuilder);return searchRequest;}/*** 封装检索结果* @param searchParam* @param searchResponse* @return*/private SearchResult buildSearchResponse(SearchParam searchParam, SearchResponse searchResponse) {return null;}
}

 buildSearchResponse(searchParam,searchResponse)方法具体实现见 1.7 SearchResponse分析&封装

1.6.1.3 EsConstant.java(Es常量类)

gulimall-search/src/main/java/com/wen/gulimall/search/constant/EsConstant.java

public class EsConstant {public static final String PRODUCT_INDEX = "gulimall_product";public static final Integer PRODUCT_PAGESIZE = 2;}

1.6.2 测试

        根据代码中System.out.println("构建的DSL语句"+sourceBuilder.toString());输出的DSL语句,在Kibana中进行测试,看输出结果是否正确。

1.7 SearchResponse分析&封装

对search()接口进行debug,根据debug确定聚合的具体类型。如下图,以分类的聚合为例:

 gulimall-search/src/main/java/com/wen/gulimall/search/service/impl/MallSearchServiceImpl.java

/*** 封装检索结果* @param searchParam* @param searchResponse* @return*/
private SearchResult buildSearchResponse(SearchParam searchParam, SearchResponse searchResponse) {SearchResult result = new SearchResult();// 1. 返回所有查询到的商品SearchHit[] hits = searchResponse.getHits().getHits();List<SkuEsModel> skuEsModels = new ArrayList<>();if(ArrayUtil.isNotEmpty(hits)) {for (SearchHit hit : hits) {String sourceAsString = hit.getSourceAsString();SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);// keyword非空设置高亮if(StrUtil.isNotEmpty(searchParam.getKeyword())) {String skuTitle = hit.getHighlightFields().get("skuTitle").getFragments()[0].string();skuEsModel.setSkuTitle(skuTitle);}skuEsModels.add(skuEsModel);}}result.setProducts(skuEsModels);2. 当前所有商品涉及到的所有属性信息List<SearchResult.AttrVo> attrVos = new ArrayList<>();ParsedNested attr_agg = searchResponse.getAggregations().get("attr_agg");ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {// 1. 获取属性idlong attrId = bucket.getKeyAsNumber().longValue();// 2. 获取属性的名字String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();// 3. 获取属性的值List<String> attrValues = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(item -> {return ((Terms.Bucket) item).getKeyAsString();}).collect(Collectors.toList());SearchResult.AttrVo attrVo = new SearchResult.AttrVo();attrVo.setAttrId(attrId);attrVo.setAttrName(attrName);attrVo.setAttrValue(attrValues);attrVos.add(attrVo);}result.setAttrs(attrVos);3. 当前所有商品涉及到的所有品牌信息List<SearchResult.BrandVo> brandVos = new ArrayList<>();ParsedLongTerms brand_agg = searchResponse.getAggregations().get("brand_agg");for (Terms.Bucket bucket : brand_agg.getBuckets()) {// 1. 获取品牌的idlong brandId = bucket.getKeyAsNumber().longValue();// 2. 获取品牌的名字String brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();// 3. 获取平品牌的图片String brandImg = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();SearchResult.BrandVo brandVo = new SearchResult.BrandVo();brandVo.setBrandId(brandId);brandVo.setBrandName(brandName);brandVo.setBrandImg(brandImg);brandVos.add(brandVo);}result.setBrands(brandVos);4. 当前所有商品涉及到的所有分类信息ParsedLongTerms catalog_agg = searchResponse.getAggregations().get("catalog_agg");List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();List<? extends Terms.Bucket> buckets = catalog_agg.getBuckets();if(CollectionUtil.isNotEmpty(buckets)){for (Terms.Bucket bucket : buckets) {SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();// 获取分类idString keyAsString = bucket.getKeyAsString();catalogVo.setCatalogId(Long.parseLong(keyAsString));// 获取分类名称ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");String catalog_name = catalog_name_agg.getBuckets().get(0).getKeyAsString();catalogVo.setCatalogName(catalog_name);catalogVos.add(catalogVo);}}result.setCatalogs(catalogVos);//5. 分页信息 - 当前页码result.setPageNum(searchParam.getPageNum());6. 分页信息 - 总记录数long total = searchResponse.getHits().getTotalHits().value;result.setTotal(total);7. 分页信息 - 总页数Integer totalPages = (int)total%EsConstant.PRODUCT_PAGESIZE == 0?(int)total/EsConstant.PRODUCT_PAGESIZE:((int)total/EsConstant.PRODUCT_PAGESIZE + 1);result.setTotalPages(totalPages);return result;
}

参数keyword不为空时,skuTitle中keyword的值设置高亮。

1.8 验证结果封装正确性

通过debug查看结果的封装。

1.9 渲染检索页面【P184-192】

(1)修改SearchParam类中库存属性hasStock不给默认值,buildSearchRequest()方法中加上库存判断,有库存,才拼接库存查询。(注意:上面的hasStock和buildSearchRequest()已经修改)
(2)要判断当前location.href是否有参数,没有以?拼接,有以&拼接。可以通过字符串的indexOf()方法进行判断,也可以通过includes()方法进行判断。具体可以参考W3C文档:https://www.w3school.com.cn/jsref/jsref_obj_string.asp
includes() :返回字符串是否包含指定值。
indexOf() :返回值在字符串中第一次出现的位置。
(3)搜索框回显,可以使用 th:value="${param.keyword}"完成回显功能。${param.keyword}可以获取请求参数keyword的值。
(4)分页功能完善,输入页码,点击确定,跳转到具体页面。
面包屑导航:
(1)SearchResult中添加面包屑相关内容
(2)buildSearchResponse()构建查询结果里返回面包屑相关数据
(3)构建属性面包屑导航功能,因为需要属性名,所以需要远程调用gulimall-product服务1)修改search服务的pom.xml,添加spring-cloud,引入openfeign2)开启openfeign3)编写远程调用接口@GetMapping("/product/attr/info/{attrId}")public R attrInfo(@PathVariable("attrId") Long attrId);
(5)浏览器对空格的编码和Java不一样,差异化处理:
private String replaceQueryString(SearchParam searchParam, String value, String key) {String encode = null;try {encode = URLEncoder.encode(value, "UTF-8");// 前端传递过来的空格被解码成+,替换成%20encode = encode.replace("+","%20"); //浏览器对空格的编码和Java不一样,差异化处理} catch (UnsupportedEncodingException e) {e.printStackTrace();}String replace = searchParam.get_queryString().replace("&"+key+"=" + encode, "");return replace;
}
(6)构建品牌面包屑导航功能,因为需要品牌名称,所以需要远程调用gulimall-product服务1)编写gulimall-product服务所需品牌的接口@GetMapping("/infos")public R info(@RequestParam("brandIds") List<Long> brandIds){List<BrandEntity> brand = brandService.getBrandsByIds(brandIds);return R.ok().put("brand", brand);}2)编写远程调用接口@GetMapping("/product/brand/infos")public R brandsInfo(@RequestParam("brandIds") List<Long> brandIds);
(7)注意th:if的优先级比th:with高,可以参照 usingthymeleaf.pdf -》 Attribute Precedence

1.9.1 检索页面完整代码

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><link rel="stylesheet" href="/static/search/css/index.css"><link rel="stylesheet" type="text/css" href="/static/search/font/iconfont.css"><!--<script src="/static/search/js/jquery-3.2.1.min.js"></script>--><script src="/static/search/js/jquery-1.12.4.js"></script><title>Document</title>
</head>
<body>
<!--头部-->
<div class="header_head"><div class="header_head_box"><b class="header_head_p"><div style="overflow: hidden"><a href="http://gulimall.com" class="header_head_p_a1" style="width:73px;">谷粒商城首页</a><a href="#" class="header_head_p_a"><!--<img src="/static/search/img/img_05.png" style="border-radius: 50%;"/>-->北京</a></div><div class="header_head_p_cs"><a href="#" style="background: #C81623;color: #fff;">北京</a><a href="#">上海</a><a href="#">天津</a><a href="#">重庆</a><a href="#">河北</a><a href="#">山西</a><a href="#">河南</a><a href="#">辽宁</a><a href="#">吉林</a><a href="#">黑龙江</a><a href="#">内蒙古</a><a href="#">江苏</a><a href="#">山东</a><a href="#">安徽</a><a href="#">浙江</a><a href="#">福建</a><a href="#">湖北</a><a href="#">湖南</a><a href="#">广东</a><a href="#">广西</a><a href="#">江西</a><a href="#">四川</a><a href="#">海南</a><a href="#">贵州</a><a href="#">云南</a><a href="#">西藏</a><a href="#">陕西</a><a href="#">甘肃</a><a href="#">青海</a><a href="#">宁夏</a><a href="#">新疆</a><a href="#">港澳</a><a href="#">台湾</a><a href="#">钓鱼岛</a><a href="#">海外</a></div></b><ul><li><a href="#" class="li_2">你好,请登录</a></li><li><a href="#">免费注册</a></li><span>|</span><li><a href="#">我的订单</a></li><span>|</span><li class="header_wdjd" style="width:80px;"><a href="#">我的谷粒商城</a><img src="/static/search/image/down-@1x.png" /><!--<b class="glyphicon glyphicon-menu-down"></b>--><div class="header_wdjd_txt"><ul><li><a href="#">待处理订单</a></li><li><a href="#">消息</a></li><li><a href="#">返修退换货</a></li><li><a href="#">我的回答</a></li><li><a href="#">降价商品</a></li><li><a href="#">我的关注</a></li></ul><ul><li><a href="#">我的京豆</a></li><li><a href="#">我的优惠券</a></li><li><a href="#">我的白条</a></li><li><a href="#">我的理财</a></li></ul></div></li><span>|</span><li><a href="#">谷粒商城会员</a></li><span>|</span><li><a href="#">企业采购</a></li><span>|</span><li class="header_wdjd1"><a href="#">客户服务</a><img src="/static/search/image/down-@1x.png" /><!--<b class="glyphicon glyphicon-menu-down"></b>--><div class="header_wdjd_txt"><ul><p style="width:100%;">客户</p><li><a href="#">帮助中心</a></li><li><a href="#">售后服务</a></li><li><a href="#">在线客服</a></li><li><a href="#">意见建议</a></li><li><a href="#">电话客服</a></li><li><a href="#">客服邮箱</a></li><li><a href="#">金融资讯</a></li><li><a href="#">售全球客服</a></li></ul><ul><p style="width:100%;">商户</p><li><a href="#">合作招商</a></li><li><a href="#">学习中心</a></li><li><a href="#">商家后台</a></li><li><a href="#">京麦工作台</a></li><li><a href="#">商家帮助</a></li><li><a href="#">规则平台</a></li></ul></div></li><span>|</span><li class="header_wzdh"><a href="#">网站导航</a><img src="/static/search/image/down-@1x.png" /><!--<b class="glyphicon glyphicon-menu-down"></b>--><div class="header_wzdh_txt"><ul style="width: 25%;"><p style="width:100%;">特色主题</p><li><a href="#">谷粒商城试用</a></li><li><a href="#">谷粒商城金融</a></li><li><a href="#">全球售</a></li><li><a href="#">国际站</a></li><li><a href="#">谷粒商城会员</a></li><li><a href="#">谷粒商城预售</a></li><li><a href="#">买什么</a></li><li><a href="#">俄语站</a></li><li><a href="#">装机大师</a></li><li><a href="#">0元评测</a></li><li><a href="#">定期送</a></li><li><a href="#">港澳售</a></li><li><a href="#">优惠券</a></li><li><a href="#">秒杀</a></li><li><a href="#">闪购</a></li><li><a href="#">印尼站</a></li><li><a href="#">谷粒商城金融科技</a></li><li><a href="#">In货推荐</a></li><li><a href="#">陪伴计划</a></li><li><a href="#">出海招商</a></li></ul><ul style="width: 20%;"><p style="width:100%;">行业频道</p><li><a href="#">手机</a></li><li><a href="#">智能数码</a></li><li><a href="#">玩3c</a></li><li><a href="#">电脑办公</a></li><li><a href="#">家用电器</a></li><li><a href="#">谷粒商城智能</a></li><li><a href="#">服装城</a></li><li><a href="#">美妆馆</a></li><li><a href="#">家装城</a></li><li><a href="#">母婴</a></li><li><a href="#">食品</a></li><li><a href="#">运动户外</a></li><li><a href="#">农资频道</a></li><li><a href="#">整车</a></li><li><a href="#">图书</a></li></ul><ul style="width: 21%;"><p style="width:100%;">生活服务</p><li><a href="#">白条</a></li><li><a href="#">谷粒商城金融App</a></li><li><a href="#">谷粒商城小金库</a></li><li><a href="#">理财</a></li><li><a href="#">智能家电</a></li><li><a href="#">话费</a></li><li><a href="#">水电煤</a></li><li><a href="#">彩票</a></li><li><a href="#">旅行</a></li><li><a href="#">机票酒店</a></li><li><a href="#">电影票</a></li><li><a href="#">谷粒商城到家</a></li><li><a href="#">谷粒商城众测</a></li><li><a href="#">游戏</a></li></ul><ul style="width: 23%; border-right: 0;"><p style="width:100%;">更多精选</p><li><a href="#">合作招商</a></li><li><a href="#">谷粒商城通信</a></li><li><a href="#">谷粒商城E卡</a></li><li><a href="#">企业采购</a></li><li><a href="#">服务市场</a></li><li><a href="#">办公生活馆</a></li><li><a href="#">乡村招募</a></li><li><a href="#">校园加盟</a></li><li><a href="#">京友帮</a></li><li><a href="#">谷粒商城社区</a></li><li><a href="#">智能社区</a></li><li><a href="#">游戏社区</a></li><li><a href="#">知识产权维权</a></li></ul></div></li><span>|</span><li class="header_sjjd"><a href="#">手机谷粒商城</a><div class="header_sjjd_div"><img src="/static/search/img/01.png" /></div></li></ul></div>
</div><!--搜索导航-->
<div class="header_sous"><div class="logo"><a href="http://gulimall.com"><img src="/static/search/image/logo1.jpg" alt=""></a></div><div class="header_form"><input id="keyword_input" type="text" placeholder="手机" th:value="${param.keyword}"/><a href="javascript:searchByKeyword()">搜索</a></div><div class="header_ico"><div class="header_gw"><span><a href="#">我的购物车</a></span><img src="/static/search/image/settleup-@1x.png" /><span>0</span></div><div class="header_ko"><p>购物车中还没有商品,赶紧选购吧!</p></div></div><div class="header_form_nav"><ul><li><a href="#">谷粒商城之家</a></li><li><a href="#">谷粒商城专卖店</a></li><li><a href="#">平板</a></li><li><a href="#">电脑</a></li><li><a href="#">ipad</a></li></ul></div><nav><ul><li class="nav_li1"><a href="#">全部商品分类</a></li><li class="nav_li"><a href="#">服装城</a></li><li class="nav_li"><a href="#">没装馆</a></li><li class="nav_li"><a href="#">超市</a></li><li class="nav_li"><a href="#">生鲜</a></li></ul><div class="spacer">|</div><ul><li class="nav_li"><a href="#">全球购</a></li><li class="nav_li"><a href="#">闪购</a></li><li class="nav_li"><a href="#">拍卖</a></li></ul><div class="spacer">|</div><ul><li class="nav_li"><a href="#">金融</a></li></ul></nav><div class="header_main_left"><ul><li><a href="#" class="header_main_left_a"><b>家用电器</b></a></li><li class="header_li2"><a href="#" class="header_main_left_a"><b>手机</b> / <b>运营商</b> / <b>数码</b></a><div class="header_main_left_main"><div class="header_sj"><a href="#" class="header_sj_a">玩3c</a><a href="#" class="header_sj_a">手机频道</a><a href="#" class="header_sj_a">网上营业厅</a><a href="#" class="header_sj_a">配件选购中心</a><a href="#" class="header_sj_a">企业购</a><a href="#" class="header_sj_a">以旧换新</a></div><ol class="header_ol"><a href="#" style="color: #111;" class="aaa">手机通讯 ></a><li><a href="#" style="color: #999;">手机</a><a href="#" style="color: #999;">对讲机</a><a href="#" style="color: #999;">手机维修</a><a href="#" style="color: #999;">以旧换新</a></li><a href="#" style="color: #111;" class="aaa">运营商 ></a><li><a href="#" style="color: #999;">合约机</a><a href="#" style="color: #999;">固话宽带</a><a href="#" style="color: #999;">办套餐</a><a href="#" style="color: #999;">从话费/流量</a><a href="#" style="color: #999;">中国电信</a><a href="#" style="color: #999;">中国移动</a><a href="#" style="color: #999;">中国联通</a><a href="#" style="color: #999;">谷粒商城通信</a><a href="#" style="color: #999;">170选号</a></li><a href="#" style="color: #111;" class="aaa">手机配件 ></a><li style="height: 60px;"><a href="#" style="color: #999;">手机壳</a><a href="#" style="color: #999;">贴膜</a><a href="#" style="color: #999;">手机储存卡</a><a href="#" style="color: #999;">数据线</a><a href="#" style="color: #999;">存电器</a><a href="#" style="color: #999;">手机耳机</a><a href="#" style="color: #999;">创业配件</a><a href="#" style="color: #999;">手机饰品</a><a href="#" style="color: #999;">手机电池</a><a href="#" style="color: #999;">苹果周边</a><a href="#" style="color: #999;">移动电源</a><a href="#" style="color: #999;">蓝牙耳机</a><a href="#" style="color: #999;">手机支架</a><a href="#" style="color: #999;">车载配件</a><a href="#" style="color: #999;">拍照配件</a></li><a href="#" style="color: #111;" class="aaa">摄影摄像 ></a><li style="height: 60px;"><a href="#" style="color: #999;">数码相机</a><a href="#" style="color: #999;">单电/微单相机</a><a href="#" style="color: #999;">单反相机</a><a href="#" style="color: #999;">拍立得</a><a href="#" style="color: #999;">运动相机</a><a href="#" style="color: #999;">摄像机</a><a href="#" style="color: #999;">镜头</a><a href="#" style="color: #999;">户外器材</a><a href="#" style="color: #999;">影棚器材</a><a href="#" style="color: #999;">冲印服务</a><a href="#" style="color: #999;">数码相框</a></li><a href="#" style="color: #111;" class="aaa">数码配件 ></a><li style="height: 60px;"><a href="#" style="color: #999;">三脚架/云台</a><a href="#" style="color: #999;">相机包</a><a href="#" style="color: #999;">滤镜</a><a href="#" style="color: #999;">散光灯/手柄</a><a href="#" style="color: #999;">相机清洁</a><a href="#" style="color: #999;">机身附件</a><a href="#" style="color: #999;">镜头附件</a><a href="#" style="color: #999;">读卡器</a><a href="#" style="color: #999;">支架</a><a href="#" style="color: #999;">电池/存电器</a></li><a href="#" style="color: #111;" class="aaa">影音娱乐 ></a><li><a href="#" style="color: #999;">耳机/耳麦</a><a href="#" style="color: #999;">音箱/音响</a><a href="#" style="color: #999;">智能音箱</a><a href="#" style="color: #999;">便携/无线音箱</a><a href="#" style="color: #999;">收音机</a><a href="#" style="color: #999;">麦克风</a><a href="#" style="color: #999;">MP3/MP4</a><a href="#" style="color: #999;">专业音频</a></li><a href="#" style="color: #111;" class="aaa">智能设备 ></a><li style="height: 60px;"><a href="#" style="color: #999;">智能手环</a><a href="#" style="color: #999;">智能手表</a><a href="#" style="color: #999;">智能眼镜</a><a href="#" style="color: #999;">智能机器人</a><a href="#" style="color: #999;">运动跟踪器</a><a href="#" style="color: #999;">健康监测</a><a href="#" style="color: #999;">智能配饰</a><a href="#" style="color: #999;">智能家居</a><a href="#" style="color: #999;">体感车</a><a href="#" style="color: #999;">无人机</a><a href="#" style="color: #999;">其他配件</a></li><a href="#" style="color: #111;" class="aaa">电子教育 ></a><li><a href="#" style="color: #999;">学生平板</a><a href="#" style="color: #999;">点读机</a><a href="#" style="color: #999;">早教益智</a><a href="#" style="color: #999;">录音笔</a><a href="#" style="color: #999;">电纸书</a><a href="#" style="color: #999;">电子词典</a><a href="#" style="color: #999;">复读机</a></li></ol><div class="header_r"><div class="header_r_tu"><a href="#"><img src="/static/search/img/56b2f385n8e4eb051.jpg" /></a><a href="#"><img src="/static/search/img/56b2f385n8e4eb051.jpg" /></a><a href="#"><img src="/static/search/img/56b2f385n8e4eb051.jpg" /></a><a href="#"><img src="/static/search/img/56b2f385n8e4eb051.jpg" /></a><a href="#"><img src="/static/search/img/56b2f385n8e4eb051.jpg" /></a><a href="#"><img src="/static/search/img/56b2f385n8e4eb051.jpg" /></a><a href="#"><img src="/static/search/img/56b2f385n8e4eb051.jpg" /></a><a href="#"><img src="/static/search/img/56b2f385n8e4eb051.jpg" /></a></div><div class="header_r_tu1"><a href="#"><img src="/static/search/img/JD_ash7 - 副本.png" /></a><a href="#"><img src="/static/search/img/JD_ash6.png" /></a></div></div></div></li><li><a href="#" class="header_main_left_a"><b>电脑</b> / <b>办公</b></a></li><li><a href="#" class="header_main_left_a"><b>家居</b> / <b>家具</b> / <b>家装</b> / <b>厨具</b></a></li><li><a href="#" class="header_main_left_a"><b>男装</b> / <b>女装</b> / <b>童装</b> / <b>内衣</b></a></li><li><a href="#" class="header_main_left_a"><b>美妆个护 </b>/ <b>宠物</b></a></li><li><a href="#" class="header_main_left_a"><b>女鞋</b> / <b>箱包</b> / <b>钟表</b> / <b>珠宝</b></a></li><li><a href="#" class="header_main_left_a"><b>男鞋</b> / <b>运动</b> / <b>户外</b></a></li><li><a href="#" class="header_main_left_a"><b>汽车</b> / <b>汽车用品</b></a></li><li><a href="#" class="header_main_left_a"><b>母婴</b> / <b>玩具乐器</b></a></li><li><a href="#" class="header_main_left_a"><b>食品</b> / <b>酒类</b> / <b>生鲜</b> / <b>特产</b></a></li><li><a href="#" class="header_main_left_a"><b>礼品鲜花</b> / <b>农资绿植</b></a></li><li><a href="#" class="header_main_left_a"><b>医药保健</b> / <b>计生情趣</b></a></li><li><a href="#" class="header_main_left_a"><b>图书</b> / <b>音箱</b>/ <b>电子书</b></a></li><li><a href="#" class="header_main_left_a"><b>机票</b> / <b>酒店</b> / <b>旅游</b> / <b>生活</b></a></li><li><a href="#" class="header_main_left_a"><b>理财</b> / <b>众筹</b> / <b>白条</b> / <b>保险</b></a></li></ul></div>
</div><hr style="border: 1px solid red;margin-top: -7px;"><!--热卖促销-->
<div class="JD_temai"><div class="JD_main"><div class="JD_left"><div class="hd">热卖推荐</div><div class="bd mc"><ul class="mc"><li><a href="#" class="mc_a"><img src="/static/search/img/5a28b5a1n8a5c095f.jpg" alt=""></a><div class="mc_div"><a href="#" class="mc_div_a1"><em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待</em></a><p><strong><em class="number J-p-5963064">¥2999.00</em></strong></p><a href="#" class="mc_div_a2">立即抢购</a></div></li><li><a href="#" class="mc_a"><img src="/static/search/img/59f5eef1n99542494.jpg" alt=""></a><div class="mc_div"><a href="#" class="mc_div_a1"><em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em></a><p><strong><em class="number J-p-5963064">¥1699.00</em></strong></p><a href="#" class="mc_div_a2">立即抢购</a></div></li><li style="margin-right: 0"><a href="#" class="mc_a"><img src="/static/search/img/59f5eef1n99542494.jpg" alt=""></a><div class="mc_div"><a href="#" class="mc_div_a1"><em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待</em></a><p><strong><em class="number J-p-5963064">¥2999.00</em></strong></p><a href="#" class="mc_div_a2">立即抢购</a></div></li></ul></div></div><div class="JD_right"><div class="hd"> 促销活动</div><div class="bd"><ul><li> . <a href="#">红米千元全面屏手机上市</a></li><li> . <a href="#">锤子坚果Pro2火爆预约中</a></li><li> . <a href="#">大牌新品 疯狂抢购</a></li><li> . <a href="#">X20 vivo蓝新色上市</a></li><li> . <a href="#">荣耀畅玩7X新品上市</a></li></ul></div></div></div>
</div><!--手机-->
<div class="JD_ipone"><div class="JD_ipone_bar"><div class="JD_ipone_one a"><a href="#">手机</a></div><i><img src="/static/search/image/right-@1x.png" alt=""></i><div class="JD_ipone_one b"><a href="#" class="qqq">手机通讯录 <img src="/static/search/image/down-@1x.png" alt=""></a><div><a href="#">手机通讯</a><a href="#">运营商</a><a href="#">手机配件</a><a href="#">手机服务</a></div></div><i><img src="/static/search/image/right-@1x.png" alt=""></i><div class="JD_ipone_one c"><a href="#" class="qqq">手机 <img src="/static/search/image/down-@1x.png" alt=""></a><div><a href="#">手机</a><a href="#">老人机</a><a href="#">对讲机</a><a href="#">女性手机</a><a href="#">超续航手机</a><a href="#">全面屏手机</a><a href="#">拍照手机</a><a href="#">游戏手机</a></div></div><div class="JD_ipone_one c"><a th:href="${nav.link}" th:each="nav:${result.navs}"><span th:text="${nav.navName}"></span> : <span th:text="${nav.navValue}"></span>×</a></div><i><img src="/static/search/image/right-@1x.png" alt=""></i></div>
</div><!--商品筛选和排序-->
<div class="JD_banner w"><div class="JD_nav"><div class="JD_selector"><!--手机商品筛选--><div class="title"><h3><b>手机</b><em>商品筛选</em></h3><div class="st-ext">共&nbsp;<span>10135</span>个商品 </div></div><div class="JD_nav_logo" th:with="brandid = ${param.brandId}" ><!--品牌--><div class="JD_nav_wrap" th:if="${#strings.isEmpty(brandid)}"><div class="sl_key"><span><b>品牌:</b></span></div><div class="sl_value"><div class="sl_value_logo"><ul><li th:each="brand:${result.brands}"><a href="#" th:href="${'javascript:searchProducts(&quot;brandId&quot;,'+brand.brandId+')'}"><img th:src="${brand.brandImg}" alt=""><div th:text="${brand.brandName}">华为(HUAWEI)</div></a></li></ul></div></div><div class="sl_ext"><a href="#">更多<i style='background: url("static/search/image/search.ele.png")no-repeat 3px 7px'></i><b style='background: url("static/search/image/search.ele.png")no-repeat 3px -44px'></b></a><a href="#">多选<i>+</i><span>+</span></a></div></div><!--分类--><div class="JD_pre"><div class="sl_key"><span><b>分类:</b></span></div><div class="sl_value"><ul><li th:each="catalog:${result.catalogs}"><a href="#"th:href="${'javascript:searchProducts(&quot;catalog3Id&quot;,'+catalog.catalogId+')'}"th:text="${catalog.catalogName}">5.56英寸及以上</a></li></ul></div><div class="sl_ext"><a href="#">更多<i style='background: url("static/search/image/search.ele.png")no-repeat 3px 7px'></i><b style='background: url("static/search/image/search.ele.png")no-repeat 3px -44px'></b></a><a href="#">多选<i>+</i><span>+</span></a></div></div><!--其他的所有需要展示的属性--><div class="JD_pre" th:each="attr:${result.attrs}" th:if="${!#lists.contains(result.attrIds,attr.attrId)}"><div class="sl_key"><span th:text="${attr.attrName}">屏幕尺寸:</span></div><div class="sl_value"><ul><li th:each="val:${attr.attrValue}"><a href="#"th:href="${'javascript:searchProducts(&quot;attrs&quot;,&quot;'+attr.attrId+'_'+val+'&quot;)'}"th:text="${val}">5.56英寸及以上</a></li></ul></div></div></div><div class="JD_show"><a href="#"><span>更多选项( CPU核数、网络、机身颜色 等)</span></a></div></div><!--排序--><div class="JD_banner_main"><!--商品精选--><div class="JD_con_left"><div class="JD_con_left_bar"><div class="JD_con_one"><div class="mt"><h3>商品精选</h3><span>广告</span></div><div class="mc"><ul><li><a href="#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt=""></a><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待</em></a><div class="mc_price"><strong class="price"><span class="J-p-5963064">¥2999.00</span></strong><span class="mc-ico" title="购买本商品送赠品"><i class="goods-icons">赠品</i></span></div><div class="mc_rev">已有<a href="#" class="number">12466</a>人评价</div></li><li><a href="#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt=""></a><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待</em></a><div class="mc_price"><strong class="price"><span class="J-p-5963064">¥2999.00</span></strong><span class="mc-ico" title="购买本商品送赠品"><i class="goods-icons">赠品</i></span></div><div class="mc_rev">已有<a href="#" class="number">12466</a>人评价</div></li><li><a href="#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/593ba628n8794c6a6.jpg" alt=""></a><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em></a><div class="mc_price"><strong class="price"><span class="J-p-5963064">¥1799.00</span></strong><span class="mc-ico" title="购买本商品送赠品"><i class="goods-icons">赠品</i></span></div><div class="mc_rev">已有<a href="#" class="number">15600</a>人评价</div></li><li><a href="#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5919637an271a1301.jpg" alt=""></a><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>vivo Xplay6 全网通 6GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待</em></a><div class="mc_price"><strong class="price"><span class="J-p-5963064">¥3498.00</span></strong><span class="mc-ico" title="购买本商品送赠品"><i class="goods-icons">赠品</i></span></div><div class="mc_rev">已有<a href="#" class="number">5369</a>人评价</div></li></ul></div></div><div class="JD_con_one"><div class="mt"><h3>达人选购</h3></div><div class="mc"><ul><li><a href="#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt=""></a><a href="#"><em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待</em></a><div class="mc_price"><strong class="price"><span class="J-p-5963064">¥2999.00</span></strong></div></li><li><a href="#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt=""></a><a href="#"><em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待</em></a><div class="mc_price"><strong class="price"><span class="J-p-5963064">¥2999.00</span></strong></div></li><li><a href="#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/593ba628n8794c6a6.jpg" alt=""></a><a href="#"><em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em></a><div class="mc_price"><strong class="price"><span class="J-p-5963064">¥1799.00</span></strong></div></li><li><a href="#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5919637an271a1301.jpg" alt=""></a><a href="#"><em>vivo Xplay6 全网通 6GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待</em></a><div class="mc_price"><strong class="price"><span class="J-p-5963064">¥3498.00</span></strong></div></li></ul></div></div><div class="JD_con_one" style="border:none;"><div class="mt"><h3>商品精选</h3><span>广告</span></div><div class="mc"><ul><li><a href="#"><img src="/static/search/img/599a806bn9d829c1c.jpg" alt=""></a></li><li><a href="#"><img src="/static/search/img/593e4de0n5ff878a4.jpg" alt=""></a></li></ul></div></div></div></div><!--综合排序--><div class="JD_con_right"><div class="filter"><!--综合排序--><!--saleCount_asc/desc skuPrice_asc/desc  --><div class="filter_top"><div class="filter_top_left" th:with="p=${param.sort},priceRange=${param.skuPrice}"><a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))?'sort_a desc':'sort_a'}"th:attr="style=${(#strings.isEmpty(p) || #strings.startsWith(p,'hotScore'))?'color: #FFF;border-color: #e4393c;background: #e4393c':'color: #333;border-color: #CCC;background: #FFF'}"sort="hotScore" href="#">综合排序[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]</a><a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'desc'))?'sort_a desc':'sort_a'}"th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount'))?'color: #FFF;border-color: #e4393c;background: #e4393c':'color: #333;border-color: #CCC;background: #FFF'}"sort="saleCount" href="#">销量[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]</a><a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice') && #strings.endsWith(p,'desc'))?'sort_a desc':'sort_a'}"th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice'))?'color: #FFF;border-color: #e4393c;background: #e4393c':'color: #333;border-color: #CCC;background: #FFF'}"sort="skuPrice" href="#">价格[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]</a><a class="sort_a" href="#">评论分</a><a class="sort_a" href="#">上架时间</a><input id="skuPriceFrom" type="number"th:value="${#strings.isEmpty(priceRange)?'':#strings.substringBefore(priceRange,'_')}"style="width: 100px; margin-left: 30px;"> -<input id="skuPriceTo" type="number"th:value="${#strings.isEmpty(priceRange)?'':#strings.substringAfter(priceRange,'_')}"style="width: 100px;"><button id="skuPriceSearchBtn">确定</button></div><div class="filter_top_right"><span class="fp-text"><b>1</b><em>/</em><i>169</i></span><a href="#" class="prev"><</a><a href="#" class="next"> > </a></div></div><!--收货地址--><div class="filter_bottom"><div class="filter_bottom_left"><div class="fs-cell">收货地</div><div class="dizhi"><div class="dizhi_show"><em>北京朝阳区三环以内</em><b></b></div></div><div class="dizhi_con"><ul id="tab"><li id="tab1" value="1">北京 <img src="/static/search/image/down-@1x.png" alt=""></li><li id="tab2" value="2">朝阳 <img src="/static/search/image/down-@1x.png" alt=""></li><li id="tab3" value="3">三环以内 <img src="/static/search/image/down-@1x.png" alt=""></li></ul><div id="container"><div id="content1" style="z-index: 1;"><a href="#">北京</a><a href="#">上海</a><a href="#">天津</a><a href="#">重庆</a><a href="#">河北</a><a href="#">山西</a><a href="#">河南</a><a href="#">辽宁</a><a href="#">吉林</a><a href="#">黑龙江</a><a href="#">内蒙古</a><a href="#">江苏</a><a href="#">山东</a><a href="#">安徽</a><a href="#">浙江</a><a href="#">福建</a><a href="#">湖北</a><a href="#">湖南</a><a href="#">广东</a><a href="#">广西</a><a href="#">江西</a><a href="#">四川</a><a href="#">海南</a><a href="#">贵州</a><a href="#">云南</a><a href="#">西藏</a><a href="#">陕西</a><a href="#">甘肃</a><a href="#">青海</a><a href="#">宁夏</a><a href="#">新疆</a><a href="#">港澳</a><a href="#">台湾</a><a href="#">钓鱼岛</a><a href="#">海外</a></div><div id="content2"><a href="#">朝阳区</a><a href="#">海淀区</a><a href="#">西城区</a><a href="#">东城区</a><a href="#">大兴区</a><a href="#">丰台区</a><a href="#">昌平区</a><a href="#">顺义区</a></div><div id="content3"><a href="#">三环以内</a><a href="#">管庄</a><a href="#">北苑</a><a href="#">定福庄</a><a href="#">三环到四环之间</a><a href="#">四环到五环之间</a><a href="#">五环到六环之间</a></div></div></div></div><div class="filter_bottom_right"><ul><li><a href="#"><i></i>谷粒商城配送</a></li><li><a href="#"><i></i>京尊达                                    </a></li><li><a href="#"><i></i>货到付款</a></li><li><a href="#" th:with="check = ${param.hasStock}"><input id="showHasStock" type="checkbox" th:checked="${#strings.equals(check,'1')}">仅显示有货</a></li><li><a href="#"><i></i>可配送全球</a></li></ul></div></div><!--排序内容--><div class="rig_tab"><div th:each="product:${result.getProducts()}"><div class="ico"><i class="iconfont icon-weiguanzhu"></i><a href="#">关注</a></div><p class="da"><a href="#" ><img th:src="${product.skuImg}" class="dim"></a></p><ul class="tab_im"><li><a href="#" title="黑色"><img th:src="${product.skuImg}"></a></li></ul><p class="tab_R"><span th:text="'¥'+${product.skuPrice}">¥5199.00</span></p><p class="tab_JE"><a href="#" th:utext="${product.skuTitle}">Apple iPhone 7 Plus (A1661) 32G 黑色 移动联通电信4G手机</a></p><p class="tab_PI">已有<span>11万+</span>热门评价<a href="#">二手有售</a></p><p class="tab_CP"><a href="#" title="谷粒商城Apple产品专营店">谷粒商城Apple产品...</a><a href='#' title="联系供应商进行咨询"><img src="/static/search/img/xcxc.png"></a></p><div class="tab_FO"><div class="FO_one"><p>自营<span>谷粒商城自营,品质保证</span></p><p>满赠<span>该商品参加满赠活动</span></p></div></div></div></div><!--分页--><div class="filter_page"><div class="page_wrap"><span class="page_span1"><a class="page_a" th:attr="pn=${result.pageNum - 1}" href="#" th:if="${result.pageNum>1}">< 上一页</a><a class="page_a" th:attr="pn=${nav},style=${nav == result.pageNum?'border: 0;color:#ee2222;background: #fff':''}" href="#"th:each="nav:${result.pageNavs}">[[${nav}]]</a><a class="page_a" th:attr="pn=${result.pageNum + 1}" href="#" th:if="${result.pageNum<result.totalPages}">下一页 ></a></span><span class="page_span2"><em>共<b>[[${result.totalPages}]]</b>页&nbsp;&nbsp;到第</em><input class="page_nb" type="number" th:value="${result.pageNum}" min="1" th:max="${result.totalPages}"><em>页</em><a class="page_submit" href="#">确定</a></span></div></div></div></div></div></div>
</div><!--商品精选-->
<div class="JD_jx"><div class="JD_jx_title"><div class="mt"><strong class="mt-title">商品精选</strong><img src="/static/search/image/u-ad.gif" alt=""></div><div class="mc"><ul><li><div class="mc_img"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em></a></div><div class="mc_price"><strong><span>¥1999.00</span></strong><span class="mc_ico" title="购买本商品送赠品">赠品</span></div><div class="mc_rev"><a href="#">15930</a><span>人好评</span></div></li><li><div class="mc_img"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em></a></div><div class="mc_price"><strong><span>¥1999.00</span></strong><span class="mc_ico" title="购买本商品送赠品">赠品</span></div><div class="mc_rev"><a href="#">15930</a><span>人好评</span></div></li><li><div class="mc_img"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em></a></div><div class="mc_price"><strong><span>¥1999.00</span></strong><span class="mc_ico" title="购买本商品送赠品">赠品</span></div><div class="mc_rev"><a href="#">15930</a><span>人好评</span></div></li><li><div class="mc_img"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em></a></div><div class="mc_price"><strong><span>¥1999.00</span></strong><span class="mc_ico" title="购买本商品送赠品">赠品</span></div><div class="mc_rev"><a href="#">15930</a><span>人好评</span></div></li><li><div class="mc_img"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待"><em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em></a></div><div class="mc_price"><strong><span>¥1999.00</span></strong><span class="mc_ico" title="购买本商品送赠品">赠品</span></div><div class="mc_rev"><a href="#">15930</a><span>人好评</span></div></li></ul></div></div>
</div><!--猜你喜欢-->
<div class="JD_cnxh"><div class="JD_jx_title"><div class="mt"><strong class="mt-title">猜你喜欢</strong><a href="#"><img src="/static/search/image/update.png" alt="">换一批</a></div><div class="mc"><ul><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em></a></div><div class="mc_rev"><a href="#">已有80万+人评价</a></div><div class="mc_price"><strong><span>¥1999.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/5a28b5c6Ndec5088f.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em></a></div><div class="mc_rev"><a href="#">已有80万+人评价</a></div><div class="mc_price"><strong><span>¥1999.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/593e4de0n5ff878a4.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em></a></div><div class="mc_rev"><a href="#">已有80万+人评价</a></div><div class="mc_price"><strong><span>¥1999.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/593e4de0n5ff878a4.jpg" alt=""></a></div><div class="mc_name"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em></a></div><div class="mc_rev"><a href="#">已有80万+人评价</a></div><div class="mc_price"><strong><span>¥1999.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/59c493a7N3f9b9c85 (1).jpg" alt=""></a></div><div class="mc_name"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em></a></div><div class="mc_rev"><a href="#">已有80万+人评价</a></div><div class="mc_price"><strong><span>¥1999.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/59c493a7N3f9b9c85 (1).jpg" alt=""></a></div><div class="mc_name"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em></a></div><div class="mc_rev"><a href="#">已有80万+人评价</a></div><div class="mc_price"><strong><span>¥1999.00</span></strong></div></li></ul></div></div>
</div><!--我的足迹-->
<div class="JD_zuji"><div class="JD_jx_title"><div class="mt"><strong class="mt-title">我的足迹</strong><a href="#">更多浏览记录</a></div><div class="mc"><ul><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/59e58a11Nc38676d5.jpg" alt=""></a></div><div class="mc_price"><strong><span>¥2998.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/5a28acccN73689386.jpg" alt=""></a></div><div class="mc_price"><strong><span>¥88.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/5a1690ddN441b5dce.jpg" alt=""></a></div><div class="mc_price"><strong><span>¥199.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/5a02bde7N7d4453b1.jpg" alt=""></a></div><div class="mc_price"><strong><span>¥799.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/5a122dbeN044ebf19.jpg" alt=""></a></div><div class="mc_price"><strong><span>¥599.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/59c493a7N3f9b9c85.jpg" alt=""></a></div><div class="mc_price"><strong><span>¥699.00</span></strong></div></li><li><div class="mc_img"><a href="#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img src="/static/search/img/5a08f6f6N5bab2c1c.jpg" alt=""></a></div><div class="mc_price"><strong><span>¥715.00</span></strong></div></li></ul></div></div>
</div><div style="width: 1210px;margin: 0 auto;margin-bottom: 10px"><img src="/static/search/img/5a33a2e0N9a04b4af.jpg" alt=""></div>
<!--底部-->
<footer class="footer"><div class="footer_top"><ul><li><span></span><h3>品类齐全,轻松购物</h3></li><li><span></span><h3>多仓直发,极速配发</h3></li><li><span></span><h3>正品行货,精致服务</h3></li><li><span></span><h3>天天低价,畅选无忧</h3></li></ul></div><div class="footer_center"><ol><li>购物指南</li><li><a href="#" style="color: rgb(114, 114, 114);">购物流程</a></li><li><a href="#" style="color: rgb(114, 114, 114);">会员介绍</a></li><li><a href="#">生活旅行</a></li><li><a href="#" style="color: rgb(114, 114, 114);">常见问题</a></li><li><a href="#">大家电</a></li><li><a href="#" style="color: rgb(114, 114, 114);">联系客服</a></li></ol><ol><li>配送方式</li><li><a href="#" style="color: rgb(114, 114, 114);">上门自提</a></li><li><a href="#" style="color: rgb(114, 114, 114);">211限时达</a></li><li><a href="#" style="color: rgb(114, 114, 114);">配送服务查询</a></li><li><a href="#" style="color: rgb(114, 114, 114);">配送费收取标准</a></li><li><a href="#" style="color: rgb(114, 114, 114);">海外配送</a></li></ol><ol><li>支付方式</li><li><a href="#" style="color: rgb(114, 114, 114);">货到付款</a></li><li><a href="#" style="color: rgb(114, 114, 114);">在线支付</a></li><li><a href="#" style="color: rgb(114, 114, 114);">分期付款</a></li><li><a href="#" style="color: rgb(114, 114, 114);">邮局汇款</a></li><li><a href="#" style="color: rgb(114, 114, 114);">公司转账</a></li></ol><ol><li>售后服务</li><li><a href="#" style="color: rgb(114, 114, 114);">售后政策</a></li><li><a href="#" style="color: rgb(114, 114, 114);">价格保护</a></li><li><a href="#" style="color: rgb(114, 114, 114);">退款说明</a></li><li><a href="#" style="color: rgb(114, 114, 114);">返修/退换货</a></li><li><a href="#">取消订单</a></li></ol><ol><li>特色服务</li><li><a href="#" style="color: rgb(114, 114, 114);">夺宝岛</a></li><li><a href="#">DIY装机</a></li><li><a href="#" style="color: rgb(114, 114, 114);">延保服务</a></li><li><a href="#" style="color: rgb(114, 114, 114);">谷粒商城E卡</a></li><li><a href="#" style="color: rgb(114, 114, 114);">谷粒商城通信</a></li><li><a href="#" style="color: rgb(114, 114, 114);">谷粒商城gulimall+</a></li></ol><ol><li>谷粒商城自营覆盖区域</li><li>谷粒商城已向全国2661个区县提供自<br> 营配送服务,支持货到付款、<br> POS机刷卡和售后上门服务。</li><li><a href="#" style="color: rgb(114, 114, 114);">查看详情&gt;</a></li></ol></div><div class="footer_foot"><p class="footer_p p1"><a href="#">关于我们</a><span></span><a href="#" style="color: rgb(114, 114, 114);">联系我们</a><span></span><a href="#">联系客服</a><span></span><a href="#" style="color: rgb(114, 114, 114);">合作招商</a><span></span><a href="#" style="color: rgb(114, 114, 114);">商家帮助</a><span></span><a href="#" style="color: rgb(114, 114, 114);">营销中心</a><span></span><a href="#" style="color: rgb(114, 114, 114);">手机谷粒商城</a><span></span><a href="#" style="color: rgb(114, 114, 114);">友情链接</a><span></span><a href="#" style="color: rgb(114, 114, 114);">销售联盟</a><span></span><a href="#" style="color: rgb(114, 114, 114);">谷粒商城社区</a><span></span><a href="#" style="color: rgb(114, 114, 114);">风险监测</a><span></span><a href="#">隐私政策</a><span></span><a href="#">谷粒商城公益</a><span></span><a href="#" style="color: rgb(114, 114, 114);">English Site</a><span></span><a href="#">media &amp; IR</a></p><p class="footer_p"><a href="#">京公网安备 11000002000088号</a><span></span><a href="#">京ICP证070359号</a><span></span><a href="#">互联网药品信息服务资格证编号(京)-经营性-2014-0008</a><span></span><a href="#">新出发京零 字第大120007号</a></p><p class="footer_p"><a href="#">互联网出版许可证编号新出网证(京)字150号</a><span></span><a href="#">出版物经营许可证</a><span></span><a href="#">网络文化经营许可证京网文[2014]2148-348号</a><span></span><a href="#">违法和不良信息举报电话:4006561155</a></p><p class="footer_p"><a href="#">Copyright © 2004 - 2017  谷粒商城JD.com 版权所有</a><span></span><a href="#">消费者维权热线:4006067733</a><a href="#">经营证照</a></p><p class="footer_p"><a href="#">谷粒商城旗下网站:</a><a href="#">谷粒商城支付</a><span></span><a href="#">谷粒商城云</a></p><ul><li></li><li></li><li></li><li></li><li></li><li></li></ul></div>
</footer><!--右侧侧边栏-->
<div class="header_bar"><div class="header_bar_box"><ul><li><a href="#"><img src="/static/search/img/wo.png" /></a><div class="div"><a href="#">谷粒商城会员</a></div></li><li><a href="#"><img src="/static/search/img/gouwuche.png" /></a><div class="div"><a href="#">购物车</a></div></li><li><a href="#"><img src="/static/search/img/taoxin.png" /></a><div class="div"><a href="#">我的关注</a></div></li><li><a href="#"><img src="/static/search/img/shi.png" /></a><div class="div"><a href="#">我的足迹</a></div></li><li><a href="#"><img src="/static/search/img/xinxi.png" /></a><div class="div"><a href="#">我的消息</a></div></li><li><a href="#"><img src="/static/search/img/qianbao.png" /></a><div class="div"><a href="#">资讯JIMI</a></div></li></ul><ul><li><a href="#"><img src="/static/search/img/fa3f24a70d38bd439261cb7439e517a5.png" /></a><div class="div"><a href="#">顶部</a></div></li><li><a href="#"><img src="/static/search/img/xinxi.png" /></a><div class="div"><a href="#">反馈</a></div></li></ul></div>
</div><script>$(".sl_ext a:nth-child(1)").hover(function(){$(this).children("b").stop(true).animate({top:"3px"},50);$(this).children("i").stop(true).animate({top:"-23px"},50)},function(){$(this).children("b").stop(true).animate({top:"24px"},50);$(this).children("i").stop(true).animate({top:"3px"},50)});$(".sl_ext a:nth-child(2)").hover(function(){$(this).children("span").stop(true).animate({top:"-1px"},100);$(this).children("i").stop(true).animate({top:"-14px"},100).css({display:"none"})},function(){$(this).children("span").stop(true).animate({top:"14px"},100);$(this).children("i").stop(true).animate({top:"-1px"},100).css({display:"block"})});$('.tab_im img').hover(function(){var a=$(this).prop('src');var index=$(this).parents('li').index();$(this).parents('li').css('border','2px solid red').siblings('li').css('border','1px solid #ccc');$(this).parents('ul').prev().find('img').prop('src',a);$(this).parents('ul').siblings('.tab_JE').find('a').eq(list).css('display','block').siblings('a').css('display','none');$(this).parents('ul').siblings('.tab_R').find('span').eq(list).css('display','block').siblings('span').css('display','none')});$(".JD_ipone_one").hover(function(){$(this).children("div").css({display:"block"})},function(){$(this).children("div").css({display:"none"})});$("#tab>li").click(function() {var i = $(this).index();$("#container>div").hide().eq(i).show()});$(".dizhi_show").hover(function(){$(".dizhi_con").css({display:"block"})},function(){$(".dizhi_con").css({display:"none"})});$(".dizhi_con").hover(function(){$(this).css({display:"block"})},function(){$(this).css({display:"none"})});//显示隐藏var $li = $(".JD_nav_logo>div:gt(3)").hide();$('.JD_show span').click(function(){if($li.is(':hidden')){$li.show();$(this).css({width:"86px"}).text('收起 ^');}else{$li.hide();$('.JD_show span').css({width:"291px"}).text('更多选项( CPU核数、网络、机身颜色 等)');}return false;});$(".rig_tab>div").hover(function(){var i = $(this).index();$(this).find('.ico').css({display:'block'}).stop(true).animate({top:"190px"},300)},function(){var i = $(this).index();$(this).find('.ico').css({display:'none'}).stop(true).animate({top:"230px"})});$('.header_main_left>ul>li').hover(function() {$(this).css({background: "#f0f0f0"}).find('.header_main_left_main').stop(true).fadeIn(300)}, function() {$(this).css({background: "#fff"}).find(".header_main_left_a").css({color: "#000"});$(this).find('.header_main_left_main').stop(true).fadeOut(100)});$(".header_sj a").hover(function() {$(this).css({background: "#444"})}, function() {$(this).css({background: "#6e6568"})});$(".nav_li1 a").hover(function(){$(".header_main_left").stop(true).fadeIn()},function(){$(".header_main_left").stop(true).fadeOut()});$(".header_main_left").hover(function(){$(this).stop(true).fadeIn()},function(){$(this).stop(true).fadeOut()});//右侧侧边栏$(".header_bar_box ul li").hover(function() {$(this).css({background: "#7A6E6E"}).children(".div").css({display: "block"}).stop(true).animate({left: "-60px"}, 300)}, function() {$(this).css({background: "#7A6E6E"}).children(".div").css({display: "none"}).stop(true).animate({left: "0"}, 300)});//底部$(".footer_foot .p1 a").hover(function(){$(this).css("color","#D70B1C")},function(){$(this).css("color","#727272")});$(".footer .footer_center ol li a").hover(function(){$(this).css("color","#D70B1C")},function(){$(this).css("color","#727272")})function searchProducts(name,value){// 原来的页面// var href = location.href + "";// if(href.includes("?")){//     location.href = location.href + "&"+name+"="+value;// }else {//     location.href = location.href + "?"+name+"="+value;// }location.href = replaceAndAddParamVal(location.href,name,value,true)}function searchByKeyword(){searchProducts("keyword",$('#keyword_input').val());}$(".page_a").click(function (){var pn = $(this).attr("pn");var href = location.href;// if(href.includes("pageNum")){// 替换pageNumlocation.href = replaceAndAddParamVal(href,"pageNum",pn);// }else {//     location.href = location.href + "&pageNum="+pn;// }return false;})function replaceAndAddParamVal(url,paramName,replaceVal,forceAdd){var oUrl = url.toString();// 没有就添加,有就替换if(oUrl.includes(paramName)){if(forceAdd){var nUrl = "";if(oUrl.includes("?")){nUrl = oUrl + "&" + paramName+'='+replaceVal;}else {nUrl = oUrl + "?" + paramName+'='+replaceVal;}return nUrl;}else {var re = eval('/('+paramName+'=)([^&]*)/gi');var nUrl = oUrl.replace(re,paramName+'='+replaceVal)return nUrl;}}else {var nUrl = "";if(oUrl.includes("?")){nUrl = oUrl + "&" + paramName+'='+replaceVal;}else {nUrl = oUrl + "?" + paramName+'='+replaceVal;}return nUrl;}}// 分页 - 点击确定,跳转到指定页面$('.page_submit').click(function (){var pageNum = $('.page_nb').val();var href = location.href;// if(href.includes("pageNum")){// 替换pageNumlocation.href = replaceAndAddParamVal(href,"pageNum",pageNum);// }else {//     location.href = location.href + "&pageNum="+pageNum;// }return false;})// 排序$('.sort_a').click(function (){// 1. 当前被点击的元素变为选中状态// 改变当前元素及兄弟元素的样式// changeStyle(this);$(this).toggleClass("desc"); //对当前被选中的元素进行进行class属性的desc隐藏或显示// 2. 跳转到指定位置 sort=saleCount_asc/descvar sort = $(this).attr("sort");sort = $(this).hasClass("desc")?sort+"_desc":sort+"_asc";location.href = replaceAndAddParamVal(location.href,"sort",sort);// 禁止默认行为,a标签的跳转行为return false;})function changeStyle(ele){// 'color: #333;border-color: #CCC;background: #FFF' 默认样式// 'color: #FFF;border-color: #e4393c;background: #e4393c' 高亮样式$('.sort_a').css({"color":"#333","border-color":"#CCC","background":"#FFF"});$('.sort_a').each(function (){var text = $(this).text().replace("↓","").replace("↑","");$(this).text(text);});$(ele).css({"color":"#FFF","border-color":"#e4393c","background":"#e4393c"});// 改变升降序  toggleClass-对当前被选中的元素进行进行class属性的desc隐藏或显示$(ele).toggleClass("desc");// 加上就是降序,不加是升序if($(ele).hasClass("desc")){// 降序var text = $(ele).text().replace("↓","").replace("↑","");text = text + "↓";$(ele).text(text);}else {// 升序var text = $(ele).text().replace("↓","").replace("↑","");text = text + "↑";$(ele).text(text);}}$('#skuPriceSearchBtn').click(function (){// 1. 拼上价格区间的查询条件var from = $('#skuPriceFrom').val();var to = $('#skuPriceTo').val();var query = from + '_'+to;location.href = replaceAndAddParamVal(location.href,"skuPrice",query);})$('#showHasStock').change(function (){if($(this).prop('checked')){location.href = replaceAndAddParamVal(location.href,"hasStock",1);}else {// 没选中var re = eval('/(hasStock=)([^&]*)/gi');// var re = eval('/(&hasStock=)([^&]*)/gi');location.href = (location.href+"").replace(re,"");}})</script>
</body>
</html>

1.9.2 检索服务后端相关代码

1.9.2.1 引入依赖

        用于面包屑导航,远程调用商品服务,根据ID获取名称。注意要引入spring-cloud依赖管理,以及版本控制。

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

1.9.2.2 vo

包 gulimall-search/src/main/java/com/wen/gulimall/search/vo/

@Data
public class AttrResponseVo {private Long attrId;/*** 属性名*/private String attrName;/*** 是否需要检索[0-不需要,1-需要]*/private Integer searchType;/*** 属性图标*/private String icon;/*** 可选值列表[用逗号分隔]*/private String valueSelect;/*** 属性类型[0-销售属性,1-基本属性,2-既是销售属性又是基本属性]*/private Integer attrType;/*** 启用状态[0 - 禁用,1 - 启用]*/private Long enable;/*** 所属分类*/private Long catelogId;/*** 快速展示【是否展示在介绍上;0-否 1-是】,在sku中仍然可以调整*/private Integer showDesc;/*** 值类型[0-为单个值,1-可以选择多个值]*/private Integer valueType;/*** 所属分组*/private Long attrGroupId;/*** 所属分组名称*/private String groupName;/*** 所属分类名称*/private String catelogName;/*** 所属分类的路径*/private Long[] catelogPath;
}
@Data
public class BrandVo {/*** "brandId": 0,* 		"brandName": "string",*/private Long brandId;private String name;
}
@Data
public class SearchParam {private String keyword; // 页面穿过来的全文匹配关键字private Long catalog3Id; // 三级分类的id/*** sort=saleCount_asc/desc* sort=skuPrice_asc/desc* sort=hotScore_asc/desc*/private String sort; //排序条件/*** 好多的过滤条件*  hasStock=0/1*  skuPrice=1_500/_500/500_*  bandId=1*  attrs=2_5寸:6寸*/private Integer hasStock; // 是否只显示有货,0(无库存)1(有库存)private String skuPrice;// 价格区间查询private List<Long> brandId;// 按照品牌进行查询,可以多选private List<String> attrs;// 按照属性进行筛选private Integer pageNum = 1;// 页码private String _queryString; // 原生的所有查询条件}
@Data
public class SearchResult {// 查询到的所有商品信息private List<SkuEsModel> products;/*** 以下是分页信息*/private Integer pageNum; // 当前页码private Long total; // 总记录数private Integer totalPages; // 总页码private List<Integer> pageNavs; // 页码导航栏private List<BrandVo> brands; // 当前查询到的结果,所有涉及到的品牌private List<CatalogVo> catalogs; // 当前查询到的结果,所有涉及到的所有分类private List<AttrVo> attrs; // 当前查询到的结果,所有涉及到的所有属性//========================以上是返回给页面的所有信息==========================// 面包屑导航数据private List<NavVo> navs = new ArrayList<>();private List<Long> attrIds = new ArrayList<>();@Datapublic static class NavVo{private String navName;private String navValue;private String link;}@Datapublic static class BrandVo{private Long brandId;private String brandName;private String brandImg;}@Datapublic static class CatalogVo{private Long catalogId;private String catalogName;}@Datapublic static class AttrVo{private Long attrId;private String attrName;private List<String> attrValue;}}

1.9.2.3 controller

@Controller
public class SearchController {@Resourceprivate MallSearchService mallSearchService;/*** 自动将页面提交过来的所有请求查询参数封装成指定的对象* @param searchParam* @return*/@GetMapping("/list.html")public String listPage(SearchParam searchParam, Model model, HttpServletRequest request){searchParam.set_queryString(request.getQueryString());// 1. 根据传递来的页面的查询参数,去es中检索商品SearchResult result = mallSearchService.search(searchParam);model.addAttribute("result", result);return "list";}
}

1.9.2.4 service

@Service
public class MallSearchServiceImpl implements MallSearchService {@Resourceprivate RestHighLevelClient restHighLevelClient;@Resourceprivate ProductFeignService productFeignService;// 根据条件去es中检索@Overridepublic SearchResult search(SearchParam searchParam) {// 动态的构建出查询所需要的DSLSearchResult result = null;// 1. 准备检索请求SearchRequest searchRequest = buildSearchRequest(searchParam);try {// 2. 执行检索请求SearchResponse searchResponse = restHighLevelClient.search(searchRequest, GulimallElasticsearchConfig.COMMON_OPTIONS);// 3. 分析响应数据,封装成需要的格式result = buildSearchResponse(searchParam,searchResponse);} catch (IOException e) {e.printStackTrace();}return result;}/*** 准备检索请求* # 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析* @return*/private SearchRequest buildSearchRequest(SearchParam searchParam) {// 构建DSL语句对象SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();/*** 查询:模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)*/// 1. 构建bool - queryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 1.1 bool - must 模糊匹配if(StrUtil.isNotEmpty(searchParam.getKeyword())){boolQuery.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));}// 1.2 bool - filter - 按照三级分类id查询if(searchParam.getCatalog3Id() != null){boolQuery.filter(QueryBuilders.termQuery("catalogId",searchParam.getCatalog3Id()));}// 1.2 bool -filter - 按照品牌id查询if(CollectionUtil.isNotEmpty(searchParam.getBrandId())){boolQuery.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));}// 1.2 bool - filter - 按照所有指定的属性进行查询if(CollectionUtil.isNotEmpty(searchParam.getAttrs())){// attrs=1_5寸:6寸&attrs=2_8G:16Gfor (String attrStr : searchParam.getAttrs()) {String[] s = attrStr.split("_");String attrId = s[0]; // 检索的属性idString[] attrValues = s[1].split(":"); // 这个属性检索所需要的值BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));// ScoreMode.None 不参与评分// 每一个必须都得生成一个nested查询NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None);boolQuery.filter(nestedQuery);}}// 1.2 bool -filter - 按照库存是否有进行查询if(searchParam.getHasStock() != null) {boolQuery.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));}// 1.2 bool -filter - 按照价格区间if(StrUtil.isNotEmpty(searchParam.getSkuPrice())){String[] s = searchParam.getSkuPrice().split("_");RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");if(s.length == 2){rangeQuery.gte(s[0]).lte(s[1]);boolQuery.filter(rangeQuery);}else if(s.length == 1){if(searchParam.getSkuPrice().startsWith("_")){rangeQuery.lte(s[0]);boolQuery.filter(rangeQuery);}if(searchParam.getSkuPrice().endsWith("_")){rangeQuery.gte(s[0]);boolQuery.filter(rangeQuery);}}}// 把以上所有的条件都拿来进行封装sourceBuilder.query(boolQuery);/*** 排序,分页,高亮*/// 2.1 排序if(StrUtil.isNotEmpty(searchParam.getSort())){// sort=saleCount_asc/descString[] s = searchParam.getSort().split("_");SortOrder order = s[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;sourceBuilder.sort(s[0],order);}// 2.2 分页 pageSize = 5// pageNum:1   from:0 size:5// pageNum:2   form:5 size:5// from = (pageNum - 1)*sizesourceBuilder.from((searchParam.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);// 2.3 高亮if(StrUtil.isNotEmpty(searchParam.getKeyword())) {HighlightBuilder builder = new HighlightBuilder();builder.field("skuTitle");builder.preTags("<b style='color:red'>");builder.postTags("</b>");sourceBuilder.highlighter(builder);}/*** 聚合分析*/// 1. 品牌聚合TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");brand_agg.field("brandId").size(50);// 品牌聚合的子聚合brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));// TODO 1.聚合brandsourceBuilder.aggregation(brand_agg);// 2. 分类聚合 catalog_aggTermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);// 分类聚合的子聚合catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));// TODO 1.聚合catalogsourceBuilder.aggregation(catalog_agg);// 3. 属性聚合NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");// 聚合出当前所有的attrIdTermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(10);// 聚合出当前所有的attr_id对应的名字attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));// 聚合出当前所有的attr_id对应的所有可能的属性值attrValueattr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));attr_agg.subAggregation(attr_id_agg);// TODO 1.聚合attrsourceBuilder.aggregation(attr_agg);System.out.println("构建的DSL语句"+sourceBuilder.toString());SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX},sourceBuilder);return searchRequest;}/*** 封装检索结果* @param searchParam* @param searchResponse* @return*/private SearchResult buildSearchResponse(SearchParam searchParam, SearchResponse searchResponse) {SearchResult result = new SearchResult();// 1. 返回所有查询到的商品SearchHit[] hits = searchResponse.getHits().getHits();List<SkuEsModel> skuEsModels = new ArrayList<>();if(ArrayUtil.isNotEmpty(hits)) {for (SearchHit hit : hits) {String sourceAsString = hit.getSourceAsString();SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);// keyword非空设置高亮if(StrUtil.isNotEmpty(searchParam.getKeyword())) {String skuTitle = hit.getHighlightFields().get("skuTitle").getFragments()[0].string();skuEsModel.setSkuTitle(skuTitle);}skuEsModels.add(skuEsModel);}}result.setProducts(skuEsModels);2. 当前所有商品涉及到的所有属性信息List<SearchResult.AttrVo> attrVos = new ArrayList<>();ParsedNested attr_agg = searchResponse.getAggregations().get("attr_agg");ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {// 1. 获取属性idlong attrId = bucket.getKeyAsNumber().longValue();// 2. 获取属性的名字String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();// 3. 获取属性的值List<String> attrValues = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(item -> {return ((Terms.Bucket) item).getKeyAsString();}).collect(Collectors.toList());SearchResult.AttrVo attrVo = new SearchResult.AttrVo();attrVo.setAttrId(attrId);attrVo.setAttrName(attrName);attrVo.setAttrValue(attrValues);attrVos.add(attrVo);}result.setAttrs(attrVos);3. 当前所有商品涉及到的所有品牌信息List<SearchResult.BrandVo> brandVos = new ArrayList<>();ParsedLongTerms brand_agg = searchResponse.getAggregations().get("brand_agg");for (Terms.Bucket bucket : brand_agg.getBuckets()) {// 1. 获取品牌的idlong brandId = bucket.getKeyAsNumber().longValue();// 2. 获取品牌的名字String brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();// 3. 获取平品牌的图片String brandImg = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();SearchResult.BrandVo brandVo = new SearchResult.BrandVo();brandVo.setBrandId(brandId);brandVo.setBrandName(brandName);brandVo.setBrandImg(brandImg);brandVos.add(brandVo);}result.setBrands(brandVos);4. 当前所有商品涉及到的所有分类信息ParsedLongTerms catalog_agg = searchResponse.getAggregations().get("catalog_agg");List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();List<? extends Terms.Bucket> buckets = catalog_agg.getBuckets();if(CollectionUtil.isNotEmpty(buckets)){for (Terms.Bucket bucket : buckets) {SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();// 获取分类idString keyAsString = bucket.getKeyAsString();catalogVo.setCatalogId(Long.parseLong(keyAsString));// 获取分类名称ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");String catalog_name = catalog_name_agg.getBuckets().get(0).getKeyAsString();catalogVo.setCatalogName(catalog_name);catalogVos.add(catalogVo);}}result.setCatalogs(catalogVos);//5. 分页信息 - 当前页码result.setPageNum(searchParam.getPageNum());5. 分页信息 - 总记录数long total = searchResponse.getHits().getTotalHits().value;result.setTotal(total);5. 分页信息 - 总页数Integer totalPages = (int)total%EsConstant.PRODUCT_PAGESIZE == 0?(int)total/EsConstant.PRODUCT_PAGESIZE:((int)total/EsConstant.PRODUCT_PAGESIZE + 1);result.setTotalPages(totalPages);5. 分页信息 - 导航栏List<Integer> pageNavs = new ArrayList<>();for(int i = 1;i<=totalPages;i++){pageNavs.add(i);}result.setPageNavs(pageNavs);// 6. 构建面包屑导航功能 - 属性if(CollectionUtil.isNotEmpty(searchParam.getAttrs())) {List<SearchResult.NavVo> collect = searchParam.getAttrs().stream().map(attr -> {// 1. 分析每一个参数传过来的参数值SearchResult.NavVo navVo = new SearchResult.NavVo();// attrs=1_5寸:6寸String[] s = attr.split("_");navVo.setNavValue(s[1]);R r = productFeignService.attrInfo(Long.parseLong(s[0]));result.getAttrIds().add(Long.parseLong(s[0]));if (r.getCode() == 0) {AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {});navVo.setNavName(data.getAttrName());} else {navVo.setNavName(s[0]);}// 2. 取消了这个面包屑以后,我们要跳转到哪个地方String replace = replaceQueryString(searchParam, attr, "attrs");navVo.setLink("http://search.gulimall.com/list.html?" + replace);return navVo;}).collect(Collectors.toList());result.setNavs(collect);}// 品牌、分类的面包屑if(CollectionUtil.isNotEmpty(searchParam.getBrandId())){List<SearchResult.NavVo> navs = result.getNavs();SearchResult.NavVo navVo = new SearchResult.NavVo();navVo.setNavName("品牌");// TODO 远程查询所有品牌R r = productFeignService.brandsInfo(searchParam.getBrandId());if(r.getCode() == 0){List<BrandVo> brand = r.getData("brand", new TypeReference<List<BrandVo>>() {});StringBuffer stringBuffer = new StringBuffer();String replace = "";for (BrandVo brandVo : brand) {stringBuffer.append(brandVo.getName()+";");replace = replaceQueryString(searchParam,brandVo.getBrandId()+"","brandId");}navVo.setNavValue(stringBuffer.toString());navVo.setLink("http://search.gulimall.com/list.html?" + replace);}navs.add(navVo);}return result;}private String replaceQueryString(SearchParam searchParam, String value, String key) {String encode = null;try {encode = URLEncoder.encode(value, "UTF-8");// 前端传递过来的空格被解码成+,替换成%20encode = encode.replace("+","%20"); //浏览器对空格的编码和Java不一样,差异化处理} catch (UnsupportedEncodingException e) {e.printStackTrace();}String replace = searchParam.get_queryString().replace("&"+key+"=" + encode, "");return replace;}
}

1.9.2.5 远程调用接口

开启openfeign

@EnableFeignClients // 开启openfeign

接口

gulimall-search/src/main/java/com/wen/gulimall/search/feign/ProductFeignService.java

@FeignClient("gulimall-product")
public interface ProductFeignService {@GetMapping("/product/attr/info/{attrId}")public R attrInfo(@PathVariable("attrId") Long attrId);@GetMapping("/product/brand/infos")public R brandsInfo(@RequestParam("brandIds") List<Long> brandIds);
}

1.9.3 远程服务相关接口

1.9.3.1 attrInfo接口

gulimall-product/src/main/java/com/wen/gulimall/product/app/AttrController.java

/*** 修改回显信息*/
@RequestMapping("/info/{attrId}")public R info(@PathVariable("attrId") Long attrId){//AttrEntity attr = attrService.getById(attrId);AttrRespVo attrRespVo = attrService.getAttrInfo(attrId);return R.ok().put("attr", attrRespVo);
}

gulimall-product/src/main/java/com/wen/gulimall/product/service/AttrService.java

AttrRespVo getAttrInfo(Long attrId);

 gulimall-product/src/main/java/com/wen/gulimall/product/service/impl/AttrServiceImpl.java

@Cacheable(value = "attr",key = "'attrinfo:'+#root.args[0]")
@Override
public AttrRespVo getAttrInfo(Long attrId) {AttrEntity attrEntity = this.getById(attrId);AttrRespVo attrRespVo = new AttrRespVo();BeanUtils.copyProperties(attrEntity, attrRespVo);if (attrEntity.getCatelogId() != null) {// 所属分类Long[] catelogPath = categoryService.findCatelogPath(attrEntity.getCatelogId());attrRespVo.setCatelogPath(catelogPath);CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());if (categoryEntity != null) {attrRespVo.setCatelogName(categoryEntity.getName());}}if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {// 所属分组AttrAttrgroupRelationEntity attrAttrgroupRelation = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrId));// 可能属性与属性分组没有关联关系if (attrAttrgroupRelation != null) {attrRespVo.setAttrGroupId(attrAttrgroupRelation.getAttrGroupId());AttrGroupEntity groupEntity = attrGroupDao.selectById(attrAttrgroupRelation.getAttrGroupId());if (groupEntity != null) {attrRespVo.setGroupName(groupEntity.getAttrGroupName());}}}return attrRespVo;
}

1.9.3.2 brandsInfo接口

gulimall-product/src/main/java/com/wen/gulimall/product/app/BrandController.java

@GetMapping("/infos")
public R info(@RequestParam("brandIds") List<Long> brandIds){List<BrandEntity> brand = brandService.getBrandsByIds(brandIds);return R.ok().put("brand", brand);
}

gulimall-product/src/main/java/com/wen/gulimall/product/service/BrandService.java

List<BrandEntity> getBrandsByIds(List<Long> brandIds);

gulimall-product/src/main/java/com/wen/gulimall/product/service/impl/BrandServiceImpl.java

@Override
public List<BrandEntity> getBrandsByIds(List<Long> brandIds) {return baseMapper.selectList(new QueryWrapper<BrandEntity>().in("brand_id",brandIds));
}

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

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

相关文章

疲劳驾驶检测和识别3:Android实现疲劳驾驶检测和识别(含源码,可实时检测)

疲劳驾驶检测和识别3&#xff1a;Android实现疲劳驾驶检测和识别(含源码&#xff0c;可实时检测) 目录 疲劳驾驶检测和识别3&#xff1a;Android实现疲劳驾驶检测和识别(含源码&#xff0c;可实时检测) 1.疲劳驾驶检测和识别方法 2.人脸检测方法 3.疲劳驾驶检测和识别模型…

《人工智能安全》课程总体结构

1 课程内容 人工智能安全观&#xff1a;人工智能安全问题、安全属性、技术体系等基本问题进行了归纳整理。人工智能安全的主要数据处理方法&#xff0c;即非平衡数据分类、噪声数据处理和小样本学习。人工智能技术赋能网络空间安全攻击与防御&#xff1a;三个典型实例及攻击图…

Vue异步更新、$nextTick

需求&#xff1a;编辑标题, 编辑框自动聚焦 1. 点击编辑&#xff0c;显示编辑框 2. 让编辑框&#xff0c; 立刻获取焦点 this. isShowEdit true // 显示输入框 this . $refs . inp . focus () // 获取焦点 问题&#xff1a;"显示之后"&#xff0c;立刻获…

24 鼠标常用事件

鼠标进入&#xff1a;enterEvent鼠标离开&#xff1a;leaveEvent鼠标按下&#xff1a;mousePressEvent鼠标释放&#xff1a;mouseRelaseEvent鼠标移动&#xff1a;mouseMoveEvent 提升为自定义控件MyLabel 代码&#xff1a; //mylabel.h #ifndef MYLABEL_H #define MYLABEL_H#…

易班开放应用授权重定向,出现跨域的解决方案

问题描述 今天开发H5网站需要接入易班&#xff0c;经过易班授权然后重定向&#xff08;code: 302&#xff09;&#xff0c;使用axios发请求&#xff0c;但是前后端均配置跨域的情况下&#xff0c;不管怎么弄都是一直跨域 但是我们看network&#xff0c;network中对应请求的res…

微服务初始

今天准备开始学习微服务&#xff0c;使用微服务肯定是因为他有好处。 首先了解到的三种架构&#xff0c;传统单体&#xff0c;集群架构&#xff0c;微服务架构 单体架构 有单点问题&#xff0c;如果宕机所有的服务都不可用所有业务的功能模块都聚集在一起&#xff0c;如果代…

tinkerCAD案例:9. Saw Shaped Wrench 锯形扳手

tinkerCAD案例&#xff1a;9. Saw Shaped Wrench 锯形扳手 ln this lesson you will learn how to create a cool saw shaped wrench. 在本课中&#xff0c;您将学习如何制作一个很酷的锯形扳手。 Start the lesson by dragging a polygon to the workplane. 通过将多边形拖动…

Windows实现端口转发(附配置过程图文详解)

文章目录 1. 前言2. 命令提示符3. 防火墙4. netsh 命令4.1 查看已有的转发规则4.2 新增转发规则4.3 删除转发规则 5. 图解汇总6. 欢迎纠正~ 1. 前言 利用Windows端口转发&#xff0c;实现本地设备 ⬅➡ 公网主机 ⬅➡ 远端服务器 2. 命令提示符 以管理员身份打开“命令提示…

python调用百度ai将图片识别为表格excel

python调用百度ai将图片识别为表格excel ocr ocr 百度ai官方文档&#xff1a;https://ai.baidu.com/ai-doc/OCR/Ik3h7y238 import requests import json import base64 import time文档&#xff1a;https://ai.baidu.com/ai-doc/OCR/Ik3h7y238 # 获取access_token def get_acc…

vue3+ts+element-plus 之使用node.js对接mysql进行表格数据展示

vue3tselement-plus axiosnode.jsmysql开发管理系统之表格展示 ✏️ 1. 新建一个node项目* 初始化node* 安装可能用到的依赖* 配置文件目录* 添加路由router1. 添加router.js文件&#xff0c;添加一个test目录2. 修改app.js ,引入router&#x1f4d2; 3. 启动并在浏览器打开 * …

【1++的C++初阶】之适配器

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的C初阶】 文章目录 一&#xff0c;什么是适配器二&#xff0c;栈与队列模拟实现三&#xff0c;优先级队列四&#xff0c;reverse_iterator 一&#xff0c;什么是适配器 适配器作为STL的六大组…

【爬虫逆向案例】某道翻译js逆向—— sign解密

声明&#xff1a;本文只作学习研究&#xff0c;禁止用于非法用途&#xff0c;否则后果自负&#xff0c;如有侵权&#xff0c;请告知删除&#xff0c;谢谢&#xff01; 【爬虫逆向案例】某道翻译js逆向—— sign解密 1、前言2、步骤3、源码4、号外 1、前言 相信各位小伙伴在写…

SAMStable-Diffusion集成进化!分割、生成一切!AI绘画新玩法

自SAM「分割一切」模型推出之后&#xff0c;二创潮就开始了&#xff0c;有想法有行动&#xff01;飞桨AI Studio开发者会唱歌的炼丹师就创作出SAM进化版&#xff0c;将SAM、Stable Diffusion集成&#xff0c;实现「分割」、「生成」能力二合一&#xff0c;并部署为应用&#xf…

JVM - 运行时数据区域

文章目录 程序计数器栈堆方法区知识延申 -- 字符串常量池 程序计数器 并发情况下&#xff0c;会发生线程之间的上下文切换&#xff0c;当 线程1 的CPU时间片用完后&#xff0c;需要程序计数器记录 线程1 的下一条JVM指令的地址&#xff0c;等下一次 线程1 继续运行的时&#x…

水环境综合治理监测系统:筑牢城市水生态安全屏障

水是生命之源&#xff0c;是人类赖以生存的基础。然而&#xff0c;随着工业化、城市化的快速发展&#xff0c;水污染问题日益凸显&#xff0c;给居民的环境卫生以及用水安全带来了巨大的威胁。因此&#xff0c;加强水环境综合治理&#xff0c;保护水资源和维护生态平衡&#xf…

28.1 kibana

Kibana 是一个免费且开放的用户界面&#xff0c;能够对 Elasticsearch 数据进行可视化操作&#xff0c;从跟踪查询负载&#xff0c;到理解请求如何流经整个应用&#xff0c;都能轻松完成。 1.Kibana安装 注意要与ES版本保持一致 https://www.elastic.co/downloads/past-relea…

看完这篇,别再说不会Spring 分库分表了

多数据源&#xff0c;读写分离&#xff0c;分库分表&#xff0c;基本上已经是现在任何一个项目的基本配置了&#xff0c;在之前的文章Spring多数据源实现https://blog.csdn.net/wangerrong/article/details/131910740 里讲了多数据源的实现&#xff0c;其实已经包含了读写分离…

Linux使用教程

一、Linux命令基础 1、ls、ll命令——展示数据 ①ls命令——平铺展示数据 其中ls命令以平铺的方式展现数据 ②ll命令——列表展示数据 ll命令以列表的方式展现数据 -a选项&#xff0c;表示&#xff1a;all的意思&#xff0c;即列出全部文件&#xff08;包含隐藏的文件/文件夹…

ARM寄存器组织

一、寄存器二、ARM寄存器三、专用寄存器四、CPSR寄存器 一、寄存器 概念 寄存器是处理器内部的存储器&#xff0c;没有地址 作用 一般用于暂时存放参与运算的数据和运算结果 分类 包括通用寄存器、专用寄存器、控制寄存器 二、ARM寄存器 注 在某个特定模式下只能使用当前模…

Web浪漫历程:揭秘二十年间与您“约会”的浏览器发展

&#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; &#x1f5a5;️ Node专栏&#xff1a;Node.js从入门到精通 &#x1f5a5;️ TS知识总结&#xff1a;十万字TS知识点总结 &#x1f449; 你的一键三连是我更新的最大动力❤️&#xff01;…