栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

leyou商城day8

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

leyou商城day8

00、课程目标
  • 实现搜索页渲染与展示
01、搜索页渲染:前端发送搜索请求到后台 1)在search.html编写请求后台逻辑

ly-portal项目中search.html


02、搜索页渲染:微服务搜索功能基本代码

今天我们主要工作就是从Elasticsearch中检索数据,最终展示到搜索页面上。效果如下:

1) 定义接收请求参数的dto对象

在ly-pojo-search模块中提供一个DTO接口,用于接收页面传递的搜索参数和分页参数

package com.leyou.search.pojo.dto;


public class SearchRequest {
    private String key;// 搜索条件

    private Integer page;// 当前页

    private static final Integer DEFAULT_SIZE = 20;// 每页大小,不从页面接收,而是固定大小
    private static final Integer DEFAULT_PAGE = 1;// 默认页

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public Integer getPage() {
        if(page == null){
            return DEFAULT_PAGE;
        }
        // 获取页码时做一些校验,不能小于1
        return Math.max(DEFAULT_PAGE, page);
    }

    public void setPage(Integer page) {
        this.page = page;
    }

    public Integer getSize() {
        return DEFAULT_SIZE;
    }
}

2) 定义一个返回商品数据的dto对象

在ly-pojo-search模块中定义一个用于返回商品数据的DTO对象

package com.leyou.search.pojo.dto;

import lombok.Data;


@Data
public class GoodsDTO {

    private Long id;
    private String spuName;
    private String subTitle;
    private String skus;
}


3)定义一个返回所有搜索结果的对象
package com.leyou.search.pojo.vo;

import com.leyou.commom.exception.pojo.PageResult;
import lombok.Data;

import java.util.Map;


@Data
public class SearchResult extends PageResult {
    private Map filterConditions;
}


4)编写处理器
package com.leyou.search.repository.search.controller;

import com.leyou.search.pojo.dto.GoodsDTO;
import com.leyou.search.pojo.dto.SearchRequest;
import com.leyou.search.pojo.vo.SearchResult;
import com.leyou.search.repository.search.service.impl.SearchServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class SearchController {
    @Autowired
    private SearchServiceImpl searchService;


    
    @PostMapping("/page")
    public ResponseEntity> goodsSearchPage(@RequestBody SearchRequest searchRequest){
        SearchResult searchResult = searchService.goodsSearchPage(searchRequest);
        return ResponseEntity.ok(searchResult);
    }

}



5)编写业务层

SearchService类添加方法:

