Authored by hugufei

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

  1 +package com.yoho.search.recall.scene.helper;
  2 +
  3 +import com.yoho.search.recall.scene.models.RecallReq;
  4 +import com.yoho.search.recall.scene.models.RecallRsp;
  5 +import com.yoho.search.recall.sort.strategy.IRecallStrategy;
  6 +import com.yoho.search.recall.sort.strategy.NewPromotionStrategy;
  7 +import com.yoho.search.recall.sort.strategy.NewReducePriceStrategy;
  8 +import com.yoho.search.recall.sort.strategy.NewShelveStrategy;
  9 +import org.elasticsearch.index.query.BoolQueryBuilder;
  10 +import org.elasticsearch.index.query.QueryBuilder;
  11 +import org.springframework.beans.factory.annotation.Autowired;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +import java.util.ArrayList;
  15 +import java.util.Arrays;
  16 +import java.util.List;
  17 +import java.util.Map;
  18 +
  19 +@Component
  20 +public class RecallByBrandService {
  21 +
  22 + @Autowired
  23 + private RecallCommonService recallHelper;
  24 +
  25 + /**
  26 + * 按用户偏好品牌批量召回
  27 + *
  28 + * @param filter
  29 + * @param query
  30 + * @param brandIds
  31 + * @param size
  32 + * @return
  33 + */
  34 + public RecallRsp batchRecallByBrand(QueryBuilder query, BoolQueryBuilder filter, List<Integer> brandIds, int size) {
  35 + //1、构造召回请求
  36 + List<RecallReq> requests = new ArrayList<RecallReq>();
  37 + for (Integer brandId : brandIds) {
  38 + // 1) 新品召回
  39 + requests.add(this.buildBrandNewShelveRequest(query,filter,brandId,size));
  40 + // 2) 新降价
  41 + requests.add(this.buildBrandNewReducePriceRequest(query,filter,brandId,size));
  42 + // 3) 新开促销
  43 + requests.add(this.buildBrandNewPromotionRequest(query,filter,brandId,size));
  44 + }
  45 + //2、执行召回
  46 + Map<String,RecallRsp> responeMap = recallHelper.batchRecall(requests);
  47 + //3、生成召回结果
  48 + List<RecallRsp.RecallSkn> skns = new ArrayList<>();
  49 + for (RecallRsp rsp : responeMap.values()){
  50 + skns.addAll(rsp.getRecallSkns());
  51 + }
  52 + RecallRsp respone = new RecallRsp();
  53 + respone.setRecallSkns(skns);
  54 + return respone;
  55 + }
  56 +
  57 + /**
  58 + * 构造【按品牌id召回新品】的请求参数
  59 + * @param query
  60 + * @param filter
  61 + * @param brandId
  62 + * @param size
  63 + * @return
  64 + */
  65 + private RecallReq buildBrandNewShelveRequest(QueryBuilder query, BoolQueryBuilder filter,Integer brandId,int size){
  66 + NewShelveStrategy strage = new NewShelveStrategy(Arrays.asList(brandId),size);
  67 + return this.buildRecallReq(query,filter,strage,size);
  68 + }
  69 +
  70 + /**
  71 + * 构造【按品牌id召回新降价】的请求参数
  72 + * @param query
  73 + * @param filter
  74 + * @param brandId
  75 + * @param size
  76 + * @return
  77 + */
  78 + private RecallReq buildBrandNewReducePriceRequest(QueryBuilder query, BoolQueryBuilder filter,Integer brandId,int size){
  79 + NewReducePriceStrategy strage = new NewReducePriceStrategy(Arrays.asList(brandId),size);
  80 + return this.buildRecallReq(query,filter,strage,size);
  81 + }
  82 +
  83 + /**
  84 + * 构造【按品牌id召回新开促销】的请求参数
  85 + * @param query
  86 + * @param filter
  87 + * @param brandId
  88 + * @param size
  89 + * @return
  90 + */
  91 + private RecallReq buildBrandNewPromotionRequest(QueryBuilder query, BoolQueryBuilder filter,Integer brandId,int size){
  92 + NewPromotionStrategy strage = new NewPromotionStrategy(Arrays.asList(brandId),size);
  93 + return this.buildRecallReq(query,filter,strage,size);
  94 + }
  95 +
  96 + private RecallReq buildRecallReq(QueryBuilder query,BoolQueryBuilder filter, IRecallStrategy strategy, int size){
  97 + return new RecallReq(query, filter, strategy.filter() , strategy.firstSortBuilder(), size);
  98 + }
  99 +
  100 +}
  1 +package com.yoho.search.recall.scene.helper;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.yoho.search.base.utils.ISearchConstants;
  5 +import com.yoho.search.base.utils.ProductIndexEsField;
  6 +import com.yoho.search.common.cache.impls.SearchRedis;
  7 +import com.yoho.search.core.es.model.SearchParam;
  8 +import com.yoho.search.core.es.model.SearchResult;
  9 +import com.yoho.search.recall.scene.models.RecallReq;
  10 +import com.yoho.search.recall.scene.models.RecallRsp;
  11 +import com.yoho.search.service.base.SearchCommonService;
  12 +import org.apache.commons.collections.MapUtils;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.stereotype.Component;
  15 +
  16 +import java.util.*;
  17 +
  18 +@Component
  19 +public class RecallCommonService {
  20 +
  21 + @Autowired
  22 + private SearchRedis searchRedis;
  23 + @Autowired
  24 + private SearchCommonService searchCommonService;
  25 +
  26 + /**
  27 + * 批量召回入口
  28 + * @param requests
  29 + * @return
  30 + */
  31 + public Map<String, RecallRsp> batchRecall(List<RecallReq> requests) {
  32 + //1、从缓存中获取
  33 + Map<String, RecallRsp> cachedResults = this.mutiGetFromCache(requests);
  34 + //2、过滤缓存命中的请求
  35 + Iterator<RecallReq> requestIterator = requests.iterator();
  36 + while (requestIterator.hasNext()) {
  37 + RecallReq request = requestIterator.next();
  38 + if (cachedResults.containsKey(request.toCacheKeyValue())) {
  39 + requestIterator.remove();
  40 + }
  41 + }
  42 + //3、判断是否全部命中了缓存
  43 + if (requests.isEmpty()) {
  44 + return cachedResults;
  45 + }
  46 + //4、执行查询并加入缓存
  47 + this.queryAndAddToCache(requests,cachedResults);
  48 + //5、返回结果
  49 + return cachedResults;
  50 + }
  51 +
  52 + /**
  53 + * 从缓存中批量获取
  54 + * @param requests
  55 + * @return
  56 + */
  57 + private Map<String, RecallRsp> mutiGetFromCache(List<RecallReq> requests) {
  58 + List<String> keys = new ArrayList<>();
  59 + for (RecallReq req : requests) {
  60 + keys.add(req.toCacheKeyValue());
  61 + }
  62 + Map<String, RecallRsp> results = new HashMap<>(requests.size());
  63 + List<String> cacheValues = searchRedis.searchValueOperations.multiGet(keys);
  64 + for (int i = 0; i < keys.size(); i++) {
  65 + String cacheValue = cacheValues.get(i);
  66 + if (cacheValue != null) {
  67 + results.put(keys.get(i), this.fromString(cacheValue));
  68 + }
  69 + }
  70 + return results;
  71 + }
  72 +
  73 + /**
  74 + * 查询未命中缓存的请求,并加入缓存
  75 + * @param requests
  76 + * @return
  77 + */
  78 + private void queryAndAddToCache(List<RecallReq> requests, Map<String, RecallRsp> cachedResults) {
  79 + //1、构造请求参数
  80 + List<SearchParam> searchParams = new ArrayList<>();
  81 + for (RecallReq request : requests) {
  82 + searchParams.add(request.searchParam());
  83 + }
  84 + //2、执行搜索
  85 + List<SearchResult> searchResults = searchCommonService.doMutiSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParams);
  86 +
  87 + //3、构造结果和缓存结果
  88 + Map<String, String> cacheValues = new HashMap<>();
  89 + for (int i = 0; i < requests.size(); i++) {
  90 + RecallReq request = requests.get(i);
  91 + SearchResult searchResult = searchResults.get(i);
  92 + RecallRsp response = this.genRecallRspFromSearchResult(searchResult);
  93 + cachedResults.put(request.toCacheKeyValue(), response);
  94 + cacheValues.put(request.toCacheKeyValue(), this.recallRspToString(response));
  95 + }
  96 +
  97 + //4、加入缓存
  98 + //TODO 设置缓存时间
  99 + searchRedis.searchValueOperations.multiSet(cacheValues);
  100 + }
  101 +
  102 + private String recallRspToString(RecallRsp response) {
  103 + return JSON.toJSONString(response);
  104 + }
  105 +
  106 + private RecallRsp fromString(String value) {
  107 + return JSON.parseObject(value, RecallRsp.class);
  108 + }
  109 +
  110 + private RecallRsp genRecallRspFromSearchResult(SearchResult searchResult) {
  111 + RecallRsp response = new RecallRsp();
  112 + response.setTotal(searchResult.getTotal());
  113 + List<Map<String, Object>> results = searchResult.getResultList();
  114 + List<RecallRsp.RecallSkn> recallSkns = new ArrayList<>();
  115 + for (Map<String, Object> result : results) {
  116 + RecallRsp.RecallSkn recallSkn = new RecallRsp.RecallSkn();
  117 + recallSkn.setProductSkn(MapUtils.getInteger(result, ProductIndexEsField.productSkn, 0));
  118 + recallSkn.setBrandId(MapUtils.getInteger(result, ProductIndexEsField.brandId, 0));
  119 + recallSkn.setMiddleSortId(MapUtils.getInteger(result, ProductIndexEsField.middleSortId, 0));
  120 + }
  121 + response.setRecallSkns(recallSkns);
  122 + return response;
  123 + }
  124 +
  125 +}
  1 +package com.yoho.search.recall.scene.models;
  2 +
  3 +
  4 +import com.yoho.search.base.utils.ProductIndexEsField;
  5 +import com.yoho.search.core.es.model.SearchParam;
  6 +import org.elasticsearch.index.query.QueryBuilder;
  7 +
  8 +import java.util.Arrays;
  9 +import java.util.List;
  10 +
  11 +public interface IRecallReq {
  12 +
  13 + /**
  14 + * 查询参数
  15 + * @return
  16 + */
  17 + SearchParam searchParam();
  18 +
  19 + /**
  20 + * 真实的过滤参数
  21 + * @return
  22 + */
  23 + QueryBuilder realFilter();
  24 +
  25 + default List<String> includeFields(){
  26 + return Arrays.asList(ProductIndexEsField.productSkn,ProductIndexEsField.brandId,ProductIndexEsField.middleSortId);
  27 + }
  28 +
  29 +}
  1 +package com.yoho.search.recall.scene.models;
  2 +
  3 +import com.yoho.search.base.utils.MD5Util;
  4 +import com.yoho.search.common.cache.aop.ISearchCacheAbleParam;
  5 +import com.yoho.search.core.es.model.SearchParam;
  6 +import org.elasticsearch.index.query.BoolQueryBuilder;
  7 +import org.elasticsearch.index.query.QueryBuilder;
  8 +import org.elasticsearch.index.query.QueryBuilders;
  9 +import org.elasticsearch.search.sort.SortBuilder;
  10 +
  11 +import java.util.Arrays;
  12 +import java.util.List;
  13 +
  14 +
  15 +/**
  16 + * 召回请求
  17 + */
  18 +public class RecallReq implements IRecallReq,ISearchCacheAbleParam{
  19 +
  20 + private QueryBuilder queryInParam;
  21 + private QueryBuilder filterInParam;
  22 + private QueryBuilder extendFilter;
  23 + private List<SortBuilder<?>> sortBuilders;
  24 + private Integer size;
  25 + private String cacheKey;
  26 +
  27 +
  28 + public RecallReq( QueryBuilder queryInParam, QueryBuilder filterInParam,List<SortBuilder<?>> sortBuilders,Integer size) {
  29 + this.filterInParam = filterInParam;
  30 + this.queryInParam = queryInParam;
  31 + this.sortBuilders = sortBuilders;
  32 + this.size = size;
  33 + }
  34 +
  35 + public RecallReq( QueryBuilder queryInParam, QueryBuilder filterInParam,QueryBuilder extendFilter,SortBuilder<?> sortBuilder,Integer size) {
  36 + this.filterInParam = filterInParam;
  37 + this.queryInParam = queryInParam;
  38 + this.extendFilter = extendFilter;
  39 + this.sortBuilders = Arrays.asList(sortBuilder);
  40 + this.size = size;
  41 + }
  42 +
  43 + public RecallReq( QueryBuilder queryInParam, QueryBuilder filterInParam,QueryBuilder extendFilter,List<SortBuilder<?>> sortBuilders,Integer size) {
  44 + this.filterInParam = filterInParam;
  45 + this.queryInParam = queryInParam;
  46 + this.extendFilter = extendFilter;
  47 + this.sortBuilders = sortBuilders;
  48 + this.size = size;
  49 + }
  50 +
  51 + @Override
  52 + public QueryBuilder realFilter() {
  53 + if(extendFilter==null){
  54 + return this.filterInParam;
  55 + }
  56 + BoolQueryBuilder realFilter = QueryBuilders.boolQuery();
  57 + realFilter.must(this.filterInParam);
  58 + realFilter.must(this.extendFilter);
  59 + return realFilter;
  60 + }
  61 +
  62 + @Override
  63 + public SearchParam searchParam() {
  64 + SearchParam searchParam = new SearchParam();
  65 + searchParam.setQuery(this.queryInParam);
  66 + searchParam.setFiter(this.realFilter());
  67 + searchParam.setIncludeFields(this.includeFields());
  68 + searchParam.setSortBuilders(this.sortBuilders);
  69 + searchParam.setOffset(0);
  70 + searchParam.setSize(this.size);
  71 + return searchParam;
  72 + }
  73 +
  74 + @Override
  75 + public String toCacheKeyValue() {
  76 + if(this.cacheKey!=null){
  77 + return cacheKey;
  78 + }
  79 + StringBuilder sb = new StringBuilder();
  80 + sb.append("filterInParam:").append(filterInParam.toString());
  81 + sb.append("queryInParam:").append(queryInParam.toString());
  82 + sb.append("extendFilter:").append(extendFilter.toString());
  83 + sb.append("sortBuilders:").append(sortBuilders.toString());
  84 + sb.append("size:").append(size.toString());
  85 + this.cacheKey = MD5Util.string2MD5(sb.toString());
  86 + return this.cacheKey;
  87 + }
  88 +
  89 +}
  1 +package com.yoho.search.recall.scene.models;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +
  5 +import java.io.Serializable;
  6 +import java.util.List;
  7 +
  8 +
  9 +/**
  10 + * 召回结果
  11 + */
  12 +public class RecallRsp implements Serializable{
  13 +
  14 + public long total;
  15 + public List<RecallSkn> recallSkns;
  16 +
  17 +
  18 + public long getTotal() {
  19 + return total;
  20 + }
  21 +
  22 + public void setTotal(long total) {
  23 + this.total = total;
  24 + }
  25 +
  26 + public List<RecallSkn> getRecallSkns() {
  27 + return recallSkns;
  28 + }
  29 +
  30 + public void setRecallSkns(List<RecallSkn> recallSkns) {
  31 + this.recallSkns = recallSkns;
  32 + }
  33 +
  34 + public static class RecallSkn{
  35 + private Integer productSkn;
  36 + private Integer brandId;
  37 + private Integer middleSortId;
  38 +
  39 + public Integer getProductSkn() {
  40 + return productSkn;
  41 + }
  42 +
  43 + public void setProductSkn(Integer productSkn) {
  44 + this.productSkn = productSkn;
  45 + }
  46 +
  47 + public Integer getBrandId() {
  48 + return brandId;
  49 + }
  50 +
  51 + public void setBrandId(Integer brandId) {
  52 + this.brandId = brandId;
  53 + }
  54 +
  55 + public Integer getMiddleSortId() {
  56 + return middleSortId;
  57 + }
  58 +
  59 + public void setMiddleSortId(Integer middleSortId) {
  60 + this.middleSortId = middleSortId;
  61 + }
  62 + }
  63 +
  64 +}