兴趣推送与相似推送逻辑设计
兴趣推送
这里的兴趣推送不涉及大数据,或者大模型的兴趣推送,而是从内容标签的角度去构建用户画像/模型达到的兴趣推送。
内容标签
如果要根据某个用户的模型去选择内容标签,再到对应的内容标签集合中推送具体的内容id,就需要知道每个内容的标签。
public void saveContent(Content content) {
for (String labl : content.getLabls()) {
redisCacheUtil.setAdd(RedisKeyConstant.LABL + labl, content.getId());
}
}
一般是当内容审核完毕后将内容id放到其标签的set中,方便在进行兴趣推送时,可以从对应的标签set中随机获取。
用户模型初始化
这里只是将一个在redis中没有的用户重新构建模型,也许是用户太久没用key过期了,或者是这个用户才注册
- 将100除以(用户看过的视频的标签集合个数),算出每个标签的权重概率值,存入map中,简化代码如下
public void initUserModel(Long userId,List<String> labls) {
final String key = RedisKeyConstant.USER_MODEL + userId;
Map<Object,Object> map = new HashMap<>();
if (!ObjectUtils.isEmpty(labls)) {
final int size = labls.size();
double probabilityValue = 100D / size;
for (String labl : labls) {
map.put(labl,probabilityValue);
}
}
redisCacheUtil.del(key);
redisCacheUtil.hmset(key,map);
redisCacheUtil.setTTL(key,1000 * 60 * 60 * 24);
}
用户模型变化
- 将用户的行为分为不同权重,比如观看完成对应标签的分数+1,点赞+2,收藏+3,然后在前端进行一部分缓冲(类似Buffer),比如用户刷了好几个内容,将这个内容的标签根据上面的不同行为进行计算累计后一起发送
- 后端接收到对应的模型变更后,获取用户缓存的模型,如果没有缓存的模型就调用上面的initUserModel,将新的标签得分填充到用户的模型中去,形成用户新的模型
用户兴趣推送行为
- 通过用户id获取用户的模型
- 根据用户的模型后,利用随机选择算法,从中随机选择8-10次(内容数量自定义)
- 比如某某用户的map->{{美女:30},{黑丝:40},{篮球:2}…}
- 那么相当于美女概率为30/(30+40+2),…,利用随机选择
- 将选择出的标签放入到新的容器中,根据容器中的不同标签的数量去标签set中获取对应的内容id
- 将内容id获取到后,在进行去重,比如用户之前刷到过的就不再推送了。
- 即使去重后只有一个,那么前端中用户在进行翻页的时候会再次出发兴趣推送,再进行推送即可。
- 在数据量越大的时候,去重后的数量变化幅度越小
- 并且前端也可以自己进行去重,因为前端客户端可以直接保留当前的历史记录
相似推送
- 利用当前内容的标签,然后去对应的标签集合中随机获取n个内容的ID。
- 同时为了避免标签过少导致随机出的内容ID数量少,可以根据当这个内容的标签数量少于某个具体数量x时,尝试获取的n个推送内容提高到每个标签2*n个,否则有多少标签就推多少个内容。
- 最后将要推送的内容和当前内容进行去重,因为相似推送就是内容详情页面中,下面的推送内容,所以只需要对当前内容去重保证相似推送的内容中不会出现重复的内容即可。
public List<Content> similarRecommendations(Content content) {
// 定义每个标签内容的推送数量
int recommendCount = 5;
// 如果标签数量小于规定的数量,则将推送数量翻倍
if (content.getLabls().size() < 5) {
recommendCount *= 2;
}
Set<Long> contentIds = new HashSet<>();
for (String labl : content.getLabls()) {
// 从redis中的标签内容id集合中随机获取内容id
contentIds.add(redisCacheUtil.setGet(RedisKeyConstant.LABL + labl, recommendCount));
}
// 排除当前内容的id
contentIds.remove(content.getId());
// 调用具体根据内容id集合获取id内容的方法返回
return getContentsByIdList(new ArrayList<>(contentIds));
}