package com.leyou.search.repository.search.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.commom.exception.pojo.PageResult;
import com.leyou.commom.utils.BeanHelper;
import com.leyou.commom.utils.HighlightUtils;
import com.leyou.commom.utils.JsonUtils;
import com.leyou.item.ItemClient;
import com.leyou.item.dto.SpuDto;
import com.leyou.item.pojo.*;
import com.leyou.search.pojo.Goods;
import com.leyou.search.pojo.dto.GoodsDTO;
import com.leyou.search.pojo.dto.SearchRequest;
import com.leyou.search.pojo.vo.SearchResult;
import com.leyou.search.repository.SearchRepository;
import org.apache.lucene.queryparser.xml.builders.BooleanQueryBuilder;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SourceFilter;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class SearchServiceImpl {
    @Autowired
    private SearchRepository searchRepository; //完成基本CRUD操作
    @Autowired
    private ElasticsearchTemplate esTemplate;//用于高级搜索

    @Autowired
    private ItemClient itemClient;



    public void importData(){
        int page = 1;//当前页码
        int rows = 100;//每页条数
        int totalPage = 1;//总页数

        do {
            //1.分页查询Spu商品数据,直到全部数据查询完毕
            PageResult pageResult = itemClient.spuPageQuery(page, rows, null, true);//注意:导入到ES的数据必须是上架的数据

            //2.取出所有商品数据
            List spuDTOList = pageResult.getItems();

            //3.判空处理
            if(CollectionUtils.isNotEmpty(spuDTOList)){
                //获取List集合
                List goodsList = spuDTOList.stream().map( spuDTO -> buildGoods(spuDTO)  ).collect(Collectors.toList());

                //保存List数据到ES中
                searchRepository.saveAll(goodsList);
            }


            //获取总页数
            totalPage = pageResult.getTotalPage().intValue();
            page++;
        }while (page<=totalPage);

    }

    
    public Goods buildGoods(SpuDto spuDto){
		Goods goods = new Goods();
        //塞入商品基本信息
        goods.setId(spuDto.getId());
        goods.setSpuName(spuDto.getName());
        goods.setSubTitle(spuDto.getSubTitle());
        goods.setCreateTime(System.currentTimeMillis());
        goods.setCategoryId(spuDto.getCid3());
        goods.setBrandId(spuDto.getBrandId());

        //根据spuId查询所有Sku
        List skuList = itemClient.findSkusBySpuId(spuDto.getId());
        //只取出Sku部分属性
        List> skuMapList = new ArrayList<>();
        //遍历Sku对象
        if(CollectionUtils.isNotEmpty(skuList)){
            skuList.forEach(sku -> {
                Map skuMap = new HashMap<>();//封装一个Sku数据
                skuMap.put("id",sku.getId());
                skuMap.put("images",sku.getImages());
                skuMap.put("price",sku.getPrice());

                skuMapList.add(skuMap);
            });
        }

        //将List转换json字符串
        String skusJosn = JsonUtils.toString(skuMapList);
        //获取all属性 (spu的name + spu的subTitle + 所有Sku的title)
        //将所有sku里边的title进行拼接  用空格进行拼接
        String skusTitle = skuList.stream().map(m -> m.getTitle()).collect(Collectors.joining(""));
        String all = spuDto.getName()+""+spuDto.getSubTitle()+""+skusTitle;
        //获取price
        Set prices = skuList.stream().map(a -> a.getPrice()).collect(Collectors.toSet());

        goods.setAll(all);
        goods.setSkus(skusJosn);
        goods.setPrice(prices);

        
 //用于存储所有搜素过滤的规格参数
        Map specsMap = new HashMap<>();

        //1)根据分类(第三级)ID查询用于搜索过滤的规格参数(searching=true)
        List specParamList = itemClient.findSpecParams(null,spuDto.getCid3(),true);

        specParamList.stream().forEach(specParam -> {
            //2)Map的key,就是规格参数名称
            String key = specParam.getName();
            Object value = null;

            //3)根据spuId查询SpuDetail数据,取出 generic_spec和special_spec属性值
            TbSpuDetail spuDetail = itemClient.findSpuDetailBySpuId(spuDto.getId());
            String genericSpec = spuDetail.getGenericSpec();
            String specialSpec = spuDetail.getSpecialSpec();
            //4)将generic_spec,special_spec转换为Map集合,使用规格参数的ID到Map集合取值
            //转换generic_spec为Map集合
            //toMap():只能转换一层集合架构
            Map genericSpecMap = JsonUtils.toMap(genericSpec, Long.class, Object.class);
            //转换special_spec为Map集合
            //nativeRead():可以转换多层集合结构
            Map> specialSpecMap = JsonUtils.nativeRead(specialSpec, new TypeReference>>() {
            });

            if(specParam.getGeneric()){
                //通用规格参数
                value = genericSpecMap.get(specParam.getId());
            }else{
                //特有规格参数
                value = specialSpecMap.get(specParam.getId());
            }


            
            if(specParam.getNumeric()){
                //只有数字类型参数,才去转换区间
                value = chooseSegment(value,specParam);
            }

            specsMap.put(key,value);
        });


        goods.setSpecs(specsMap);

        return goods;
    }

    
    private String chooseSegment(Object value, SpecParam p) {
        if (value == null || StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        double val = parseDouble(value.toString());
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (Exception e) {
            return 0;
        }
    }

    public SearchResult goodsSearchPage(SearchRequest searchRequest) {
        //1.创建SearchResult
        SearchResult searchResult = new SearchResult<>();

        //2.封装SearchResult
        //2.1 查询商品分页列表数据
        PageResult pageResult = itemQueryPage(searchRequest);

        //2.2 查询搜索过滤条件数据
        Map filterConditions = filterConditionsQuery(searchRequest);

        searchResult.setItems(pageResult.getItems());
        searchResult.setTotal(pageResult.getTotal());
        searchResult.setTotalPage(pageResult.getTotalPage());
        searchResult.setFilterConditions(filterConditions);

        //3.返回SearchResult
        return searchResult;
    }

    
    public Map filterConditionsQuery(SearchRequest searchRequest) {
        return null;
    }

    
    public PageResult itemQueryPage(SearchRequest searchRequest) {
        return null;
    }
}




03、搜索页渲染:渲染商品列表-后端代码 1)原生查询语句(了解)

