Authored by hugufei

新版本按品牌召回的缓存策略

package com.yoho.search.recall.scene.helper;
import com.yoho.search.recall.scene.models.RecallReq;
import com.yoho.search.recall.scene.models.RecallRsp;
import com.yoho.search.recall.sort.strategy.IRecallStrategy;
import com.yoho.search.recall.sort.strategy.NewPromotionStrategy;
import com.yoho.search.recall.sort.strategy.NewReducePriceStrategy;
import com.yoho.search.recall.sort.strategy.NewShelveStrategy;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Component
public class RecallByBrandService {
@Autowired
private RecallCommonService recallHelper;
/**
* 按用户偏好品牌批量召回
*
* @param filter
* @param query
* @param brandIds
* @param size
* @return
*/
public RecallRsp batchRecallByBrand(QueryBuilder query, BoolQueryBuilder filter, List<Integer> brandIds, int size) {
//1、构造召回请求
List<RecallReq> requests = new ArrayList<RecallReq>();
for (Integer brandId : brandIds) {
// 1) 新品召回
requests.add(this.buildBrandNewShelveRequest(query,filter,brandId,size));
// 2) 新降价
requests.add(this.buildBrandNewReducePriceRequest(query,filter,brandId,size));
// 3) 新开促销
requests.add(this.buildBrandNewPromotionRequest(query,filter,brandId,size));
}
//2、执行召回
Map<String,RecallRsp> responeMap = recallHelper.batchRecall(requests);
//3、生成召回结果
List<RecallRsp.RecallSkn> skns = new ArrayList<>();
for (RecallRsp rsp : responeMap.values()){
skns.addAll(rsp.getRecallSkns());
}
RecallRsp respone = new RecallRsp();
respone.setRecallSkns(skns);
return respone;
}
/**
* 构造【按品牌id召回新品】的请求参数
* @param query
* @param filter
* @param brandId
* @param size
* @return
*/
private RecallReq buildBrandNewShelveRequest(QueryBuilder query, BoolQueryBuilder filter,Integer brandId,int size){
NewShelveStrategy strage = new NewShelveStrategy(Arrays.asList(brandId),size);
return this.buildRecallReq(query,filter,strage,size);
}
/**
* 构造【按品牌id召回新降价】的请求参数
* @param query
* @param filter
* @param brandId
* @param size
* @return
*/
private RecallReq buildBrandNewReducePriceRequest(QueryBuilder query, BoolQueryBuilder filter,Integer brandId,int size){
NewReducePriceStrategy strage = new NewReducePriceStrategy(Arrays.asList(brandId),size);
return this.buildRecallReq(query,filter,strage,size);
}
/**
* 构造【按品牌id召回新开促销】的请求参数
* @param query
* @param filter
* @param brandId
* @param size
* @return
*/
private RecallReq buildBrandNewPromotionRequest(QueryBuilder query, BoolQueryBuilder filter,Integer brandId,int size){
NewPromotionStrategy strage = new NewPromotionStrategy(Arrays.asList(brandId),size);
return this.buildRecallReq(query,filter,strage,size);
}
private RecallReq buildRecallReq(QueryBuilder query,BoolQueryBuilder filter, IRecallStrategy strategy, int size){
return new RecallReq(query, filter, strategy.filter() , strategy.firstSortBuilder(), size);
}
}
... ...
package com.yoho.search.recall.scene.helper;
import com.alibaba.fastjson.JSON;
import com.yoho.search.base.utils.ISearchConstants;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.common.cache.impls.SearchRedis;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.model.SearchResult;
import com.yoho.search.recall.scene.models.RecallReq;
import com.yoho.search.recall.scene.models.RecallRsp;
import com.yoho.search.service.base.SearchCommonService;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class RecallCommonService {
@Autowired
private SearchRedis searchRedis;
@Autowired
private SearchCommonService searchCommonService;
/**
* 批量召回入口
* @param requests
* @return
*/
public Map<String, RecallRsp> batchRecall(List<RecallReq> requests) {
//1、从缓存中获取
Map<String, RecallRsp> cachedResults = this.mutiGetFromCache(requests);
//2、过滤缓存命中的请求
Iterator<RecallReq> requestIterator = requests.iterator();
while (requestIterator.hasNext()) {
RecallReq request = requestIterator.next();
if (cachedResults.containsKey(request.toCacheKeyValue())) {
requestIterator.remove();
}
}
//3、判断是否全部命中了缓存
if (requests.isEmpty()) {
return cachedResults;
}
//4、执行查询并加入缓存
this.queryAndAddToCache(requests,cachedResults);
//5、返回结果
return cachedResults;
}
/**
* 从缓存中批量获取
* @param requests
* @return
*/
private Map<String, RecallRsp> mutiGetFromCache(List<RecallReq> requests) {
List<String> keys = new ArrayList<>();
for (RecallReq req : requests) {
keys.add(req.toCacheKeyValue());
}
Map<String, RecallRsp> results = new HashMap<>(requests.size());
List<String> cacheValues = searchRedis.searchValueOperations.multiGet(keys);
for (int i = 0; i < keys.size(); i++) {
String cacheValue = cacheValues.get(i);
if (cacheValue != null) {
results.put(keys.get(i), this.fromString(cacheValue));
}
}
return results;
}
/**
* 查询未命中缓存的请求,并加入缓存
* @param requests
* @return
*/
private void queryAndAddToCache(List<RecallReq> requests, Map<String, RecallRsp> cachedResults) {
//1、构造请求参数
List<SearchParam> searchParams = new ArrayList<>();
for (RecallReq request : requests) {
searchParams.add(request.searchParam());
}
//2、执行搜索
List<SearchResult> searchResults = searchCommonService.doMutiSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParams);
//3、构造结果和缓存结果
Map<String, String> cacheValues = new HashMap<>();
for (int i = 0; i < requests.size(); i++) {
RecallReq request = requests.get(i);
SearchResult searchResult = searchResults.get(i);
RecallRsp response = this.genRecallRspFromSearchResult(searchResult);
cachedResults.put(request.toCacheKeyValue(), response);
cacheValues.put(request.toCacheKeyValue(), this.recallRspToString(response));
}
//4、加入缓存
//TODO 设置缓存时间
searchRedis.searchValueOperations.multiSet(cacheValues);
}
private String recallRspToString(RecallRsp response) {
return JSON.toJSONString(response);
}
private RecallRsp fromString(String value) {
return JSON.parseObject(value, RecallRsp.class);
}
private RecallRsp genRecallRspFromSearchResult(SearchResult searchResult) {
RecallRsp response = new RecallRsp();
response.setTotal(searchResult.getTotal());
List<Map<String, Object>> results = searchResult.getResultList();
List<RecallRsp.RecallSkn> recallSkns = new ArrayList<>();
for (Map<String, Object> result : results) {
RecallRsp.RecallSkn recallSkn = new RecallRsp.RecallSkn();
recallSkn.setProductSkn(MapUtils.getInteger(result, ProductIndexEsField.productSkn, 0));
recallSkn.setBrandId(MapUtils.getInteger(result, ProductIndexEsField.brandId, 0));
recallSkn.setMiddleSortId(MapUtils.getInteger(result, ProductIndexEsField.middleSortId, 0));
}
response.setRecallSkns(recallSkns);
return response;
}
}
... ...
package com.yoho.search.recall.scene.models;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.core.es.model.SearchParam;
import org.elasticsearch.index.query.QueryBuilder;
import java.util.Arrays;
import java.util.List;
public interface IRecallReq {
/**
* 查询参数
* @return
*/
SearchParam searchParam();
/**
* 真实的过滤参数
* @return
*/
QueryBuilder realFilter();
default List<String> includeFields(){
return Arrays.asList(ProductIndexEsField.productSkn,ProductIndexEsField.brandId,ProductIndexEsField.middleSortId);
}
}
... ...
package com.yoho.search.recall.scene.models;
import com.yoho.search.base.utils.MD5Util;
import com.yoho.search.common.cache.aop.ISearchCacheAbleParam;
import com.yoho.search.core.es.model.SearchParam;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import java.util.Arrays;
import java.util.List;
/**
* 召回请求
*/
public class RecallReq implements IRecallReq,ISearchCacheAbleParam{
private QueryBuilder queryInParam;
private QueryBuilder filterInParam;
private QueryBuilder extendFilter;
private List<SortBuilder<?>> sortBuilders;
private Integer size;
private String cacheKey;
public RecallReq( QueryBuilder queryInParam, QueryBuilder filterInParam,List<SortBuilder<?>> sortBuilders,Integer size) {
this.filterInParam = filterInParam;
this.queryInParam = queryInParam;
this.sortBuilders = sortBuilders;
this.size = size;
}
public RecallReq( QueryBuilder queryInParam, QueryBuilder filterInParam,QueryBuilder extendFilter,SortBuilder<?> sortBuilder,Integer size) {
this.filterInParam = filterInParam;
this.queryInParam = queryInParam;
this.extendFilter = extendFilter;
this.sortBuilders = Arrays.asList(sortBuilder);
this.size = size;
}
public RecallReq( QueryBuilder queryInParam, QueryBuilder filterInParam,QueryBuilder extendFilter,List<SortBuilder<?>> sortBuilders,Integer size) {
this.filterInParam = filterInParam;
this.queryInParam = queryInParam;
this.extendFilter = extendFilter;
this.sortBuilders = sortBuilders;
this.size = size;
}
@Override
public QueryBuilder realFilter() {
if(extendFilter==null){
return this.filterInParam;
}
BoolQueryBuilder realFilter = QueryBuilders.boolQuery();
realFilter.must(this.filterInParam);
realFilter.must(this.extendFilter);
return realFilter;
}
@Override
public SearchParam searchParam() {
SearchParam searchParam = new SearchParam();
searchParam.setQuery(this.queryInParam);
searchParam.setFiter(this.realFilter());
searchParam.setIncludeFields(this.includeFields());
searchParam.setSortBuilders(this.sortBuilders);
searchParam.setOffset(0);
searchParam.setSize(this.size);
return searchParam;
}
@Override
public String toCacheKeyValue() {
if(this.cacheKey!=null){
return cacheKey;
}
StringBuilder sb = new StringBuilder();
sb.append("filterInParam:").append(filterInParam.toString());
sb.append("queryInParam:").append(queryInParam.toString());
sb.append("extendFilter:").append(extendFilter.toString());
sb.append("sortBuilders:").append(sortBuilders.toString());
sb.append("size:").append(size.toString());
this.cacheKey = MD5Util.string2MD5(sb.toString());
return this.cacheKey;
}
}
... ...
package com.yoho.search.recall.scene.models;
import com.alibaba.fastjson.JSON;
import java.io.Serializable;
import java.util.List;
/**
* 召回结果
*/
public class RecallRsp implements Serializable{
public long total;
public List<RecallSkn> recallSkns;
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public List<RecallSkn> getRecallSkns() {
return recallSkns;
}
public void setRecallSkns(List<RecallSkn> recallSkns) {
this.recallSkns = recallSkns;
}
public static class RecallSkn{
private Integer productSkn;
private Integer brandId;
private Integer middleSortId;
public Integer getProductSkn() {
return productSkn;
}
public void setProductSkn(Integer productSkn) {
this.productSkn = productSkn;
}
public Integer getBrandId() {
return brandId;
}
public void setBrandId(Integer brandId) {
this.brandId = brandId;
}
public Integer getMiddleSortId() {
return middleSortId;
}
public void setMiddleSortId(Integer middleSortId) {
this.middleSortId = middleSortId;
}
}
}
... ...