Showing
5 changed files
with
407 additions
and
0 deletions
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 | +} |
-
Please register or login to post a comment