品优购项目记录:day10

今日目标:

(1)实现品优购搜索结果高亮显示功能

(2)理解品优购搜索的业务规则和实现思路

(3)完成查询分类列表的功能

(4)完成缓存品牌和规格数据的功能

(5)完成显示品牌和规格数据的功能

(6)完成过滤条件构建的功能

(7)完成过滤查询

 

目录

1、搜索结果高亮显示

1.1 后端(search-service)

1.2 前端(search-web)

2、搜索业务规则分析

2.1 需求分析

2.2 实现思路

3、查询分类列表

3.1 服务层实现(search-service)

3.2 前端

4、缓存规格和品牌数据

4.1 需求分析

4.2 将分类数据放入缓存

4.2 缓存规格和品牌数据

4.3 为什么放在findByParentId和findPage中

5、展示品牌和规格数据

5.1 后端

5.2 前端

6、过滤条件构建

6.1 需求分析

6.2 新增搜索条件到searchMap中

6.3 撤销搜索条件

7、过滤查询

7.1 服务层实现(search-service)


 

 

1、搜索结果高亮显示

1.1 后端(search-service)

(1)修改SearchServiceImpl中的search方法

    @Override
    public Map search(Map searchMap) {
        // 构造高亮查询对象
        HighlightQuery query = new SimpleHighlightQuery();
        // 创建高亮查询设置对象,并设置要高亮显示的域
        HighlightOptions highlightOptions = new HighlightOptions();
        /*
         * 设置高亮相关属性
         *          addField:设置需要高亮显示的域,可以设置多个,获取高亮数据时通过下标获取
         *          setSimplePrefix:设置高亮前缀
         *          setSimplePostfix:设置高亮后缀
         */
        highlightOptions.addField("item_title");
        highlightOptions.setSimplePrefix("<em style='color:red'>");
        highlightOptions.setSimplePostfix("</em>");
        // 将高亮设置到查询对象中
        query.setHighlightOptions(highlightOptions);
        //按照关键字查询
        Criteria criteria = new Criteria("item_keywords").is(searchMap.get("keywords"));
        query.addCriteria(criteria);

        // 执行查询,注意:这里面的结果集中的getContent返回的结果集中,并没有带上高亮,需要自行处理
        HighlightPage<TbItem> pageInfo = solrTemplate.queryForHighlightPage(query, TbItem.class);
        // 从结果集中取出数据,最先遍历高亮结果入口集
        for (HighlightEntry<TbItem> entry : pageInfo.getHighlighted()) {
            // 健壮性判断
            if (entry.getHighlights().size() > 0 && entry.getHighlights().get(0).getSnipplets().size() > 0) {
                // 通过高亮域下标从高亮结果入口集合中再获取高亮数据
                String title = entry.getHighlights().get(0).getSnipplets().get(0);
                // 从entry中获取当前遍历到的item,并将高亮数据设置到标题
                TbItem item = entry.getEntity();
                item.setTitle(title);
            }
        }

        // 返回结果
        Map resultMap = new HashMap();
        resultMap.put("rows", pageInfo.getContent());

        return resultMap;
    }

(2)效果

原因:因为angular js的安全机制,为了防止html攻击,所以我们还需要修改前端,如果是传统的jsp的话,这里其实就已经完成了页面高亮显示了。

 

1.2 前端(search-web)

(1)解决html原样输出

我们测试后发现高亮显示的html代码原样输出,这是angularJS为了防止html攻击采取的安全机制。我们如何在页面上显示html的结果呢?我们会用到$sce服务的trustAsHtml方法来实现转换。

 

(2)修改base.js,加入过滤器,并注入$sce服务

// 自定义模块
var app = angular.module('pinyougou', []);

// 定义过滤器
app.filter('trustHtml',['$sce',function ($sce) {
    // data为要过滤的内容
    return function (data) {
        // 使用$sce的trustAsHtml方法信任该html内容
        return $sce.trustAsHtml(data);
    }
    
}]);

 

(3)页面修改绑定变量

 

(4)效果

 

 

2、搜索业务规则分析

2.1 需求分析

我们今天要完成的目标是在关键字搜索的基础上添加面板搜索功能。

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

业务规则:

  1. 当用户输入关键字搜索后,除了显示列表结果外,还应该显示通过这个关键字搜索到的记录都有哪些商品分类。
  2. 根据第一个商品分类查询对应的模板,根据模板查询出品牌列表
  3. 根据第一个商品分类查询对应的模板,根据模板查询出规格列表
  4. 当用户点击搜索面板的商品分类时,显示按照这个关键字查询结果的基础上,筛选此分类的结果。
  5. 当用户点击搜索面板的品牌时,显示在以上结果的基础上,筛选此品牌的结果
  6. 当用户点击搜索面板的规格时,显示在以上结果的基础上,筛选此规格的结果
  7. 当用户点击价格区间时,显示在以上结果的基础上,按价格进行筛选的结果
  8. 当用户点击搜索面板的相应条件时,隐藏已点击的条件。

 

