Authored by 胡古飞

男女频道降分考虑性别

1 package com.yoho.search.service.personalized; 1 package com.yoho.search.service.personalized;
2 2
3 -import com.yoho.search.core.personalized.BigDataRedisOper; 3 +import java.util.HashMap;
  4 +import java.util.List;
  5 +import java.util.Map;
  6 +
  7 +import org.apache.commons.lang3.StringUtils;
4 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.stereotype.Service; 9 import org.springframework.stereotype.Service;
6 10
  11 +import com.alibaba.fastjson.JSONArray;
  12 +import com.yoho.search.core.personalized.BigDataRedisOper;
  13 +
7 /** 14 /**
8 * Created by ginozhang on 2017/1/5. 15 * Created by ginozhang on 2017/1/5.
9 */ 16 */
@@ -12,6 +19,8 @@ public class PersonalizedRedisService { @@ -12,6 +19,8 @@ public class PersonalizedRedisService {
12 19
13 // 保存用户特征向量的key格式,比如 “1022102:w2v:20170103” 值是一个字符串,各个值之间用逗号分隔 20 // 保存用户特征向量的key格式,比如 “1022102:w2v:20170103” 值是一个字符串,各个值之间用逗号分隔
14 private static final String USER_FEATURE_KEY_TEMPLATE = "%s:w2v:%s"; 21 private static final String USER_FEATURE_KEY_TEMPLATE = "%s:w2v:%s";
  22 +
  23 + private static final String USER_GENDER_KEY= "%s:gender";
15 24
16 @Autowired 25 @Autowired
17 private BigDataRedisOper<?,?> bigDataRedisOper; 26 private BigDataRedisOper<?,?> bigDataRedisOper;
@@ -20,5 +29,39 @@ public class PersonalizedRedisService { @@ -20,5 +29,39 @@ public class PersonalizedRedisService {
20 String key = String.format(USER_FEATURE_KEY_TEMPLATE, uid, generateDate); 29 String key = String.format(USER_FEATURE_KEY_TEMPLATE, uid, generateDate);
21 return bigDataRedisOper.getValue(key); 30 return bigDataRedisOper.getValue(key);
22 } 31 }
  32 +
  33 + /**
  34 + * 获取用户性别维度
  35 + * @param uid
  36 + * @param generateDate
  37 + * @return
  38 + */
  39 + public Map<String,Float> getUserGenderFeature(String uid) {
  40 + String key = String.format(USER_GENDER_KEY, uid);
  41 + String userGenderWeightValue = bigDataRedisOper.getValue(key);
  42 + if(StringUtils.isBlank(userGenderWeightValue)){
  43 + return new HashMap<String, Float>();
  44 + }
  45 + Map<String,Float> result = new HashMap<String, Float>();
  46 + List<String> userGenderWeightList = JSONArray.parseArray(userGenderWeightValue, String.class);
  47 + for (String genderWeight : userGenderWeightList) {
  48 + String [] userGenderWeight = genderWeight.split(":");
  49 + result.put(userGenderWeight[0], Float.parseFloat(userGenderWeight[1]));
  50 + }
  51 + return result;
  52 + }
  53 +
  54 + public static void main(String[] args) {
  55 + String userGenderWeightValue = "['2:0.1686046511627907', '1:0.6957364341085271', '3:0.13565891472868216']";
  56 + Map<String,Float> result = new HashMap<String, Float>();
  57 + List<String> userGenderWeightList = JSONArray.parseArray(userGenderWeightValue, String.class);
  58 + for (String genderWeight : userGenderWeightList) {
  59 + String [] userGenderWeight = genderWeight.split(":");
  60 + result.put(userGenderWeight[0], Float.parseFloat(userGenderWeight[1]));
  61 + }
  62 + for (Map.Entry<String, Float> entry : result.entrySet()) {
  63 + System.out.println(entry.getKey()+ ":" + entry.getValue());
  64 + }
  65 + }
23 66
24 } 67 }
1 package com.yoho.search.service.service.helper; 1 package com.yoho.search.service.service.helper;
2 2
  3 +import java.util.ArrayList;
  4 +import java.util.List;
3 import java.util.Map; 5 import java.util.Map;
4 6
5 import org.apache.commons.lang.StringUtils; 7 import org.apache.commons.lang.StringUtils;
@@ -15,8 +17,10 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -15,8 +17,10 @@ import org.springframework.beans.factory.annotation.Autowired;
15 import org.springframework.stereotype.Component; 17 import org.springframework.stereotype.Component;
16 18
17 import com.yoho.search.service.personalized.PersonalVectorFeatureSearch; 19 import com.yoho.search.service.personalized.PersonalVectorFeatureSearch;
  20 +import com.yoho.search.service.personalized.PersonalizedRedisService;
