电商推荐系统三:创建项目并初始化业务数据

三、创建项目并初始化业务数据


我们的项目主体用Scala编写,采用IDEA作为开发环境进行项目编写,采用maven作为项目构建和管理工具。

3.1 在IDEA中创建maven项目

打开IDEA,创建一个maven项目,命名为ECommerceRecommendSystem。为了方便后期的联调,我们会把业务系统的代码也添加进来,所以我们可以以ECommerceRecommendSystem作为父项目,并在其下建一个名为recommender的子项目,然后再在下面搭建多个子项目用于提供不同的推荐服务。

3.1.1 项目框架搭建

在ECommerceRecommendSystem下新建一个 maven module作为子项目,命名为recommender。同样的,再以recommender为父项目,新建一个maven module作为子项目。我们的第一步是初始化业务数据,所以子项目命名为 DataLoader。

父项目只是为了规范化项目结构,方便依赖管理,本身是不需要代码实现的,所以ECommerceRecommendSystem和recommender下的src文件夹都可以删掉。

目前的整体项目框架如下:
在这里插入图片描述

3.1.2 声明项目中工具的版本信息

我们整个项目需要用到多个工具,它们的不同版本可能会对程序运行造成影响,所以应该在最外层的ECommerceRecommendSystem中声明所有子项目共用的版本信息。

在pom.xml中加入以下配置:ECommerceRecommendSystem/pom.xml

<properties>
    <log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.22</slf4j.version>
<mongodb-spark.version>2.0.0</mongodb-spark.version>
<casbah.version>3.1.1</casbah.version>
<redis.version>2.9.0</redis.version>
<kafka.version>0.10.2.1</kafka.version>
<spark.version>2.1.1</spark.version>
<scala.version>2.11.8</scala.version>
<jblas.version>1.2.1</jblas.version>
</properties>

3.1.3 添加项目依赖

首先,对于整个项目而言,应该有同样的日志管理,我们在ECommerceRecommendSystem中引入公有依赖:

ECommerceRecommendSystem/pom.xml

<dependencies>
    <!-- 引入共同的日志管理工具 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
    </dependency>
</dependencies>

同样,对于maven项目的构建,可以引入公有的插件:

<build>
    <!--声明并引入子项目共有的插件-->
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.6.1</version>
            <!--所有的编译用JDK1.8-->
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
    <pluginManagement>
        <plugins>
            <!--maven的打包插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!--该插件用于将scala代码编译成class文件-->
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <!--绑定到maven的编译阶段-->
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

然后,在recommender模块中,我们可以为所有的推荐模块声明spark相关依赖(这里的dependencyManagement表示仅声明相关信息,子项目如果依赖需要自行引入):

ECommerceRecommendSystem/recommender/pom.xml

<dependencyManagement>
    <dependencies>
        <!-- 引入Spark相关的Jar包 -->
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-mllib_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-graphx_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
<dependency>
            <groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
    </dependencies>
</dependencyManagement>

由于各推荐模块都是scala代码,还应该引入scala-maven-plugin插件,用于scala程序的编译。因为插件已经在父项目中声明,所以这里不需要再声明版本和具体配置:

<build>
    <plugins>
        <!-- 父项目已声明该plugin,子项目在引入的时候,不用声明版本和已经声明的配置 -->
        <plugin>
            <groupId>net.alchim31.maven</groupId>
            <artifactId>scala-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

对于具体的DataLoader子项目,需要spark相关组件,还需要mongodb的相关依赖,我们在pom.xml文件中引入所有依赖(在父项目中已声明的不需要再加详细信息):
ECommerceRecommendSystem/recommender/DataLoader/pom.xml