在Kibana编写根据条件查询商品列表

# 搜索"手机"关键词

GET /goods/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "all": "手机"
          }
        }
      ]
    }
  },
  "_source": {
    "includes": ["id","spuName","subTitle","skus"]
  }, 
  "from": 0,
  "size": 20
}


2)SearchService的itemPageQuery方法
    public PageResult itemQueryPage(SearchRequest searchRequest) {
        //1.创建本地查询构造器对象
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //2.封装地查询构造器(*)
        //1)创建布尔查询构造器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //2)在布尔查询构造器中追加must条件
        boolQueryBuilder.must( QueryBuilders.matchQuery("all",searchRequest.getKey()) );
        //3)把布尔查询构造器存入本地查询构造器
        queryBuilder.withQuery(boolQueryBuilder);

        //4) 设置结果过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","spuName","subTitle","skus"},null));

        //5)设置分页参数
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));

        //3.执行查询,获取结果
        Page pageBean = esTemplate.queryForPage(queryBuilder.build(),Goods.class);

        //4.处理结果,并返回
        //1)取出Goods集合
        List goodsList = pageBean.getContent();
        //2)把Goods集合拷贝到GoodsDTO集合
        List goodsDTOList = BeanHelper.copyWithCollection(goodsList,GoodsDTO.class);
        //3)封装PageResult
        PageResult pageResult = new PageResult(
                pageBean.getTotalElements(),
                Long.valueOf(pageBean.getTotalPages()),
                goodsDTOList
        );

        return pageResult;
    }
3)页面查看返回数据

确定前端可以接收到后端返回结果

04、搜索页渲染:渲染商品列表-前端实现 1)前端接收返回数据

在之前的loadSearchPage方法中,接收后端返回的结果,注意:

  • skus属性为字符串类型,需要转换为Sku的JSON对象
  • 需要添加selectedSku属性标记为当前选中的Sku对象
methods:{
            //商品搜索方法
            loadSearchPage(){
                //ajax请求后端
                ly.http.post('/search/page',this.searchParams).then(resp=>{
                    //接收后端返回的数据

                    //处理resp.data.items
                    //把items中每个item的skus属性转换为js对象
                    resp.data.items.forEach(item=>{
                        item.skus = JSON.parse(item.skus);
                        //在item中添加一个selectedSku,用于存储当前选中的Sku数据(默认把skus的第一个作为选中的)
                        item.selectedSku = item.skus[0];
                    });


                    this.items = resp.data.items;
                    this.total = resp.data.total;
                    this.totalPage = resp.data.totalPage;
                }).catch(e=>{
                    console.log('查询商品失败');
                });
            }
        },
2)渲染数据到页面 删除多余li元素

找到class为goods-list的div,在该div里面把多余的li元素删除,剩下一个li即可

渲染数据
 

注意一个细节:

效果:

05、搜索页渲染:高亮显示 1)后端代码 ly-common导入依赖
 
            org.springframework.data
            spring-data-elasticsearch

ly-common添加高亮工具类

在ly-common模块下添加高亮工具类

2)底层REST指令
# 搜索"手机"关键词

GET /goods/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "手机",
            "fields": ["all","spuName"]
          }
        }
      ]
    }
  },
  "_source": {
    "includes": ["id","spuName","subTitle","skus"]
  }, 
  "from": 0,
  "size": 20,
  "highlight": {
    "pre_tags": "",
    "post_tags": "",
    "fields": {
      "spuName": {}
    }
  }
}

修改itemPageQuery方法

