搜索业务规则分析、过滤条件构建

本文详细介绍了一个电商网站如何通过AngularJS实现商品搜索的优化,包括需求分析、前后端代码实现、过滤查询等关键环节,旨在提升用户体验和搜索效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1 需求分析

  • 面板上有商品分类、品牌、各种规格、价格区间等过滤条件。

  • 业务规则:
  1. 当用户点击搜索面板的商品分类时, 显示按照这个关键字查询结果的基础上,筛选此分类的结果。
  2. 当用户点击搜索面板的品牌时,显示在以上结果的基础上,筛选此品牌的结果。
  3. 当用户点击搜索面板的规格时,显示在以上结果的基础上,筛选此规格结果。
  4. 当用户点击价格区间时,显示在以上结果的基础上,按价格进行筛选的结果。
  5. 当用户点击搜索面板的相应条件时,隐藏已点击的条件。

1.2 实现思路

  1.  查询条件的构建、面板的隐藏需要使用AngularJS来实现。
  2. 后端的商品分类、品牌、规格选项、价格区间查询需要使用过滤查询来实现。

1.3 搜索业务规则分析

  •  业务规则:
  1. 根据关键字搜索
  2. 高亮显示
  3. 根据商品分类过滤
  4. 根据品牌过滤
  5. 根据规则选项过滤
  6. 根据价格区间过滤
  7. 价格排序
  8. 新品排序
  9. 销量排序
  10. 评论数排序
  11. 综合排序

 2.添加过滤条件【前端代码】

  • 封装过滤条件参数
  • 实现步骤:
  1. 第一步:为页面上的过滤条件绑定点击事件。
  2. 第二步:提供一个事件方法,封装过滤条件(需要把过滤条件传到后台)
  3. 第三步:在页面导航区域显示用户选择的过滤条件。 

前端参数封装如下:

{"keywords":"","category":"手机","brand":"苹果","price":"1500-2000","spec":{"网络":"电信4G","机身内存":"128G"}}  

2.1 添加搜索选项