2.2 实现思路

  1. 搜索面板的商品分类需要使用Spring Data Solr的分组查询来实现
  2. 为了能够提高查询速度,我们需要把查询面板的品牌、规格数据提前放入redis
  3. 查询条件的构建、面板的隐藏需要使用angularJS来实现
  4. 后端的分类、品牌、规格、价格区间查询需要使用过滤查询来实现

 

 

3、查询分类列表

3.1 服务层实现(search-service)

(1)在ItemSearchServiceImpl新增方法

    /**
     * 查询分类列表
     *
     * @param searchMap 查询条件
     * @return java.util.List<java.lang.String>
     */
    private List<String> searchCategoryList(Map searchMap) {
        // 创建查询对象
        Query query = new SimpleQuery("*:*");
        // 按item_keywords查询
        Criteria criteria = new Criteria("item_keywords").is(searchMap.get("keywords"));
        query.addCriteria(criteria);
        /*
         * 创建分组选项对象
         *          addGroupByField:设置分组的域
         */
        GroupOptions groupOptions = new GroupOptions();
        groupOptions.addGroupByField("item_category");
        // 将分组选项设置到查询对象中
        query.setGroupOptions(groupOptions);
        // 执行查询
        GroupPage<TbItem> pageInfo = solrTemplate.queryForGroupPage(query, TbItem.class);

        // 从结果集中获取数据,参数为分组域中的一个
        GroupResult<TbItem> groupResult = pageInfo.getGroupResult("item_category");
        // 获取分组页对象
        Page<GroupEntry<TbItem>> groupEntries = groupResult.getGroupEntries();
        // 从分组页对象中获取分组入口集合
        List<GroupEntry<TbItem>> content = groupEntries.getContent();
        // 遍历集合获取分组数据,并设置到返回结果集合中
        List<String> resultList = new ArrayList<>();
        for (GroupEntry<TbItem> entry : content) {
            resultList.add(entry.getGroupValue());
        }

        return resultList;
    }

(2)在search方法中调用

 

3.2 前端

(1)在页面的分类显示处绑定变量

 

(2)效果

 

 

4、缓存规格和品牌数据

 

4.1 需求分析

将商品分类数据、品牌数据、和规格数据都放入Redis存储。

  1. 当用户进入运营商后台的商品分类页面时,将商品分类数据放入缓存(Hash)。以分类名称作为key ,以模板ID作为值
  2. 当用户进入运营商后台的模板管理页面时,分别将品牌数据和规格数据放入缓存(Hash)。以模板ID作为key,以品牌列表和规格列表作为值。

 

4.2 将分类数据放入缓存

在ItemCatServiceImpl中添加私有方法(sellergoods-service),并在findByParentId中执行该方法

    /**
     * 将分类列表放入缓存
     */
    private void saveToRedis() {
        // 查询全部分类
        List<TbItemCat> itemCatList = findAll();
        // 遍历列表
        for (TbItemCat itemCat : itemCatList) {
            // 放入缓存,大键:itemCat   小键:分类名称
            redisTemplate.boundHashOps("itemCat").put(itemCat.getName(), itemCat.getTypeId());
        }
    }

4.2 缓存规格和品牌数据

在TypeTemplateServiceImpl中新增方法(sellergoods-service),在findPage中执行

    /**
     * 将品牌和规格数据放入缓存
     */
    private void saveToRedis() {
        // 查询全部的模板数据
        List<TbTypeTemplate> typeTemplateList = findAll();
        // 遍历
        for (TbTypeTemplate template : typeTemplateList) {
            // 获取其品牌数据, 并放入缓存
            List<Map> brandList = JSON.parseArray(template.getBrandIds(), Map.class);
            redisTemplate.boundHashOps("brandList").put(template.getId(), brandList);

            // 获取其规格数据, 并放入缓存
            List<Map> specList = findSpecList(template.getId());
            redisTemplate.boundHashOps("specList").put(template.getId(), specList);
        }
    }

 

 

4.3 为什么放在findByParentId和findPage中

(1)首先我们为什么要将这些数据放入缓存?因为我们在前端的搜索页面会用到这些数据,而且前端访问的压力是很大的,所以我们需要将这些数据放入缓存,减轻数据库的压力

(2)为什么放在这两个方法中?这两个方法是在运营商后台调用的,访问压力相较与前端来说,访问的频率要小得多;再一个就是关于更新缓存的问题,在增删改的时候,我们都需要对缓存进行更新。按一般的思路,我们可以将更新缓存的操作放入增删改方法中,这我们放入这两个方法分原因是,每当我们进行了增删改查操作过后,我们都会调用这两个方法,重新加载数据到运营商后台中,意思就是每次数据改动这个方法都会被执行,所以我们将方法放在这两个方法中,只要数据更新,缓存就会被刷新,这样就减少一定的代码书写,当然也可以按照正常的把更新缓存操作放入增删改方法中。

