引入
面对复杂的搜索业务和数据量,使用传统数据库搜索就显得力不从心,一般我们都会使用全文检索技术。
elasticsearch的基本使用见这里
elasticsearchTemplate基本使用见这里
数据导入
需要的数据Goods结构
@Document(indexName = "goods", type = "docs", shards = 1, replicas = 0)
public class Goods {
@Id
/**
* spuId
*/
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
/**
* 所有需要被搜索的信息,包含标题,分类,甚至品牌
*/
private String all;
@Field(type = FieldType.Keyword, index = false)
/**
* 卖点
*/
private String subTitle;
/**
* 品牌id
*/
private Long brandId;
/**
* 1级分类id
*/
private Long cid1;
/**
* 2级分类id
*/
private Long cid2;
/**
* 3级分类id
*/
private Long cid3;
/**
* 创建时间
*/
private Date createTime;
/**
* 价格
*/
private List<Long> price;
@Field(type = FieldType.Keyword, index = false)
/**
* sku信息的json结构
*/
private String skus;
/**
* 可搜索的规格参数,key是参数名,值是参数值
*/
private Map<String, Object> specs;
SPU转Good分析
需要哪些服务?
第一:分批查询spu的服务,已有。
第二:根据spuId查询sku的服务,已有
第三:根据spuId查询SpuDetail的服务,已有
第四:根据商品分类id,查询商品分类名称,没有
第五:根据商品品牌id,查询商品的品牌,没有
问题
已有的服务在别的微服务中。如果在本服务中调用?
解决
使用Feign。Feign详情见这里
导入数据
@Service public class SearchService {
@Autowired
private CategoryClient categoryClient;
@Autowired
private GoodsClient goodsClient;
private ObjectMapper mapper = new ObjectMapper();
public Goods buildGoods(Spu spu) throws IOException {
Goods goods = new Goods();
//1.查询商品分类名称
List<String> names = this.categoryClient.queryNameByIds(Arrays.asList(spu.getCid1(),spu.getCid2(),spu.getCid3())).getBody();
//2.查询sku
List<Sku> skus = this.goodsClient.querySkuBySpuId(spu.getId());
//3.查询详情
SpuDetail spuDetail = this.goodsClient.querySpuDetailBySpuId(spu.getId());
//4.处理sku,仅封装id,价格、标题、图片、并获得价格集合
List<Long> prices = new ArrayList<>();
List<Map<String,Object>> skuLists = new ArrayList<>();
skus.forEach(sku -> {
prices.add(sku.getPrice());
Map<String,Object> skuMap = new HashMap<>();
skuMap.put("id",sku.getId());
skuMap.put("title",sku.getTitle());
skuMap.put("price",sku.getPrice());
//取第一张图片
skuMap.put("image", StringUtils.isBlank(sku.getImages()) ? "" : StringUtils.split(sku.getImages(),",")[0]);
skuLists.add(skuMap);
});
//提取公共属性
List<Map<String,Object>> genericSpecs = mapper.readValue(spuDetail.getSpecifications(),new TypeReference<List<Map<String,Object>>>(){});
//过滤规格模板,把所有可搜索的信息保存到Map中
Map<String,Object> specMap = new HashMap<>();
String searchable = "searchable";
String v = "v";
String k = "k";
String options = "options";
genericSpecs.forEach(m -> {
List<Map<String, Object>> params = (List<Map<String, Object>>) m.get("params");
params.forEach(spe ->{
if ((boolean)spe.get(searchable)){
if (spe.get(v) != null){
specMap.put(spe.get(k).toString(), spe.get(v));
}else if (spe.get(options) != null){
specMap.put(spe.get(k).toString(), spe.get(options));
}
}
});
});
goods.setId(spu.getId());
goods.setSubTitle(spu.getSubTitle());
goods.setBrandId(spu.getBrandId());
goods.setCid1(spu.getCid1());
goods.setCid2(spu.getCid2());
goods.setCid3(spu.getCid3());
goods.setCreateTime(spu.getCreateTime());
goods.setAll(spu.getTitle() + " " + StringUtils.join(names, " "));
goods.setPrice(prices);
goods.setSkus(mapper.writeValueAsString(skuLists));
goods.setSpecs(specMap);
return goods;
}