• 3、点评搜索服务&推荐系统 -门店


    点评门店推荐搜索初级


    整理总结一下 ElasticSearch7+Spark 构建高相关性搜索服务&千人千面推荐系统的笔记

    完成一个点评搜索推荐系统,初步实现关键词搜索与多条件筛选,结合LBS地理围栏通过距离控制搜索排序,同时优先将距离与评价好的商户门店推荐给用户。

    门店搜索推荐简介

    关键词搜索与多条件筛选,可检索出满意的商家门户

    门店推荐-猜你喜欢是通过计算用户到门店的距离和门店的评分综合给门店打分后排序输出给用户的。

    推荐效果:

    在这里插入图片描述

    搜索效果:

    在这里插入图片描述

    在这里插入图片描述

    数据库里的门店数据

    在这里插入图片描述

    搜索推荐基础

    搜索推荐基础
    距离计算:

    1、需要接入百度地图sdk通过api获取用户当前经纬度传给服务端

    http://lbs.baidu.com/index.php?title=webapi/guide/webservice-geocoding

    调用百度地图的接口获取当前地址的经纬度

    http://lbs.baidu.com/apiconsole/key#/home

    在这里插入图片描述

    eg:

    GET请求 address=你的地址,ak你的申请注册的key

    在这里插入图片描述

    2、利用球体平面计算公式,在sql语句层面计算出用户和门店之间的距离

    经纬度计算

    3、对应距离数值可集成到门店模型内用于展示或后续的排序

    应用程序集成

    4、通过spring mvc集成推荐的controller,service和dao

    5、完成前端页面闭环

    门店搜索推荐实现

    创建门店表

    CREATE TABLE `dianpingdb`.`shop`  (
      `id` int(0) NOT NULL AUTO_INCREMENT,
      `created_at` datetime(0) NOT NULL DEFAULT '0000-00-00 00:00:00',
      `updated_at` datetime(0) NOT NULL DEFAULT '0000-00-00 00:00:00',
      `name` varchar(80) NOT NULL DEFAULT '',
      `remark_score` decimal(2, 1) NOT NULL DEFAULT 0,
      `price_per_man` int(0) NOT NULL DEFAULT 0,
      `latitude` decimal(10, 6) NOT NULL DEFAULT 0,
      `longitude` decimal(10, 6) NOT NULL DEFAULT 0,
      `category_id` int(0) NOT NULL DEFAULT 0,
      `tags` varchar(2000) NOT NULL DEFAULT '',
      `start_time` varchar(200) NOT NULL DEFAULT '',
      `end_time` varchar(200) NOT NULL DEFAULT '',
      `address` varchar(200) NOT NULL DEFAULT '',
      `seller_id` int(0) NOT NULL DEFAULT 0,
      `icon_url` varchar(100) NOT NULL DEFAULT '',
      PRIMARY KEY (`id`)
    );
    

    id门店的编号,created_at创建时间 updated_at更新时间

    name店名 remark_score门店的评价 price_per_man平均每个人消费钱

    latitude、longitude店的位置经纬度,category_id门店属于那种分类,

    seller_id属于那个商家公司的,address门店地址,icon_url商家图片地址。

    在项目的src/main/java目录下,创建一个com.awen.dianping.service包,并在包中创建接口ShopService,接口中定义一系列方法,代码如下所示。

    interface ShopService.java

    public interface ShopService {
        ShopModel create(ShopModel shopModel) throws BusinessException;
        ShopModel get(Integer id);
        List<ShopModel> selectAll();
        List<ShopModel> recommend(BigDecimal longitude,BigDecimal latitude);
    
        List<Map<String,Object>> searchGroupByTags(String keyword,Integer categoryId,String tags);
    
        Integer countAllShop();
    
        List<ShopModel> search(BigDecimal longitude,BigDecimal latitude,
                               String keyword,Integer orderby,Integer categoryId,String tags);
    }
    

    在com.awen.dianping.service.impl包下,创建ShopService接口的实现类ShopServiceImpl,该类需要实现接口中的所有方法,代码如下所示。

    @Service
    public class ShopServiceImpl implements ShopService{
    
        @Autowired
        private ShopModelMapper shopModelMapper;
    
        @Autowired
        private CategoryService categoryService;
    
        @Autowired
        private SellerService sellerService;
    
        @Override
        @Transactional
        public ShopModel create(ShopModel shopModel) throws BusinessException {
            shopModel.setCreatedAt(new Date());
            shopModel.setUpdatedAt(new Date());
    
            //校验商家是否存在正确
            SellerModel sellerModel = sellerService.get(shopModel.getSellerId());
            if(sellerModel == null){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"商户不存在");
            }
    
            if(sellerModel.getDisabledFlag().intValue() == 1){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"商户已禁用");
            }
    
            //校验类目
            CategoryModel categoryModel = categoryService.get(shopModel.getCategoryId());
            if(categoryModel == null){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"类目不存在");
            }
            shopModelMapper.insertSelective(shopModel);
    
            return get(shopModel.getId());
        }
    
        @Override
        public ShopModel get(Integer id) {
            ShopModel shopModel = shopModelMapper.selectByPrimaryKey(id);
            if(shopModel == null){
                return null;
            }
            shopModel.setSellerModel(sellerService.get(shopModel.getSellerId()));
            shopModel.setCategoryModel(categoryService.get(shopModel.getCategoryId()));
            return shopModel;
        }
    
        @Override
        public List<ShopModel> selectAll() {
            List<ShopModel> shopModelList = shopModelMapper.selectAll();
            shopModelList.forEach(shopModel -> {
                shopModel.setSellerModel(sellerService.get(shopModel.getSellerId()));
                shopModel.setCategoryModel(categoryService.get(shopModel.getCategoryId()));
            });
            return shopModelList;
        }
    
        @Override
        public List<ShopModel> recommend(BigDecimal longitude, BigDecimal latitude) {
            List<ShopModel> shopModelList = shopModelMapper.recommend(longitude, latitude);
            shopModelList.forEach(shopModel -> {
                shopModel.setSellerModel(sellerService.get(shopModel.getSellerId()));
                shopModel.setCategoryModel(categoryService.get(shopModel.getCategoryId()));
            });
            return shopModelList;
        }
    
        @Override
        public List<Map<String, Object>> searchGroupByTags(String keyword, Integer categoryId, String tags) {
            return shopModelMapper.searchGroupByTags(keyword,categoryId,tags);
        }
    
        @Override
        public Integer countAllShop() {
            return shopModelMapper.countAllShop();
        }
    
        @Override
        public List<ShopModel> search(BigDecimal longitude,
                                      BigDecimal latitude, String keyword,Integer orderby,
                                      Integer categoryId,String tags) {
            List<ShopModel> shopModelList = shopModelMapper.search(longitude,latitude,keyword,orderby,categoryId,tags);
            shopModelList.forEach(shopModel -> {
                shopModel.setSellerModel(sellerService.get(shopModel.getSellerId()));
                shopModel.setCategoryModel(categoryService.get(shopModel.getCategoryId()));
            });
            return shopModelList;
        }
    }
    
    

    interface ShopModelMapper.java 定义方法

    public interface ShopModelMapper {
        
        int deleteByPrimaryKey(Integer id);
    
        Integer countAllShop();
    
       
        int insert(ShopModel record);
    
        
        int insertSelective(ShopModel record);
    
        
        ShopModel selectByPrimaryKey(Integer id);
    
        List<ShopModel> selectAll();
    
       
        int updateByPrimaryKeySelective(ShopModel record);
    
        int updateByPrimaryKey(ShopModel record);
        List<ShopModel> recommend(@Param("longitude") BigDecimal longitude, @Param("latitude") BigDecimal latitude);
        List<ShopModel> search(@Param("longitude") BigDecimal longitude,
                               @Param("latitude") BigDecimal latitude,
                               @Param("keyword")String keyword,
                               @Param("orderby")Integer orderby,
                               @Param("categoryId")Integer categoryId,
                               @Param("tags")String tags);
    
        List<Map<String,Object>> searchGroupByTags(@Param("keyword")String keyword,
                                                   @Param("categoryId")Integer categoryId,
                                                   @Param("tags")String tags);
    
    }
    

    在src/main/resources/mapping,创建ShopModelMapper.xml

    写一系列SQL。

    在com.awen.dianping.controller包下,创建ShopController,代码如下所示。

    ShopController.java

    @Controller("/shop")
    @RequestMapping("/shop")
    public class ShopController {
    
        @Autowired
        private ShopService shopService;
    
        @Autowired
        private CategoryService categoryService;
    
        //推荐服务V1.0
        @RequestMapping("/recommend")
        @ResponseBody
        public CommonRes recommend(@RequestParam(name="longitude")BigDecimal longitude,
                                   @RequestParam(name="latitude")BigDecimal latitude) throws BusinessException {
            if(longitude == null || latitude == null){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
            }
    
            List<ShopModel> shopModelList = shopService.recommend(longitude,latitude);
            return CommonRes.create(shopModelList);
        }
    
    
        //搜索服务V1.0
        @RequestMapping("/search")
        @ResponseBody
        public CommonRes search(@RequestParam(name="longitude")BigDecimal longitude,
                                @RequestParam(name="latitude")BigDecimal latitude,
                                @RequestParam(name="keyword")String keyword,
                                @RequestParam(name="orderby",required = false)Integer orderby,
                                @RequestParam(name="categoryId",required = false)Integer categoryId,
                                @RequestParam(name="tags",required = false)String tags) throws BusinessException {
            if(StringUtils.isEmpty(keyword) || longitude == null || latitude == null){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
            }
    
            List<ShopModel> shopModelList = shopService.search(longitude,latitude,keyword,orderby,categoryId,tags);
            List<CategoryModel> categoryModelList = categoryService.selectAll();
            List<Map<String,Object>> tagsAggregation = shopService.searchGroupByTags(keyword,categoryId,tags);
            Map<String,Object> resMap = new HashMap<>();
            resMap.put("shop",shopModelList);
            resMap.put("category",categoryModelList);
            resMap.put("tags",tagsAggregation);
            return CommonRes.create(resMap);
        }
    }
    
    

    等等 代码 Github:https://github.com/liuawen/dianping

    运行执行

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    小结

    门店推荐

    通过数据库的线性计算公式给门店打分后排序输出给用户,线性计算公式,人的理解和实际计算操作过程有误差,线性计算公式没有办法很好的满足实际的需求。

    这个推荐只是简单计算了距离和门店评分,没有考虑人的特点喜欢什么经常浏览什么,没有办法做到个性化的推荐。

    门店搜索

    通过数据库的关键词模糊匹配的方式结合线性计算公式给门店打分后排序输出给用户
    关键词模糊匹配,最基础的文本匹配方式,没办法考虑分词,中文特性,用户深层次需求等,连最基本的分词模糊匹配都无法完成。

    和推荐一样的线性排序计算公式,人的理解和实际计算操作过程有误差,线性计算公式没有办法很好的满足实际的需求

    数据库做搜索性能很差,性能非常非常差的。

    最终要实现

    搜索推荐

    • 快速根据用户关键词搜索给出满意的结果
    • 结合用户的历史行为、兴趣偏好等推荐给用户想到的服务商品以提升浏览点击率和交易转化率。

    搜索服务必备

    • 完整丰富的待搜索数据源

    • 高效且稳定的搜索引擎

    • 相关性语义的智能理解

    推荐系统必备

    • 完整丰富的训练及测试数据集

    • 个性化千人千面的召回及排序机器学习算法

    • 可解释且有实际意义的评价指标

    在这里插入图片描述

    后续改善

  • 相关阅读:
    读<<关于程序员的技能>> 惭愧,希望有人能进来解惑! 人工智能
    我也来说说多核 人工智能
    有感于.net社区挺唐骏的作假的文章,诚实是美德,你们可以做假发财成功,但请不要批评,嘲笑他人诚实贫穷 人工智能
    几个程序员也难已回答的问题 人工智能
    Flash已死,有事烧纸! 人工智能
    关于Windows7的用户体验,我们是属于跟不上时代的古董,还是那个敢说出皇帝新衣的男孩? 人工智能
    企鹅帝国的疯狂反扑! 人工智能
    微软IE9预览版全面(100%)支持HTML5和CSS3,到学习使用Html5的时候了 人工智能
    Flash已死终成现实,Adobe亲手埋葬Flash,并公开支持Html5 人工智能
    4个月,你就是企业需要的程序员人才,听蒋涛(CSDN)在线说“黑马” 人工智能
  • 原文地址:https://www.cnblogs.com/liuawen/p/12854018.html
Copyright © 2020-2023  润新知