谷粒商城实战笔记-179~183-商城业务-检索服务-SearchRequest和SearchResponse构建

news2024/12/28 18:29:09

文章目录

  • 一,179-商城业务-检索服务-SearchRequest构建-检索
    • 1,Controller接口
  • 二,180-商城业务-检索服务-SearchRequest构建-排序、分页、高亮&测试
  • 三,181-商城业务-检索服务-SearchRequest构建-聚合
  • 四,182-商城业务-检索服务-SearchResponse分析&封装
  • 五,接口代码
  • 六,183-商城业务-检索服务-验证结果封装正确性

这一节主要是将上一节的DSL语句转换为使用Elasticsearch的Java客户端构建查询。

当从首页跳转到搜索界面,后端会根据搜索条件封装请求,向ES发出检索请求,查询到数据后,封装为之前设计好的数据结构,然后交给Thyleaf编译整合到页面模板中。

一,179-商城业务-检索服务-SearchRequest构建-检索

1,Controller接口

	@GetMapping(value = "/list.html")
    public String listPage(SearchParam searchParam, Model model) {
        SearchResult search = mallSearchService.search(searchParam);
        model.addAttribute("result", search);
        return "list";
    }

把查询结果放入Model,是为了Thymeleaf将数据整合到页面模板中。

二,180-商城业务-检索服务-SearchRequest构建-排序、分页、高亮&测试

三,181-商城业务-检索服务-SearchRequest构建-聚合

四,182-商城业务-检索服务-SearchResponse分析&封装

这四节的代码量比较大,难度笔记高,要求对DSL和Elasticsearch的Client API比较熟悉。这部分内容可以借助AI生成,比如将DSL交给ChatGPT,让它生成Java代码,然后在自测的过程中微调。

五,接口代码


@Slf4j
@Service
public class MallSearchServiceImpl implements MallSearchService {

    @Autowired
    private RestHighLevelClient esRestClient;

    @Override
    public SearchResult search(SearchParam param) {

        //动态构建出查询需要的DSL语句
        SearchResult result = null;

        //1、准备检索请求
        SearchRequest searchRequest = buildSearchRequest(param);

        try {
            //2、执行检索请求
            SearchResponse response = esRestClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

            //3、分析响应数据,封装成我们需要的格式
            result = buildSearchResult(response,param);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 构建结果数据
     * 模糊匹配,过滤(按照属性、分类、品牌,价格区间,库存),完成排序、分页、高亮,聚合分析功能
     * @param response
     * @return
     */
    private SearchResult buildSearchResult(SearchResponse response,SearchParam param) {

        SearchResult result = new SearchResult();

        //1、返回的所有查询到的商品
        SearchHits hits = response.getHits();

        List<SkuEsModel> esModels = new ArrayList<>();
        //遍历所有商品信息
        if (hits.getHits() != null && hits.getHits().length > 0) {
            for (SearchHit hit : hits.getHits()) {
                String sourceAsString = hit.getSourceAsString();
                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);
            }
        }
        result.setProduct(esModels);

        //2、当前商品涉及到的所有属性信息
        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();
            attrVo.setAttrId(attrId);

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

            //3、得到属性的所有值
            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
            List<String> attrValues = attrValueAgg.getBuckets().stream().map(item -> item.getKeyAsString()).collect(Collectors.toList());
            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 brandId = bucket.getKeyAsNumber().longValue();
            brandVo.setBrandId(brandId);

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

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

            brandVos.add(brandVo);
        }
        result.setBrands(brandVos);

        //4、当前商品涉及到的所有分类信息
        //获取到分类的聚合
        List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        ParsedLongTerms catalogAgg = response.getAggregations().get("catalog_agg");
        for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            //得到分类id
            String keyAsString = bucket.getKeyAsString();
            catalogVo.setCatalogId(Long.parseLong(keyAsString));

            //得到分类名
            ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
            String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogName(catalogName);
            catalogVos.add(catalogVo);
        }

        result.setCatalogs(catalogVos);
        //===============以上可以从聚合信息中获取====================//
        //5、分页信息-页码
        result.setPageNum(param.getPageNum());
        //5、1分页信息、总记录数
        long total = hits.getTotalHits().value;
        result.setTotal(total);

