0
点赞
收藏
分享

微信扫一扫

ElasticSearch-商品上架+商品查询

public class EsConstant {
    //经常用的常量 抽取出来  static是全局的 可以让访问变得很方便,而且不会被修改。一般可以放配置信息,还有一些状态码的定义。 
    //其他的补充: static修饰的对象是放在引用的根下的,意味着几乎不会被回收
    public static final String PRODUCT_INDEX ="product";//sku在es中索引
}

商品上架 传入 商品id 封装SkuEsModel upProducts 调用远程 searchFeignService.productStatusUp(upProducts); 商品上架 productStatusUp(List<SkuEsModel> skuEsModels)

@Override
public Boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {
    //保存到es中
    //1.建议es索引 product 建立映射关系
    //bulk 批量操作 传入BulkRequest bulkRequest, RequestOptions options
    BulkRequest bulkRequest = new BulkRequest();
    for (SkuEsModel model : skuEsModels) {
        //new 一个索引 保存product
        IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
        //指定唯一di
        indexRequest.id(model.getSkuId().toString());
        String s = JSON.toJSONString(model);
        indexRequest.source(s, XContentType.JSON);
        bulkRequest.add(indexRequest);
    }
    BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
    //todo 感知错误并处理
    boolean b = bulk.hasFailures();
    List<String> collect = Arrays.stream(bulk.getItems()).map(item -> {
        return item.getId();
    }).collect(Collectors.toList());
    log.info("商品上架wancheng:{},返回数据{}",collect,bulk.toString());
    return  b;
}

查询检索

/**
 * @param param 传入SearchParam
 * @param model
 * @return 返回SearchReult
 */
@GetMapping({"/", "list.html"})
public String listPage(SearchParam param, Model model, HttpServletRequest request) {
    //HttpServletRequest 中有拿到前端传参路径
    param.set_queryString(request.getQueryString());
    //1. 根据传递来的页面的查询参数,去es中检索商品
    SearchResult result  =mallSearchService.search(param);
    model.addAttribute("result",result);
    return "list";
}
@Override
public SearchResult search(SearchParam param) {
    //动态构建DSL语句
    SearchResult result = null;//返回

    //1、准备检索请求 从新创建一个私有方法
    SearchRequest searchRequest = buildSearchRequest(param);

    try {
        //2.执行检索请求
        SearchResponse response = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
        //3. 分析响应数据封装成我们需要的格式
        result = buildSearchResult(response, param);
    } catch (IOException e) {
        e.printStackTrace();
    }

    return result;
}
/**
 * 构建结果数据
 * 模糊匹配,过滤(按照属性、分类、品牌,价格区间,库存),完成排序、分页、高亮,聚合分析功能
 */
