商品上架

在商城中搜索商品,只能搜索到已上架的商品。

而商品上架时,需要把数据也同步到elasticsearch中以供搜索。

但是肯定不能把完整的数据全部存到es中,因为es中的数据是存储在内存中的,就算es是分布式的,理论上可以存储非常多的数据,但是内存产品终究是比硬盘贵的。所以考虑到经济效益,我们是要取某些数据存到es中,而不是完整数据都存进去。

哪些数据存储在es中?

sku在es中存储模型分析

es中应存储能够被检索的条件。

比如这个商城能够用于检索的条件就有:

分类品牌综合销量价格区间CPU型号,运行内存,机身内存(这些应该是每种分类特有的属性)、等等

分析:商品上架在 es 中是存 sku 还是 spu?

答:搜索一般是搜索sku的信息,因为一个spu中包含多个sku,每个sku的情况都不一样,搜索的时候可能一个spu里的某几个sku符合条件,几个sku不符合条件。(我的理解)

  1. 检索的时候输入名字,是需要按照 sku 的 title 进行全文检索的

  2. 检索使用商品规格,规格是 spu 的公共属性,每个 spu 是一样的

  3. 按照分类 id 进去的都是直接列出 spu 的,还可以切换。

  4. 我们如果将 sku 的全量信息保存到 es 中(包括 spu 属性)就太多量字段了。

  5. 我们如果将 spu 以及他包含的 sku 信息保存到 es 中,也可以方便检索。但是 sku 属于spu 的级联对象,在 es 中需要 nested 模型,这种性能差点。

  6. 但是存储与检索我们必须性能折中。

  7. 如果我们分拆存储,spu 和 attr 一个索引,sku 单独一个索引可能涉及的问题。检索商品的名字,如“手机”,对应的 spu 有很多,我们要分析出这些 spu 的所有关联属性,再做一次查询,就必须将所有 spu_id 都发出去。假设有 1 万个数据,数据传输一次就10000*4=4MB;并发情况下假设 1000 检索请求,那就是 4GB 的数据,,传输阻塞时间会很长,业务更加无法继续。

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

text类型和keyword类型的区别:

text:会分词,然后进行索引

​ 支持模糊、精确查询

​ 不支持聚合

keyword:不进行分词,直接索引

​ 支持模糊、精确查询

​ 支持聚合

字段 含义
index 默认 true,如果为 false,表示该字段不会被索引,但是检索结果里面有,但字段本身不能
doc_values 默认 true,设置为 false,表示不可以做排序、聚合以及脚本操作,这样更节省磁盘空间。还可以通过设定 doc_values 为 true,index 为 false 来让字段不能被搜索但可以用于排序、聚合以及脚本操作

es的扁平化处理

字段 含义
nested 嵌入式的字段,就可以不使用es的扁平化处理了。

构造数据实体类

由于这些数据是要在多个服务之间进行传输的,所以定义为to类型。为了方便每一个服务调用,我们直接创建在common模块中。

// 根据分析出来的模型构建
@Data
public class SkuEsTo {private Long skuId;private Long spuId;private String skuTitle;private BigDecimal skuPrice;private String skuImg;private Long saleCount;private Boolean hasStock;private Long hotScore;private Long brandId;private Long catalogId;private String brandName;private String brandImg;private String catalogName;private List<Attr> attrs;@Datapublic static class Attr {private Long attrId;private String attrName;private String attrValue;}
}

商品上架功能实现

具体步骤

  1. 获取spu下的所有所属sku
  2. 获取每一个sku中的需要使用的信息
    • 库存信息
    • 可检索属性
  3. 组装SkuEsTo的数据到集合中。
  4. List<SkuEsTo>发送给es,让es保存,达到上架的状态。
  5. 修改数据库中spu的发布状态(1-上架)

具体实现

  1. 因为可检索属性是每一个sku都相同的,所以能在一开始直接获取,不要在循环内获取。
  2. 库存信息需要调用其他服务,这种操作速度应该会比较慢,所以也在循环外一次性获取,需要新建一个存储库存信息的TO实体类。

以下列出部分代码。(因为代码太多了,只列出一些关键的)

  1. 商品上架的逻辑处理层