2.1.1 绑定点击事件(为页面上的过滤条件绑定点击事件

修改pinyougou-search-web/src/main/webapp/search.html页面,为搜索面板添加点击事件

点击商品分类标签(168多行):

<div class="fl value">
   <
a href="javascript:;" style="margin-right: 10px"
     
ng-click="addSearchItem('category','手机')"
>手机</a>
</
div>

点击品牌标签(177多行):

<ul class="logo-list">
   <
li style="cursor: pointer"
     
ng-click="addSearchItem('brand','苹果')"
>
      <
img src="img/_/phone01.jpg"/></li>
   <
li style="cursor: pointer"
     
ng-click="addSearchItem('brand','三星')"
>
      <
img src="img/_/phone02.jpg"/></li>
   <
li style="cursor: pointer"
     
ng-click="addSearchItem('brand','华为')"
>
      <
img src="img/_/phone03.jpg"/></li>
   <
li style="cursor: pointer"
     
ng-click="addSearchItem('brand','小米')"
>
      <
img src="img/_/phone04.jpg"/></li>
   <
li style="cursor: pointer"
     
ng-click="addSearchItem('brand','OPPO')"
>
      <
img src="img/_/phone05.jpg"/></li>
   <
li style="cursor: pointer"
     
ng-click="addSearchItem('brand','VIVO')"
>
      <
img src="img/_/phone06.jpg"/></li>
</
ul> 

 点击网格标签(205多行):(网络标签的数据格式比较特殊

<li>
   <
a ng-click="addSearchItem('网络','移动3G')">移动3G</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('网络','联通3G')">联通3G</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('网络','电信3G')">电信3G</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('网络','移动4G')">移动4G</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('网络','联通4G')">联通4G</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('网络','电信4G')">电信4G</a>
</
li>

点击机身内存标签(231多行):

<li>
   <
a ng-click="addSearchItem('机身内存','16G')">16G</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('机身内存','32G')">32G</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('机身内存','64G')">64G</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('机身内存','128G')">128G</a>
</
li> 

点击价格区间标签(251行):

<li>
   <
a ng-click="addSearchItem('price','0-500')">0-500</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('price','500-1000')">500-1000</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('price','1000-1500')">1000-1500</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('price','1500-2000')">1500-2000</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('price','2000-3000')">2000-3000</a>
</
li>
<
li>
   <
a ng-click="addSearchItem('price','3000-*')">3000元以上</a>
</
li> 

2.1.2 添加搜索选项方法 提供一个事件方法,封装过滤条件(需要把过滤条件传到后台)

pinyougou-search-web/src/main/webapp/js/controller/searchController.js:

/** 定义搜索参数对象 */
$scope.searchParam = {keywords : '', category : '',
   
brand : '', price : '', spec : {}};
/** 添加搜索选项方法 */
$scope.addSearchItem = function(key, value){
   
/** 判断是商品分类、品牌、价格 */
   
if(key == 'category' || key == 'brand' || key == 'price'){
        $scope.
searchParam[key] = value;
    }
else{
       
/** 规格选项 */
       
$scope.searchParam.spec[key] = value;
    }
};

 2.1.3 显示面包屑 (在页面导航区域显示用户选择的过滤条件

修改pinyougou-search-web/src/main/webapp/search.html页面,用面包屑形式显示搜索条件(147行)

  • 在js的事件方法中定义了搜索参数对象,searchParam 放在了scope中,所以在页面上相对应的显示出来即可。由于机身内存和网络数据格式的特殊性,需要特殊处理 。格式: {"机身内存":"16G","网络":"联通4G"}
<!--bread-->

<div class="bread">

   <ul class="fl sui-breadcrumb">

      <li>搜索条件</li>

   </ul>

   <ul class="tags-choose">

      <li class="tag" ng-if="searchParam.category != ''">

         商品分类:{{searchParam.category}}

         <i class="sui-icon icon-tb-close"></i>

      </li>

      <li class="tag" ng-if="searchParam.brand != ''">

         品牌:{{searchParam.brand}}

         <i class="sui-icon icon-tb-close"></i>

      </li>

      <li class="tag" ng-repeat="(key,value) in searchParam.spec">

         {{key}} : {{value}}

         <i class="sui-icon icon-tb-close"></i>

      </li>

<li class="tag" ng-if="searchParam.price != ''">
          
价格:{{searchParam.price}}
          <class="sui-icon icon-tb-close"></i>
      </
li>
   </
ul>
</
div>
<!--selector-->


  3.添加过滤条件【前端代码】

  • 减少过滤条件参数。
  • 实现步骤:
  1. 为页面上的<i>标签绑定点击事件。
  2. 在js的控制器中定义一个事件方法 

 3.1 绑定点击事件 (为页面上的<i>标签绑定点击事件

修改pinyougou-search-web/src/main/webapp/search.html页面

<ul class="tags-choose">
   <
li class="tag" ng-if="searchParam.category != ''">
     
商品分类:{{searchParam.category}}
      <
i class="sui-icon icon-tb-close"
        
ng-click="removeSearchItem('category')"
></i>
   </
li>
   <
li class="tag" ng-if="searchParam.brand != ''">
     
品牌:{{searchParam.brand}}
      <
i class="sui-icon icon-tb-close"
        
ng-click="removeSearchItem('brand')"
></i>
   </
li>
   <
li class="tag" ng-repeat="(key,value) in searchParam.spec">
      {{
key}} : {{value}}
      <
i class="sui-icon icon-tb-close"
        
ng-click="removeSearchItem(key)"
></i>
   </
li>
   <
li class="tag" ng-if="searchParam.price != ''">
     
价格:{{searchParam.price}}
      <
class="sui-icon icon-tb-close"
        
ng-click="removeSearchItem('price')"
></i>
   </
li>
</
ul>

 3.2  删除搜索选项方法 (在js的控制器中定义一个事件方法 

pinyougou-search-web/src/main/webapp/js/controller/searchController.js,增加删除搜索选项方法:

/** 删除搜索选项方法 */
$scope.removeSearchItem = function(key){
   
/** 判断是商品分类、品牌、价格 */
   
if(key == "category" ||  key == "brand" || key == 'price'){
        $scope.
searchParam[key] = "";
    }
else{
       
/** 删除规格选项 */
       
delete $scope.searchParam.spec[key];
    }
};


  4.隐藏查询面板【前端代码】 

页面动画效果

  • 实现步骤:
  1. 控制搜索面板显示还是隐藏
    ng-if="searchParam.spec['机身内存'] == null" // 规格选项
    ng-if="searchParam.brand == ''" // 品牌

 4.1 隐藏商品分类面板

修改pinyougou-search-web/src/main/webapp/search.html(175行)

<div class="type-wrap" ng-if="searchParam.category == ''">
   <
div class="fl key">商品分类</div>

    ......
   <
div class="fl ext"></div>
</
div>

  4.2 隐藏品牌面板

修改pinyougou-search-web/src/main/webapp/search.html(183行)

<div class="type-wrap logo" ng-if="searchParam.brand == ''">

   <div class="fl key brand">品牌</div>

   <div class="value logos">

......
    </
div>
</
div>

   4.3 隐藏网络面板

修改pinyougou-search-web/src/main/webapp/search.html(211行)

<div class="type-wrap" ng-if="searchParam.spec['网络'] == null">   

<div class="fl key">网络</div>

......
   <
div class="fl ext"></div>
</
div>

  4.4 隐藏机身内存面板

修改pinyougou-search-web/src/main/webapp/search.html(237行)

<div class="type-wrap" ng-if="searchParam.spec['机身内存'] == null">   

<div class="fl key">机身内存</div>

......
   <
div class="fl ext"></div>
</
div>

   4.5 隐藏价格区间面板

修改pinyougou-search-web/src/main/webapp/search.html(257行)

<div class="type-wrap" ng-if="searchParam.price == ''">   

<div class="fl key">价格</div>

......
   <
div class="fl ext"></div>
</
div>

 4.6 提交查询

pinyougou-search-web/src/main/webapp/js/controller/searchController.js ,在添加和删除筛选条件时调用搜索方法

/** 添加搜索选项方法 */
$scope.addSearchItem = function(key, value){
   
/** 判断是商品分类、品牌、价格 */
   
if(key == 'category' || key == 'brand' || key == 'price'){
        $scope.
searchParam[key] = value;
    }
else{
       
/** 规格选项 */
       
$scope.searchParam.spec[key] = value;
    }
   
/** 执行搜索 */
   
$scope.search();

};
/** 删除搜索选项方法 */
$scope.removeSearchItem = function(key){
   
/** 判断是商品分类、品牌、价格 */
   
if(key == "category" ||  key == "brand" || key == 'price'){
        $scope.
searchParam[key] = "";
    }
else{
       
/** 删除规格选项 */
       
delete $scope.searchParam.spec[key];
    }
   
/** 执行搜索 */
   
$scope.search();

};


5.过滤查询【后端代码】

5.1 分类过滤【后端代码】

 请求参数:

{keywords: "", category: "手机", brand: "三星", price: "1500-2000", spec: {网络: "电信3G", 机身内存: "128G"}}

  • 实现步骤:
  1.  第一步:判断商品分类category是否有参数值
  2. 第二步:按商品分类过滤查询(添加过滤查询条件)

// 商品分类过滤
String category = (String)params.get("category");
if (StringUtils.isNoneBlank(category)){
    // 过滤条件
    Criteria criteria1 = new Criteria("category").is(category);
    // 添加过滤查询
    query.addFilterQuery(new SimpleFilterQuery(criteria1));

 5.2 品牌过滤【后端代码】

  • 实现步骤:
  1. 第一步:判断商品品牌brand是否有参数值
  2. 第二步:按商品品牌过滤查询(添加过滤查询条件)

// 按商品品牌过滤
String brand = (String)params.get("brand");
if (StringUtils.isNoneBlank(brand)){
    // 过滤条件
    Criteria criteria1 = new Criteria("brand").is(brand);
    // 添加过滤查询
    query.addFilterQuery(new SimpleFilterQuery(criteria1));

  5.3 规格过滤【后端代码】

  • 实现思路:规格有多选项,需要循环过滤。循环规格查询条件,根据key得到域名称,根据value设置过滤条件。
  • 实现步骤:
  1. 判断规格spec是否有参数值
  2. 按规格过滤查询(添加过滤查询条件)

    规格Field是动态域: spec_*

    "spec_网络": "联通4G",

    spec机身内存": "64G",

// 按规格过滤 spec: {网络: "电信3G", 机身内存: "128G"}
Map<String, String> specMap = (Map<String, String>)params.get("spec");
if (specMap != null && specMap.size() > 0){
    // 迭代规格
    for (String key : specMap.keySet()){
        // 过滤条件 spec_*
        Criteria criteria1 = new Criteria("spec_" + key).is(specMap.get(key));
        // 添加过滤查询
        query.addFilterQuery(new SimpleFilterQuery(criteria1));
    }

  5.4 价格过滤【后端代码】

  • 实现步骤:
  1. 第一步:判断价格price是否有参数值
  2. 第二步:按价格区间过滤查询(添加过滤查询条件)

// 按价格区间过滤 0-500 1000-1500 3000-*
String price = (String)params.get("price");
if (StringUtils.isNoneBlank(price)){
    // 得到价格区间数组
    String[] priceArr = price.split("-");

    // 判断起始价格是不是零,不是零添加过滤条件
    if (!"0".equals(priceArr[0])){
        // 过滤条件 price >= ?
        Criteria criteria1 = new Criteria("price").greaterThanEqual(priceArr[0]);
        // 添加过滤查询
        query.addFilterQuery(new SimpleFilterQuery(criteria1));
    }

    // 判断结束价格是不是星号,不是星号添加过滤条件
    if (!"*".equals(priceArr[1])){
        // 过滤条件 price <= ?
        Criteria criteria1 = new Criteria("price").lessThanEqual(priceArr[1]);
        // 添加过滤查询
        query.addFilterQuery(new SimpleFilterQuery(criteria1));
    }
}

 

  5.5  完整代码

@Service(interfaceName = "com.pinyougou.service.ItemSearchService")
public class ItemSearchServiceImpl implements ItemSearchService {

    @Autowired
    private SolrTemplate solrTemplate;

    /**
     * @Author: hyh
     * @Description:搜索结果高亮显示
     * @Date: 17:50 2018/12/1
     */
    @Override
    public Map<String, Object> search(Map<String, Object> params) {
        //1.创建Map集合封装返回的数据
        Map<String, Object> data = new HashMap<>();
        //2.获取查询关键字
        String keywords = (String) params.get("keywords");
        //获取当前页码
        Integer page = (Integer) params.get("page");
        if (page == null){
            //默认第一页
            page = 1;
        }
        //获取每页显示的记录数
        Integer rows = (Integer) params.get("rows");
        if (rows == null){
            /**默认20条记录*/
            rows = 20;
        }
        //3.判断检索关键字是否为空
        if (StringUtils.isNoneBlank(keywords)) {//高亮查询
            //创建高亮查询对象
            HighlightQuery highlightQuery = new SimpleHighlightQuery();
            //创建高亮选项对象
            HighlightOptions highlightOptions = new HighlightOptions();
            //设置高亮域
            highlightOptions.addField("title");
            //设置高亮前缀
            highlightOptions.setSimplePrefix("<font color='red'>");
            //设置高亮后缀
            highlightOptions.setSimplePostfix("</font>");
            //设置高亮选项
            highlightQuery.setHighlightOptions(highlightOptions);
            //创建查询条件对象
            Criteria criteria = new Criteria("keywords").is(keywords);
            //添加查询条件(关键字)
           highlightQuery.addCriteria(criteria);
           /**按商品分类过滤*/
           if (!"".equals(params.get("category"))){
               Criteria criteria1 = new Criteria("category").is(params.get("category"));
               /**添加过滤条件*/
               highlightQuery.addFilterQuery(new SimpleFilterQuery(criteria1));
           }
           /**按品牌过滤*/
           if (!"".equals(params.get("brand"))){
               Criteria criteria1 = new Criteria("brand").is(params.get("brand"));
               /**添加过滤条件*/
               highlightQuery.addFilterQuery(new SimpleFilterQuery(criteria1));
           }
           /**按规格过滤*/
           if (params.get("spec")!=null){
               Map<String,String> specMap = (Map) params.get("spec");
               for (String  key : specMap.keySet()) {
                   Criteria criteria1 = new Criteria("spec_"+key).is(specMap.get(key));
                   /**添加过滤条件*/
                   highlightQuery.addFilterQuery(new SimpleFilterQuery(criteria1));
               }
           }
           /**按价格过滤*/
           if (!"".equals(params.get("price"))){
               /**得到价格的范围数组*/
               String[] price = params.get("price").toString().split("-");
               /**如果价格区间起点不等于0*/
                if (!price[0].equals("0")){
                    Criteria criteria1 = new Criteria("price").greaterThanEqual(price[0]);
                    /**添加过滤条件*/
                    highlightQuery.addFilterQuery(new SimpleFilterQuery(criteria1));
                }
               /**如果价格区间起点不等于星号*/
                if (!price[1].equals("*")){
                    Criteria criteria1 = new Criteria("price").lessThanEqual(price[1]);
                    /**添加过滤条件*/
                    highlightQuery.addFilterQuery(new SimpleFilterQuery(criteria1));
                }
           }
           /**设置起始记录查询数*/
           highlightQuery.setOffset((page-1)*rows);
           /**设置每页显示记录数*/
           highlightQuery.setRows(rows);
            //分页查询,得到高亮分页查询对象
            HighlightPage<SolrItem> highlightPage = solrTemplate
                    .queryForHighlightPage(highlightQuery,SolrItem.class);
            /**循环高亮项集合*/
            for (HighlightEntry<SolrItem> he :highlightPage.getHighlighted() ) {
                /**获取检索到的原实体*/
                SolrItem solrItem = he.getEntity();
                /**判断高亮集合及集合中第一个Field的高亮内容*/
                if (he.getHighlights().size()>0 &&
                        he.getHighlights().get(0).getSnipplets().size()>0){
                        /**设置高亮的结果*/
                        solrItem.setTitle(he.getHighlights().get(0).getSnipplets().get(0));
                }
            }
            data.put("rows",highlightPage.getContent());
            /**设置总页数*/
            data.put("totalPages",highlightPage.getTotalPages());
            /**设置总记录数*/
            data.put("total",highlightPage.getTotalElements());

        } else {
            //简单查询:创建简单查询对象
            SimpleQuery simpleQuery = new SimpleQuery("*:*");
            /**设置起始记录查询数*/
            simpleQuery.setOffset((page-1)*rows);
            /**设置吗,每页显示记录数*/
            simpleQuery.setRows(rows);
            ScoredPage  scoredPage = solrTemplate.queryForPage(simpleQuery, SolrItem.class);
            data.put("rows",scoredPage.getContent());
            data.put("totalPages",scoredPage.getTotalPages());
            data.put("total",scoredPage.getTotalElements());
        }
       return data;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hyhcloud

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值