5、展示品牌和规格数据

5.1 后端

(1)服务层实现(search-service),在ItemSearchServiceImpl中新增方法

    /**
     * 通过分类名称获取品牌和规格列表
     * 
     * @param category 分类名称
     * @return java.util.Map 
     */
    private Map<String, Object> searchBrandAndSpec(String category) {
        // 使用redis查询分类id
        Long categoryId = (Long) redisTemplate.boundHashOps("itemCat").get(category);

        // 通过分类id从redis中查询品牌列表和规格列表
        List brandList = (List) redisTemplate.boundHashOps("brandList").get(category);
        List specList = (List) redisTemplate.boundHashOps("specList").get(category);

        // 将数据放入返回结果中
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("brandList",brandList);
        resultMap.put("specList",specList);

        return resultMap;
    }

(2)在search方法中进行调用,并将返回的数据保存到返回结果集中

5.2 前端

(1)页面绑定变量

(2)效果

6、过滤条件构建

6.1 需求分析

点击搜索面板上的分类、品牌和规格,实现查询条件的构建。查询条件以面包屑的形式显示。

当面包屑显示分类、品牌和规格时,要同时隐藏搜索面板对应的区域。

用户可以点击面包屑上的X 撤销查询条件。撤销后显示搜索面包相应的区域。

6.2 新增搜索条件到searchMap中

(1)在searchController.js中定义初始化查询条件变量

    // 初始化搜索对象,包括分类名称、品牌、规格选项
    $scope.searchMap = {'keywords':'','category':'','brand':'','spec':{}};

(2)在searchController.js中新增方法

    // 添加搜索选项
    $scope.addSearchOptions = function (key, value) {
        // 判断key值
        if (key == 'category' || key == 'brand') {// 分类名称和品牌
            // 将查询条件值放入对应的key
            $scope.searchMap[key] = value;
        } else {// 规格选项
            $scope.searchMap.spec[key] = value;
        }
    }

(3)修改页面,绑定单击事件

(4)绑定面包屑导航栏的变量

(5)效果

6.3 撤销搜索条件

(1)在searchController.js中新增方法

    // 移除搜索选项
    $scope.removeSearchOptions = function (key) {
        // 判断key值
        if (key == 'category' || key == 'brand') {// 分类名称和品牌
            // 设置为空字符串
            $scope.searchMap[key] = '';
        } else {// 规格选项
            // 使用delete关键字移除该key
            delete $scope.searchMap.spec[key];
        }
    }

(2)页面绑定单击事件

(3)在执行添加查询条件和移除查询条件的方法中,调用搜索方法

7、过滤查询

7.1 服务层实现(search-service)

(1)新增构建过滤查询条件的方法

    /**
     * 根据搜索条件集设置查询过滤条件,并将查询过滤条件设置给查询对象
     *
     * @param searchMap 搜索条件集
     * @param query     查询对象
     * @return org.springframework.data.solr.core.query.Query
     */
    private void setFilterOptions(Map searchMap, Query query) {
        // 设置分类过滤查询
        if (!"".equals(searchMap.get("category"))) {// 该条件不为空串
            // 创建查询过滤对象
            FilterQuery filterQuery = new SimpleFilterQuery();
            // 设置过滤域,并设置条件
            Criteria criteria = new Criteria("item_category").is(searchMap.get("category"));
            // 将条件设置会查询过滤对象
            filterQuery.addCriteria(criteria);
            // 设置到查询对象中
            query.addFilterQuery(filterQuery);
        }

        // 设置品牌过滤查询
        if (!"".equals(searchMap.get("brand"))) {// 该条件不为空串
            // 创建查询过滤对象
            FilterQuery filterQuery = new SimpleFilterQuery();
            // 设置过滤域,并设置条件
            Criteria criteria = new Criteria("item_brand").is(searchMap.get("brand"));
            // 将条件设置会查询过滤对象
            filterQuery.addCriteria(criteria);
            // 设置到查询对象中
            query.addFilterQuery(filterQuery);
        }

        // 设置规格过滤查询
        if (searchMap.get("spec") != null) {// 该条件不为空
            // 设置过滤域,并设置条件
            Map<String, String> map = (Map<String, String>) searchMap.get("spec");
            for (String key : map.keySet()) {
                Criteria criteria = new Criteria("item_spec_" + key).is(map.get(key));
                // 通过过滤条件创建查询过滤对象
                FilterQuery filterQuery = new SimpleFilterQuery(criteria);
                // 设置到查询对象中
                query.addFilterQuery(filterQuery);
            }

        }
    }

(2)选择商品分类的时候,动态加载品牌和规格选项,修改服务层实现的search方法

 

猜你喜欢

转载自blog.csdn.net/qq1031893936/article/details/81100879