电商项目——全文检索-ElasticSearch——第一章——中篇
电商项目——商城业务-商品上架——第二章——中篇
电商项目——商城业务-首页——第三章——中篇
电商项目——性能压测——第四章——中篇
电商项目——缓存——第五章——中篇
电商项目——商城业务-检索服务——第六章——中篇
电商项目——商城业务-异步——第七章——中篇
电商项目——商品详情——第八章——中篇
电商项目——认证服务——第九章——中篇
电商项目——购物车——第十章——中篇
电商项目——消息队列——第十一章——中篇
电商项目——订单服务——第十二章——中篇
电商项目——分布式事务——第十三章——中篇
文章目录
1:sku在es中的存储模型分析
es作为全文检索引擎,1.它承担项目里的所有全文检索功能,2.也会承担日志里面的全文检索信息功能(项目里面有些日志我们要进行快速定位,日志也有检索需求,我们把它传输到ES里面(有一个技术栈叫ELK,,LogStash负责收集日志,把收集来的日志存到ES里面,使用Kibana来进行可视化界面获取))
mysql的全文检索功能没有es强大,做这么复杂的检索数据,mysql性能不行(es都是将数据存在内存中)
第一步:将商品数据给es中存一份,去es里面检索商品数据,我们首先要分析,将那些数据保存在es中,在分析这个之前,我们要达成一个共识:es里面的数据都是存在内存中的,内存比硬盘贵的多,所以我们在es中只保存有用的数据,没用的信息不保存,要用的时候,大不了就直接检索出他们的id,然后再去数据库里面查找对应的信息,除了保存sku的信息,还要保存它的品牌,所属三级分类,还有他的规格检索
启动idea,webstorm,nacos
如上会出现一个极大问题,检索手机的时候每选中一个规格,规格都在不断发生变化,最大的特点,查出的商品必须会展现出来,不可呢列出一个比如机身存储,它检索完没有
怎么动态计算出来的呢?
比如我们检索一个手机,他会找到标题里面所有包含手机的商品聚合起来,分析出这些商品里所有的规格参数(属性)和属性值,保证点击一个属性值,就会有对应的商品被检索出来
空间和时间不可以兼得,第一种方法浪费了空间获得了时间
第二种相反
最终我们商品中的es数据模型构建成如下的样子
2:nested数据类型场景
嵌套类型是对象数据类型的专门化版本,它允许以一种可以相互独立查询的方式对对象数组进行索引。
https://www.elastic.co/guide/en/elasticsearch/reference/7.9/nested.html
- 如果需要为对象数组建立索引并维护数组中每个对象的独立性,请使用嵌套数据类型而不是对象数据类型。
- 在内部,嵌套对象索引数组中的每个对象作为一个单独的隐藏文档,这意味着每个嵌套对象可以通过嵌套查询独立于其他对象进行查询:
PUT my-index-000001
{
"mappings": {
"properties": {
"user": {
//用户字段被映射为类型嵌套,而不是类型对象。
"type": "nested"
}
}
}
}
PUT my-index-000001/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
//这个查询不匹配,因为Alice和Smith不在同一个嵌套对象中。
GET my-index-000001/_search
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "Smith" }}
]
}
}
}
}
}
//这个查询匹配是因为Alice和White在同一个嵌套对象中。
GET my-index-000001/_search
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "White" }}
]
}
},
"inner_hits": {
"highlight": {
"fields": {
"user.first": {}
}
}
}
}
}
}
3:构造基本数据
响应数据
{
"msg": "success",
"code": 0
}
mall-product
SpuInfoController
@RestController
@RequestMapping("product/spuinfo")
public class SpuInfoController {
@Autowired
private SpuInfoService spuInfoService;
// /product/spuinfo/{spuId}/up
@PostMapping("/{spuId}/up")
public R spuUp(@PathVariable("spuId") Long spuId){
spuInfoService.spuUp(spuId);
return R.ok();
}
SpuInfoServiceImpl
/**
* 商品上架功能
* @param spuId
*/
@Override
public void spuUp(Long spuId) {
// List<SkuEsModel> uoProducts=new ArrayList<>();
//1.组装需要的数据
// SkuEsModel skuEsModel=new SkuEsModel();
//1:查出当前spuid对应的所有sku信息,品牌名字
List<SkuInfoEntity> skuInfoEntities= skuInfoService.getSkusBySpuId(spuId);
//todo 4:查询出当前sku的所有的可以被用来检索的规格属性(只要查一遍就可以了,放在stream外面来做
// private List<Attrs> attrs;
// @Data
// public static class Attrs{
// private Long attrId;
// private String attrName;
// private StringBuilder attrValue;
//// }
//2:封装每个sku的信息
List<SkuEsModel> collect = skuInfoEntities.stream().map((sku) -> {
//1.组装需要的数据
SkuEsModel skuEsModel = new SkuEsModel();
//属性对拷
BeanUtils.copyProperties(sku,skuEsModel);
//SkuEsModel SkuInfoEntity里面不一样的属性是:
/**
* private BigDecimal skuPrice;
private String skuImg;
private Boolean hasStock;
private Long hotScore;
* private String brandName;
private String brandImg;
private String catalogName;
private List<Attrs> attrs;
@Data
public static class Attrs{
private Long attrId;
private String attrName;
private StringBuilder attrValue;
}
*/
// private BigDecimal skuPrice;
// private String skuImg;
skuEsModel.setSkuPrice(sku.getPrice());
skuEsModel.setSkuImg(sku.getSkuDefaultImg());
// private Boolean hasStock;
// private Long hotScore;
//todo 1:发送远程调用,库存系统查询是否有库存
//todo 2:热度评分 0
// todo 3:查询品牌和分类的名字信息
// private String brandName;
// private String brandImg;
BrandEntity byId = brandService.getById(skuEsModel.getBrandId());
skuEsModel.setBrandName(byId.getName());
skuEsModel.setBrandImg(byId.getLogo());
// private String catalogName;
CategoryEntity byId1 = categoryService.getById(skuEsModel.getCatalogId());
skuEsModel.setCatalogName(byId1.getName());
return skuEsModel;
}).collect(Collectors.toList());
// todo:5:将数据发送给es进行保存:mall-search
}
4:构造sku检索属性
AttrServiceImpl
/**
* 在指定的所有数据集合里面,挑选出检索属性
* @param attrIds
* @return
*/
@Override
public List<Long> selectSearchAttrs(List<Long> attrIds) {
List<Long> longs = baseMapper.selectSearchAttrIds(attrIds);
return longs;
}
AttrDao
<select id="selectSearchAttrIds" resultType="java.lang.Long">
SELECT attr_id FROM 'pms_attr' WHERE attr_id IN
<foreach collection="attrIds" item="id" separator="," open="(" close=")">
#{attrIds}
</foreach>
and search_type=1
</select>
SpuInfoServiceImpl
/**
* 商品上架功能
* @param spuId
*/
@Override
public void spuUp(Long spuId) {
// List<SkuEsModel> uoProducts=new ArrayList<>();
//1.组装需要的数据
// SkuEsModel skuEsModel=new SkuEsModel();
//1:查出当前spuid对应的所有sku信息,品牌名字
List<SkuInfoEntity> skuInfoEntities= skuInfoService.getSkusBySpuId(spuId);
//todo 4:查询出当前sku的所有的可以被用来检索的规格属性(只要查一遍就可以了,放在stream外面来做
// private List<Attrs> attrs;
// @Data
// public static class Attrs{
// private Long attrId;
// private String attrName;
// private StringBuilder attrValue;
//// }
//todo 4.1 检索出pms_product_attr_value中的AttrId的值通过spuId,然后在attrService中调用selectSearchAttrs过滤出search_type=0
List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrlistforspu(spuId);
List<Long> attrIds = baseAttrs.stream().map((attr) -> {
return attr.getAttrId();
}).collect(Collectors.toList());
List<Long> searchAttrId= attrService.selectSearchAttrs(attrIds);
// todo 4.2 在baseAttrs中判断是否baseAttrs中的所有属性可以被上架,如果和searchAttrId相等就可以上架,并且保存在es中
java.util.List<SkuEsModel.Attrs> attr=new ArrayList<>();
HashSet<Long> idSet = new HashSet<>(searchAttrId);
List