<dependencies>
    <!-- Spark的依赖引入 -->
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_2.11</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-sql_2.11</artifactId>
    </dependency>
    <!-- 引入Scala -->
    <dependency>
        <groupId>org.scala-lang</groupId>
        <artifactId>scala-library</artifactId>
    </dependency>
    <!-- 加入MongoDB的驱动 -->
    <dependency>
        <groupId>org.mongodb</groupId>
        <artifactId>casbah-core_2.11</artifactId>
        <version>${casbah.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mongodb.spark</groupId>
        <artifactId>mongo-spark-connector_2.11</artifactId>
        <version>${mongodb-spark.version}</version>
    </dependency>
</dependencies>

至此,我们做数据加载需要的依赖都已配置好,可以开始写代码了。

3.2 数据加载准备

在src/main/目录下,可以看到已有的默认源文件目录是java,我们可以将其改名为scala。将数据文products.csv,ratings.csv复制到资源文件目录src/main/resources下,我们将从这里读取数据并加载到mongodb中。

3.2.1 Products数据集

数据格式:

productId,name,categoryIds, amazonId, imageUrl, categories, tags

例如:

3982^Fuhlen 富勒 M8眩光舞者时尚节能无线鼠标(草绿)(眩光.悦动.时尚炫舞鼠标 12个月免换电池 高精度光学寻迹引擎 超细微接收器10米
传输距离)^1057,439,736^B009EJN4T2^https://images-cn-4.ssl-images-amazon.com/images/I/31QPvUDNavL._SY300_QL70
_.jpg^外设产品|鼠标|电脑/办公^富勒|鼠标|电子产品|好用|外观漂亮

Product数据集有7个字段,每个字段之间通过“^”符号进行分割。其中的categoryIds、amazonId对于内容特征没有实质帮助,我们只需要其它5个字段:

字段名字段类型字段描述字段备注
productIdInt商品ID
nameString商品名称
categoriesString商品分类每一项用“|”分割
imageUrlString商品图片URL
tagsString商品UGC标签每一项用“|”分割

3.2.2 Ratings数据集

数据格式:

userId,prudcutId,rating,timestamp

例如:

4867,457976,5.0,1395676800

Rating数据集有4个字段,每个字段之间通过“,”分割。

字段名字段类型字段描述字段备注
userIdInt用户ID
produtIdInt商品ID
scoreDouble评分值
timestampLong评分的时间

3.2.3 日志管理配置文件

log4j对日志的管理,需要通过配置文件来生效。在src/main/resources下新建配置文件log4j.properties,写入以下内容:

log4j.rootLogger=info, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS}  %5p --- [%50t]  %-80c(line:%5L
)  :  %m%n

3.3 数据初始化到MongoDB

3.3.1 启动MongoDB数据库(略)

3.3.2 数据加载程序主体实现

我们会为原始数据定义几个样例类,通过SparkContext的textFile方法从文件中读取数据,并转换成DataFrame,再利用Spark SQL提供的write方法进行数据的分布式插入。

在DataLoader/src/main/scala下新建package,命名为com.atguigu.recommender,新建名为DataLoader的scala class文件。

程序主体代码如下:

DataLoader/src/main/scala/com.recom.recommerder/DataLoader.scala

// 定义样例类
case class Product(productId: Int, name: String, imageUrl: String, categories: String, 
tags: String)
case class Rating(userId: Int, productId: Int, score: Double, timestamp: Int)

case class MongoConfig(uri:String, db:String)

object DataLoader {
  // 以window下为例,需替换成自己的路径,linux下为 /YOUR_PATH/resources/products.csv
  val PRODUCT_DATA_PATH = " YOUR_PATH\\resources\\products.csv"
  val RATING_DATA_PATH = " YOUR_PATH\\resources\\ratings.csv"