@Override
public void up(Long spuId) {// 1.获取该spu的所有可检索属性。// attrs;因为spu的属性是每个sku共享的,用空间换时间,所以在每一个sku里面都存一遍!!过滤出需要检索的属性,0为不需要检索,1为需要检索List<ProductAttrValueEntity> productAttrValueList = productAttrValueService.getAttrsBySpuId(spuId);// 获取spu下所有属性idList<Long> attrIds = productAttrValueList.stream().map(ProductAttrValueEntity::getAttrId).collect(Collectors.toList());// 得到spu下所有的可检索属性idList<Long> searchAttrIds = attrService.selectSearchAttrIds(attrIds);// 过滤出所有可检索的属性信息List<SkuEsTo.Attr> searchAttrList = productAttrValueList.stream().filter(attr -> searchAttrIds.contains(attr.getAttrId()))// 组装skuEsTo中的attrs.map(searchAttrItem -> {SkuEsTo.Attr attr = new SkuEsTo.Attr();attr.setAttrId(searchAttrItem.getAttrId());attr.setAttrName(searchAttrItem.getAttrName());attr.setAttrValue(searchAttrItem.getAttrValue());return attr;}).collect(Collectors.toList());// 2.获取sku库存信息List<SkuInfoEntity> skuInfoEntityList = skuInfoService.getInfoBySpuId(spuId);// 得到所有的skuId,调用远程服务,获取库存信息List<Long> skuIds = skuInfoEntityList.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());Object skuStockInfoList = null;try {R r = wareFeignService.hasStock(skuIds);skuStockInfoList = r.get("skuStockToList");}catch (Exception e){log.error("库存服务查询失败,原因:{}", e.getMessage());}Object finalSkuStockInfoList = skuStockInfoList;List<SkuEsTo> esProductList = skuInfoEntityList.stream().map(skuInfoEntity -> {SkuEsTo skuEsTo = new SkuEsTo();BeanUtils.copyProperties(skuInfoEntity, skuEsTo);// skuPrice;skuImg;skuEsTo.setSkuPrice(skuInfoEntity.getPrice());skuEsTo.setSkuImg(skuInfoEntity.getSkuDefaultImg());// hasStock;hotScore;if (finalSkuStockInfoList != null){// 做一个类型转换,把list中的数据放到map中,map<skuId, 是否有库存>Map<Long, Boolean> stockInfoMap = TypeConversion.objListToMapOfLongBoolean(finalSkuStockInfoList);Boolean hasStock = stockInfoMap.get(skuInfoEntity.getSkuId());skuEsTo.setHasStock(hasStock);} else {skuEsTo.setHasStock(true);}// todo:热度评分,默认新上架的商品评分为0,给钱就高分skuEsTo.setHotScore(0L);// brandName;brandImg;BrandEntity brandEntity = brandService.getById(skuInfoEntity.getBrandId());skuEsTo.setBrandName(brandEntity.getName());skuEsTo.setBrandImg(brandEntity.getLogo());// catalogName;CategoryEntity categoryEntity = categoryService.getById(skuInfoEntity.getCatalogId());skuEsTo.setCatalogName(categoryEntity.getName());// attrs;skuEsTo.setAttrs(searchAttrList);return skuEsTo;}).collect(Collectors.toList());// 3.将数据发给es,让es做一个保存R r = searchFeignService.productStatusUp(esProductList);if (r.getCode() == 0){boolean status = (boolean) r.get("status");// 4.上架成功,修改数据库中的上架状态spuInfoDao.updateSpuPublishStatus(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());}else {log.error("商品上架失败");// TODO: 重复调用?接口幂等性?重试机制?}
}
  1. 在common模块中再新建一个存储库存信息的TO实体类。
@Data
public class SkuStockTo {/*** skuId*/private Long skuId;/*** 是否有库存*/private Boolean hasStock;
}
  1. 批量将数据存储到es
@Override
public boolean productStatusUp(List<SkuEsTo> skuEsToList) {boolean success = true;// 1.给es中建立索引。product,建立好映射关系。// 2.在es中保存这些数据。数据多,不可能一个一个组装,使用bulk批量处理// BulkRequest bulkRequest, RequestOptions optionsBulkRequest bulkRequest = new BulkRequest();for (SkuEsTo skuEsTo : skuEsToList) {// 3.组装数据IndexRequest request = new IndexRequest(SearchConstant.IndexEnum.PRODUCT_INDEX.getIndex());// 指定idrequest.id(String.valueOf(skuEsTo.getSkuId()));String skuEsToJson = JSON.toJSONString(skuEsTo);// 指定json数据System.out.println(skuEsTo);request.source(skuEsToJson, XContentType.JSON);bulkRequest.add(request);}try {// 4.执行BulkResponse response = client.bulk(bulkRequest, GulimallElasticsearchConfig.COMMON_OPTIONS);if (response.hasFailures()){success = false;List<String> failureList = Arrays.stream(response.getItems()).filter(item -> item.getFailure() != null).map(BulkItemResponse::getId).collect(Collectors.toList());log.error("批量操作错误项:{}", failureList);}} catch (IOException e) {log.error("批量操作遇到错误,原因:{}", e.getMessage());}return success;
}

