Quellcode durchsuchen

feat:添加头部视频同品类召回

zhaohaipeng vor 1 Woche
Ursprung
Commit
735d44b7e2

+ 174 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/HeadCate2RovRecallStrategy.java

@@ -0,0 +1,174 @@
+package com.tzld.piaoquan.recommend.server.service.recall.strategy;
+
+import com.tzld.piaoquan.recommend.server.model.Video;
+import com.tzld.piaoquan.recommend.server.service.FeatureService;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterParam;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterResult;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterService;
+import com.tzld.piaoquan.recommend.server.service.recall.FilterParamFactory;
+import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
+import com.tzld.piaoquan.recommend.server.service.recall.RecallStrategy;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 头部视频,类似品类召回
+ */
+@Slf4j
+@Component
+public class HeadCate2RovRecallStrategy implements RecallStrategy {
+
+    @Autowired
+    private FilterService filterService;
+    @Autowired
+    @Qualifier("redisTemplate")
+    public RedisTemplate<String, String> redisTemplate;
+
+    @Autowired
+    private FeatureService featureService;
+
+    private static final String PUSH_FROM = "recall_strategy_head_cate2_rov";
+
+    private static final String SIM_MERGE_CATE2_KEY_FORMAT = "alg_recsys_good_cate_pair_list:%s";
+    private static final String MERGE_CATE2_VIDEO_LIST_KEY_FORMAT = "alg_recsys_recall_good_cate_pair_rovn:%s";
+
+    @Override
+    public List<Video> recall(RecallParam param) {
+        if (Objects.isNull(param.getVideoId())) {
+            return Collections.emptyList();
+        }
+
+        // 获取头部视频基础信息
+        String vidStr = String.valueOf(param.getVideoId());
+        Map<String, String> headVideoInfo = featureService.getHeadVideoInfo(vidStr);
+        if (MapUtils.isEmpty(headVideoInfo)) {
+            return Collections.emptyList();
+        }
+
+        // 不存在品类或无效品类
+        String mergeCate2 = headVideoInfo.get("merge_second_level_cate");
+        if (StringUtils.isBlank(mergeCate2) || "unknown".equals(mergeCate2)) {
+            return Collections.emptyList();
+        }
+
+        // 获取相似品类
+        String simCate2Key = String.format(SIM_MERGE_CATE2_KEY_FORMAT, mergeCate2);
+        String simCate2List = redisTemplate.opsForValue().get(simCate2Key);
+        if (Objects.isNull(simCate2List)) {
+            return Collections.emptyList();
+        }
+
+        List<Video> videoResult = new ArrayList<>();
+
+        Map<String, Double> mergeCate2Pair = this.parsePair(simCate2List);
+
+        Map<String, Map<String, Double>> recallVideoMap = this.cate2Recall(new ArrayList<>(mergeCate2Pair.keySet()));
+
+        // 过滤
+        List<Long> allVid = recallVideoMap.values().stream()
+                .map(Map::keySet)
+                .flatMap(Collection::stream)
+                .map(Long::parseLong)
+                .collect(Collectors.toList());
+
+        FilterParam filterParam = FilterParamFactory.create(param, allVid);
+        FilterResult filterResult = filterService.filter(filterParam);
+        Set<Long> filterVids = new HashSet<>(filterResult.getVideoIds());
+
+        for (Map.Entry<String, Double> entry : mergeCate2Pair.entrySet()) {
+            String cate = entry.getKey();
+            Double cateScore = entry.getValue();
+
+            Map<String, Double> videoMap = recallVideoMap.getOrDefault(cate, new HashMap<>());
+            for (Map.Entry<String, Double> videoEntry : videoMap.entrySet()) {
+                long vid = Long.parseLong(videoEntry.getKey());
+
+                // 过滤之后不存在的视频,过滤掉
+                if (!filterVids.contains(vid)) {
+                    continue;
+                }
+
+                Double videoScore = videoEntry.getValue();
+
+                Video video = new Video();
+                video.setVideoId(vid);
+                video.setRovScore(cateScore * videoScore);
+                video.setPushFrom(PUSH_FROM);
+                videoResult.add(video);
+            }
+
+        }
+
+        videoResult.sort(Comparator.comparingDouble(o -> -o.getRovScore()));
+
+        return videoResult;
+    }
+
+    private Map<String, Double> parsePair(String value) {
+        if (StringUtils.isBlank(value)) {
+            return new HashMap<>();
+        }
+
+        String[] split = value.split("\t");
+        if (split.length != 2) {
+            return new HashMap<>();
+        }
+
+        String[] valueList = split[0].trim().split(",");
+        String[] scoreList = split[1].trim().split(",");
+        if (valueList.length != scoreList.length) {
+            return new HashMap<>();
+        }
+
+        Map<String, Double> resultMap = new HashMap<>();
+        for (int i = 0; i < valueList.length; i++) {
+            resultMap.put(valueList[i].trim(), Double.parseDouble(scoreList[i].trim()));
+        }
+
+        return resultMap;
+    }
+
+    private Map<String, Map<String, Double>> cate2Recall(List<String> mergeCate2List) {
+
+
+        List<String> redisKeys = mergeCate2List.stream().map(i -> String.format(MERGE_CATE2_VIDEO_LIST_KEY_FORMAT, i)).collect(Collectors.toList());
+        List<String> values = redisTemplate.opsForValue().multiGet(redisKeys);
+        if (CollectionUtils.isEmpty(values)) {
+            return new HashMap<>();
+        }
+
+        Map<String, Map<String, Double>> resultMap = new HashMap<>();
+
+        for (int i = 0; i < mergeCate2List.size(); i++) {
+            String mergeCate2 = mergeCate2List.get(i);
+
+            String value = values.get(i);
+            if (StringUtils.isBlank(value)) {
+                continue;
+            }
+
+            Map<String, Double> recallVideoMap = this.parsePair(value);
+            if (MapUtils.isEmpty(recallVideoMap)) {
+                continue;
+            }
+
+            resultMap.put(mergeCate2, recallVideoMap);
+        }
+
+        return resultMap;
+    }
+
+    @Override
+    public String pushFrom() {
+        return PUSH_FROM;
+    }
+}