        //5、2分页信息-总页码-计算
        int totalPages = (int)total % EsConstant.PRODUCT_PAGESIZE == 0 ?
                (int)total / EsConstant.PRODUCT_PAGESIZE : ((int)total / EsConstant.PRODUCT_PAGESIZE + 1);
        result.setTotalPages(totalPages);

        List<Integer> pageNavs = new ArrayList<>();
        for (int i = 1; i <= totalPages; i++) {
            pageNavs.add(i);
        }
        result.setPageNavs(pageNavs);

        return result;
    }


    /**
     * 准备检索请求
     * 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析
     * @return
     */
    private SearchRequest buildSearchRequest(SearchParam param) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        /**
         * 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)
         */
        //1. 构建bool-query
        BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();

        //1.1 bool-must
        if(!StringUtils.isEmpty(param.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
        }

        //1.2 bool-fiter
        //1.2.1 catelogId
        if(null != param.getCatalog3Id()){
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));
        }

        //1.2.2 brandId
        if(null != param.getBrandId() && param.getBrandId().size() >0){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
        }

        //1.2.3 attrs
        if(param.getAttrs() != null && param.getAttrs().size() > 0){

            param.getAttrs().forEach(item -> {
                //attrs=1_5寸:8寸&2_16G:8G
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

                //attrs=1_5寸:8寸
                String[] s = item.split("_");
                String attrId=s[0];
                String[] attrValues = s[1].split(":");//这个属性检索用的值
                boolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));

                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            });

        }

        //1.2.4 hasStock
        if(null != param.getHasStock()){
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",param.getHasStock() == 1));
        }


        //1.2.5 skuPrice
        if(!StringUtils.isEmpty(param.getSkuPrice())){
            //skuPrice形式为:1_500或_500或500_
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
            String[] price = param.getSkuPrice().split("_");
            if(price.length==2){
                rangeQueryBuilder.gte(price[0]).lte(price[1]);
            }else if(price.length == 1){
                if(param.getSkuPrice().startsWith("_")){
                    rangeQueryBuilder.lte(price[1]);
                }
                if(param.getSkuPrice().endsWith("_")){
                    rangeQueryBuilder.gte(price[0]);
                }
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        //封装所有的查询条件
        searchSourceBuilder.query(boolQueryBuilder);


        /**
         * 排序,分页,高亮
         */

        //排序
        //形式为sort=hotScore_asc/desc
        if(!StringUtils.isEmpty(param.getSort())){
            String sort = param.getSort();
            String[] sortFileds = sort.split("_");

            SortOrder sortOrder="asc".equalsIgnoreCase(sortFileds[1])?SortOrder.ASC:SortOrder.DESC;

            searchSourceBuilder.sort(sortFileds[0],sortOrder);
        }

        //分页
        searchSourceBuilder.from((param.getPageNum()-1)* EsConstant.PRODUCT_PAGESIZE);
        searchSourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);

        //高亮
        if(!StringUtils.isEmpty(param.getKeyword())){

            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");

            searchSourceBuilder.highlighter(highlightBuilder);
        }

        /**
         * 聚合分析
         */
        //1. 按照品牌进行聚合
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brand_agg");
        brandAgg.field("brandId").size(50);


        //1.1 品牌的子聚合-品牌名聚合
        brandAgg.subAggregation(AggregationBuilders.terms("brand_name_agg")
                .field("brandName").size(1));
        //1.2 品牌的子聚合-品牌图片聚合
        brandAgg.subAggregation(AggregationBuilders.terms("brand_img_agg")
                .field("brandImg").size(1));

        searchSourceBuilder.aggregation(brandAgg);

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

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

        searchSourceBuilder.aggregation(catalogAgg);

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

        log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());

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

        return searchRequest;
    }
}

  1. 依赖注入
  • @Autowired:自动注入RestHighLevelClient,这是Elasticsearch的Java高级REST客户端。
  1. 搜索方法

    • search(SearchParam param):根据传入的搜索参数param执行搜索,并返回SearchResult对象。
  2. 构建搜索请求

    • buildSearchRequest(SearchParam param):动态构建Elasticsearch的搜索请求,包括查询条件、排序、分页、高亮和聚合分析。
  3. 构建搜索结果

    • buildSearchResult(SearchResponse response, SearchParam param):从Elasticsearch的响应中提取数据,并构建SearchResult对象。
  4. 查询构建细节

    • 使用BoolQueryBuilder来构建复合查询,包括必须匹配的条件(must)、过滤条件(filter)等。
    • 对于每个搜索参数,如关键字、分类ID、品牌ID、属性、库存、价格区间等,都有相应的查询构建逻辑。
  5. 高亮显示

    • 如果搜索包含关键字,则使用HighlightBuilder来高亮显示匹配的文本。
  6. 排序和分页

    • 根据参数设置排序字段和顺序。
    • 设置分页的页码和每页大小。
  7. 聚合分析

    • 对品牌、分类和属性进行聚合分析,以便于在搜索结果的侧边栏展示统计信息。

