热度值计算
热度值的计算公式 (单位时间内点击次数)
hotValue= p o i n t N u m n o w T i m e − c r e a t e T i m e \frac{pointNum}{nowTime - createTime} nowTime−createTimepointNum
p o i n t N u m pointNum pointNum 是当前时间至产品创建时间内,所产生的点击次数
n o w T i m e nowTime nowTime 是当前时间的时间戳
c r e a t e T i m e createTime createTime 是产品创建时间的时间戳
这个公式可以理解为:产品在创建那一刻到目前为止,产品单位时间内 有被点击了多少次。
为了便于理解,我们联想一下,为了体现一辆汽车的速度快不快,通过单位时间内位移即可表示。同理,为了体现这个产品是否为热门产品,通过它的单位时间内点击次数来表示。
当然了为了避免分母为0,计算公式可以加一个常数 Δ \Delta Δ计算,比如变成如下的公式。
hotValue= p o i n t N u m n o w T i m e − c r e a t e T i m e + Δ \frac{pointNum}{nowTime - createTime + \Delta} nowTime−createTime+ΔpointNum
代码过程
在我们日常生活中,热门推荐是一个很重要的推荐,也是一个推荐系统的起步。不需要详细记录用户的信息,就可以为用户推荐热门的产品。本文从日志打印、到日志分析、最终形成推荐候选集的方面进行阐述,本文的设计热门产品计算的计算瓶颈取决于Java中的Map能存放多少条数据。
如下图是计算每个产品的流程图。
1 在用户端需要添加日志记录,比如以用户点击为热度指标,那么在用户点击某个产品后,则进行记录,并将日志记录在某个文件内。
2 在用户行为信息形成日志后,需要对其对日志聚合分析,形成如下的Map结构性的数据
"itemId":112233,
"pointNum": 123
key: 112233, value: 123
3 根据热度值进行计算
{
"itemId":112233,
"hotValue":0.5
}
4 数据库查找的时候,以hotValue为排序指标查找前多少位的数据,即可查找到钱多少的热门数据。
知道热门试题的大致步骤后,开始实现的部分,在后端中每个用户如果点击产品后,我我采用log4j2将日志打印到一个指定的文件内。
<RollingFile name="XiaotiBehaviorInfo" fileName="${LOG_PATH}/xiaotiapp/xiaoti_behavior.log"
filePattern="${LOG_PATH}/xiaotiapp/xiaoti_behavior-log-date-%d{yyyy-MM-dd}-%i.log" append="true">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${sys:FILE_LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="24"/>
<SizeBasedTriggeringPolicy/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!--拦截某个方法,调用XiaotiBehaviorInfo,打印到另一个文件中-->
<logger name="com.marsdl.api.xiaotiapi.service.QuestionService" level="info" additivity="false">
<AppenderRef ref="XiaotiBehaviorInfo"/>
</logger>
配置好后端服务的日志后,日志就记录在xiaoti_behavior.log文件内,后面按行读取然后判断是否为你所想要的日志即可。如下代码检测每一行的日志中是否包含xiaotiapp-behavior-answerResult,如果包含则为我所想要的那条日志,然后记录在Map中达到聚合的目的。
public void handleLogLine(String line) {
if (StringUtils.isBlank(line)) {
return;
}
String[] itemArr = line.split("\\s\\s");
if (StringUtils.isNotBlank(itemArr[1]) && itemArr[1].contains("xiaotiapp-behavior-answerResult")) {
HotQuestion hotQuestion = new HotQuestion();
for (String item : itemArr[1].trim().split("\\,")) {
item = item.trim();
String titleId = "0";
if (item.startsWith("titleId:")) {
titleId = item.split("titleId:")[1];
hotQuestion.setTitleId(titleId);
Long count = titleIdCountMap.get(titleId);
if (count != null) {
titleIdCountMap.put(titleId, count + 1L);
} else {
titleIdCountMap.put(titleId, 1L);
}
}
}
}
}
为了便于您的更好的分析,如下代码是按行读取日志文件,判断特征,聚合特征,然后计算热度值。
//指定路径按行读取
public void readLog(final String filepath) throws Exception {
//仔细查看
dao.dropCollection(xiaotiHotquestion);
long nowTime = System.currentTimeMillis();
try (
BufferedReader reader = new BufferedReader(new FileReader(filepath));
) {
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
handleLogLine(line);
}
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
long wasteTime = System.currentTimeMillis() - nowTime;
System.out.println("读取解析日志耗时(ms): " + wasteTime);
for (Map.Entry<String, Long> entry : titleIdCountMap.entrySet()) {
String titleId = entry.getKey();
XiaotiQuestionDbResource dbResource = dao.findQuestion(titleId,
XiaotiQuestionDbResource.class, DB_COLLECTION);
if (dbResource == null) {
continue;
}
Long answerNum = entry.getValue();
Double answerNumDouble = answerNum.doubleValue();
long time = System.currentTimeMillis();
long addTime = dbResource.getAddTime();
//时长
long delta = Math.abs(addTime - time);
delta = delta == 0 ? 1 : delta;
//天数
double days = delta / (1000 * 60 * 60 * 24);
if (delta < (1000 * 60 * 60 * 24)) {
days = 0.5;
}
//计算热度值
double hotValue = answerNumDouble / days;
//封装存储数据
HotQuestion hotQuestion = new HotQuestion();
String titleContent = dbResource.getTitleContent();
if (titleContent.length() > 14) {
titleContent = titleContent.substring(0, 14);
}
hotQuestion.setTitleContent(titleContent);
hotQuestion.setTitleId(titleId);
hotQuestion.setHotValue(hotValue);
hotQuestion.setCourse(dbResource.getCourse());
System.out.println(JSON.toJSONString(hotQuestion));
dao.saveEntity(hotQuestion, xiaotiHotquestion);
}
}