目录:
(1)封装搜索相关实体对象
(2)搜索接口封装
(3)在service-list-client模块添加远程接口
(1)封装搜索相关实体对象
搜索参数实体:SearchParam
搜索参数实体:SearchParam
package com.atguigu.gmall.model.list;
/*** 商品搜索参数* 参数说明:* 1,商标品牌:trademark=2:华为 * 2:为品牌id,搜索字段* 华为:品牌名称,页面回显属性* 2,平台属性:props=23:4G:运行内存* 23:平台属性id,搜索字段* 运行内存:平台属性名称,页面回显属性* 4G:平台属性值,搜索字段与页面回显属性* </p>**/
@Data
public class SearchParam {// ?category3Id=61&trademark=2:华为&props=23:4G:运行内存&order=1:desc//category3Id=61private Long category1Id;;//三级分类idprivate Long category2Id;private Long category3Id;//trademark=2:华为private String trademark;//品牌idprivate String keyword;//检索的关键字// order=1:asc 排序规则 0:ascprivate String order = "";// 1:综合排序/热点 2:价格//props=23:4G:运行内存private String[] props;//页面提交的数组private Integer pageNo = 1;//分页信息private Integer pageSize = 12;
}
搜索结果集实体:SearchResponseVo
搜索结果集实体:SearchResponseVopackage com.atguigu.gmall.model.list;@Data
public class SearchResponseVo implements Serializable {//品牌 此时vo对象中的id字段保留(不用写) name就是“品牌” value: [{id:100,name:华为,logo:xxx},{id:101,name:小米,log:yyy}]private List<SearchResponseTmVo> trademarkList;//所有商品的顶头显示的筛选属性private List<SearchResponseAttrVo> attrsList = new ArrayList<>();//检索出来的商品信息private List<Goods> goodsList = new ArrayList<>();private Long total;//总记录数private Integer pageSize;//每页显示的内容private Integer pageNo;//当前页面private Long totalPages;}
结果集品牌实体:SearchResponseTmVo
package com.atguigu.gmall.model.list;@Data
public class SearchResponseTmVo implements Serializable {//当前属性值的所有值private Long tmId;//属性名称private String tmName;//网络制式,分类//图片urlprivate String tmLogoUrl;
}
结果集平台属性实体:SearchResponseAttrVo
package com.atguigu.gmall.model.list;@Data
public class SearchResponseAttrVo implements Serializable {private Long attrId;//1//当前属性值的所有值private List<String> attrValueList = new ArrayList<>();//属性名称private String attrName;//网络制式,分类
}
(2)搜索接口封装
SearchService接口
/*** 搜索列表* @param searchParam* @return* @throws IOException*/
SearchResponseVo search(SearchParam searchParam) throws IOException;
接口实现类
api参考文档:
Java REST Client [7.8] | Elastic
Java REST Client [7.8] | Elastic
@Autowired
private RestHighLevelClient restHighLevelClient;
@Override
public SearchResponseVo search(SearchParam searchParam) throws IOException {// 构建dsl语句SearchRequest searchRequest = this.buildQueryDsl(searchParam);SearchResponse response = this.restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);System.out.println(response);SearchResponseVo responseVO = this.parseSearchResult(response);responseVO.setPageSize(searchParam.getPageSize());responseVO.setPageNo(searchParam.getPageNo());long totalPages = (responseVO.getTotal()+searchParam.getPageSize()-1)/searchParam.getPageSize();responseVO.setTotalPages(totalPages);return responseVO;
}//封装查询条件
// 制作dsl 语句
private SearchRequest buildQueryDsl(SearchParam searchParam) {// 构建查询器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 构建多条件对象boolQueryBuilderBoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();// 判断查询条件是否为空 关键字if (!StringUtils.isEmpty(searchParam.getKeyword())){// 小米手机 小米and手机// MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("title",searchParam.getKeyword()).operator(Operator.AND);MatchQueryBuilder title = QueryBuilders.matchQuery("title", searchParam.getKeyword()).operator(Operator.AND);boolQueryBuilder.must(title);}// 构建品牌查询String trademark = searchParam.getTrademark();if (!StringUtils.isEmpty(trademark)){// trademark=2:华为String[] split = StringUtils.split(trademark, ":");if (split != null && split.length == 2) {//构建过滤品牌TermQueryBuilder tmId=QueryBuilders.termQuery("tmId", split[0]);// 根据品牌Id过滤 添加到多条件对象boolQueryBuilder.filter(tmId);}}// 构建分类过滤 用户在点击的时候,只能点击一个值,所以此处使用termif(null!=searchParam.getCategory1Id()){boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id",searchParam.getCategory1Id()));}// 构建分类过滤if(null!=searchParam.getCategory2Id()){boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id",searchParam.getCategory2Id()));}// 构建分类过滤if(null!=searchParam.getCategory3Id()){boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id",searchParam.getCategory3Id()));}// 构建平台属性查询// 23:4G:运行内存String[] props = searchParam.getProps();if (props!=null && props.length>0){// 循环遍历for (String prop : props) {// 23:4G:运行内存 平台属性id:平台属性值名称:平台属性名String[] split = StringUtils.split(prop, ":");if (split!=null && split.length==3){// 构建嵌套查询 创建多条件对象BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 嵌套查询子查询BoolQueryBuilder subBoolQuery = QueryBuilders.boolQuery();// 构建子查==询中的过滤条件subBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",split[0]));subBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));// ScoreMode.None ?boolQuery.must(QueryBuilders.nestedQuery("attrs",subBoolQuery, ScoreMode.None));// 添加到整个过滤对象中,外层对象boolQueryBuilder.filter(boolQuery);}}}// 执行查询方法searchSourceBuilder.query(boolQueryBuilder);// 构建分页int from = (searchParam.getPageNo()-1)*searchParam.getPageSize();searchSourceBuilder.from(from);searchSourceBuilder.size(searchParam.getPageSize());// 排序 1:hotScore 2:price 1:综合排序/热度 2:价格//1:ascString order = searchParam.getOrder();if (!StringUtils.isEmpty(order)){// 判断排序规则String[] split = StringUtils.split(order, ":");if (split!=null && split.length==2){// 排序的字段String field = null;// 数组中的第一个参数switch (split[0]){case "1":field="hotScore";break;case "2":field="price";break;}searchSourceBuilder.sort(field,"asc".equals(split[1])? SortOrder.ASC:SortOrder.DESC);}else {// 没有传值的时候给默认值searchSourceBuilder.sort("hotScore",SortOrder.DESC);}}// 构建高亮HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field("title");highlightBuilder.postTags("</span>");highlightBuilder.preTags("<span style=color:red>");searchSourceBuilder.highlighter(highlightBuilder);// 设置品牌聚合TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("tmIdAgg").field("tmId").subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName")).subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"));searchSourceBuilder.aggregation(termsAggregationBuilder);// 设置平台属性聚合searchSourceBuilder.aggregation(AggregationBuilders.nested("attrAgg", "attrs").subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName")).subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))));// 结果集过滤searchSourceBuilder.fetchSource(new String[]{"id","defaultImg","title","price"},null);SearchRequest searchRequest = new SearchRequest("goods");//searchRequest.types("_doc");//将构建对象添加到请求中searchRequest.source(searchSourceBuilder);System.out.println("dsl:"+searchSourceBuilder.toString());return searchRequest;
}// 制作返回结果集
private SearchResponseVo parseSearchResult(SearchResponse response) {SearchHits hits = response.getHits();//声明对象SearchResponseVo searchResponseVo = new SearchResponseVo();//获取品牌的集合Map<String, Aggregation> aggregationMap = response.getAggregations().asMap();//ParsedLongTerms ?ParsedLongTerms tmIdAgg = (ParsedLongTerms) aggregationMap.get("tmIdAgg");List<SearchResponseTmVo> trademarkList = tmIdAgg.getBuckets().stream().map(bucket -> {SearchResponseTmVo trademark = new SearchResponseTmVo();//获取品牌Idtrademark.setTmId((Long.parseLong(((Terms.Bucket) bucket).getKeyAsString())));//trademark.setTmId(Long.parseLong(bucket.getKeyAsString()));//获取品牌名称Map<String, Aggregation> tmIdSubMap = ((Terms.Bucket) bucket).getAggregations().asMap();ParsedStringTerms tmNameAgg = (ParsedStringTerms) tmIdSubMap.get("tmNameAgg");String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();trademark.setTmName(tmName);
ParsedStringTerms tmLogoUrlAgg = (ParsedStringTerms) tmIdSubMap.get("tmLogoUrlAgg");
String tmLogoUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
trademark.setTmLogoUrl(tmLogoUrl);return trademark;}).collect(Collectors.toList());searchResponseVo.setTrademarkList(trademarkList);//赋值商品列表SearchHit[] subHits = hits.getHits();List<Goods> goodsList = new ArrayList<>();if (subHits!=null && subHits.length>0){//循环遍历for (SearchHit subHit : subHits) {// 将subHit 转换为对象Goods goods = JSONObject.parseObject(subHit.getSourceAsString(), Goods.class);//获取高亮if (subHit.getHighlightFields().get("title")!=null){Text title = subHit.getHighlightFields().get("title").getFragments()[0];goods.setTitle(title.toString());}goodsList.add(goods);}}searchResponseVo.setGoodsList(goodsList);//获取平台属性数据ParsedNested attrAgg = (ParsedNested) aggregationMap.get("attrAgg");ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");List<? extends Terms.Bucket> buckets = attrIdAgg.getBuckets();if (!CollectionUtils.isEmpty(buckets)){List<SearchResponseAttrVo> searchResponseAttrVOS = buckets.stream().map(bucket -> {//声明平台属性对象SearchResponseAttrVo responseAttrVO = new SearchResponseAttrVo();//设置平台属性值IdresponseAttrVO.setAttrId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");List<? extends Terms.Bucket> nameBuckets = attrNameAgg.getBuckets();responseAttrVO.setAttrName(nameBuckets.get(0).getKeyAsString());//设置规格参数列表ParsedStringTerms attrValueAgg = ((Terms.Bucket) bucket).getAggregations().get("attrValueAgg");List<? extends Terms.Bucket> valueBuckets = attrValueAgg.getBuckets();List<String> values = valueBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());responseAttrVO.setAttrValueList(values);return responseAttrVO;}).collect(Collectors.toList());searchResponseVo.setAttrsList(searchResponseAttrVOS);}// 获取总记录数searchResponseVo.setTotal(hits.getTotalHits().value);return searchResponseVo;
}
控制器ListApiController
/*** 搜索商品* @param searchParam* @return* @throws IOException*/
@PostMapping
public Result list(@RequestBody SearchParam searchParam) throws IOException {SearchResponseVo response = searchService.search(searchParam);return Result.ok(response);
}
在service-list 模块中配置logstash
首先在service模块中添加依赖
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>5.1</version>
</dependency>
其次,将日志配置文件放入到resources目录下!
(3)在service-list-client模块添加远程接口
package com.atguigu.gmall.list.client;
@FeignClient(value = "service-list", fallback = ListDegradeFeignClient.class)
public interface ListFeignClient {/*** 搜索商品* @param listParam* @return*/@PostMapping("/api/list")Result list(@RequestBody SearchParam listParam);/*** 上架商品* @param skuId* @return*/@GetMapping("/api/list/inner/upperGoods/{skuId}")Result upperGoods(@PathVariable("skuId") Long skuId);/*** 下架商品* @param skuId* @return*/@GetMapping("/api/list/inner/lowerGoods/{skuId}")Result lowerGoods(@PathVariable("skuId") Long skuId);}
package com.atguigu.gmall.list.client.impl;@Component
public class ListDegradeFeignClient implements ListFeignClient {@Overridepublic Result list(SearchParam searchParam) {return Result.fail();}@Overridepublic Result upperGoods(Long skuId) {return null;}@Overridepublic Result lowerGoods(Long skuId) {return null;}
}