谷粒商城-商城业务(商品上架)相关推荐

  1. 谷粒商城项目8——商品上架 上架商品sku保存到es nginx配置

    文章目录 一.商城业务 1.商品上架 1.1 ES 的存储结构分析 1.2 PUT product 1.3 一些细节 2.商品上架-构造基本数据 3.商品上架-业务代码: 4.商品上架-search模 ...

  2. gulimall-商城业务-商品上架

    商城业务 前言 一.商品上架 1.1 商品 Mapping 1.2 商品信息保存到es 1.3 es数组的扁平化处理 1.4 构造基本数据 前言 本文继续记录B站谷粒商城项目视频 P128-135 的 ...

  3. 16.商品业务-商品上架

    文章目录 1 sku在es中的存储模型分析 1.1 商品Mapping 2 nested数据类型 3 商品上架服务 3.1 远程调用查询库存服务 3.2 实现es存储业务 3.2.1 远程调用接口 3 ...

  4. 【谷粒商城高级篇】商品服务 商品上架

    谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...

  5. 谷粒商城十elasticsearch搜索服务及商品上架

    springboot整合Elasticsearch-Rest-Client <?xml version="1.0" encoding="UTF-8"?&g ...

  6. 谷粒商城:秒杀商品定时上架

    1.定时任务 1.spring中6位组成,不允许第七位的年,即秒.分.时.日.月.周 2.在周几的位置,1-7代表周一到周日,MON-SUN 3.定时任务不应该是阻塞的,默认是阻塞的. (1)可以让业 ...

  7. 谷粒商城高级篇(36)——商品上架之上传数据到Elasticsearch

    商品上架之上传数据到Elasticsearch 功能需求分析 分析-怎么设计存储结构来保存数据 空间换时间 时间换空间 最终方案-存储结构 关于 nested 类型 商品上架功能实现 guimall- ...

  8. 谷粒商城(商品上架、首页、异步、商品详细)思路详解

    商品业务总结 1.商品上架 1.sku模型分析 2.嵌入式 3.构造基本数据 4.检索属性 5.设置库存信息 6.保存model到es中 7.R的修改 2.首页 1.新建页面 2.获取一级目录. 3. ...

  9. 【谷粒商城】ElasticSearch、上架与检索

    笔记-基础篇-1(P1-P28):https://blog.csdn.net/hancoder/article/details/106922139 笔记-基础篇-2(P28-P100):https:/ ...

最新文章

  1. (SpringMVC)概述和简单使用
  2. MySql 创建存储过程
  3. c++ map 多线程同时更新值 崩溃_深入理解并发安全的 sync.Map
  4. 世界上最伟大的十个公式,看看你懂得几个?
  5. nginx.conf文件详解
  6. iOS连续上传多张图片
  7. Android记录日志方式,关于Android中处理崩溃异常和记录日志的另一种实现思路
  8. 自学python3 最好的入门书籍-学习python3入门书籍选哪些?
  9. PTA 程序设计天梯赛(1~180题)
  10. ln多少等于2用计算机,ln2(log计算器在线)
  11. 英特尔:准备好放弃芯片制造了吗?
  12. HTML超链接的使用
  13. ubuntu下使用mosquitto与分析
  14. 2018苹果发布会新品 是如何成为众商家的追热点目标
  15. java获取时间下周几的时间
  16. IO模型-异步I/O模型
  17. 图解红黑树及Java进行红黑二叉树遍历的方法
  18. JavaScript设计模式----单例模式
  19. http://www.55zm.com/a/20120702/38037.html
  20. NestJS 7.x 折腾记: (6) 异常过滤器,取其精华去其糟粕!比如响应异常数据的包装~

热门文章

  1. c语言规定的主函数名是,C语言源程序中主函数名由系统规定为_______,程序员是不能改变的。...
  2. xilinx芯片的 IOB 以及 IOB = false
  3. 【RF基础】RF调用python函数基础
  4. Android RSA加密解密的 工具类的使用
  5. 不同进制数之间的转换
  6. java 编码app_智慧职教mooc的APPJava编码技术(四川交通职业技术学院)答案搜题公众号...
  7. 国标GB/T28181视频流媒体服务器4G摄像头视频无插件直播方案对接过程中前端设备正常上线但视频无法播放问题解决
  8. Apollo 2.0 传感器标定方法 使用指南(官方)
  9. SNAP处理哨兵2号遥感数据的步骤和方法
  10. 3-4-搭建自己的vue-ssr