前言
需求四:获取top10热门品类的活跃session
一、需求分析
上一章节中,获取到了top10热门商品,本章节,基于热门商品,分别获取到Top10热门品类中的Top10活跃Session
二、思路及步骤
三、实现
- 声明需求四的主函数
def top10ActiveSession(sparkSession: SparkSession,
taskUUID: String,
top10Category: Array[(SortKey, String)],
sessionId2FilterActionRDD: RDD[(String, UserVisitAction)]) :Unit ={
...
}
- 第一步:出所有点击过top10热门品类的action;有两种方法,从性能上,方法二更优
1.方法一:jion的方式,需要shuffle
// 1. top10商品品类数据 转换cid作为key
val cid2CountInfoRDD = sparkSession.sparkContext.makeRDD(top10Category).map{
case (sortKey, countInfo) =>
val cid = StringUtils.getFieldFromConcatString(countInfo, "\\|", Constants.FIELD_CATEGORY_ID).toLong
// 获取到top10商品的cid
(cid, countInfo)
}
// 2. action数据 同样 转换cid作为key,进行后面的join
val cid2ActionRDD = sessionId2FilterActionRDD.map{
case (sessionId, action) =>
val cid = action.click_category_id
// 获取到action数据的cid
(cid, action)
}
// 3. join操作,获取点击过top10品类的action,获得符合条件的 (sessionId, action)
val sessionId2ActionRDD = cid2CountInfoRDD.join(cid2ActionRDD).map{
case (cid, (countInfo, action)) =>
val sessionId = action.session_id
(sessionId, action)
}
2.方法二:将top10Category提取出所有cid作为一个array,通过对action数据进行filter操作,看其点击cid是否包含在在array里即可
// cidArray: Array[Long] 包含了top10的cid信息
val cidArray = top10Category.map{
case (sortKey, countInfo) =>
val cid = StringUtils.getFieldFromConcatString(countInfo, "\\|", Constants.FIELD_CATEGORY_ID).toLong
cid
}
// 获得点击过top10品类的action数据RDD
val sessionId2ActionRDD = sessionId2FilterActionRDD.filter{
case (sessionId, action) =>
cidArray.contains(action.click_category_id)
}
- 第二步:通过以上两种方法,获得获得点击过top10品类的action数据RDD后,开始统计每个session对top10品类的点击次数
// 按照sessionId进行聚合
val sessionId2GroupRDD = sessionId2ActionRDD.groupByKey()
// 遍历所有session的所有action,统计每个session对各品类的点击次数,维护一个map
val cid2SessionCountRDD = sessionId2GroupRDD.flatMap{
case (sessionId, iterableAction) =>
// 定义一个map用来存储各品类的点击次数
val categoryCountMap = new mutable.HashMap[Long, Long]()
// 遍历所有action
for (action <- iterableAction){
val cid = action.click_category_id
if (!categoryCountMap.contains(cid))
categoryCountMap += (cid -> 0)
categoryCountMap.update(cid, categoryCountMap(cid)+1)
}
// 记录了一个session对于它所有点击过的品类的点击次数
for ((cid, count) <- categoryCountMap)
yield (cid, sessionId + "=" + count)
}
首先,根据sessionId对action进行聚合,获得每个session的全部action;然后,统计每个session对每个categoryId的点击次数count,并维护一个map<categoryId, count>来存储信息;最后,为了避免map中session信息的丢失,需要将map改为<categoryId, sessionN=count>的形式。如下图所示:
- 第三步:聚合操作,获得每个categoryId对应的,点击过它的session的count
上一步获得的cid2SessionCountRDD
,其内部结构如下图:是由多个session的map<categoryId, session=count>组成的
为了统计每个categoryId的活跃点击session,需要对categoryId进行聚合操作,预期得到的数据如下图:
// cid2SessionCountRDD: Map<cid, session=count> 对这个map按照cid进行groupbykey,可以获得每个cid的点击过它的session的点击次数count
val cid2GroupRDD = cid2SessionCountRDD.groupByKey()
- 第四步:抽取每个categoryId(10个)的前十活跃点击session的count,一共100个
// top10SessionRDD: RDD[Top10Session]
val top10SessionRDD = cid2GroupRDD.flatMap{
case (cid, iterableSessionCount) =>
// 对每个cid的session按照count次数大小排序 sortWith(true)则item1放在前面
// sortList: RDD[sessionCount]
val sortList = iterableSessionCount.toList.sortWith((item1, item2) =>{
item1.split("=")(1).toLong > item2.split("=")(1).toLong
}).take(10)
// top10Session: RDD[Top10Session]
val top10Session = sortList.map{
case item =>
val sessionId = item.split("=")(0)
val count = item.split("=")(1).toLong
Top10Session(taskUUID, cid, sessionId, count)
}
top10Session
}
- 第五步:转成dataframe,写入mysql
import sparkSession.implicits._
top10SessionRDD.toDF().write
.format