Authored by 胡古飞

去除老个性化搜索的逻辑

package com.yoho.search.service.personalized;
import com.yoho.search.base.utils.HttpServletRequestUtils;
import com.yoho.search.service.service.SearchDynamicConfigService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
... ... @@ -17,8 +19,6 @@ import java.util.Map;
@Service
public class PersonalVectorFeatureSearch {
private static final Logger logger = LoggerFactory.getLogger(PersonalVectorFeatureSearch.class);
private static final Logger PERSONALIZED = LoggerFactory.getLogger("PERSONALIZED");
... ... @@ -32,19 +32,23 @@ public class PersonalVectorFeatureSearch {
@Autowired
private PersonalizedRedisService personalizedRedisService;
public void addPersonalizedScriptScore(FunctionScoreQueryBuilder functionScoreQueryBuilder, String uid) {
public void addPersonalizedScriptScore(FunctionScoreQueryBuilder functionScoreQueryBuilder, Map<String,String> paramMap) {
// 1. 获取特征向量版本(即生成时间,该时间需要与skn的生成时间一致才有意义)
String vectorFeatureVersion = searchDynamicConfigService.personalizedSearchVersion();
if (StringUtils.isEmpty(vectorFeatureVersion) || "-1".equals(vectorFeatureVersion)) {
return;
}
String uid = paramMap.get("uid");
// 2. 获取用户的特征向量
String userVectorFeature = personalizedRedisService.getUserVectorFeature(uid, vectorFeatureVersion);
if (StringUtils.isEmpty(userVectorFeature)) {
return;
}
PERSONALIZED.info("do personal search , paramString is [{}]", HttpServletRequestUtils.genParamString(paramMap));
// 3. 传入参数调用脚本
// field -> productindex索引种保存skn特征向量的字段名
// userFeatureFactors -> 用户特征向量值,多个值之间用逗号分隔
... ... @@ -56,7 +60,6 @@ public class PersonalVectorFeatureSearch {
scriptParams.put("vectorFeatureVersion", vectorFeatureVersion);
scriptParams.put("baseConstant", BASE_CONSTANT);
scriptParams.put("factorConstant", FACTOR_CONSTANT);
PERSONALIZED.info("[PersonalVectorFeatureSearch.build][scriptParams={}]", scriptParams);
Script script = new Script("feature_factor_vector_score", ScriptService.ScriptType.INLINE, "native", scriptParams);
functionScoreQueryBuilder.add(ScoreFunctionBuilders.scriptFunction(script));
}
... ... @@ -106,7 +109,7 @@ public class PersonalVectorFeatureSearch {
// 3. 计算相关性得分
productVectorFeatureMap.forEach((skn, vector) -> {
Map<String, Object> content = new HashMap();
Map<String, Object> content = new HashMap<String, Object>();
content.put("vector", vector);
content.put("score", calculateScore(vector, vectorFeatureVersion, userFeatureFactors, userFeatureVectorNorm));
scoreMap.put(skn, content);
... ...
... ... @@ -10,16 +10,15 @@ import org.springframework.stereotype.Service;
@Service
public class PersonalizedRedisService {
// 保存用户特征向量的key格式,比如 “1022102:w2v:20170103” 值是一个字符串,各个值之间用逗号分隔
private static final String USER_FEATURE_KEY_TEMPLATE = "%s:w2v:%s";
// 保存用户特征向量的key格式,比如 “1022102:w2v:20170103” 值是一个字符串,各个值之间用逗号分隔
private static final String USER_FEATURE_KEY_TEMPLATE = "%s:w2v:%s";
@Autowired
private BigDataRedisOper bigDataRedisOper;
@Autowired
private BigDataRedisOper<?,?> bigDataRedisOper;
public String getUserVectorFeature(String uid, String generateDate)
{
String key = String.format(USER_FEATURE_KEY_TEMPLATE, uid, generateDate);
return bigDataRedisOper.getValue(key);
}
public String getUserVectorFeature(String uid, String generateDate) {
String key = String.format(USER_FEATURE_KEY_TEMPLATE, uid, generateDate);
return bigDataRedisOper.getValue(key);
}
}
... ...
package com.yoho.search.service.personalized;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.common.lucene.search.function.FieldValueFactorFunction;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.index.query.functionscore.fieldvaluefactor.FieldValueFactorFunctionBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yoho.search.base.utils.HttpServletRequestUtils;
import com.yoho.search.core.personalized.PConsts;
import com.yoho.search.service.personalized.model.SearchFeature;
import com.yoho.search.service.service.SearchDynamicConfigService;
@Service
public final class PersonalizedSearch {
@Autowired
private UserFeaturesRedis userFeatures;
@Autowired
private SearchDynamicConfigService dynamicConfig;
private static final Logger PERSONALIZED = LoggerFactory.getLogger("PERSONALIZED");
private String getUidFromParamMap(Map<String, String> paramMap) {
return paramMap.get("uid");
}
private String getPageIdFromParamMap(Map<String, String> paramMap) {
String pageId = paramMap.get("pageId");
if (StringUtils.isBlank(pageId)) {
pageId = PConsts.PAGE_ID_NEW;
}
return pageId;
}
private Map<String, List<SearchFeature>> getUserGroupSearchFeatures(String uid, String pageId) {
// 1、获取用户信息和页面信息
List<SearchFeature> sfRedis = userFeatures.getUserFeaturesFromRedis(uid, pageId);
if (sfRedis == null || sfRedis.isEmpty()) {
return new HashMap<String, List<SearchFeature>>();
}
// 按照boost进行排序
Collections.sort(sfRedis, new Comparator<SearchFeature>() {
@Override
public int compare(SearchFeature left, SearchFeature right) {
Float fleft = Float.valueOf(left.getBoost());
Float fright = Float.valueOf(right.getBoost());
return fright.compareTo(fleft);
}
});
// 2、按用户特征的维度分组
Map<String, List<SearchFeature>> useFeatureGroup = new HashMap<String, List<SearchFeature>>();
for (SearchFeature searchFeature : sfRedis) {
String targetParam = searchFeature.getTargetParam();
List<SearchFeature> userFeatureList = useFeatureGroup.get(targetParam);
if (userFeatureList == null) {
userFeatureList = new ArrayList<>();
useFeatureGroup.put(targetParam, userFeatureList);
}
if (searchFeature.getBoost() <= 0) {
continue;
}
if (userFeatureList.size() > dynamicConfig.userPersonalizedMaxCount()) {
continue;
}
userFeatureList.add(searchFeature);
}
return useFeatureGroup;
}
public void addPersonalizedFieldValueFactor(FunctionScoreQueryBuilder functionScoreQueryBuilder, Map<String, String> paramMap) {
String pageId = this.getPageIdFromParamMap(paramMap);
float factor = getFunctionScoreFactor(pageId);
String fieldName = getFuncScoreField(pageId);
FieldValueFactorFunctionBuilder fieldValueFactorFunctionBuilder = ScoreFunctionBuilders.fieldValueFactorFunction(fieldName).factor(factor)
.modifier(FieldValueFactorFunction.Modifier.LOG2P).missing(PConsts.PRODUCT_FUNCTION_MISSING_VALUE);
functionScoreQueryBuilder.add(fieldValueFactorFunctionBuilder);
}
public void addPersonalizedUserFetureFactor(FunctionScoreQueryBuilder functionScoreQueryBuilder, Map<String, String> paramMap) {
PERSONALIZED.info("do personal search , paramString is [{}]", HttpServletRequestUtils.genParamString(paramMap));
// 1、获取用户特征息信息
String uid = this.getUidFromParamMap(paramMap);
String pageId = this.getPageIdFromParamMap(paramMap);
Map<String, List<SearchFeature>> userGroupFeatures = this.getUserGroupSearchFeatures(uid, pageId);
if (userGroupFeatures == null || userGroupFeatures.isEmpty()) {
return;
}
// 2、保存已经操作过的值
Set<String> filteredKeySet = new HashSet<String>();
for (List<SearchFeature> userFeatures : userGroupFeatures.values()) {
// 2.1 、获取加分基准
float maxBoost = this.getMaxBoost(userFeatures);// 获取用户最大的boost,已调整boost值
boolean isFuzzySearch = this.isFuzzySearch(pageId);
// 2.2 、对用户权重 进行加分[以乘的方式]
for (SearchFeature searchFeature : userFeatures) {
float boost = searchFeature.getBoost();
if (boost <= 0) {
continue;
}
String filteredKey = searchFeature.getTargetParam() + "_" + searchFeature.getParamValues();
if (filteredKeySet.contains(filteredKey)) {
continue;
}
filteredKeySet.add(filteredKey);
float weight = boost / maxBoost;// 非模糊查询时,用户特征最大加分值为3
if (isFuzzySearch) {
weight = weight * 0.1f;// 模糊查询时,用户特征最大加分值为1.1
} else {
weight = weight * 2f;// 非模糊查询时,用户特征最大加分值为2[三种维度同时满足,则weight为8]
}
functionScoreQueryBuilder.add(QueryBuilders.termQuery(searchFeature.getTargetParam(), searchFeature.getParamValues()),
ScoreFunctionBuilders.weightFactorFunction(1 + weight));
}
}
}
private float getMaxBoost(List<SearchFeature> searchFeatures) {
float boost = 0f;
for (SearchFeature searchFeature : searchFeatures) {
float searchFeatureBoost = searchFeature.getBoost();
if (boost < searchFeatureBoost) {
boost = searchFeatureBoost;
}
}
return boost;
}
/**
* 当前页面是否是模糊搜索
*
* @param pageId
* @return
*/
private boolean isFuzzySearch(String pageId) {
if (!StringUtils.isBlank(pageId) && pageId.equals(PConsts.PAGE_ID_SEARCH)) {
return true;
}
return false;
}
private float getFunctionScoreFactor(String pageId) {
float factor = 0.0f;
// 对于模糊搜索, 需要把商品特征的权重得分降低, 其余的按其排序
if (isFuzzySearch(pageId)) {
factor = PConsts.FUZZY_SEARCH_FUNCTION_SCORE_FACTOR;
} else {
factor = PConsts.DEFAULT_FUNCTION_SCORE_FACTOR;
}
return factor;
}
private String getFuncScoreField(String pageId) {
String fieldName = PConsts.PAGE_ID_SEARCH_FIELD;
if (pageId != null) {
switch (pageId) {
case PConsts.PAGE_ID_NEW:
fieldName = PConsts.PAGE_ID_NEW_FIELD;
break;
case PConsts.PAGE_ID_BRAND:
fieldName = PConsts.PAGE_ID_BRAND_FIELD;
break;
case PConsts.PAGE_ID_SEARCH:
fieldName = PConsts.PAGE_ID_SEARCH_FIELD;
break;
case PConsts.PAGE_ID_SORT:
fieldName = PConsts.PAGE_ID_SORT_FIELD;
break;
default:
break;
}
}
return fieldName;
}
}
package com.yoho.search.service.personalized;
import com.yoho.search.core.personalized.BigDataRedisOper;
import com.yoho.search.core.personalized.PConsts;
import com.yoho.search.core.personalized.PersonalizedHelper;
import com.yoho.search.core.personalized.calculate.BoostCalculate;
import com.yoho.search.core.personalized.modal.FeatureRela;
import com.yoho.search.core.personalized.modal.UserFeatureRela;
import com.yoho.search.service.personalized.model.SearchFeature;
import com.yoho.search.service.personalized.model.UserFeature;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
@Service
public class UserFeaturesRedis {
@Autowired
private BigDataRedisOper<String, String> redisOper;
@Resource(name = "multipleSum")
private BoostCalculate boostCalculate;
private volatile Map<String, Map<String, String>> pageBoosts = null;
private volatile Map<String, Map<String, String>> _pageBoosts = null;
private Map<String, FeatureRela> userFtRelas = null; // 用户特征
private Map<String, String> userFbRelas = null; // 用户特征与页面boost对应关系
@PostConstruct
public void init() {
userFtRelas = PersonalizedHelper.getInstance().getUserFtRelas();
userFbRelas = tansUserFtRelasToBoosts(userFtRelas);
pageBoosts = loadPageBoosts();
}
/**
* 定时重载页面权重数据
*/
@Scheduled(cron="0 0 3 * * ?")
public void reload() {
_pageBoosts = pageBoosts;
pageBoosts = loadPageBoosts();
if (pageBoosts == null) { // 重载失败回退掉
pageBoosts = _pageBoosts;
}
return;
}
/**
* 根据uid从大数据的redis里获取用户特征以及权重值
* @param uid
*/
public List<SearchFeature> getUserFeaturesFromRedis(String uid, String pageId) {
String key = PConsts.USER_FEATURE_SUFFIX + uid;
Map<String, String> features = redisOper.entries(key);
if (features.isEmpty()) {
return null;
}
Map<String, String> featureBoosts = null;
if (!StringUtils.isBlank(pageId)) {
if (pageBoosts.containsKey(pageId)) {
featureBoosts = pageBoosts.get(pageId);
}
}
List<SearchFeature> listRet = new ArrayList<SearchFeature>();
List<UserFeature> userFeatures = tansUserFeature(features);
for (UserFeature userFeature : userFeatures) {
String targetParam = userFeature.getTargetParam();
String paramValues = userFeature.getFid();
float boost = boostCalculate.calculate(userFeature.getFeatureVals(), featureBoosts,
userFeature.getFeatureZooms());
if (!equalZero(boost)) { // 权重值为0, 则跳过该特征值
listRet.add(new SearchFeature(targetParam, paramValues, boost));
}
}
return listRet;
}
private boolean equalZero(float boost) {
return Math.abs(boost) < 0.00001;
}
private List<UserFeature> tansUserFeature(Map<String, String> features) {
List<UserFeature> userFeatures = new ArrayList<UserFeature>();
for (Map.Entry<String, String> entry : features.entrySet()) {
String hk = entry.getKey();
String hv = entry.getValue();
String[] hks = hk.split(":");
if (hks.length == 2) {
String fKey = hks[0];
String fVal = hks[1];
UserFeatureRela rela = (UserFeatureRela) userFtRelas.get(fKey);
if (rela != null && rela.getTarget() != null) {
UserFeature userFeature = new UserFeature(fVal, rela.getTarget());
int offset = userFeatures.indexOf(userFeature);
if (offset >= 0) {
userFeatures.get(offset).addFeatureAndValue(fKey, hv, rela.getZoom());
} else {
userFeature.addFeatureAndValue(fKey, hv, rela.getZoom());
userFeatures.add(userFeature);
}
} else {
continue;
}
}
}
return userFeatures;
}
private Map<String, Map<String, String>> loadPageBoosts() {
Map<String, Map<String, String>> pageBoosts = new HashMap<String, Map<String, String>>();
for (int i = 0; i < PConsts.pageIds.length; ++i) {
String key = PConsts.PAGE_BOOSTS_SUFFIX + PConsts.pageIds[i];
Map<String, String> boosts = redisOper.entries(key);
if (boosts.isEmpty()) {
continue;
}
Map<String, String> fboosts = new HashMap<String, String>();
for (Map.Entry<String, String> entry : boosts.entrySet()) {
String hkey = entry.getKey();
if (userFbRelas.containsKey(hkey)) {
fboosts.put(userFbRelas.get(hkey), entry.getValue());
}
}
pageBoosts.put(PConsts.pageIds[i], fboosts);
}
return pageBoosts;
}
private Map<String, String> tansUserFtRelasToBoosts(Map<String, FeatureRela> relas) {
Map<String, String> fbRelas = new HashMap<String, String>();
for (FeatureRela rela : relas.values()) {
String boostMark = rela.getBoost();
String feature = rela.getFeature();
fbRelas.put(boostMark, feature);
}
return fbRelas;
}
/**
* 返回大数据pageId权重信息,用于调试和问题定位。
* @param pageId 页面ID,非法的ID则查询全部
* @return 大数据pageId权重信息
*/
public Map<String, Map<String, String>> getBigDataPageBoosts(String pageId) {
Map<String, Map<String, String>> pageBoosts = new HashMap<String, Map<String, String>>();
if(Arrays.asList(PConsts.pageIds).contains(pageId)) {
// 查询单独的pageId
String key = PConsts.PAGE_BOOSTS_SUFFIX + pageId;
pageBoosts.put(pageId, redisOper.entries(key));
return pageBoosts;
}
for (int i = 0; i < PConsts.pageIds.length; ++i) {
String key = PConsts.PAGE_BOOSTS_SUFFIX + PConsts.pageIds[i];
pageBoosts.put(PConsts.pageIds[i], redisOper.entries(key));
}
return pageBoosts;
}
public Map<String, String> getBigDataUserBoost(String uid) {
String key = PConsts.USER_FEATURE_SUFFIX + uid;
return redisOper.entries(key);
}
}
package com.yoho.search.service.restapi;
import com.alibaba.fastjson.JSONObject;
import com.yoho.search.service.cache.LocalCacheService;
import com.yoho.search.service.personalized.PersonalVectorFeatureSearch;
import com.yoho.search.service.personalized.UserFeaturesRedis;
import com.yoho.search.service.service.SearchKeyWordService;
import com.yoho.search.service.servicenew.IProductListService;
import com.yoho.search.service.vo.KeyWordWithCount;
import com.yoho.search.service.vo.SearchApiResult;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse.AnalyzeToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
... ... @@ -16,9 +12,12 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.yoho.search.service.cache.LocalCacheService;
import com.yoho.search.service.personalized.PersonalVectorFeatureSearch;
import com.yoho.search.service.service.SearchKeyWordService;
import com.yoho.search.service.servicenew.IProductListService;
import com.yoho.search.service.vo.KeyWordWithCount;
import com.yoho.search.service.vo.SearchApiResult;
@Controller
@RequestMapping(value = "/tools")
... ... @@ -29,8 +28,6 @@ public class ToolsController {
@Autowired
private LocalCacheService localCacheService;
@Autowired
private UserFeaturesRedis UserFeaturesRedis;
@Autowired
private PersonalVectorFeatureSearch personalVectorFeatureSearch;
@Autowired
private IProductListService productListService;
... ... @@ -189,28 +186,15 @@ public class ToolsController {
return new SearchApiResult().setMessage("清除本地缓存成功");
}
@RequestMapping(method = RequestMethod.GET, value = "/pageBoost")
@ResponseBody
public SearchApiResult pageBoost(@RequestParam(defaultValue = "all") String pageId) throws Exception {
return new SearchApiResult().setData(UserFeaturesRedis.getBigDataPageBoosts(pageId));
}
@RequestMapping(method = RequestMethod.GET, value = "/userBoost")
@ResponseBody
public SearchApiResult userBoost(@RequestParam String uid) throws Exception {
return new SearchApiResult().setData(UserFeaturesRedis.getBigDataUserBoost(uid));
}
@RequestMapping(method = RequestMethod.GET, value = "/calVectorFeature")
@ResponseBody
public SearchApiResult calVectorFeature(@RequestParam String uid, @RequestParam String skns, @RequestParam String version) throws Exception {
SearchApiResult productListResult = productListService.productListBySknList(skns);
if(productListResult.getCode() != 200)
{
if (productListResult.getCode() != 200) {
return productListResult;
}
Map<String, String> productVectorFeatureMap = (Map<String, String>)productListResult.getData();
Map<String, String> productVectorFeatureMap = (Map<String, String>) productListResult.getData();
return new SearchApiResult().setData(personalVectorFeatureSearch.calVectorFeature(uid, version, productVectorFeatureMap));
}
}
... ...
package com.yoho.search.service.service.helper;
import com.yoho.search.service.personalized.PersonalVectorFeatureSearch;
import com.yoho.search.service.personalized.PersonalizedSearch;
import com.yoho.search.service.service.SearchDynamicConfigService;
import com.yoho.search.service.utils.SearchRequestParams;
import java.util.Map;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
... ... @@ -13,7 +11,9 @@ import org.elasticsearch.index.query.functionscore.weight.WeightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import com.yoho.search.service.personalized.PersonalVectorFeatureSearch;
import com.yoho.search.service.service.SearchDynamicConfigService;
import com.yoho.search.service.utils.SearchRequestParams;
@Component
public class FunctionScoreSearchHelper {
... ... @@ -21,8 +21,6 @@ public class FunctionScoreSearchHelper {
@Autowired
private SearchCommonHelper searchCommonHelper;
@Autowired
private PersonalizedSearch personalizedSearch;
@Autowired
private PersonalVectorFeatureSearch personalVectorFeatureSearch;
@Autowired
private SearchDynamicConfigService dynamicConfig;
... ... @@ -41,13 +39,7 @@ public class FunctionScoreSearchHelper {
ScoreFunctionBuilders.weightFactorFunction(1000));
}
if (searchCommonHelper.isNeedPersonalSearch(paramMap)) {
if(dynamicConfig.openVectorFeaturePersonalized()) {
personalVectorFeatureSearch.addPersonalizedScriptScore(functionScoreQueryBuilder, paramMap.get("uid"));
}
else {
personalizedSearch.addPersonalizedUserFetureFactor(functionScoreQueryBuilder, paramMap);
personalizedSearch.addPersonalizedFieldValueFactor(functionScoreQueryBuilder, paramMap);
}
personalVectorFeatureSearch.addPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);
}
if (searchCommonHelper.containGlobal(paramMap)) {
functionScoreQueryBuilder.add(QueryBuilders.termQuery("isGlobal", "Y"), genWeightFactorBuilder(globalWeight));
... ...
... ... @@ -267,12 +267,6 @@ public class SearchServiceHelper {
int[] sizeids = ConvertUtils.stringToIntArray(paramMap.get(SearchRequestParams.PARAM_SEARCH_SIZE), ",");
boolFilter.must(QueryBuilders.termsQuery("sizeIds", sizeids));
}
// promotion
if (paramMap.containsKey(SearchRequestParams.PARAM_SEARCH_PROMOTIONID) && StringUtils.isNotBlank(paramMap.get(SearchRequestParams.PARAM_SEARCH_PROMOTIONID))
&& !SearchRequestParams.PARAM_SEARCH_PROMOTIONID.equals(filterParamName)) {
int[] promotionids = ConvertUtils.stringToIntArray(paramMap.get(SearchRequestParams.PARAM_SEARCH_PROMOTIONID), ",");
boolFilter.must(QueryBuilders.termsQuery("promotionIds", promotionids));
}
// 销售价格
if (paramMap.containsKey(SearchRequestParams.PARAM_SEARCH_PRICE) && StringUtils.isNotBlank(paramMap.get(SearchRequestParams.PARAM_SEARCH_PRICE))
&& !SearchRequestParams.PARAM_SEARCH_PRICE.equals(filterParamName)) {
... ...
... ... @@ -82,8 +82,6 @@ public class SearchRequestParams {
public static final String SHOPS_PARAM_SHOPSTYPE = "shopsType";
public static final String SHOPS_PARAM_STATUS = "status";
public static final String SHOPS_PARAM_ISGLOBAL = "isGlobal";
public static final String PARAM_SEARCH_PROMOTIONID = "newPromotionId";
public static final String FIRST_PRODUCRSKN = "firstProductSkn";
... ...
package com.yoho.search.service.personalized;
import com.yoho.search.service.BaseTest;
import com.yoho.search.core.personalized.BigDataRedisOper;
import com.yoho.search.core.personalized.PConsts;
import com.yoho.search.core.personalized.calculate.BoostCalculate;
import com.yoho.search.core.personalized.calculate.impl.MultipleSumBoostCalculate;
import com.yoho.search.service.personalized.model.SearchFeature;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserFeaturesRedis.class)
public class UserFeaturesRedisTest extends BaseTest {
@InjectMocks
private UserFeaturesRedis userFeaturesRedis;
@Mock
BigDataRedisOper<String, String> redisOperMock;
@Spy
BoostCalculate boostCalcSpy = new MultipleSumBoostCalculate();
private Map<String, String> mockfeatures = new HashMap<String, String>();
private Map<String, String> mockPageBoosts = new HashMap<String, String>();
private Map<String, String> mockEmpty = new HashMap<String, String>();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.userFeaturesRedis.init();
PowerMockito.when(redisOperMock.entries(PConsts.USER_FEATURE_SUFFIX + "1400006")).thenReturn(mockfeatures);
PowerMockito.when(redisOperMock.entries(PConsts.PAGE_BOOSTS_SUFFIX + PConsts.PAGE_ID_NEW)).thenReturn(mockPageBoosts);
PowerMockito.when(redisOperMock.entries(PConsts.PAGE_BOOSTS_SUFFIX + PConsts.PAGE_ID_BRAND)).thenReturn(mockEmpty);
PowerMockito.when(redisOperMock.entries(PConsts.PAGE_BOOSTS_SUFFIX + PConsts.PAGE_ID_SORT)).thenReturn(mockEmpty);
}
@Test
public void testPageBoostsIsNull() throws Exception {
resetMockValue();
mockfeatures.put("s1:35", "12.5");
mockfeatures.put("s2:35", "35");
List<SearchFeature> list = userFeaturesRedis.getUserFeaturesFromRedis("1400006", PConsts.PAGE_ID_NEW);
SearchFeatureAssert sfAssert = new SearchFeatureAssert();
sfAssert.addSearchFeature(new SearchFeature("smallSortId", "35", 4750));
for (SearchFeature node : list) {
Assert.assertEquals(true, sfAssert.assertNode(node));
}
}
@Test
public void testMutliParamsWhenPageBoostsIsNull() throws Exception {
resetMockValue();
mockfeatures.put("s1:35", "12.5");
mockfeatures.put("s2:35", "35");
mockfeatures.put("s2:37", "35");
mockfeatures.put("b1:35", "12.5");
mockfeatures.put("b2:35", "35");
List<SearchFeature> list = userFeaturesRedis.getUserFeaturesFromRedis("1400006", PConsts.PAGE_ID_NEW);
SearchFeatureAssert sfAssert = new SearchFeatureAssert();
sfAssert.addSearchFeature(new SearchFeature("smallSortId", "35", 4750));
sfAssert.addSearchFeature(new SearchFeature("smallSortId", "37", 3500));
sfAssert.addSearchFeature(new SearchFeature("brandId", "35", 4750));
for (SearchFeature node : list) {
Assert.assertEquals(true, sfAssert.assertNode(node));
}
}
@Test
public void testOneParamWithPageBoost() throws Exception {
resetMockValue();
mockfeatures.put("s1:35", "12.4");
mockfeatures.put("s2:35", "35");
mockPageBoosts.put("f39", "5");
userFeaturesRedis.reload();
List<SearchFeature> list = userFeaturesRedis.getUserFeaturesFromRedis("1400006", PConsts.PAGE_ID_NEW);
SearchFeatureAssert sfAssert = new SearchFeatureAssert();
sfAssert.addSearchFeature(new SearchFeature("smallSortId", "35", 9700));
for (SearchFeature node : list) {
Assert.assertEquals(true, sfAssert.assertNode(node));
}
}
private void resetMockValue() {
mockfeatures.clear();
mockPageBoosts.clear();
mockEmpty.clear();
}
private static final class SearchFeatureAssert {
private List<SearchFeature> searchFeatures = new ArrayList<SearchFeature>();
public void addSearchFeature(SearchFeature sf) {
searchFeatures.add(sf);
}
public boolean assertNode(SearchFeature node) {
for (SearchFeature sf : searchFeatures) {
if (sf.equals(node)) {
return true;
}
}
return false;
}
}
}
package com.yoho.search.service.personalized.calculate;
import com.yoho.search.core.personalized.calculate.BoostCalculate;
import com.yoho.search.core.personalized.calculate.impl.MultipleSumBoostCalculate;
import com.yoho.search.core.personalized.calculate.impl.SimpleSumBoostCalculate;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
public class CalculateTest {
@Test
public void testSimpleCalulate() {
BoostCalculate calc = new SimpleSumBoostCalculate();
Map<String, String> featureVals = new HashMap<String, String>();
Map<String, String> featureBoosts = null;
Map<String, Integer> featureZooms = null;
featureVals.put("s1", "300");
featureVals.put("s2", "35.6");
Assert.assertEquals(335.6, calc.calculate(featureVals, featureBoosts, featureZooms), 0.0001);
featureBoosts = new HashMap<String, String>();
featureVals.put("s3", "24.4");
Assert.assertEquals(360, calc.calculate(featureVals, featureBoosts, featureZooms), 0.0001);
featureBoosts.put("s1", "0.1");
featureBoosts.put("s3", "10");
Assert.assertEquals(309.6, calc.calculate(featureVals, featureBoosts, featureZooms), 0.0001);
}
@Test
public void testMultiCalculateDefaultZoom() {
BoostCalculate calc = new MultipleSumBoostCalculate();
Map<String, String> featureVals = new HashMap<String, String>();
Map<String, String> featureBoosts = null;
Map<String, Integer> featureZooms = null;
featureVals.put("s1", "300");
featureVals.put("s2", "35.6");
Assert.assertEquals(33560, calc.calculate(featureVals, featureBoosts, featureZooms), 0.0001);
featureBoosts = new HashMap<String, String>();
featureVals.put("s3", "24.4");
Assert.assertEquals(36000, calc.calculate(featureVals, featureBoosts, featureZooms), 0.0001);
featureBoosts.put("s1", "0.1");
featureBoosts.put("s3", "10");
Assert.assertEquals(30960, calc.calculate(featureVals, featureBoosts, featureZooms), 0.0001);
}
@Test
public void testMultiCalculateWithZoom() {
BoostCalculate calc = new MultipleSumBoostCalculate();
Map<String, String> featureVals = new HashMap<String, String>();
Map<String, Integer> featureZooms = new HashMap<String, Integer>();
Map<String, String> featureBoosts = null;
featureVals.put("s1", "300");
featureVals.put("s2", "35.6");
featureZooms.put("s1", 10);
Assert.assertEquals(6560, calc.calculate(featureVals, featureBoosts, featureZooms), 0.001);
featureBoosts = new HashMap<String, String>();
featureVals.put("s3", "24.4");
featureZooms.put("s3", 2);
Assert.assertEquals(6608.8, calc.calculate(featureVals, featureBoosts, featureZooms), 0.001);
featureBoosts.put("s1", "0.1");
featureBoosts.put("s3", "10");
Assert.assertEquals(4348, calc.calculate(featureVals, featureBoosts, featureZooms), 0.001);
}
}