修改item-search模块的SearchService类的itemPageQuery方法

    public PageResult itemQueryPage(SearchRequest searchRequest) {
        //1.创建本地查询构造器对象
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //2.封装地查询构造器(*)
        //1)创建布尔查询构造器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //2)在布尔查询构造器中追加must条件
        //boolQueryBuilder.must( QueryBuilders.matchQuery("all",searchRequest.getKey()) );
        boolQueryBuilder.must( QueryBuilders.multiMatchQuery(searchRequest.getKey(),"all","spuName") );
        //3)把布尔查询构造器存入本地查询构造器
        queryBuilder.withQuery(boolQueryBuilder);

        //4) 设置结果过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","spuName","subTitle","skus"},null));

        //5)设置分页参数
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));

        //6)设置高亮字段与格式
        HighlightUtils.highlightField(queryBuilder,"spuName");

        //3.执行查询,获取结果
        Page pageBean = esTemplate.queryForPage(queryBuilder.build(),Goods.class,HighlightUtils.highlightBody(Goods.class,"spuName"));

        //4.处理结果,并返回
        //1)取出Goods集合
        List goodsList = pageBean.getContent();
        //2)把Goods集合拷贝到GoodsDTO集合
        List goodsDTOList = BeanHelper.copyWithCollection(goodsList,GoodsDTO.class);
        //3)封装PageResult
        PageResult pageResult = new PageResult(
                pageBean.getTotalElements(),
                Long.valueOf(pageBean.getTotalPages()),
                goodsDTOList
        );

        return pageResult;
    }

主要修改的地方如下:

2)前端代码

效果:

06、搜索页渲染:渲染固定过滤条件-后端代码 1)原生查询语句(了解)

需求:渲染搜索页过滤条件中的分类和品牌这两个固定过滤条件

# 聚合查询

GET /goods/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "手机",
            "fields": ["all","spuName"]
          }
        }
      ]
    }
  },
  "_source": {
    "includes": [""]
  },
  "aggs": {
    "categoryAgg": {
      "terms": {
        "field": "categoryId"
      }
    },
    "brandAgg": {
      "terms": {
        "field": "brandId"
      }
    }
  }
}



2)filterConditionsQuery方法