六,183-商城业务-检索服务-验证结果封装正确性

在首页搜索框输入“华为”,点击搜索,跳转到搜索页面,后端会执行ES检索,查看日志,判断接口响应结果是否符合预期。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2051082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

x64汇编语言与逆向工程实战指南(一)

逆向程序demo网址&#xff1a;https://crackmes.one/&#xff0c;下载的压缩包密码均为.cracksme.one或cracksme.de 实例一&#xff1a;基本 网络钓鱼密码程序 破解 目录 1. DIE确定程序基本信息1.1 DIE程序与下载1.2 分析demo的架构 2. x64dbg调试获取密码2.1 功能初探2.2 调试…

C++基础——合集

1.C关键字&#xff08;C98&#xff09; C总计63个关键字&#xff0c;C语言32个关键字 2.命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存 在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的…

虚拟机可以玩Steam游戏吗?虚拟机怎么玩Steam Windows游戏 PD19虚拟机玩Steam

你有没有在苹果电脑上玩游戏的需求呢&#xff1f;很多人认为只有“双系统”才能实现Mac电脑运行Windows操作系统&#xff0c;其实不然&#xff0c;近些年来&#xff0c;虚拟机技术在不断发展&#xff0c;越来越多的苹果用户开始使用虚拟机在苹果设备上玩游戏。Steam是一个非常受…

【运维高级内容--KEEPALIVED高可用集群】

目录 1.简介 2.实现master/slave的 Keepalived 单主架构 3.vip通行 &#xff08;ping通: 4.启用日志功能 5.实现独立子配置文件 6.非抢占式模式 7.抢占延迟模式 8.单播配置 9.keepalived状态切换的通知脚本 10.双主结构&#xff1a;两个虚拟路由&#xff08;多主模式&…

精武杯的部分复现

标红的为答案 计算机手机部分 1、请综合分析计算机和⼿机检材&#xff0c;计算机最近⼀次登录的账户名是&#xff1f;admin 2.请综合分析计算机和⼿机检材&#xff0c;计算机最近⼀次插⼊的USB存储设备串号是?S3JKNX0JA05097Y 3.请综合分析计算机和⼿机检材&#xff0c;谢弘…

Xilinx FPGA:vivado关于以太网的零碎知识点

一、OSI七层模型 为了实现网络通信的标准化&#xff0c;普及网络应用&#xff0c;国际标准化组织&#xff08;ISO&#xff09;将整个以太网通信结构制定了OSI模型&#xff0c;即开放式系统互联。 OSI定义了网络互连的七层框架&#xff08;物理层、数据链路层、网络层、传输层、…

web前端之html弹窗面板的popover新属性

MENU 前言效果图htmlstyle 前言 1、代码段的功能是在网页上实现一个弹出框。当用户点击"Open Popup"按钮时&#xff0c;会显示一个中央定位的弹出框&#xff0c;弹出框里有"This is a popup"文本&#xff0c;以及两个按钮(“Close"和"confirm”)…

XXX【3】模板方法

一.GOF-23 模式分类 从目的来看&#xff1a; 创建型模式&#xff1a;解决对象创建的工作。结构型模式&#xff1a;解决需求变化为对象结构带来的冲击。行为型模式&#xff1a;解决多个类交互之间责任的划分问题。 从范围来看&#xff1a; 类模式处理类与子类的静态关系&…

