...
|
...
|
@@ -33,8 +33,6 @@ public class UserRecallResponseBuilder { |
|
|
private static final Logger RECALL_NEW_LOGGER = LoggerFactory.getLogger("RECALL");
|
|
|
|
|
|
@Autowired
|
|
|
private ProductListSortService productListSortService;
|
|
|
@Autowired
|
|
|
private PersonalVectorFeatureSearch personalVectorFeatureSearch;
|
|
|
@Autowired
|
|
|
private ProductFeatureFactorHepler productFeatureFactorHepler;
|
...
|
...
|
@@ -55,25 +53,19 @@ public class UserRecallResponseBuilder { |
|
|
sknResultList = this.fillBaseInfo(sknResultList);
|
|
|
|
|
|
//4、填充是否满足品类价格带的过滤
|
|
|
sknResultList = this.fillIsLikePriceArea(userRecallRequest.getUid(),sknResultList, userPersonalFactor);
|
|
|
sknResultList = this.fillIsLikePriceArea(userRecallRequest.getUid(), sknResultList, userPersonalFactor);
|
|
|
|
|
|
//5、按相关性计算得分
|
|
|
sknResultList = this.doCalScoreAndSort(sknResultList, userRecallRequest.getUid());
|
|
|
|
|
|
//6、品牌品类平衡
|
|
|
if (searchDynamicConfigService.searchPersionalNewStrategySortBrandBalance()) {
|
|
|
sknResultList = this.doBalance(sknResultList);
|
|
|
}
|
|
|
sknResultList = this.doCalScoreAndSort(sknResultList, userRecallRequest.getUid(), userRecallRequest.getPageSize());
|
|
|
|
|
|
//7、处理firstSkn-直通车等信息
|
|
|
//6、处理firstSkn-直通车等信息
|
|
|
sknResultList = this.doReRank(sknResultList);
|
|
|
|
|
|
//8、添加日志
|
|
|
//7、添加日志
|
|
|
for (RecallMergerResult.SknResult sknResult : sknResultList) {
|
|
|
this.logSknStrategyAndScore(userRecallRequest, sknResult);
|
|
|
}
|
|
|
|
|
|
//9、分页处理
|
|
|
//8、分页处理
|
|
|
int pageSize = userRecallRequest.getPageSize();
|
|
|
int recallTotalPage = (sknResultList.size() / pageSize);
|
|
|
if (recallTotalPage == 0) {
|
...
|
...
|
@@ -82,7 +74,7 @@ public class UserRecallResponseBuilder { |
|
|
recallTotalPage = Math.min(recallTotalPage, SknCountConstants.MAX_USER_RECALL_SKN_CACHE_COUNT / pageSize);//为用户最多保留X个skn进缓存
|
|
|
sknResultList = CollectionUtils.safeSubList(sknResultList, 0, recallTotalPage * pageSize);
|
|
|
|
|
|
//10、构造返回结果
|
|
|
//9、构造返回结果
|
|
|
List<RecallSknInfo> sknList = new ArrayList<>();
|
|
|
for (RecallMergerResult.SknResult sknResult : sknResultList) {
|
|
|
sknList.add(new RecallSknInfo(sknResult.getProductSkn(), sknResult.getStrategy().name()));
|
...
|
...
|
@@ -145,50 +137,50 @@ public class UserRecallResponseBuilder { |
|
|
for (RecallMergerResult.SknResult sknResult : sknResults) {
|
|
|
Integer misortId = sknResult.getMiddleSortId();
|
|
|
List<Integer> userPriceAreas = userMisort2PriceAreasMap.getOrDefault(misortId, new ArrayList<>());
|
|
|
boolean isLikePriceArea = this.isLikePriceArea(userPriceAreas,sknResult.getPriceArea());
|
|
|
boolean isLikePriceArea = this.isLikePriceArea(userPriceAreas, sknResult.getPriceArea());
|
|
|
sknResult.setLikePriceArea(isLikePriceArea);
|
|
|
}
|
|
|
return sknResults;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
* @param userPriceAreas
|
|
|
* @param sknPriceArea
|
|
|
* @return
|
|
|
*/
|
|
|
private boolean isLikePriceArea(List<Integer> userPriceAreas,Integer sknPriceArea){
|
|
|
private boolean isLikePriceArea(List<Integer> userPriceAreas, Integer sknPriceArea) {
|
|
|
try {
|
|
|
if(userPriceAreas==null || userPriceAreas.isEmpty()){
|
|
|
if (userPriceAreas == null || userPriceAreas.isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
|
return userPriceAreas.contains(sknPriceArea);
|
|
|
}catch (Exception e){
|
|
|
RECALL_NEW_LOGGER.error(e.getMessage(),e);
|
|
|
} catch (Exception e) {
|
|
|
RECALL_NEW_LOGGER.error(e.getMessage(), e);
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 价格带左右各扩展一位
|
|
|
*
|
|
|
* @param userPriceAreas
|
|
|
* @param sknPriceArea
|
|
|
* @return
|
|
|
*/
|
|
|
private boolean isLikePriceAreaWithBStrategy(List<Integer> userPriceAreas,Integer sknPriceArea){
|
|
|
private boolean isLikePriceAreaWithBStrategy(List<Integer> userPriceAreas, Integer sknPriceArea) {
|
|
|
Collections.sort(userPriceAreas);
|
|
|
int min = userPriceAreas.get(0);
|
|
|
int max = userPriceAreas.get(userPriceAreas.size()-1);
|
|
|
int leftCount = min<=1 ? 0 : max>=7 ? 2 : 1;
|
|
|
int rightCount = min<=1 ? 2 : max>=7 ? 0 : 1;
|
|
|
for(int i = 1;i<=leftCount;i++){
|
|
|
if(min-i>=1){
|
|
|
userPriceAreas.add(0,min-i);
|
|
|
int max = userPriceAreas.get(userPriceAreas.size() - 1);
|
|
|
int leftCount = min <= 1 ? 0 : max >= 7 ? 2 : 1;
|
|
|
int rightCount = min <= 1 ? 2 : max >= 7 ? 0 : 1;
|
|
|
for (int i = 1; i <= leftCount; i++) {
|
|
|
if (min - i >= 1) {
|
|
|
userPriceAreas.add(0, min - i);
|
|
|
}
|
|
|
}
|
|
|
for(int i = 1;i<=rightCount;i++){
|
|
|
if(max+i<=7){
|
|
|
userPriceAreas.add(max+i);
|
|
|
for (int i = 1; i <= rightCount; i++) {
|
|
|
if (max + i <= 7) {
|
|
|
userPriceAreas.add(max + i);
|
|
|
}
|
|
|
}
|
|
|
return userPriceAreas.contains(sknPriceArea);
|
...
|
...
|
@@ -200,7 +192,7 @@ public class UserRecallResponseBuilder { |
|
|
* @param sknResultList
|
|
|
* @param uid
|
|
|
*/
|
|
|
private List<RecallMergerResult.SknResult> doCalScoreAndSort(List<RecallMergerResult.SknResult> sknResultList, int uid) {
|
|
|
private List<RecallMergerResult.SknResult> doCalScoreAndSort(List<RecallMergerResult.SknResult> sknResultList, int uid, int pageSize) {
|
|
|
//1、获取用户向量
|
|
|
Map<String, String> paramMap = new HashMap<>();
|
|
|
paramMap.put("uid", "" + uid);
|
...
|
...
|
@@ -217,52 +209,61 @@ public class UserRecallResponseBuilder { |
|
|
continue;
|
|
|
}
|
|
|
//2)如果是推荐出来的,则单独加分[以减分的方式依次保证顺序]
|
|
|
if (strategy.equals(StrategyEnum.REC_SKN)||strategy.equals(StrategyEnum.RT_SIM_SKN)) {
|
|
|
if (strategy.equals(StrategyEnum.REC_SKN) || strategy.equals(StrategyEnum.RT_SIM_SKN)) {
|
|
|
sknResult.setScore((double) recommendSknIndex--);
|
|
|
continue;
|
|
|
}
|
|
|
//3)向量计算
|
|
|
score = productFeatureFactorHepler.calProductFeatureFactor(userFeatureFactor, sknResult.getFactor());
|
|
|
|
|
|
//4)如果是直通车商品,则拿人气和向量综合评分
|
|
|
if (strategy.equals(StrategyEnum.DIRECT_TRAIN)) {
|
|
|
score = score * sknResult.getHeatValue();
|
|
|
}
|
|
|
|
|
|
//5)如果满足品类价格带偏好,则加分
|
|
|
if (sknResult.isLikePriceArea()) {
|
|
|
//5)如果当前skn不是兜底召回的,则加分【将兜底和非兜底的拆分】
|
|
|
if (!this.isCommonStrategy(strategy)) {
|
|
|
score = score + 1000;
|
|
|
}
|
|
|
|
|
|
//6)如果兜底策略不参与评分,并且当前skn不是兜底召回的,则加分【将兜底和非兜底的拆分】
|
|
|
if (!searchDynamicConfigService.searchPersionalNewStrategyCommonJoinScoreOpen() && !strategy.equals(StrategyEnum.COMMON)) {
|
|
|
//6)如果满足品类价格带偏好,则加分
|
|
|
if (sknResult.isLikePriceArea()) {
|
|
|
score = score + 500;
|
|
|
}
|
|
|
sknResult.setScore(score);
|
|
|
}
|
|
|
//3、按得分排序-得分高的在前面
|
|
|
//3、兜底数据处理
|
|
|
Iterator<RecallMergerResult.SknResult> iterator = sknResultList.iterator();
|
|
|
List<RecallMergerResult.SknResult> commonResultList = new ArrayList<>();
|
|
|
List<RecallMergerResult.SknResult> commonHeatValueResultList = new ArrayList<>();
|
|
|
List<RecallMergerResult.SknResult> commonCtrValueResultList = new ArrayList<>();
|
|
|
while (iterator.hasNext()) {
|
|
|
RecallMergerResult.SknResult sknResult = iterator.next();
|
|
|
if (sknResult.equals(StrategyEnum.COMMON)) {
|
|
|
commonResultList.add(sknResult);
|
|
|
iterator.remove();
|
|
|
}
|
|
|
if (sknResult.equals(StrategyEnum.COMMON_HEAT_VALUE)) {
|
|
|
commonHeatValueResultList.add(sknResult);
|
|
|
iterator.remove();
|
|
|
}
|
|
|
if (sknResult.equals(StrategyEnum.COMMON_CTR_VALUE)) {
|
|
|
commonCtrValueResultList.add(sknResult);
|
|
|
iterator.remove();
|
|
|
}
|
|
|
}
|
|
|
commonHeatValueResultList = CollectionUtils.safeSubList(commonHeatValueResultList, 0, pageSize);
|
|
|
commonCtrValueResultList = CollectionUtils.safeSubList(commonCtrValueResultList, 0, pageSize);
|
|
|
sknResultList.addAll(commonHeatValueResultList);
|
|
|
sknResultList.addAll(commonCtrValueResultList);
|
|
|
//3.1:如果是数量少于一页,则加会兜底的数据
|
|
|
if (sknResultList.size() < pageSize) {
|
|
|
sknResultList.addAll(commonResultList);
|
|
|
}
|
|
|
//4、按得分排序
|
|
|
Collections.sort(sknResultList, (o1, o2) -> o2.getScore().compareTo(o1.getScore()));
|
|
|
return sknResultList;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 精排-品类品牌平衡
|
|
|
*
|
|
|
* @param sknResultList
|
|
|
*/
|
|
|
private List<RecallMergerResult.SknResult> doBalance(List<RecallMergerResult.SknResult> sknResultList) {
|
|
|
List<RecallMergerResult.SknResult> results = productListSortService.sortProductList(sknResultList, new ProductListSortKey<RecallMergerResult.SknResult>() {
|
|
|
@Override
|
|
|
public String getSortKey(RecallMergerResult.SknResult product) {
|
|
|
return new StringBuilder().append(product.getBrandId()).append("_").append(product.getMiddleSortId()).toString();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public int getMaxCount() {
|
|
|
return 4;
|
|
|
}
|
|
|
});
|
|
|
return results;
|
|
|
private boolean isCommonStrategy(StrategyEnum strategy) {
|
|
|
return strategy.equals(StrategyEnum.COMMON) || strategy.equals(StrategyEnum.COMMON_HEAT_VALUE) || strategy.equals(StrategyEnum.COMMON_CTR_VALUE);
|
|
|
}
|
|
|
|
|
|
/**
|
...
|
...
|
@@ -289,21 +290,21 @@ public class UserRecallResponseBuilder { |
|
|
iterator = sknResultList.iterator();
|
|
|
while (iterator.hasNext()) {
|
|
|
RecallMergerResult.SknResult sknResult = iterator.next();
|
|
|
if (!Arrays.asList(StrategyEnum.DIRECT_TRAIN,StrategyEnum.REC_SKN,StrategyEnum.RT_SIM_SKN).contains(sknResult.getStrategy())){
|
|
|
if (!Arrays.asList(StrategyEnum.DIRECT_TRAIN, StrategyEnum.REC_SKN, StrategyEnum.RT_SIM_SKN).contains(sknResult.getStrategy())) {
|
|
|
results.add(sknResult);
|
|
|
iterator.remove();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 3、插入【REC_SKN】的商品-随机插入
|
|
|
this.addByIndexIndex(sknResultList, results, 1, 2, (sknResult -> StrategyEnum.REC_SKN.equals(sknResult.getStrategy())),dropTransfer);
|
|
|
this.addByIndexIndex(sknResultList, results, 1, 2, (sknResult -> StrategyEnum.REC_SKN.equals(sknResult.getStrategy())), dropTransfer);
|
|
|
|
|
|
// 4、插入【RT_SIM_SKN】的商品-随机插入
|
|
|
this.addByIndexIndex(sknResultList, results, 2, 3, (sknResult -> StrategyEnum.RT_SIM_SKN.equals(sknResult.getStrategy())),dropTransfer);
|
|
|
this.addByIndexIndex(sknResultList, results, 2, 3, (sknResult -> StrategyEnum.RT_SIM_SKN.equals(sknResult.getStrategy())), dropTransfer);
|
|
|
|
|
|
// 4、插入【直通车】商品-随机插入
|
|
|
// 5、插入【直通车】商品-随机插入
|
|
|
int directTrainIndexInterval = searchDynamicConfigService.directTrainIndexInterval();
|
|
|
this.addByIndexIndex(sknResultList, results, 4, directTrainIndexInterval, (sknResult -> StrategyEnum.DIRECT_TRAIN.equals(sknResult.getStrategy())),dropTransfer);
|
|
|
this.addByIndexIndex(sknResultList, results, 4, directTrainIndexInterval, (sknResult -> StrategyEnum.DIRECT_TRAIN.equals(sknResult.getStrategy())), dropTransfer);
|
|
|
|
|
|
return results;
|
|
|
}
|
...
|
...
|
@@ -311,9 +312,9 @@ public class UserRecallResponseBuilder { |
|
|
/**
|
|
|
* 单策略召回时,超出数量直接丢弃
|
|
|
*/
|
|
|
private static Transfer<RecallMergerResult.SknResult,Boolean> dropTransfer = (sknResult) ->sknResult.isOnlyOneStrategy()?true:false;
|
|
|
private static Transfer<RecallMergerResult.SknResult, Boolean> dropTransfer = (sknResult) -> sknResult.isOnlyOneStrategy() ? true : false;
|
|
|
|
|
|
private static <T> void addByIndexIndex(List<T> fromList, List<T> toList,int fromIndex, int indexInterval,Transfer<T, Boolean> match,Transfer<T, Boolean> drop) {
|
|
|
private static <T> void addByIndexIndex(List<T> fromList, List<T> toList, int fromIndex, int indexInterval, Transfer<T, Boolean> match, Transfer<T, Boolean> drop) {
|
|
|
Iterator<T> iterator = fromList.iterator();
|
|
|
while (iterator.hasNext()) {
|
|
|
T object = iterator.next();
|
...
|
...
|
@@ -329,35 +330,35 @@ public class UserRecallResponseBuilder { |
|
|
//超出新列表的长度,判断是否直接丢弃
|
|
|
if (fromIndex <= toListNewSize) {
|
|
|
toList.add(fromIndex, object);
|
|
|
}else if(drop !=null && !drop.transfer(object)){
|
|
|
} else if (drop != null && !drop.transfer(object)) {
|
|
|
toList.add(object);
|
|
|
}
|
|
|
iterator.remove();
|
|
|
fromIndex = fromIndex + indexInterval;
|
|
|
if(indexInterval>1){
|
|
|
fromIndex = fromIndex + (int)(indexInterval * Math.random());
|
|
|
}else{
|
|
|
fromIndex = fromIndex + (Math.random()>0.5?1:0);
|
|
|
if (indexInterval > 1) {
|
|
|
fromIndex = fromIndex + (int) (indexInterval * Math.random());
|
|
|
} else {
|
|
|
fromIndex = fromIndex + (Math.random() > 0.5 ? 1 : 0);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
List<Integer> userPriceAreas = new ArrayList<>();
|
|
|
userPriceAreas.addAll(Arrays.asList(5,6,7));
|
|
|
userPriceAreas.addAll(Arrays.asList(5, 6, 7));
|
|
|
Collections.sort(userPriceAreas);
|
|
|
int min = userPriceAreas.get(0);
|
|
|
int max = userPriceAreas.get(userPriceAreas.size()-1);
|
|
|
int leftCount = min<=1 ? 0 : max>=7 ? 2 : 1;
|
|
|
int rightCount = min<=1 ? 2 : max>=7 ? 0 : 1;
|
|
|
for(int i = 1;i<=leftCount;i++){
|
|
|
if(min-i>=1){
|
|
|
userPriceAreas.add(0,min-i);
|
|
|
int max = userPriceAreas.get(userPriceAreas.size() - 1);
|
|
|
int leftCount = min <= 1 ? 0 : max >= 7 ? 2 : 1;
|
|
|
int rightCount = min <= 1 ? 2 : max >= 7 ? 0 : 1;
|
|
|
for (int i = 1; i <= leftCount; i++) {
|
|
|
if (min - i >= 1) {
|
|
|
userPriceAreas.add(0, min - i);
|
|
|
}
|
|
|
}
|
|
|
for(int i = 1;i<=rightCount;i++){
|
|
|
if(max+i<=7){
|
|
|
userPriceAreas.add(max+i);
|
|
|
for (int i = 1; i <= rightCount; i++) {
|
|
|
if (max + i <= 7) {
|
|
|
userPriceAreas.add(max + i);
|
|
|
}
|
|
|
}
|
|
|
System.out.println(userPriceAreas);
|
...
|
...
|
|