SearchService类:

 
    public Map filterConditionsQuery(SearchRequest searchRequest) {
        //1.创建Map集合  使用有序的Map集合:LinkedHashMap
        Map filterConditionsMap = new LinkedHashMap<>();

        //2.封装Map集合

        //2.1 封装固定过滤条件(分类和品牌)

        //1)获取搜索条件
        NativeSearchQueryBuilder queryBuilder = createNativeQueryBuilder(searchRequest);
        //2)设置结果过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""},null));
        //3)添加聚合条件
        String categoryAgg = "categoryAgg";
        String brandAgg = "brandAgg";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAgg).field("categoryId"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAgg).field("brandId"));

        //4)执行聚合查询
        AggregatedPage aggregatedPage = esTemplate.queryForPage(queryBuilder.build(),Goods.class);

        //5)获取所有聚合结果
        Aggregations aggregations = aggregatedPage.getAggregations();

        //6)取出分类的聚合结果
        Terms categoryTerms = aggregations.get(categoryAgg);

        //6.1 取出所有结果的分类ID
        List categoryIds = categoryTerms.getBuckets()
                                    .stream()
                                    .map(Terms.Bucket::getKeyAsNumber)//以Number类型获取key
                                    .map(Number::longValue) // 使用Number的longValue方法把Number类型转换为Long类型
                                    .collect(Collectors.toList());

        //6.2 把分类的ID集合转换为分类对象集合
        List categoryList = itemClient.findCategoriesByIds(categoryIds);

        //6.3.把品牌对象集合存入Map集合
        filterConditionsMap.put("分类",categoryList);

        //7)取出品牌的聚合结果
        Terms brandTerms = aggregations.get(brandAgg);

        //7.1 取出所有结果的品牌ID
        List brandIds = brandTerms.getBuckets()
                .stream()
                .map(Terms.Bucket::getKeyAsNumber)//以Number类型获取key
                .map(Number::longValue) // 使用Number的longValue方法把Number类型转换为Long类型
                .collect(Collectors.toList());

        //7.2 把品牌的ID集合转换为品牌对象集合
        List brandList = itemClient.findBrandsByIds(brandIds);

        //7.3.把品牌对象集合存入Map集合
        filterConditionsMap.put("品牌",brandList);



        //2.2 封装动态过滤条件



        //3.返回Map集合
        return filterConditionsMap;
    }

    
    public PageResult itemQueryPage(SearchRequest searchRequest) {
        //1.创建本地查询构造器对象
        NativeSearchQueryBuilder queryBuilder = createNativeQueryBuilder(searchRequest);

        //4) 设置结果过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","spuName","subTitle","skus"},null));

        //5)设置分页参数
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));

        //6)设置高亮字段与格式
        HighlightUtils.highlightField(queryBuilder,"spuName");

        //3.执行查询,获取结果
        Page pageBean = esTemplate.queryForPage(queryBuilder.build(),Goods.class,HighlightUtils.highlightBody(Goods.class,"spuName"));

        //4.处理结果,并返回
        //1)取出Goods集合
        List goodsList = pageBean.getContent();
        //2)把Goods集合拷贝到GoodsDTO集合
        List goodsDTOList = BeanHelper.copyWithCollection(goodsList,GoodsDTO.class);
        //3)封装PageResult
        PageResult pageResult = new PageResult(
                pageBean.getTotalElements(),
                Long.valueOf(pageBean.getTotalPages()),
                goodsDTOList
        );

        return pageResult;
    }

    
    public NativeSearchQueryBuilder createNativeQueryBuilder(SearchRequest searchRequest) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //2.封装地查询构造器(*)
        //1)创建布尔查询构造器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //2)在布尔查询构造器中追加must条件
        //boolQueryBuilder.must( QueryBuilders.matchQuery("all",searchRequest.getKey()) );
        boolQueryBuilder.must( QueryBuilders.multiMatchQuery(searchRequest.getKey(),"all","spuName") );
        //3)把布尔查询构造器存入本地查询构造器
        queryBuilder.withQuery(boolQueryBuilder);
        return queryBuilder;
    }
3)在ly-item项目提供方法

BrandController:

   
    @GetMapping("/brand/list")
    public ResponseEntity> findBrandsByIds(@RequestParam("ids") List ids){
        List brands = brandService.findBrandsByIds(ids);
        return ResponseEntity.ok(brands);
    }

BrandService:

  public List findBrandsByIds(List ids) {
        List brands = brandMapper.selectBatchIds(ids);
        if(CollectionUtils.isEmpty(brands)){
            throw new LyException(ExceptionEnum.BRAND_NOT_FOUND);
        }
        return brands;
    }
4)在Feign接口提供方法

ItemClient接口:

 
    @GetMapping("/category/list")
    public List findCategoriesByIds(@RequestParam("ids") List ids);

      
    @GetMapping("/brand/list")
    public List findBrandsByIds(@RequestParam("ids") List ids);
3)页面查看返回数据

07、搜索页渲染:渲染固定过滤条件-前端代码 1)接收后端返回数据

2)渲染过滤条件页面
{{key}}
更多arrow_drop_down 收起arrow_drop_up

 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B7aDlXYT-1657867880638)(assets/1599037979400.png)]

为了页面样式好看,将事先准备好的样式放开。

最终页面效果如下:

08、搜索页渲染:渲染动态过滤条件-后端代码 1)原生查询语句(了解)
# 聚合查询

# 注意:在聚合查询,参与聚合的字段必须是不分词字段

GET /goods/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "手机",
            "fields": ["all","spuName"]
          }
        }
      ]
    }
  },
  "_source": {
    "includes": [""]
  },
  "aggs": {
    "CPU核数": {
      "terms": {
        "field": "specs.CPU核数.keyword"
      }
    },
    "CPU品牌": {
      "terms": {
        "field": "specs.CPU品牌.keyword"
      }
    }
  }
}

