【电商项目】商品搜索开发复盘(4):封装查询面板功能逻辑思考与编写
目录
这是商品搜索相关文章的最后一篇了。前几篇在这里👇
【电商项目】商品搜索开发复盘(1):根据需求拆解搜索接口的设计逻辑
我们继续(3)往下编写封装查询面板功能。
需求如下:查询面板中包含品牌、分类、规格项。根据查询条件,找到查询结果关联度前20名的商品数据,遍历这些数据,找到它们的品牌、分类、规格项,最后封装到查询结果中。
样式参考京东页面:
一、构造搜索条件
封装查询面板,首先就是根据需求构建搜索条件,把这最符合的20条数据查出来。
其实本质就是一个分页查询,构造出一页,一页有20条嘛,并且遵循默认排序。
// 1.构造搜索条件
goodsSearchParam.setPage(1);
goodsSearchParam.setSize(20);
goodsSearchParam.setSort(null); // 默认排序方式
goodsSearchParam.setSortFiled(null); //默认排序字段
NativeQuery query = buildQuery(goodsSearchParam); //构建查询条件
二、搜索
拿到条件后搜索,详细讲解见(3).二。
// 2.搜索
SearchHits<GoodsES> search = elasticsearchTemplate.search(nativeQuery, GoodsES.class);
三、将结果封装为List对象
详解见(3).三。
// 3.将结果封装为List对象
List<GoodsES> list = new ArrayList<>();
for (SearchHit<GoodsES> goodsESSearchHit : search) {
GoodsES content = goodsESSearchHit.getContent();
list.add(content);
}
四、遍历集合,封装查询面板
思路就是 获取搜索数据 -> 按需求分类收集封装。参考1图的京东页面可知查询面板封装的有“品牌、类型、规格”。具体实现如下:
- 创建需要的空集合,用于聚合筛选数据
- 遍历ES返回的所有商品数据
- 收集商品需求对象,并完成去重
- 封装,返回结果对象
// 4.遍历集合,封装查询面板
// 商品相关的品牌列表
Set<String> brands = new HashSet<>();
// 商品相关的类型列表
Set<String> productType = new HashSet<>();
// 商品相关的规格列表
HashMap<String, Set<String>> specifications = new HashMap<>();
for (GoodsES l : list) {
// 获取品牌
brands.add(l.getBrand());
// 获取类型
productType.addAll(l.getProductType());
// 获取规格
Map<String, List<String>> specification = l.getSpecification();
Set<Map.Entry<String, List<String>>> entries = specification.entrySet();
for (Map.Entry<String, List<String>> entry : entries) {
String key = entry.getKey();
List<String> values = entry.getValue();
// 如果没有遍历出该规格,新增键值对,如果已经遍历出该规格,则向规格中添加规格项
if(!specifications.containsKey(key)){
specifications.put(key, new HashSet(values)); // 新增键值对
}
specifications.get(key).addAll(values); // 向规格中添加规格项
}
}
goodsSearchResult.setBrands(brands);
goodsSearchResult.setProductType(productType);
goodsSearchResult.setSpecifications(specifications);
Q1: HashSet 和 HashMap 的区别?为什么品牌、类目用 HashSet,规格用 HashMap?
A:
HashSet:单列集合,只存值,元素不可重复;底层就是HashMap,只保留 key, value 占位。适合存无分组的零散字符串。HashMap:双列集合,存【key:value 键值对】,key 唯一。适合存需分组键值结构的数据。- 品牌、类目只是独立的字符串,不需要分组 →
Set<String>。- 规格是 【 规格名:[规格值 1,规格值 2] 】的分组结构 →
Map<String,Set<String>>此举利用了 Hash自动去重的特性。
Q2:为什么要先单独定义集合,最后统一 set 进 Result,不能遍历直接 get** ().add (**)?
我最开始就这么写的哈,真是错极了,运行报错,我去查了下为何不妥。

A:
- 根因:
拿 goodsSearchResult.getBrands().add() 举例:如果
goodsSearchResult内部的 brands 集合初始是 null,那么getBrands().add()会直接空指针报错(getBrands()返回 null,null.add()就炸)。
- 从业务逻辑层面考虑:
搜索页需求:前端筛选框只展示不重复的品牌、分类、规格,必须先用 Set 全局汇总所有商品数据、自动去重,全部遍历完再整体赋值给返回体,不能边遍历边往 Result 里塞。
- 标准写法:
先手动
new HashSet初始化空集合,遍历全部商品把数据全部汇总完毕,最后一次性 set 进 Result 对象,即可避免上述问题。
五、代码整体展示
/*
封装查询面板,即根据查询条件,找到查询结果关联度前20名的商品进行封装
@param goodsSearchParam
@param goodsSearchResult
*/
public void buildSearchPanel(GoodsSearchParam goodsSearchParam, GoodsSearchResult goodsSearchResult){
// 1.构造搜索条件
goodsSearchParam.setPage(1);
goodsSearchParam.setSize(20);
goodsSearchParam.setSort(null);
goodsSearchParam.setSortFiled(null);
NativeQuery nativeQuery = buildQuery(goodsSearchParam);
// 2.搜索
SearchHits<GoodsES> search = elasticsearchTemplate.search(nativeQuery, GoodsES.class);
// 3.将结果封装为List对象
List<GoodsES> list = new ArrayList<>();
for (SearchHit<GoodsES> goodsESSearchHit : search) {
GoodsES content = goodsESSearchHit.getContent();
list.add(content);
}
// 4.遍历集合,封装查询面板
// 商品相关的品牌列表
Set<String> brands = new HashSet<>();
// 商品相关的类型列表
Set<String> productType = new HashSet<>();
// 商品相关的规格列表
HashMap<String, Set<String>> specifications = new HashMap<>();
for (GoodsES l : list) {
// 获取品牌
brands.add(l.getBrand());
// 获取类型
productType.addAll(l.getProductType());
// 获取规格
Map<String, List<String>> specification = l.getSpecification();
Set<Map.Entry<String, List<String>>> entries = specification.entrySet();
for (Map.Entry<String, List<String>> entry : entries) {
String key = entry.getKey();
List<String> values = entry.getValue();
// 如果没有遍历出该规格,新增键值对,如果已经遍历出该规格,则向规格中添加规格项
if(!specifications.containsKey(key)){
specifications.put(key, new HashSet(values)); // 新增键值对
}
specifications.get(key).addAll(values); // 向规格中添加规格项
}
}
goodsSearchResult.setBrands(brands);
goodsSearchResult.setProductType(productType);
goodsSearchResult.setSpecifications(specifications);
}
六、总结
所写即为总结,感谢您的阅读🤝给予我坚持记录的动力。虽然很多问题不够有深度甚至可以说是拿出来说都招笑,不过我会继续努力快速成长的。
更多推荐




所有评论(0)