cnfox

简要分析B2C电商系统为你推荐(推荐算法)模块的原理与实现
前言 "为你推荐"一直是电商平台的重要流量入口。以往在电商平台上,推荐的场景更多的覆盖在交易的各个环节...
扫描右侧二维码阅读全文
06
2019/08

简要分析B2C电商系统为你推荐(推荐算法)模块的原理与实现

前言

 "为你推荐"一直是电商平台的重要流量入口。以往在电商平台上,推荐的场景更多的覆盖在交易的各个环节,比如详情页、购物车、订单及支付等。近年来推荐发展逐渐的多样化,场景上逐渐覆盖到各流量入口,推荐的实体也扩展到活动、类目、运营位等。
 在电商网站里进行商品推荐,可以提高整个网站商品销售的有效转化率,增加商品销量。通过用户已经浏览、收藏、购买的记录,更精准的理解用户需求,对用户进行聚类、打标签,推荐用户感兴趣的商品,帮助用户快速找到需要的商品,适时放大需求,售卖更加多样化的商品。甚至在站外推广时,能够做个性化营销。
本文以初学者的视角简单的分析B2C电商平台的"为你推荐"模块的简单分析和代码实现,旨在帮助初学者串联实训Java Web 里的Cookie、域、Mysql主外键关联等知识点.
 本人能力有限,本文内容浅显且多有疏漏和考虑不周的地方,还请大家批评指正,一起学习交流!

什么是推荐系统

 商品推荐分为常规推荐、个性化推荐。常规推荐是指商家选择一些固定商品放在推荐位,或者基于商品之间的关联性,进行相关的商品推荐。例如:在用户买了奶瓶之后推荐奶粉。个性化推荐指基于用户购物习惯,根据商品特性来进行推荐。例如'看过此商品后的顾客还购买的其他商品'推荐项。
 个性化智能推荐最终的目标就是让一个普通访问电商平台的用户,在进入平台页面时,系统能够根据用户日常的行为偏好和习惯,用户心理想要购买的商品,在还没有发生点击行为时,系统能自动推荐到用户访问的页面。
本文介绍基于商品名称进行模糊查询,进行相关的商品推荐。

准备

 使用到的工具:Eclipse、Mysql、Chrome
 需要提前做好的准备:

  1. 在数据库中建一个商品的分类表和商品信息表;
  2. 准备一个用于获取用户输入和展示推荐商品的jsp文件;
  3. 前端获取用户输入参数的步骤备注在jsp文件中了,不多赘述
#商品分类表
CREATE TABLE category (
        #分类主键
    cid INT PRIMARY KEY AUTO_INCREMENT,
        #分类名称
    cname VARCHAR(30) NOT NULL,
        #分类是否启用
    state INT DEFAULT 1,
        #分类顺序
    order_number INT NOT NULL,
        #分类描述
    description VARCHAR(100) NOT NULL,
        #分类创建时间
    create_time DATETIME
);
#商品信息表
CREATE TABLE `goods`
(        #商品主键
    `gid` INT PRIMARY KEY AUTO_INCREMENT,
         #商品外键
    `cid` INT,
         #商品名称
    `gname` VARCHAR(50) NOT NULL,
         #商品颜色
    `color` VARCHAR(50),
         #商品尺寸
    `size` VARCHAR(50),
         #商品价格
    `price` DOUBLE NOT NULL,
         #商品简介
    `description` VARCHAR(500),
         #商品详情
    `full_description` VARCHAR(1000),
         #商品图片
    `pic` VARCHAR(200),
         #商品是否被启用
    `state` INT DEFAULT 0,
         #商品版本信心
    `version` VARCHAR(50),
         #商品生产日期
    `product_date` DATETIME,
         #关联
    CONSTRAINT fk_goods_category FOREIGN KEY(cid) REFERENCES category(cid)
);