private SearchResult buildSearchResult(SearchResponse response, SearchParam param) {
    SearchResult result = new SearchResult();
    SearchHits hits = response.getHits();//获取 命中的记录
    ArrayList<SkuEsModel> esModels = new ArrayList<>();
    //1、返回的所有查询到的商品
    //命中hits的hits 不为空并且有数值
    if (hits.getHits() != null && hits.getHits().length > 0) {
        //每一条命中的记录
        for (SearchHit hit : hits.getHits()) {
            //得到命中中的_Source 以字符串形势 转换成SkuEsModell
            String sourceAsString = hit.getSourceAsString();
            //用阿里巴巴JSON.parseObject 转换成SkuEsModel.class)
            SkuEsModel esModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
            //判断是否按关键字检索,若是就显示高亮,否则不显示
            if (!StringUtils.isEmpty(param.getKeyword())) {
                //拿到高亮信息显示标题
                HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                String skuTitleValue = skuTitle.getFragments()[0].string();
                esModel.setSkuTitle(skuTitleValue);
            }
            esModels.add(esModel); //定义ArrayList esModels放入集合中
        }
    }
    result.setProducts(esModels);


    //4、当前商品涉及到的所有分类信息
    List<SearchResult.AttrVo> attrVos = new ArrayList<>();
    //获取属性信息的聚合
    ParsedNested attrsAgg = response.getAggregations().get("attr_agg");
    ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attr_id_agg");
    for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
        SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
        //1、得到属性的id
        long attrId = bucket.getKeyAsNumber().longValue();


        //2、得到属性的名字
        ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
        String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();


        //3、得到属性的所有值
        ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
        List<String> attrValues =
                attrValueAgg.getBuckets().stream().map(item -> item.getKeyAsString()).collect(Collectors.toList());
        attrVo.setAttrId(attrId);
        attrVo.setAttrName(attrName);
        attrVo.setAttrValue(attrValues);
        attrVos.add(attrVo);
    }
    result.setAttrs(attrVos);


    //3、当前商品涉及到的所有品牌信息
    List<SearchResult.BrandVo> brandVos = new ArrayList<>();
    //获取到品牌的聚合
    ParsedLongTerms brandAgg = response.getAggregations().get("brand_agg");
    for (Terms.Bucket bucket : brandAgg.getBuckets()) {
        SearchResult.BrandVo brandVo = new SearchResult.BrandVo();

        //1、得到品牌的id 直接为数字并转换成long类型的
        long brandId = bucket.getKeyAsNumber().longValue();
        brandVo.setBrandId(brandId);

        //2、得到品牌的名字
        ParsedStringTerms brandNameAgg = (ParsedStringTerms) bucket.getAggregations().get("brand_name_agg");
        String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();

        //3、得到品牌的图片
        ParsedStringTerms brandImgAgg = (ParsedStringTerms) bucket.getAggregations().get("brand_img_agg");
        String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
        brandVo.setBrandImg(brandImg);
        brandVo.setBrandName(brandName);
        brandVos.add(brandVo);
    }
    result.setBrands(brandVos);


    //2、当前商品涉及到的所有属性信息 聚合的类型是 0=ParsedLongTerms@8429 直接转换ParsedLongTerms
    List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
    ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");//拿到分类聚合信息.get 获取名字

    List<? extends Terms.Bucket> buckets = catalog_agg.getBuckets();
    //遍历buckets
    for (Terms.Bucket bucket : buckets) {

        //拿到分类的id 和分类的名字
        SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
        String keyAsString = bucket.getKeyAsString();
        catalogVo.setCatalogId(Long.parseLong(keyAsString));
        //得到分类名 子聚合
        ParsedStringTerms  catalogNameAgg  = bucket.getAggregations().get("catalog_name_agg");
        String catalog_name = catalogNameAgg .getBuckets().get(0).getKeyAsString();
        catalogVo.setCatalogName(catalog_name);
        catalogVos.add(catalogVo);
    }
    result.setCatalogs(catalogVos);


    //===============以上可以从聚合信息中获取====================//
    //5、分页信息-页码 来源param
    result.setPageNum(param.getPageNum());
    //总记录数
    long total = hits.getTotalHits().value; //获取总记录数的value值
    result.setTotal(total);//保存总记录数
    //总页码 计算得到 中记录数/ 每页大小;先判断有没有余数 ==0 没有余数
    int totalPages = (int) total % EsConstant.PRODUCT_PAGESIEZE == 0 ?
            (int) total / EsConstant.PRODUCT_PAGESIEZE : ((int) total / EsConstant.PRODUCT_PAGESIEZE + 1);
    result.setTotalPages(totalPages);

    //可遍历的页码
    List<Integer> pageNavs = new ArrayList<>();
    for (int i = 1; i <= totalPages; i++) {
        pageNavs.add(i);
    }
    result.setPageNavs(pageNavs);

    //6、构建面包屑导航
    if (param.getAttrs() != null && param.getAttrs().size() > 0) {
        List<SearchResult.NavVo> collect = param.getAttrs().stream().map(attr -> {
            //1、分析每一个attrs传过来的参数值
            SearchResult.NavVo navVo = new SearchResult.NavVo();
            String[] s = attr.split("_");
            navVo.setNavValue(s[1]);
            R r = productFeignService.attrInfo(Long.parseLong(s[0]));
            if (r.getCode() == 0) {
                AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {
                });
                navVo.setNavName(data.getAttrName());
            } else {
                navVo.setNavName(s[0]);
            }

            //2、取消了这个面包屑以后,我们要跳转到哪个地方,将请求的地址url里面的当前置空
            //拿到所有的查询条件,去掉当前
            String encode = null;
            try {
                encode = URLEncoder.encode(attr,"UTF-8");
                encode = encode.replace("+","%20");  //浏览器对空格的编码和Java不一样,差异化处理
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            String replace = param.get_queryString().replace("&attrs=" + encode, "");
            navVo.setLink("http://search.gulimall.com/list.html?" + replace);

            return navVo;
        }).collect(Collectors.toList());

        result.setNavs(collect);
    }


    return result;
}
private SearchRequest buildSearchRequest(SearchParam param) {
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//所有检索请求构建DSL语句

    /**
     * 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)
     */

    //1.构建bool -must-query
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    //1.1 构建 must 判断 must 如果传过来keyword 不为空  构建must模糊匹配
    if (!StringUtils.isEmpty(param.getKeyword())) {
        //在must中继续构建匹配match 按照名字[]和数值[param 传过来的]进行匹配
        boolQuery.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
    }
    //1.2 构建 bool -must-filter
    //1.2.1  bool -must-filter-item  三级分类id
    if (param.getCatalog3Id() != null) {
        boolQuery.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
    }

    //1.2.2 bool -must-filter-items品牌id brandId id 判断brandId集合不为空 且大小大于0
    if (param.getBrandId() != null && param.getBrandId().size() > 0) {
        boolQuery.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
    }
    //1.2.3 属性查询 是嵌入式
    if (param.getAttrs() != null && param.getAttrs().size() > 0) {

        //attr lattrs= 1_5寸:8寸&attrs=2_16G:8G
        for (String attrStr : param.getAttrs()) {
            BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();
            //先分割属性
            String[] s = attrStr.split("_");
            String attrId = s[0];//检索书属性id
            //在分割属属性里面的多个数值 attrValues 检索属性值
            String[] attrValues = s[1].split(":");
            nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId));
            nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));
            //是嵌入式 用nestedQuery 传入三个值  每一一个必须都得生成一个nested查询
            NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None);
            boolQuery.filter(nestedQuery);
        }

    }

    //1.2.4 按照是否有库存 如果getHasStock等于1 就是ture
    if (null != param.getHasStock()) {
        boolQuery.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
    }
    //1.2. 按价格区间
    if (!StringUtils.isEmpty(param.getSkuPrice())) {
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
        String[] s = param.getSkuPrice().split("_");//分割价格
        //长度为2 是一个区间
        if (s.length == 2 && s[0] != "") {
            //大于等于s[0] 第一个 小于等于s[1]第二个
            rangeQuery.gte(null).lte(s[1]);
        } else if (s.length == 2) {
            //大于等于s[0] 第一个 小于等于s[1]第二个
            rangeQuery.gte(s[0]).lte(s[1]);
        } else if (s.length == 1) {
            //如果长度等他1 是有两种情况 _500 和500_
            if (param.getSkuPrice().startsWith("_")) {
                rangeQuery.lte(s[0]);
            }
            if (param.getSkuPrice().endsWith("_")) {
                rangeQuery.gte(s[0]);
            }
        }

        boolQuery.filter(rangeQuery);
    }

    //把以前的所有条件都拿来进行封装
    sourceBuilder.query(boolQuery);


    /**
     * 排序,分页,高亮
     */
    //2.1 排序
    if (!StringUtils.isEmpty(param.getSort())) {
        //sort=hostSore_asc/desc
        String[] s = param.getSort().split("_");
        //构建排序 不区分大小写
        SortOrder sortOrder = "asc".equalsIgnoreCase(s[1]) ? SortOrder.ASC : SortOrder.DESC;
        sourceBuilder.sort(s[0], sortOrder);
    }

    //2.2分页
    //2.2.分页pageSize:5
    // pageNum:1 from:0 size:5 [0,1,2,3,4]
    // pageNum:2 from:5  size:5
    //from = (pageNum- 1)*size
    sourceBuilder.from((param.getPageNum() - 1) * EsConstant.PRODUCT_PAGESIEZE);
    sourceBuilder.size(EsConstant.PRODUCT_PAGESIEZE);

    //2.3 高亮设置 只有传入参数才计算
    if (!StringUtils.isEmpty(param.getKeyword())) {
        HighlightBuilder builder = new HighlightBuilder();
        builder.field("skuTitle");//设置哪个高亮
        builder.preTags("<b style=color:red>");
        builder.postTags("</b>");
        sourceBuilder.highlighter(builder);
    }

    /**
     * 聚合分析
     */

    //品牌聚合
    TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
    brand_agg.field("brandId").size(50);
    //品牌聚合 名字图片子聚合
    brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
    brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
    sourceBuilder.aggregation(brand_agg);


    //2. 按照分类信息进行聚合
    TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg");
    catalog_agg.field("catalogId").size(20);

    catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));

    sourceBuilder.aggregation(catalog_agg);

    //2. 按照属性信息进行聚合 嵌入聚合
    NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
    //2.1 按照属性ID进行聚合 子聚合 AggregationBuilders
    TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
    attr_agg.subAggregation(attr_id_agg);
    //2.1.1 子聚合的子聚合subAggregation
    attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
    //2.1.1 在每个属性ID下,按照属性值进行聚合
    attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
    sourceBuilder.aggregation(attr_agg);

    String s = sourceBuilder.toString();
    System.out.println("构建DSL" + s);
    log.debug("构建的DSL语句 {}", sourceBuilder.toString());


    SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
    return searchRequest;
}
}

举报

相关推荐

0 条评论