  val MONGODB_PRODUCT_COLLECTION = "Product"
  val MONGODB_RATING_COLLECTION = "Rating"
 
// 主程序的入口
  def main(args: Array[String]): Unit = {
    // 定义用到的配置参数
    val config = Map(
      "spark.cores" -> "local[*]",
      "mongo.uri" -> "mongodb://localhost:27017/recommender",
      "mongo.db" -> "recommender"
    )
    // 创建一个SparkConf配置
    val sparkConf = new
SparkConf().setAppName("DataLoader").setMaster(config("spark.cores"))
    // 创建一个SparkSession 
    val spark = SparkSession.builder().config(sparkConf).getOrCreate()

    // 在对DataFrame和Dataset进行操作许多操作都需要这个包进行支持
    import spark.implicits._

    // 将Product、Rating数据集加载进来
    val productRDD = spark.sparkContext.textFile(PRODUCT_DATA_PATH)
      //将ProdcutRDD装换为DataFrame
    val productDF = productRDD.map(item =>{
        val attr = item.split("\\^")
Product(attr(0).toInt,attr(1).trim,attr(4).trim,attr(5).trim,attr(6).trim)
    }).toDF()

    val ratingRDD = spark.sparkContext.textFile(RATING_DATA_PATH)
      //将ratingRDD转换为DataFrame
    val ratingDF = ratingRDD.map(item => {
        val attr = item.split(",")
Rating(attr(0).toInt,attr(1).toInt,attr(2).toDouble,attr(3).toInt)
    }).toDF()

    // 声明一个隐式的配置对象
    implicit val mongoConfig =
	MongoConfig(config.get("mongo.uri").get,config.get("mongo.db").get)
    // 将数据保存到MongoDB中
    storeDataInMongoDB(productDF, ratingDF)

    // 关闭Spark
    spark.stop()
  }

3.3.3 将数据写入MongoDB

接下来,实现storeDataInMongo方法,将数据写入mongodb中:

def storeDataInMongoDB(productDF: DataFrame, ratingDF:DataFrame)
	(implicit mongoConfig: MongoConfig): Unit = {

  //新建一个到MongoDB的连接
  val mongoClient = MongoClient(MongoClientURI(mongoConfig.uri))

  // 定义通过MongoDB客户端拿到的表操作对象
  val productCollection = mongoClient(mongoConfig.db)(MONGODB_PRODUCT_COLLECTION)
val ratingCollection = mongoClient(mongoConfig.db)(MONGODB_RATING_COLLECTION)

  //如果MongoDB中有对应的数据库,那么应该删除
  productCollection.dropCollection()
  ratingCollection.dropCollection()

  //将当前数据写入到MongoDB
  productDF
    .write
    .option("uri",mongoConfig.uri)
    .option("collection",MONGODB_PRODUCT_COLLECTION)
    .mode("overwrite")
    .format("com.mongodb.spark.sql")
    .save()
  ratingDF
    .write
    .option("uri",mongoConfig.uri)
    .option("collection",MONGODB_RATING_COLLECTION)
    .mode("overwrite")
    .format("com.mongodb.spark.sql")
    .save()

  //对数据表建索引
productCollection.createIndex(MongoDBObject("productId" -> 1))
ratingCollection.createIndex(MongoDBObject("userId" -> 1))
ratingCollection.createIndex(MongoDBObject("productId" -> 1))

  //关闭MongoDB的连接
  mongoClient.close()
}

3.4 附件:完整代码

package com.recom.dataload

import com.mongodb.casbah.commons.MongoDBObject
import com.mongodb.casbah.{MongoClient, MongoClientURI, MongoCollection}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SparkSession}


/**
 * Product数据集
 * 3982                            商品ID
 * Fuhlen 富勒 M8眩光舞者时尚节能    商品名称
 * 1057,439,736                    商品分类ID,不需要
 * B009EJN4T2                      亚马逊ID,不需要
 * https://images-cn-4.ssl-image   商品的图片URL
 * 外设产品|鼠标|电脑/办公           商品分类
 * 富勒|鼠标|电子产品|好用|外观漂亮   商品UGC标签
 */
case class Product(productId:Int,name:String,imageUrl:String,categories:String,tags:String)


/**
 * Rating数据集
 * 4867        用户ID
 * 457976      商品ID
 * 5.0         评分
 * 1395676800  时间戳
 */
case class Rating(userId:Int,productId:Int,score:Double,timestamp:Int)

