|
@@ -40,12 +40,11 @@ public class FunnelAggregator {
|
|
|
row.put("step_2_filter_reasons", JSON.toJSONString(buildStep2Reasons(ctx)));
|
|
row.put("step_2_filter_reasons", JSON.toJSONString(buildStep2Reasons(ctx)));
|
|
|
row.put("step_3_truncated", JSON.toJSONString(buildStep3(ctx)));
|
|
row.put("step_3_truncated", JSON.toJSONString(buildStep3(ctx)));
|
|
|
|
|
|
|
|
- // step 4-8
|
|
|
|
|
|
|
+ // step 4-7 (step_8 已并入 step_7 — 冷启融合后即最终输出)
|
|
|
row.put("step_4_merged", JSON.toJSONString(buildStep4(ctx)));
|
|
row.put("step_4_merged", JSON.toJSONString(buildStep4(ctx)));
|
|
|
row.put("step_5_ranked", JSON.toJSONString(buildStep5(ctx)));
|
|
row.put("step_5_ranked", JSON.toJSONString(buildStep5(ctx)));
|
|
|
row.put("step_6_rank_truncated", JSON.toJSONString(buildStep6(ctx)));
|
|
row.put("step_6_rank_truncated", JSON.toJSONString(buildStep6(ctx)));
|
|
|
row.put("step_7_cold_start", JSON.toJSONString(buildStep7(ctx)));
|
|
row.put("step_7_cold_start", JSON.toJSONString(buildStep7(ctx)));
|
|
|
- row.put("step_8_output", JSON.toJSONString(buildStep8(ctx)));
|
|
|
|
|
|
|
|
|
|
return row;
|
|
return row;
|
|
|
}
|
|
}
|
|
@@ -138,19 +137,23 @@ public class FunnelAggregator {
|
|
|
|
|
|
|
|
private static JSONArray buildStep5(FunnelContext ctx) {
|
|
private static JSONArray buildStep5(FunnelContext ctx) {
|
|
|
JSONArray arr = new JSONArray();
|
|
JSONArray arr = new JSONArray();
|
|
|
|
|
+ int rankIdx = 0;
|
|
|
for (Long vid : ctx.getStep4MergedVideoIds()) {
|
|
for (Long vid : ctx.getStep4MergedVideoIds()) {
|
|
|
RankVideoEntry r = ctx.getStep5RankedData().get(vid);
|
|
RankVideoEntry r = ctx.getStep5RankedData().get(vid);
|
|
|
if (r == null) continue;
|
|
if (r == null) continue;
|
|
|
|
|
+ rankIdx++;
|
|
|
JSONObject o = new JSONObject();
|
|
JSONObject o = new JSONObject();
|
|
|
o.put("vid", vid);
|
|
o.put("vid", vid);
|
|
|
o.put("attributedPushFrom", ctx.getStep4MergedAttribution().get(vid));
|
|
o.put("attributedPushFrom", ctx.getStep4MergedAttribution().get(vid));
|
|
|
o.put("recalls", buildRecallsForVid(ctx, vid));
|
|
o.put("recalls", buildRecallsForVid(ctx, vid));
|
|
|
o.put("rank", buildRankBlock(r));
|
|
o.put("rank", buildRankBlock(r));
|
|
|
|
|
+ o.put("rank_index", rankIdx);
|
|
|
arr.add(o);
|
|
arr.add(o);
|
|
|
}
|
|
}
|
|
|
return arr;
|
|
return arr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 排序截断: rovRecallRank 的前 size 条(不含冷启)— 给 BI 做"不冷启会是谁"的参考 */
|
|
|
private static JSONArray buildStep6(FunnelContext ctx) {
|
|
private static JSONArray buildStep6(FunnelContext ctx) {
|
|
|
JSONArray arr = new JSONArray();
|
|
JSONArray arr = new JSONArray();
|
|
|
for (int i = 0; i < ctx.getStep6RankTruncatedVideoIds().size(); i++) {
|
|
for (int i = 0; i < ctx.getStep6RankTruncatedVideoIds().size(); i++) {
|
|
@@ -167,37 +170,8 @@ public class FunnelAggregator {
|
|
|
return arr;
|
|
return arr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 冷启融合后的最终输出(实际下发列表)— 用 step8OutputVideoIds */
|
|
|
private static JSONArray buildStep7(FunnelContext ctx) {
|
|
private static JSONArray buildStep7(FunnelContext ctx) {
|
|
|
- JSONArray arr = new JSONArray();
|
|
|
|
|
- // step 7 = step 6 + if_cold_start
|
|
|
|
|
- for (int i = 0; i < ctx.getStep6RankTruncatedVideoIds().size(); i++) {
|
|
|
|
|
- long vid = ctx.getStep6RankTruncatedVideoIds().get(i);
|
|
|
|
|
- JSONObject o = new JSONObject();
|
|
|
|
|
- o.put("vid", vid);
|
|
|
|
|
- o.put("attributedPushFrom", ctx.getStep4MergedAttribution().get(vid));
|
|
|
|
|
- o.put("recalls", buildRecallsForVid(ctx, vid));
|
|
|
|
|
- RankVideoEntry r = ctx.getStep5RankedData().get(vid);
|
|
|
|
|
- if (r != null) o.put("rank", buildRankBlock(r));
|
|
|
|
|
- o.put("rank_index", i + 1);
|
|
|
|
|
- ColdStartAction action = ctx.getStep7ColdStartActions().getOrDefault(vid, ColdStartAction.NONE);
|
|
|
|
|
- o.put("if_cold_start", action != ColdStartAction.NONE);
|
|
|
|
|
- o.put("cold_start_action", action.name());
|
|
|
|
|
- arr.add(o);
|
|
|
|
|
- }
|
|
|
|
|
- // 冷启 INSERTED 的视频可能不在 step6RankTruncatedVideoIds 里,补
|
|
|
|
|
- ctx.getStep7ColdStartActions().forEach((vid, action) -> {
|
|
|
|
|
- if (action == ColdStartAction.INSERTED && !ctx.getStep6RankTruncatedVideoIds().contains(vid)) {
|
|
|
|
|
- JSONObject o = new JSONObject();
|
|
|
|
|
- o.put("vid", vid);
|
|
|
|
|
- o.put("if_cold_start", true);
|
|
|
|
|
- o.put("cold_start_action", action.name());
|
|
|
|
|
- arr.add(o);
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- return arr;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static JSONArray buildStep8(FunnelContext ctx) {
|
|
|
|
|
JSONArray arr = new JSONArray();
|
|
JSONArray arr = new JSONArray();
|
|
|
for (int i = 0; i < ctx.getStep8OutputVideoIds().size(); i++) {
|
|
for (int i = 0; i < ctx.getStep8OutputVideoIds().size(); i++) {
|
|
|
long vid = ctx.getStep8OutputVideoIds().get(i);
|
|
long vid = ctx.getStep8OutputVideoIds().get(i);
|
|
@@ -209,6 +183,7 @@ public class FunnelAggregator {
|
|
|
if (r != null) o.put("rank", buildRankBlock(r));
|
|
if (r != null) o.put("rank", buildRankBlock(r));
|
|
|
ColdStartAction action = ctx.getStep7ColdStartActions().getOrDefault(vid, ColdStartAction.NONE);
|
|
ColdStartAction action = ctx.getStep7ColdStartActions().getOrDefault(vid, ColdStartAction.NONE);
|
|
|
o.put("if_cold_start", action != ColdStartAction.NONE);
|
|
o.put("if_cold_start", action != ColdStartAction.NONE);
|
|
|
|
|
+ o.put("cold_start_action", action.name());
|
|
|
o.put("rank_index", i + 1);
|
|
o.put("rank_index", i + 1);
|
|
|
arr.add(o);
|
|
arr.add(o);
|
|
|
}
|
|
}
|
|
@@ -221,12 +196,13 @@ public class FunnelAggregator {
|
|
|
ctx.getStages123RecallByStrategy().forEach((pf, entries) -> {
|
|
ctx.getStages123RecallByStrategy().forEach((pf, entries) -> {
|
|
|
for (RecallVideoEntry e : entries) {
|
|
for (RecallVideoEntry e : entries) {
|
|
|
if (e.getVideoId() != vid) continue;
|
|
if (e.getVideoId() != vid) continue;
|
|
|
- if (e.getSelect() != SelectKind.SELF) continue; // 只列该路实际贡献到合并的视频
|
|
|
|
|
|
|
+ if (e.getSelect() == null) continue; // 列出该 vid 在该路 top-recallNum 名次内的命中(self + other)
|
|
|
JSONObject r = new JSONObject();
|
|
JSONObject r = new JSONObject();
|
|
|
r.put("strategy", pf);
|
|
r.put("strategy", pf);
|
|
|
r.put("index", displayIndex(e.getIndex()));
|
|
r.put("index", displayIndex(e.getIndex()));
|
|
|
r.put("score", round6(e.getScore()));
|
|
r.put("score", round6(e.getScore()));
|
|
|
r.put("index_new", displayIndex(e.getIndexNewAfterFilter()));
|
|
r.put("index_new", displayIndex(e.getIndexNewAfterFilter()));
|
|
|
|
|
+ r.put("select", e.getSelect().name().toLowerCase());
|
|
|
r.put("truncate", e.getTruncate());
|
|
r.put("truncate", e.getTruncate());
|
|
|
recalls.add(r);
|
|
recalls.add(r);
|
|
|
break;
|
|
break;
|
|
@@ -244,8 +220,11 @@ public class FunnelAggregator {
|
|
|
return o;
|
|
return o;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /** double → 保留 6 位小数 */
|
|
|
|
|
|
|
+ /** double → 保留 6 位小数 (NaN / Infinity 兜底为 0,避免 BigDecimal.valueOf 抛 NumberFormatException) */
|
|
|
private static BigDecimal round6(double v) {
|
|
private static BigDecimal round6(double v) {
|
|
|
|
|
+ if (Double.isNaN(v) || Double.isInfinite(v)) {
|
|
|
|
|
+ return BigDecimal.ZERO.setScale(6);
|
|
|
|
|
+ }
|
|
|
return BigDecimal.valueOf(v).setScale(6, RoundingMode.HALF_UP);
|
|
return BigDecimal.valueOf(v).setScale(6, RoundingMode.HALF_UP);
|
|
|
}
|
|
}
|
|
|
|
|
|