SELECT cname, gname, color FROM category NATURAL JOIN goods;
<html>
  <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <title>www.icnfox.cn推荐算法</title>
  </head>
    <body>
       <!-- 一个搜索框和一个按钮,用于获取记录用户输入的数据 -->
        <form class="logo_right">
            <div class="logo_input"><input type="text" id="search"/></div>
            <a class="logo_right_a" ><img src="https://cnfox.oss-cn- 
            beijing.aliyuncs.com/icnfox/img/blog/find.jpg"></a>
        </form>
    </body>
     <!-- 导入jQuery文件 -->
    <script type="text/javascript" src="https://cnfox.oss-cn- 
    beijing.aliyuncs.com/icnfox/img/blog/jquery-3.3.1.js"></script>
    <script type="text/javascript">
    <!-- a标签触发点击事件 通过id选择器获取到form表单内的value值,进行非空非空判断  -->
    $(".logo_right_a").click(function(){
        var search =$("#search").val();
        if(search==null||search==""){
    <!-- 判断为空 弹窗,聚焦事件提示用户继续输入  -->
            alert("请输入商品名称");
            $("#search").focus();
        }else{
                <!-- Ajax传值  -->
        $.post(
                            
              "index",
                       <!-- data传回func参数,在Servlet中进行操作的分发;传回用户输入search  -->
               {"func":"searchgoods","search":search},
              function(obj){
                        <!--暂时使用弹窗的形式来判断添加Cookie是否成功 -->
               if(obj){
                    alert("添加cookie成功");
                   }
                      },
               "json"
              )
        }
    })
  </script>
</html>

-1 添加Cookie

 我们循序渐进的来,先通过jQuery获取到用户搜索历史之后,通过Ajax传到Servlet进行方法的分发(完全遵循MVC框架模式,感兴趣看下我的上一篇博客).因为我们期间需要多次对Cookie进行添加和查询操作,所以我们封装一个Cookie的工具类,降低代码的耦合度.
 在Utils工具类中完成创建Cookie,设置生命周期添加Cookie的操作,在Servlet类中给Ajax回调一个ture的boolean值,暂时以弹窗的形式判断添加Cookie是否成功.
 首先这样做肯定是不合理的,因为我们添加一个Cookie,如果在进行搜索操作再一次执行添加Cookie的操作,原先的Cookie将被覆盖,这不是我们想要看到的效果,暂且这样,我们继续分析
添加Cookie之前
添加Cookie成功
添加Cookie之后

package cn.icnfox.Servlet;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.icnfox.Utils.CookieUtils;

@WebServlet("/index")
public class IndexInfoServlet extends HttpServlet{

    private static final long serialVersionUID = 1L;
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //
        String func = request.getParameter("func");
        switch (func) {
        case "searchgoods":
            searchgoods(request,response);
            break;
        default:
            break;
        }
    }
    //记录搜索历史的方法
    private void searchgoods(HttpServletRequest request, HttpServletResponse response) throws IOException {
               //获取到搜索历史
        String search = request.getParameter("search");
        //
        CookieUtils.addCookie(search, request, response);
        response.getWriter().print(true);
    }
package cn.icnfox.Utils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//添加Cookie的工具类
public class CookieUtils {

    public static void addCookie(String search,HttpServletRequest request, HttpServletResponse response) {
        
        //创建cookie,并传值
        Cookie cookie = new  Cookie("search", search);
        //设置生命周期
        cookie.setMaxAge(60*60*24);
        response.addCookie(cookie);
        
    }
}

-2 添加获取Cookie2

 我们上面实现的添加Cookie很明显是不合理的,我们设定Cookie中至少要存储3个搜索历史,获取到第二次搜索历史,我们需要先获取第一次的搜索历史Cookie,然后把第二次的拼接在Cookie上进行存储,第三次也是,所以我们要实现一个查询Cookie的功能.
 而且我们还要考虑到,如果获取到的第一次的搜索历史和第二次是一样的,我们就不需要进行Cookie的拼接.,如果第三次以后搜索,我们还需要把第一次的搜索舍弃掉,将第三次以后的历史添加上.
 重新整理下Utils工具类代码,封装获取Cookie代码
 到此为之我们获取Cookie就做完了,下一步就是模糊查询展示数据了.