18 import com.yoho.search.service.service.SearchDynamicConfigService; 21 import com.yoho.search.service.service.SearchDynamicConfigService;
19 import com.yoho.search.service.utils.SearchRequestParams; 22 import com.yoho.search.service.utils.SearchRequestParams;
  23 +import com.yoho.search.service.vo.PhysicalChannelScore;
20 24
21 @Component 25 @Component
22 public class FunctionScoreSearchHelper { 26 public class FunctionScoreSearchHelper {
@@ -27,6 +31,8 @@ public class FunctionScoreSearchHelper { @@ -27,6 +31,8 @@ public class FunctionScoreSearchHelper {
27 private PersonalVectorFeatureSearch personalVectorFeatureSearch; 31 private PersonalVectorFeatureSearch personalVectorFeatureSearch;
28 @Autowired 32 @Autowired
29 private SearchDynamicConfigService dynamicConfig; 33 private SearchDynamicConfigService dynamicConfig;
  34 + @Autowired
  35 + private PersonalizedRedisService personalizedRedisService;
30 36
31 static float globalWeight = 0.50f; 37 static float globalWeight = 0.50f;
32 38
@@ -52,10 +58,9 @@ public class FunctionScoreSearchHelper { @@ -52,10 +58,9 @@ public class FunctionScoreSearchHelper {
52 } 58 }
53 // 针对频道降分 59 // 针对频道降分
54 if (searchCommonHelper.isNeedDeScoreForChannel(paramMap)) { 60 if (searchCommonHelper.isNeedDeScoreForChannel(paramMap)) {
55 - QueryBuilder physicalChannelQueryBuilder = this.getPhysicalChannelQueryBuilder(paramMap);  
56 - if (physicalChannelQueryBuilder != null) {  
57 - float physicalChannelWeight = (float) dynamicConfig.getDeScorePhysicalChannelWeight();  
58 - functionScoreQueryBuilder.add(physicalChannelQueryBuilder, ScoreFunctionBuilders.weightFactorFunction(physicalChannelWeight)); 61 + List<PhysicalChannelScore> physicalChannelScores = this.getPhysicalChannelQueryBuilder(paramMap);
  62 + for (PhysicalChannelScore physicalChannelScore : physicalChannelScores) {
  63 + functionScoreQueryBuilder.add(physicalChannelScore.getQueryBuilder(), ScoreFunctionBuilders.weightFactorFunction(physicalChannelScore.getWeight()));
59 } 64 }
60 } 65 }
61 functionScoreQueryBuilder.boostMode(CombineFunction.MULT); 66 functionScoreQueryBuilder.boostMode(CombineFunction.MULT);
@@ -75,39 +80,69 @@ public class FunctionScoreSearchHelper { @@ -75,39 +80,69 @@ public class FunctionScoreSearchHelper {
75 return functionScoreQueryBuilder; 80 return functionScoreQueryBuilder;
76 } 81 }
77 82
78 - private QueryBuilder getPhysicalChannelQueryBuilder(Map<String, String> paramMap) { 83 + /**
  84 + * 获取将要降分的性别权重
  85 + *
  86 + * @param uid
  87 + * @param baseScore
  88 + * @param descoreGender
  89 + * @return
  90 + */
  91 + public float getDescoreGenderWeight(String uid, float baseScore, String descoreGender) {
  92 + Map<String, Float> userGenderFloat = personalizedRedisService.getUserGenderFeature(uid);
  93 + Float userGenderWeight = userGenderFloat.get(descoreGender);
  94 + if (userGenderWeight == null) {
  95 + return baseScore;
  96 + }
  97 + return baseScore * (1 + userGenderWeight);
  98 + }
  99 +
  100 + private List<PhysicalChannelScore> getPhysicalChannelQueryBuilder(Map<String, String> paramMap) {
  101 + float physicalChannelWeight = (float) dynamicConfig.getDeScorePhysicalChannelWeight();
  102 + String uid = paramMap.get("uid");
  103 +
79 String physicalChannel = paramMap.get(SearchRequestParams.PHYSICAL_CHANNEL); 104 String physicalChannel = paramMap.get(SearchRequestParams.PHYSICAL_CHANNEL);
80 - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();  
81 TermsQueryBuilder physicalChannelsTermsQueryBuilder = QueryBuilders.termsQuery("physicalChannels", physicalChannel); 105 TermsQueryBuilder physicalChannelsTermsQueryBuilder = QueryBuilders.termsQuery("physicalChannels", physicalChannel);
  106 +
  107 + List<PhysicalChannelScore> results = new ArrayList<PhysicalChannelScore>();
  108 +
82 // 潮童频道,对非潮童频道的或成人的商品降分 109 // 潮童频道,对非潮童频道的或成人的商品降分
83 if (physicalChannel.equals("3")) { 110 if (physicalChannel.equals("3")) {
  111 + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
84 boolQueryBuilder.should(QueryBuilders.boolQuery().mustNot(physicalChannelsTermsQueryBuilder)); 112 boolQueryBuilder.should(QueryBuilders.boolQuery().mustNot(physicalChannelsTermsQueryBuilder));
85 boolQueryBuilder.should(QueryBuilders.termsQuery("ageLevel", "1")); 113 boolQueryBuilder.should(QueryBuilders.termsQuery("ageLevel", "1"));
86 - return boolQueryBuilder; 114 + results.add(new PhysicalChannelScore(boolQueryBuilder, physicalChannelWeight));
  115 + return results;
87 } 116 }
88 // 创意生活频道,对非创意生活频道的商品降分 117 // 创意生活频道,对非创意生活频道的商品降分
89 if (physicalChannel.equals("4")) { 118 if (physicalChannel.equals("4")) {
90 - return boolQueryBuilder.mustNot(physicalChannelsTermsQueryBuilder); 119 + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().mustNot(physicalChannelsTermsQueryBuilder);
  120 + results.add(new PhysicalChannelScore(boolQueryBuilder, physicalChannelWeight));
  121 + return results;
91 } 122 }
92 -  
93 // 如果用户在男女频道的意图中包含了明显的性别意图,则只针对频道降分 123 // 如果用户在男女频道的意图中包含了明显的性别意图,则只针对频道降分
94 if (isUserSearchContainGender(paramMap)) { 124 if (isUserSearchContainGender(paramMap)) {
95 - return boolQueryBuilder.mustNot(physicalChannelsTermsQueryBuilder); 125 + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().mustNot(physicalChannelsTermsQueryBuilder);
  126 + results.add(new PhysicalChannelScore(boolQueryBuilder, physicalChannelWeight));
  127 + return results;
96 } 128 }
97 -  
98 // 男生频道,对非男生频道的商品或者性别女的降分 129 // 男生频道,对非男生频道的商品或者性别女的降分
99 if (physicalChannel.equals("1")) { 130 if (physicalChannel.equals("1")) {
  131 + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
100 boolQueryBuilder.should(QueryBuilders.boolQuery().mustNot(physicalChannelsTermsQueryBuilder)); 132 boolQueryBuilder.should(QueryBuilders.boolQuery().mustNot(physicalChannelsTermsQueryBuilder));
101 boolQueryBuilder.should(QueryBuilders.termsQuery("gender", "2")); 133 boolQueryBuilder.should(QueryBuilders.termsQuery("gender", "2"));
102 - return boolQueryBuilder; 134 + results.add(new PhysicalChannelScore(boolQueryBuilder, this.getDescoreGenderWeight(uid, physicalChannelWeight, "2")));
  135 + return results;
103 } 136 }
104 // 女生频道,对非女生频道的商品或者性别男的降分 137 // 女生频道,对非女生频道的商品或者性别男的降分
105 if (physicalChannel.equals("2")) { 138 if (physicalChannel.equals("2")) {
  139 + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
106 boolQueryBuilder.should(QueryBuilders.boolQuery().mustNot(physicalChannelsTermsQueryBuilder)); 140 boolQueryBuilder.should(QueryBuilders.boolQuery().mustNot(physicalChannelsTermsQueryBuilder));
107 boolQueryBuilder.should(QueryBuilders.termsQuery("gender", "1")); 141 boolQueryBuilder.should(QueryBuilders.termsQuery("gender", "1"));
108 - return boolQueryBuilder; 142 + results.add(new PhysicalChannelScore(boolQueryBuilder, this.getDescoreGenderWeight(uid, physicalChannelWeight, "1")));
  143 + return results;
109 } 144 }
110 - return null; 145 + return new ArrayList<PhysicalChannelScore>();
111 } 146 }
112 147
113 private boolean isUserSearchContainGender(Map<String, String> paramMap) { 148 private boolean isUserSearchContainGender(Map<String, String> paramMap) {
  1 +package com.yoho.search.service.vo;
  2 +
  3 +import org.elasticsearch.index.query.QueryBuilder;
  4 +
  5 +public class PhysicalChannelScore {
  6 +
  7 + QueryBuilder queryBuilder;
  8 + float weight;
  9 +
  10 + public PhysicalChannelScore(QueryBuilder queryBuilder, float weight) {
  11 + super();
  12 + this.queryBuilder = queryBuilder;
  13 + this.weight = weight;
  14 + }
  15 +
  16 + public QueryBuilder getQueryBuilder() {
  17 + return queryBuilder;
  18 + }
  19 +
  20 + public float getWeight() {
  21 + return weight;
  22 + }
  23 +}