/**
 * MongoDB连接配置
 * @param uri    MongoDB的连接uri
 * @param db     要操作的db
 */
case class MongoConfig(uri:String,db:String)

object DataLoader {

  //定义数据文件路径
  val PRODUCT_DATA_PATH="D:\\IdeaProject\\gmallRecommend\\recommender\\DataLoader\\src\\main\\resources\\products.csv"
  val RATING_DATA_PATH="D:\\IdeaProject\\gmallRecommend\\recommender\\DataLoader\\src\\main\\resources\\ratings.csv"
  
  //定义monogobd中存储的表名
  val MONGODB_PRODUCT_COLLECTION="Product"
  val MONGODB_RATING_COLLECTION = "Rating"
  
  
  def main(args: Array[String]): Unit = {
    
    //定义基础配置的集合(可以放入配置文件,通过方法获取属性的值)
    val config = Map(
      "spark.cores"->"local[*]",
      "mongo.uri"->"mongodb://hadoop102:27017/recommender",
      "mongo.db"->"recommender"
    )
    
    //创建一个spark config
    val sparkConf = new SparkConf().setMaster(config("spark.cores")).setAppName("DataLoader")
    //创建一个spark session
    val spark = SparkSession.builder().config(sparkConf).getOrCreate()
    
    //导入隐式转换类,在DFDS转换的过程中会使用到
    import spark.implicits._
    
    //加载数据
    val productRDD: RDD[String] = spark.sparkContext.textFile(PRODUCT_DATA_PATH)
    val productDF: DataFrame = productRDD.map(item => {
      val attr: Array[String] = item.split("\\^")
      //封装为ProductProduct(attr(0).toInt, attr(1).trim, attr(4).trim, attr(5).trim, attr(6).trim)
    }).toDF()
   
    val ratingRDD: RDD[String] = spark.sparkContext.textFile(RATING_DATA_PATH)
    val ratingDF: DataFrame = ratingRDD.map(item => {
      val attr: Array[String] = item.split(",")
      //封装数据
      Rating(attr(0).toInt, attr(1).toInt, attr(2).toDouble, attr(3).toInt)
    }).toDF()

    
    //通过隐式类的方法创建mongodb连接对象
    implicit val mongoConfig = MongoConfig(config("mongo.uri"),config("mongo.db"))
    //将数据加载到mongodb
    storeInMongoDB(productDF,ratingDF)
    
    spark.stop()
  }
  
  //使用隐式传参的方法,减少数据的反复传递
  def storeInMongoDB(productDF:DataFrame,ratingDF:DataFrame)(implicit mongoConfig: MongoConfig)={
    
    //新建一个mongodb的客户端连接
    val mongoClient: MongoClient = MongoClient(MongoClientURI(mongoConfig.uri))
    //定义要做操作的表
    val productCollection: MongoCollection = mongoClient(mongoConfig.db)(MONGODB_PRODUCT_COLLECTION)
    val ratingCollection: MongoCollection = mongoClient(mongoConfig.db)(MONGODB_RATING_COLLECTION)
    
    //如果表存在,则删除
    productCollection.dropCollection()
    ratingCollection.dropCollection()
    
    
    //将当前数据存入到对应的表中
    productDF.write
      .option("uri",mongoConfig.uri)
      .option("collection",MONGODB_PRODUCT_COLLECTION)
      .mode("overwrite")
      .format("com.mongodb.spark.sql")
      .save()
  
    ratingDF.write
      .option("uri",mongoConfig.uri)
      .option("collection",MONGODB_RATING_COLLECTION)
      .mode("overwrite")
      .format("com.mongodb.spark.sql")
      .save()
    
    //对表创建索引
    productCollection.createIndex(MongoDBObject("productId"->1))
    ratingCollection.createIndex(MongoDBObject("productId"->1))
    ratingCollection.createIndex(MongoDBObject("userId"->1))
    
    mongoClient.close()
  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值