流程图

package cn.icnfox.Utils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class CookieUtils {

    public static void addCookie(String search,HttpServletRequest request, HttpServletResponse response) {
        
        //创建cookie,并传值
        /*
         * 1. 线获取存储搜索历史的Cookie中的数据
         * 2. 拼接上当前搜索的内容(已经存在不拼接),只存储三天搜索关键字
         * 3. 将存储搜索历史的Cookie响应到浏览器保存
         * */
        
        String sContent = getCookie(request);
        /*
         * 在遍历完Cookie之后,sContent有两种可能
         * 1. 依旧是空字符串 ""
         * 2.存储了搜索的内容
         * 
         * */
        if (sContent.equals("")) {
            sContent=search;
        }else {
            //现有所有历史中不包含新传入的搜索关键字,在进行拼接
            if (!sContent.contains(search)) {
                //判断sContent中是否含有#
                boolean iscontain = sContent.contains("#");
                if (iscontain ) {
                    //按照#分割字符串
                    String[] strs = sContent.split("#");
                    //判断有几个#
                    if (strs.length==3) {
                        //舍弃第一条
                        sContent=strs[1]+"#"+strs[2];
                    }
                }
                sContent=sContent+"#"+search;
            }
        }
        Cookie cookie = new  Cookie("search", sContent);
        //设置生命周期
        cookie.setMaxAge(60*60*24);
        response.addCookie(cookie);
        
    }
    
    //获取Cookie内容
    public static String getCookie(HttpServletRequest request) {
        //先创建一个变量来存储之前存储的搜索历史
        String sContent="";
        Cookie[] cookies = request.getCookies();
        if (!( cookies==null||(cookies.length==0 ))) {
            for (Cookie c : cookies) {
                //获取Cookie中关键字和值的方法
                if (c.getName().equals("search")) {
                    sContent=c.getValue();
                }
            }
        }
        return sContent;
    }
    
}

-3 模糊查询

 获取到Cookie,我们就可以调用Mysql进行数据库的查询了.我们不清楚传入的search有多长,所以需要拆分字符串进行判断,必要的我都备注在代码里了.
 数据库的数据需要自己准备,后期的页面展示由于篇幅过长了,我这边就不展示了,感兴趣的可以从评论区和我要整个项目的源码 (づ ̄3 ̄)づ╭❤~

//模糊查询代码片段
//手机#笔记本#净水器
        /*
         * select * from goods where gname like "%手机%" or gname like "%笔记本%" or gname like "%净水器%";
         * 
         * */
        //需要判断传入search#的个数,要进行字符串的拼接
StringBuilder sBuilder = new StringBuilder("select * from goods where");
        if (!search.contains("#")) {
            //不包含# search=手机
            sBuilder.append(" gname like '%" +search+ "%'" );
        }else {
            String[] strs = search.split("#");
            for (int i = 0; i < strs.length; i++) {
                if (i==0) {
                    sBuilder.append(" gname like '%"+strs[i]+"%' ");
                }else {
                    sBuilder.append("or gname like '%"+strs[i]+"%' ");
                }
            }
        }
        sBuilder.append("limit "+count);
        System.out.println(sBuilder.toString());
        //打印的sql语句 可以复制到mysql中验证下
        //select * from goods where gname like '%手机%' or gname like '%笔记本%' or gname like '%空调%' limit 5
        try {
            goods=qRunner.query(sBuilder.toString(), new BeanListHandler<Goods>(Goods.class));
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return goods;
    }
Last modification:August 12th, 2019 at 05:09 pm
如果觉得我的文章对你有用,请随意赞赏

Leave a Comment