timing derate失效,cppr为0原因分析

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 timing derate失效&#xff0c;crpr结果为0&#xff0c;可能是错误的timing derate设置引起的&#xff0c;以下图为例&#xff1a; setup violation path的cppr为0&#xff0c;…

汇编语言指令 jmp: jmp word ptr、jmp dword ptr、jmp 寄存器

1. 转移地址在内存中的jmp指令有2种形式 1.1 jmp word ptr 内存单元地址 jmp word ptr 内存单元地址是段内转移指令&#xff0c;也就是说该指令只修改IP值&#xff0c;其功能是控制CPU下一条执行的指令是一个字&#xff08;2个字节&#xff09;内存中存放的偏移地址所指向的指…

集合的知识点

一、集合的简介 1.1 什么是集合 集合(Collection)&#xff0c;也是一个数据容器&#xff0c;类似于数组&#xff0c;但是和数组是不一样的。集合是一个可变的容器&#xff0c;可以随时向集合集合中添加元素&#xff0c;也可以随时从集合中删除元素。另外&#xff0c;集合还提…

在线图片编辑网站推荐(图片压缩)

&#x1f525;发现神器&#xff01;「可乐改图」——一站式在线图片编辑平台&#xff0c;让工作更高效&#xff01;&#x1f680; 大家好&#xff01;今天我要给大家安利一个我最近发现的宝藏工具——「可乐改图」&#xff0c;一个集多功能于一身的在线图片编辑平台&#xff0…

前端(Vue)动态换肤的通用解决方案及原理分析(2)

文章目录 动态换肤的主题解决方案总结处理 第三方( element-plus )主题变更原理与步骤分析**实现原理**实现步骤处理 element-plus 主题变更补充 > 步骤 2&#xff1a;获取当前 element-plus 的默认样式表&#xff0c;并且把需要进行替换的色值打上标记补充>步骤 3&#…

Android 手机恢复出厂设置后,还能恢复其中数据吗?

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份、网络及终端数据安全等解决方案与服务。 同时&#xff0c;鸿萌是众多国际主流数据恢复软件的授权代理商&#xff0c;为专业用户提供正版的数据恢复软件。 对于 A…

网络版计算器(理解协议与序列化与反序列化)

一、理解协议 在网络层面&#xff0c;协议&#xff08;Protocol&#xff09;是一组规则、标准或约定&#xff0c;它们定义了在网络环境中&#xff0c;计算机、服务器、路由器、交换机等网络设备之间如何相互通信和交换信息。这些规则涵盖了数据格式、数据交换的顺序、速度、以及…

调研-音视频

音视频 基础概念主要内容音频基础概念音频量化过程音频压缩技术视频基础概念视频bug视频编码H264视频像素格式YUVRGB参考文献基础概念 ● 实时音视频应用环节 ○ 采集、编码、前后处理、传输、解码、缓冲、渲染等很多环节。 主要内容 音频 基础概念 三要素:音调(音频)、…

阿里云注册、认证、短信资质、签名、模板申请过程

一、帐号注册 输入“帐号密码注册”中的相关信息即可。 手机号是必须的&#xff0c;先确定好手机号。 正常的可以直接注册成功的。 二、实名认证 注册成功之后&#xff0c;就可以点击上述的“快速实名认证”。 这次选择的是“企业认证”。 有几种方式&#xff0c;如下&#x…

学习嵌入式第二十八天

有名管道 在C语言中&#xff0c;有名管道&#xff08;Named Pipe&#xff09;是一种特殊的文件类型&#xff0c;它允许进程间通信。有名管道与匿名管道&#xff08;Anonymous Pipe&#xff09;不同&#xff0c;它在文件系统中有一个路径名&#xff0c;因此可以被多个进程访问。…

项目实战-Linux部署-安装jdk以及shell脚本检查jdk

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

STM32之MPU6050实战

MPU6050 MPU6050是一个6轴姿态传感器&#xff0c;可以测量芯片自身X、Y、Z轴的加速度、角速度参数&#xff0c;通过数据融合&#xff0c;可进一步得到姿态角&#xff0c;常应用于平衡车、飞行器等需要检测自身姿态的场景 3轴加速度计&#xff08;Accelerometer&#xff09;&a…