2)filterConditionsQuery方法
    public Map filterConditionsQuery(SearchRequest searchRequest) {
        //1.创建Map集合  使用有序的Map集合:LinkedHashMap
        Map filterConditionsMap = new LinkedHashMap<>();

        //2.封装Map集合

        //2.1 封装固定过滤条件(分类和品牌)

        //1)获取搜索条件
        NativeSearchQueryBuilder queryBuilder = createNativeQueryBuilder(searchRequest);
        //2)设置结果过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""},null));
        //3)添加聚合条件
        String categoryAgg = "categoryAgg";
        String brandAgg = "brandAgg";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAgg).field("categoryId"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAgg).field("brandId"));

        //4)执行聚合查询
        AggregatedPage aggregatedPage = esTemplate.queryForPage(queryBuilder.build(),Goods.class);

        //5)获取所有聚合结果
        Aggregations aggregations = aggregatedPage.getAggregations();

        //6)取出分类的聚合结果
        Terms categoryTerms = aggregations.get(categoryAgg);

        //6.1 取出所有结果的分类ID
        List categoryIds = categoryTerms.getBuckets()
                                    .stream()
                                    .map(Terms.Bucket::getKeyAsNumber)//以Number类型获取key
                                    .map(Number::longValue) // 使用Number的longValue方法把Number类型转换为Long类型
                                    .collect(Collectors.toList());

        //6.2 把分类的ID集合转换为分类对象集合
        List categoryList = itemClient.findCategoriesByIds(categoryIds);

        //6.3.把品牌对象集合存入Map集合
        filterConditionsMap.put("分类",categoryList);

        //7)取出品牌的聚合结果
        Terms brandTerms = aggregations.get(brandAgg);

        //7.1 取出所有结果的品牌ID
        List brandIds = brandTerms.getBuckets()
                .stream()
                .map(Terms.Bucket::getKeyAsNumber)//以Number类型获取key
                .map(Number::longValue) // 使用Number的longValue方法把Number类型转换为Long类型
                .collect(Collectors.toList());

        //7.2 把品牌的ID集合转换为品牌对象集合
        List brandList = itemClient.findBrandsByIds(brandIds);

        //7.3.把品牌对象集合存入Map集合
        filterConditionsMap.put("品牌",brandList);


        //2.2 封装动态过滤条件(规格参数聚合)

        if(categoryIds!=null){
            categoryIds.forEach(categoryId -> {
                //1)根据分类ID查询参与搜索过滤条件的规格参数
                List specParams = itemClient.findSpecParams(null, categoryId, true);

                //2)遍历规格参数,逐个添加到聚合条件中
                specParams.forEach(specParam -> {
                    queryBuilder.addAggregation( AggregationBuilders.terms(specParam.getName()).field("specs."+specParam.getName()+".keyword") );
                });

                //3)执行聚合查询
                AggregatedPage specParamAggPage = esTemplate.queryForPage(queryBuilder.build(),Goods.class);

                Aggregations specParamAggs = specParamAggPage.getAggregations();

                //4)遍历规格参数, 获取所有规格参数的聚合结果,把每个规格参数的聚合结果存入Map集合
                specParams.forEach(specParam -> {
                    Terms specParamTerms = specParamAggs.get(specParam.getName());

                    List specParamAggKeyList = specParamTerms.getBuckets()
                                                        .stream()
                                                        .map(Terms.Bucket::getKey)
                                                        .collect(Collectors.toList());

                    filterConditionsMap.put(specParam.getName(),specParamAggKeyList);
                });
            });
        }


        //3.返回Map集合
        return filterConditionsMap;
    }
 
3)页面查看返回数据 

09、搜索页渲染:渲染动态过滤条件-前端代码
{{key}}

10、搜索页渲染:过滤条件收起和更多功能 1) 在data中定义控制收起和更多的布尔值
        data: {
            ly,
            //前端->后端
            searchParams:{
                key:"",  //搜索关键字
                page:1, //页码值
            },

            //后端->前端
            items:[],//接收商品列表
            total:1,//接收总记录数
            totalPage:1,//接收总页数
            filterConditions:{},//接收搜索过滤条件

            //控制收起和更多的布尔值
            showMore: false

        },
2) 给收起和更多按钮绑定点击事件
更多arrow_drop_down 收起arrow_drop_up
3) 指定过滤条件的显示条件

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号