Authored by hugufei

skn召回使用本地缓存+redis缓存

package com.yoho.search.recall.scene.beans.cache;
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
import com.yoho.search.base.utils.ISearchConstants;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.common.cache.impls.EhCache;
import com.yoho.search.common.cache.impls.SearchRedis;
import com.yoho.search.common.cache.model.CacheObject;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.model.SearchResult;
import com.yoho.search.recall.scene.beans.persional.PageProductIdBitSetComponent;
import com.yoho.search.recall.scene.beans.strategy.impls.RecommendSknStrategy;
import com.yoho.search.recall.scene.models.common.ParamQueryFilter;
import com.yoho.search.recall.scene.models.personal.PageProductIdBitSet;
import com.yoho.search.recall.scene.models.req.RecallRequest;
import com.yoho.search.recall.scene.models.req.RecallRequestResponse;
import com.yoho.search.recall.scene.models.req.RecallResponse;
... ... @@ -21,31 +26,114 @@ import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class SknRecallCacheBean{
public class SknRecallCacheBean {
@Autowired
private SearchCommonService searchCommonService;
@Autowired
private PageProductIdBitSetComponent pageProductIdBitSetComponent;
@Autowired
private SknBaseInfoCacheBean sknBaseInfoCacheBean;
private SearchRedis searchRedis;
@Autowired
private EhCache ehCache;
private static final Boolean recallWithCache = true;
/**
* 将推荐的skn构造成召回对象
*
* @return
*/
public List<RecallRequestResponse> batchRecallRecommedSknList(UserRecallRequest userRecallRequest,List<Integer> recommedSknList,int maxReturnCount){
if(recommedSknList==null || recommedSknList.isEmpty()){
public List<RecallRequestResponse> batchRecallRecommedSknList(UserRecallRequest userRecallRequest, List<Integer> recommedSknList, int maxReturnCount) {
if (recommedSknList == null || recommedSknList.isEmpty()) {
return new ArrayList<>();
}
List<Integer> filterSknList = this.filterRecommedSknList(userRecallRequest,recommedSknList,maxReturnCount);
return this.buildResults(userRecallRequest,filterSknList);
List<Integer> filterSknList;
if (recallWithCache) {
filterSknList = this.filterRecommendWithCache(userRecallRequest, recommedSknList);
} else {
filterSknList = this.filterRecommedSknListByEs(userRecallRequest, recommedSknList);
}
return this.buildResults(userRecallRequest, filterSknList, maxReturnCount);
}
/**
* 从ehcahc或者redis中构造返回结果
* @param userRecallRequest
* @param recommedSknList
* @return
*/
private List<Integer> filterRecommendWithCache(UserRecallRequest userRecallRequest, List<Integer> recommedSknList) {
ParamQueryFilter paramQueryFilter = userRecallRequest.getParamQueryFilter();
RedisKeyBuilder redisKeyBuilder = pageProductIdBitSetComponent.genRedisKeyBuilder(paramQueryFilter);
//1、先从本地缓存获取-本地的是完整的bitset对象
CacheObject cacheObject = ehCache.get(redisKeyBuilder);
if (cacheObject != null) {
return this.filterSknByPageProductIdBitSet(recommedSknList, (BitSet) cacheObject.toObject());
}
//2、直接从redis的bitset缓存结构中过滤
Map<Integer, Boolean> redisQueryResult = searchRedis.getFromBitSet(redisKeyBuilder, recommedSknList);
if (redisQueryResult != null) {
return this.filterSknByRedisQueryResult(recommedSknList, redisQueryResult);
}
//3、执行查询,加添到并本地缓存
PageProductIdBitSet pageProductIdBitSet = pageProductIdBitSetComponent.queryPageProductIdBitSet(userRecallRequest.getParamQueryFilter());
//3.1)添加到本地缓存
this.ehCache.setBitSet(redisKeyBuilder,pageProductIdBitSet.getProductIdBitSet(),pageProductIdBitSetComponent.cacheTimeInMinute());
//3.2)添加到redis缓存
this.searchRedis.setBitSet(redisKeyBuilder,pageProductIdBitSet.getProductIdBitSet(),pageProductIdBitSetComponent.cacheTimeInMinute());
return this.filterSknByPageProductIdBitSet(recommedSknList, pageProductIdBitSet.getProductIdBitSet());
}
private List<Integer> filterRecommedSknList(UserRecallRequest userRecallRequest, List<Integer> recommedSknList,int maxReturnCount){
if(recommedSknList==null || recommedSknList.isEmpty()){
return new ArrayList<>();
/**
* 从bitset对象中构造返回对象
*
* @param recommedSknList
* @param bitSet
* @return
*/
private List<Integer> filterSknByPageProductIdBitSet(List<Integer> recommedSknList, BitSet bitSet) {
List<Integer> results = new ArrayList<>();
for (Integer recommedSkn : recommedSknList) {
if (bitSet.get(recommedSkn)) {
results.add(recommedSkn);
}
}
return results;
}
/**
* 根据redis返回结果按顺序构结果,只返回为true的
*
* @param recommedSknList
* @param redisQueryResult
* @return
*/
private List<Integer> filterSknByRedisQueryResult(List<Integer> recommedSknList, Map<Integer, Boolean> redisQueryResult) {
List<Integer> filterResults = new ArrayList<>();
for (Integer recommedSkn : recommedSknList) {
if (redisQueryResult.getOrDefault(recommedSkn, false)) {
filterResults.add(recommedSkn);
}
}
return filterResults;
}
/**
* 直接从es召回
*
* @param userRecallRequest
* @param recommedSknList
* @return
*/
private List<Integer> filterRecommedSknListByEs(UserRecallRequest userRecallRequest, List<Integer> recommedSknList) {
if (recommedSknList == null || recommedSknList.isEmpty()) {
return new ArrayList<>();
}
//1、构造searchParam
ParamQueryFilter paramQueryFilter = userRecallRequest.getParamQueryFilter();
... ... @@ -53,7 +141,7 @@ public class SknRecallCacheBean{
searchParam.setQuery(paramQueryFilter.getParamQuery());
BoolQueryBuilder realFilter = QueryBuilders.boolQuery();
realFilter.must(paramQueryFilter.getParamFilter());
realFilter.must(QueryBuilders.termsQuery(ProductIndexEsField.productSkn,recommedSknList));
realFilter.must(QueryBuilders.termsQuery(ProductIndexEsField.productSkn, recommedSknList));
searchParam.setFiter(realFilter);
//2、设置分页参数
... ... @@ -74,11 +162,8 @@ public class SknRecallCacheBean{
//5、按顺序构造最终结果
List<Integer> results = new ArrayList<>();
for (Integer recommedSkn: recommedSknList) {
if(results.size()>=maxReturnCount){
continue;
}
if(existsProductSkns.contains(recommedSkn)){
for (Integer recommedSkn : recommedSknList) {
if (existsProductSkns.contains(recommedSkn)) {
results.add(recommedSkn);
}
}
... ... @@ -87,18 +172,22 @@ public class SknRecallCacheBean{
/**
* 将推荐的skn构造成召回对象
*
* @return
*/
private List<RecallRequestResponse> buildResults(UserRecallRequest userRecallRequest,final List<Integer> recommedSknList){
private List<RecallRequestResponse> buildResults(UserRecallRequest userRecallRequest, final List<Integer> recommedSknList, int maxReturnCount) {
List<RecallRequestResponse> results = new ArrayList<>();
if(recommedSknList==null || recommedSknList.isEmpty()){
if (recommedSknList == null || recommedSknList.isEmpty()) {
return results;
}
ParamQueryFilter paramQueryFilter = userRecallRequest.getParamQueryFilter();
for (Integer recommendSkn : recommedSknList){
RecallRequest recallRequest = new RecallRequest(paramQueryFilter,new RecommendSknStrategy(recommendSkn));
for (Integer recommendSkn : recommedSknList) {
if (results.size() >= maxReturnCount) {
break;
}
RecallRequest recallRequest = new RecallRequest(paramQueryFilter, new RecommendSknStrategy(recommendSkn));
RecallRequestResponse recallRequestResponse = new RecallRequestResponse(recallRequest);
recallRequestResponse.setResponse(new RecallResponse(1L, Arrays.asList(recommendSkn)),false);
recallRequestResponse.setResponse(new RecallResponse(1L, Arrays.asList(recommendSkn)), false);
results.add(recallRequestResponse);
}
return results;
... ...
... ... @@ -77,6 +77,12 @@ public abstract class AbstractPageComponent<T> {
return null;
}
private T doInnerQuery(RedisKeyBuilder redisKeyBuilder, ParamQueryFilter paramQueryFilter) {
T result = this.doRealQuery(paramQueryFilter);
this.addJavaObjectToRedis(redisKeyBuilder,result);
return result;
}
private T getJavaObjectFromEhcache(RedisKeyBuilder redisKeyBuilder){
CacheObject cacheObject = ehCache.get(redisKeyBuilder);
if (cacheObject != null) {
... ... @@ -86,10 +92,10 @@ public abstract class AbstractPageComponent<T> {
}
private void addJavaObjectToEhcache(RedisKeyBuilder redisKeyBuilder,T result){
ehCache.addOrUpdate(redisKeyBuilder, new CacheObject(result), this.cacheTimeInSecond() / 60);
ehCache.addOrUpdate(redisKeyBuilder, new CacheObject(result), this.cacheTimeInMinute());
}
private T getJavaObjectFromRedis(RedisKeyBuilder redisKeyBuilder,Class<T> clazz){
protected T getJavaObjectFromRedis(RedisKeyBuilder redisKeyBuilder,Class<T> clazz){
CacheObject cacheObject = searchRedis.get(redisKeyBuilder);
if (cacheObject == null) {
return null;
... ... @@ -99,23 +105,15 @@ public abstract class AbstractPageComponent<T> {
return result;
}
private void addJavaObjectToRedis(RedisKeyBuilder redisKeyBuilder,T result){
protected void addJavaObjectToRedis(RedisKeyBuilder redisKeyBuilder,T result){
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(result));
CacheObject toCacheResult = new CacheObject(jsonObject);
searchRedis.addOrUpdate(redisKeyBuilder, toCacheResult, this.cacheTimeInSecond() / 60);
}
private T doInnerQuery(RedisKeyBuilder redisKeyBuilder, ParamQueryFilter paramQueryFilter) {
T queryResult = doRealQuery(paramQueryFilter);
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(queryResult));
CacheObject toCacheResult = new CacheObject(jsonObject);
searchRedis.addOrUpdate(redisKeyBuilder, toCacheResult, this.cacheTimeInSecond() / 60);
return queryResult;
searchRedis.addOrUpdate(redisKeyBuilder, toCacheResult, this.cacheTimeInMinute());
}
protected abstract RedisKeyBuilder genRedisKeyBuilder(ParamQueryFilter paramQueryFilter);
protected abstract int cacheTimeInSecond();
protected abstract int cacheTimeInMinute();
protected abstract T doRealQuery(ParamQueryFilter paramQueryFilter);
... ...
... ... @@ -46,7 +46,7 @@ public class PagePersionalFactorComponent extends AbstractPageComponent<PagePers
}
@Override
protected int cacheTimeInSecond() {
protected int cacheTimeInMinute() {
return CacheTimeConstants.PAGE_PERSIONAL_FACTOR;
}
... ...
package com.yoho.search.recall.scene.beans.persional;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
import com.yoho.search.base.utils.ISearchConstants;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.common.cache.model.CacheObject;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.model.SearchResult;
import com.yoho.search.recall.scene.beans.helper.ExtendFilterHelper;
... ... @@ -40,19 +42,16 @@ public class PageProductIdBitSetComponent extends AbstractPageComponent<PageProd
* @return
*/
public PageProductIdBitSet queryPageProductIdBitSet(ParamQueryFilter paramQueryFilter) {
//Object value = super.queryWithCache(paramQueryFilter);
//return value == null ? null : (PageProductIdBitSet) value;
//return doRealQuery(paramQueryFilter);
return null;
return this.doRealQuery(paramQueryFilter);
}
@Override
protected RedisKeyBuilder genRedisKeyBuilder(ParamQueryFilter paramQueryFilter) {
public RedisKeyBuilder genRedisKeyBuilder(ParamQueryFilter paramQueryFilter) {
return RedisKeyBuilder.newInstance().appendFixed("YOHOSEARCH:PAGE_PRODUCT_BITSET:").appendVar(paramQueryFilter.getParamMd5Key());
}
@Override
protected int cacheTimeInSecond() {
public int cacheTimeInMinute() {
return CacheTimeConstants.PAGE_SKN_BITSET;
}
... ...