|
@@ -48,6 +48,29 @@ public class RecallUtils {
|
|
|
return Collections.emptyList();
|
|
return Collections.emptyList();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public static int countDistinctCandidates(RankParam param, Set<Long> excludedVideoIds,
|
|
|
|
|
+ Set<String> includedPushFroms) {
|
|
|
|
|
+ if (param == null || param.getRecallResult() == null
|
|
|
|
|
+ || CollectionUtils.isEmpty(param.getRecallResult().getData())
|
|
|
|
|
+ || CollectionUtils.isEmpty(includedPushFroms)) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ Set<Long> candidates = new HashSet<>();
|
|
|
|
|
+ for (RecallResult.RecallData data : param.getRecallResult().getData()) {
|
|
|
|
|
+ if (data == null || CollectionUtils.isEmpty(data.getVideos())
|
|
|
|
|
+ || !includedPushFroms.contains(data.getPushFrom())) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Video video : data.getVideos()) {
|
|
|
|
|
+ if (video == null) continue;
|
|
|
|
|
+ long videoId = video.getVideoId();
|
|
|
|
|
+ if (excludedVideoIds != null && excludedVideoIds.contains(videoId)) continue;
|
|
|
|
|
+ candidates.add(videoId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return candidates.size();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public static void extractOldSpecialRecall(int sizeReturn, RankParam param, Set<Long> setVideo, List<Video> rovRecallRank) {
|
|
public static void extractOldSpecialRecall(int sizeReturn, RankParam param, Set<Long> setVideo, List<Video> rovRecallRank) {
|
|
|
List<Video> oldRovs = new ArrayList<>();
|
|
List<Video> oldRovs = new ArrayList<>();
|
|
|
oldRovs.addAll(extractAndSort(param, RegionHRecallStrategy.PUSH_FORM));
|
|
oldRovs.addAll(extractAndSort(param, RegionHRecallStrategy.PUSH_FORM));
|
|
@@ -93,8 +116,12 @@ public class RecallUtils {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
Map<Long, Double> coarseMap = coarseRankMap != null ? coarseRankMap : Collections.emptyMap();
|
|
Map<Long, Double> coarseMap = coarseRankMap != null ? coarseRankMap : Collections.emptyMap();
|
|
|
|
|
+ Set<Long> selectedBefore = rovRecallRank.stream()
|
|
|
|
|
+ .map(Video::getVideoId)
|
|
|
|
|
+ .collect(Collectors.toSet());
|
|
|
// 1. 全并 + dedupe(首次命中保留,只挑 includedPushFroms 命中的路)
|
|
// 1. 全并 + dedupe(首次命中保留,只挑 includedPushFroms 命中的路)
|
|
|
Map<Long, Video> mergedById = new LinkedHashMap<>();
|
|
Map<Long, Video> mergedById = new LinkedHashMap<>();
|
|
|
|
|
+ Map<Long, String> attributionById = new HashMap<>();
|
|
|
for (RecallResult.RecallData data : param.getRecallResult().getData()) {
|
|
for (RecallResult.RecallData data : param.getRecallResult().getData()) {
|
|
|
if (data == null || CollectionUtils.isEmpty(data.getVideos())) continue;
|
|
if (data == null || CollectionUtils.isEmpty(data.getVideos())) continue;
|
|
|
if (!includedPushFroms.contains(data.getPushFrom())) continue;
|
|
if (!includedPushFroms.contains(data.getPushFrom())) continue;
|
|
@@ -103,11 +130,13 @@ public class RecallUtils {
|
|
|
long vid = v.getVideoId();
|
|
long vid = v.getVideoId();
|
|
|
if (setVideo.contains(vid)) continue;
|
|
if (setVideo.contains(vid)) continue;
|
|
|
mergedById.putIfAbsent(vid, v);
|
|
mergedById.putIfAbsent(vid, v);
|
|
|
|
|
+ attributionById.putIfAbsent(vid, data.getPushFrom());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
if (mergedById.isEmpty()) {
|
|
if (mergedById.isEmpty()) {
|
|
|
- // 仍然给 includedPushFroms 范围内 entry 归因:本次没新增 vid,但要标 OTHER(被前面池抢光 + 没命中本类)
|
|
|
|
|
- markCoarseRankAttribution(param, includedPushFroms, Collections.emptySet(), topN, coarseMap);
|
|
|
|
|
|
|
+ // 本池没有新增 vid 时,只标记确实已被前序池选走的重复候选。
|
|
|
|
|
+ markCoarseRankAttribution(param, includedPushFroms, Collections.emptyMap(),
|
|
|
|
|
+ selectedBefore, topN, coarseMap);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -122,19 +151,25 @@ public class RecallUtils {
|
|
|
|
|
|
|
|
// 3. 漏斗归因(仅 includedPushFroms 范围内的 entry):
|
|
// 3. 漏斗归因(仅 includedPushFroms 范围内的 entry):
|
|
|
// - entry.score 覆盖为粗排分(命运分;同 vid 在所有命中路 entry 都覆盖同一个值)
|
|
// - entry.score 覆盖为粗排分(命运分;同 vid 在所有命中路 entry 都覆盖同一个值)
|
|
|
- // - entry.select = SELF/OTHER 复用 V568 二元语义:vid 在本次截断胜出 → SELF,否则 OTHER
|
|
|
|
|
- // (跨类抢占场景: vid 同时在个性化+非个性化命中, 个性化先抢走时, 非个性化路的 vid entry
|
|
|
|
|
- // 在第二次调用时 pickedIds 不含 it → 自动标 OTHER, 含义="vid 被前面池抢走", 与 V568 一致)
|
|
|
|
|
|
|
+ // - SELF = 本次截断胜出且由该 pushFrom 首次贡献
|
|
|
|
|
+ // - OTHER = 同一 vid 被本次其他 pushFrom 或前序池选走
|
|
|
|
|
+ // - 普通粗排落选保持 null,不进入阶段 3
|
|
|
// - entry.truncate = 本次调用的 topN
|
|
// - entry.truncate = 本次调用的 topN
|
|
|
- markCoarseRankAttribution(param, includedPushFroms, pickedIds, topN, coarseMap);
|
|
|
|
|
|
|
+ Map<Long, String> pickedAttribution = new HashMap<>();
|
|
|
|
|
+ for (Long pickedId : pickedIds) {
|
|
|
|
|
+ pickedAttribution.put(pickedId, attributionById.get(pickedId));
|
|
|
|
|
+ }
|
|
|
|
|
+ markCoarseRankAttribution(param, includedPushFroms, pickedAttribution,
|
|
|
|
|
+ selectedBefore, topN, coarseMap);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * V564:覆盖 entry.score + 写 SELF/OTHER + truncate(仅 includedPushFroms 范围)。
|
|
|
|
|
|
|
+ * 统一粗排漏斗归因:仅阶段 2 过滤后的视频可进入阶段 3。
|
|
|
* 流量池 3 路等 includedPushFroms 之外的 entry 保持原状(select=null, 不参与 V564 截断)。
|
|
* 流量池 3 路等 includedPushFroms 之外的 entry 保持原状(select=null, 不参与 V564 截断)。
|
|
|
*/
|
|
*/
|
|
|
private static void markCoarseRankAttribution(RankParam param, Set<String> includedPushFroms,
|
|
private static void markCoarseRankAttribution(RankParam param, Set<String> includedPushFroms,
|
|
|
- Set<Long> pickedIds, int truncate,
|
|
|
|
|
|
|
+ Map<Long, String> pickedAttribution,
|
|
|
|
|
+ Set<Long> selectedBefore, int truncate,
|
|
|
Map<Long, Double> coarseRankMap) {
|
|
Map<Long, Double> coarseRankMap) {
|
|
|
if (param == null || param.getFunnelContext() == null
|
|
if (param == null || param.getFunnelContext() == null
|
|
|
|| CollectionUtils.isEmpty(includedPushFroms)) return;
|
|
|| CollectionUtils.isEmpty(includedPushFroms)) return;
|
|
@@ -143,10 +178,17 @@ public class RecallUtils {
|
|
|
List<RecallVideoEntry> entries = ctx.getStages123RecallByStrategy().get(pf);
|
|
List<RecallVideoEntry> entries = ctx.getStages123RecallByStrategy().get(pf);
|
|
|
if (CollectionUtils.isEmpty(entries)) continue;
|
|
if (CollectionUtils.isEmpty(entries)) continue;
|
|
|
for (RecallVideoEntry e : entries) {
|
|
for (RecallVideoEntry e : entries) {
|
|
|
|
|
+ if (!e.isFilteredIn()) continue;
|
|
|
Double coarse = coarseRankMap.get(e.getVideoId());
|
|
Double coarse = coarseRankMap.get(e.getVideoId());
|
|
|
if (coarse != null) e.setScore(coarse);
|
|
if (coarse != null) e.setScore(coarse);
|
|
|
- e.setSelect(pickedIds.contains(e.getVideoId()) ? SelectKind.SELF : SelectKind.OTHER);
|
|
|
|
|
- e.setTruncate(truncate);
|
|
|
|
|
|
|
+ String attributedPushFrom = pickedAttribution.get(e.getVideoId());
|
|
|
|
|
+ if (attributedPushFrom != null) {
|
|
|
|
|
+ e.setSelect(attributedPushFrom.equals(pf) ? SelectKind.SELF : SelectKind.OTHER);
|
|
|
|
|
+ e.setTruncate(truncate);
|
|
|
|
|
+ } else if (selectedBefore.contains(e.getVideoId())) {
|
|
|
|
|
+ e.setSelect(SelectKind.OTHER);
|
|
|
|
|
+ e.setTruncate(truncate);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|