Authored by 胡古飞

Merge branch 'master' into gray

@@ -42,9 +42,9 @@ public class FunctionScoreSearchHelper { @@ -42,9 +42,9 @@ public class FunctionScoreSearchHelper {
42 42
43 // 普通个性化的时间维度 43 // 普通个性化的时间维度
44 private final int oneDaySecondCount = 24 * 60 * 60; 44 private final int oneDaySecondCount = 24 * 60 * 60;
45 - private FirstShelveTimeScore commonFirstShelveTimeScore = new FirstShelveTimeScore(90, 30, 60); 45 + private FirstShelveTimeScore commonFirstShelveTimeScore = new FirstShelveTimeScore(60, 30, 30);
46 // 新品到着的个性化时间维度 46 // 新品到着的个性化时间维度
47 - private FirstShelveTimeScore newRecShelveTimeScore = new FirstShelveTimeScore(30, 10, 20); 47 + private FirstShelveTimeScore newRecShelveTimeScore = new FirstShelveTimeScore(14, 7, 7);
48 private final float globalWeight = 0.50f; 48 private final float globalWeight = 0.50f;
49 49
50 private WeightBuilder genWeightFactorBuilder(float factor) { 50 private WeightBuilder genWeightFactorBuilder(float factor) {
@@ -26,6 +26,8 @@ import org.elasticsearch.search.aggregations.metrics.tophits.TopHits; @@ -26,6 +26,8 @@ import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
26 import org.elasticsearch.search.sort.SortBuilder; 26 import org.elasticsearch.search.sort.SortBuilder;
27 import org.elasticsearch.search.sort.SortBuilders; 27 import org.elasticsearch.search.sort.SortBuilders;
28 import org.elasticsearch.search.sort.SortOrder; 28 import org.elasticsearch.search.sort.SortOrder;
  29 +import org.slf4j.Logger;
  30 +import org.slf4j.LoggerFactory;
29 import org.springframework.beans.factory.annotation.Autowired; 31 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.stereotype.Service; 32 import org.springframework.stereotype.Service;
31 33
@@ -43,12 +45,14 @@ import com.yoho.search.service.service.helper.SearchCommonHelper; @@ -43,12 +45,14 @@ import com.yoho.search.service.service.helper.SearchCommonHelper;
43 import com.yoho.search.service.service.helper.SearchServiceHelper; 45 import com.yoho.search.service.service.helper.SearchServiceHelper;
44 import com.yoho.search.service.service.helper.SearchSortHelper; 46 import com.yoho.search.service.service.helper.SearchSortHelper;
45 import com.yoho.search.service.servicenew.IGoodProductsService; 47 import com.yoho.search.service.servicenew.IGoodProductsService;
46 -import com.yoho.search.service.utils.SearchRequestParams; 48 +import com.yoho.search.service.vo.GoodProductBO;
47 import com.yoho.search.service.vo.SearchApiResult; 49 import com.yoho.search.service.vo.SearchApiResult;
48 50
49 @Service 51 @Service
50 public class GoodProductListService implements IGoodProductsService { 52 public class GoodProductListService implements IGoodProductsService {
51 53
  54 + private static final Logger logger = LoggerFactory.getLogger(GoodProductListService.class);
  55 +
52 @Autowired 56 @Autowired
53 private SearchCommonService searchCommonService; 57 private SearchCommonService searchCommonService;
54 @Autowired 58 @Autowired
@@ -64,14 +68,12 @@ public class GoodProductListService implements IGoodProductsService { @@ -64,14 +68,12 @@ public class GoodProductListService implements IGoodProductsService {
64 @Autowired 68 @Autowired
65 private FunctionScoreSearchHelper functionScoreSearchHelper; 69 private FunctionScoreSearchHelper functionScoreSearchHelper;
66 70
67 - private final int maxSmallSortCount = 20;  
68 - private final int maxProductSknCountPerSort = 5;  
69 - private final int maxCountPerGroup = 3;  
70 - // private final float firstSknScore = 300;  
71 - private final float recommendedSknMaxScore = 200;  
72 -  
73 @Override 71 @Override
74 public SearchApiResult goodProductList(Map<String, String> paramMap) { 72 public SearchApiResult goodProductList(Map<String, String> paramMap) {
  73 + boolean openLog = "13420925".equals(paramMap.getOrDefault("uid", "0"));
  74 + if (openLog) {
  75 + logger.warn("goodProductList param is : [{}]", paramMap.toString());
  76 + }
75 // 1、检测分页参数 77 // 1、检测分页参数
76 int pageSize = StringUtils.isBlank(paramMap.get("viewNum")) ? 30 : Integer.parseInt(paramMap.get("viewNum")); 78 int pageSize = StringUtils.isBlank(paramMap.get("viewNum")) ? 30 : Integer.parseInt(paramMap.get("viewNum"));
77 int page = StringUtils.isBlank(paramMap.get("page")) ? 1 : Integer.parseInt(paramMap.get("page")); 79 int page = StringUtils.isBlank(paramMap.get("page")) ? 1 : Integer.parseInt(paramMap.get("page"));
@@ -79,9 +81,14 @@ public class GoodProductListService implements IGoodProductsService { @@ -79,9 +81,14 @@ public class GoodProductListService implements IGoodProductsService {
79 return new SearchApiResult().setCode(400).setMessage("分页参数不合法"); 81 return new SearchApiResult().setCode(400).setMessage("分页参数不合法");
80 } 82 }
81 // 2、先获取用户浏览的SKN对应的品类列表 83 // 2、先获取用户浏览的SKN对应的品类列表
82 - List<Integer> smallSortIds = this.getProductSknSmallSortIds(paramMap, maxSmallSortCount);  
83 - // 3、再每个品类下获取5个SKN[每次随机打乱]  
84 - List<String> recommondSkns = this.getRecommondedSkns(smallSortIds, maxProductSknCountPerSort, paramMap); 84 + JSONObject sortAndBrandInfo = this.querySknSortAndBrand(paramMap.getOrDefault("product_skn", ""));
  85 +
  86 + // 3、再根据品类和品牌推荐SKN
  87 + List<String> recommondSkns = this.recommondSknsBySortAndBrandInfo(sortAndBrandInfo, paramMap);
  88 +
  89 + if (paramMap.getOrDefault("uid", "0").equals("13420925")) {
  90 + logger.warn("goodProductList recommondSkns is : [{}]", recommondSkns);
  91 + }
85 92
86 // 4、构造搜索参数 93 // 4、构造搜索参数
87 SearchParam searchParam = new SearchParam(); 94 SearchParam searchParam = new SearchParam();
@@ -103,6 +110,11 @@ public class GoodProductListService implements IGoodProductsService { @@ -103,6 +110,11 @@ public class GoodProductListService implements IGoodProductsService {
103 CacheEnum cacheEnum = CacheEnum.EHCACHE; 110 CacheEnum cacheEnum = CacheEnum.EHCACHE;
104 JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(cacheEnum, indexName, searchParam); 111 JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(cacheEnum, indexName, searchParam);
105 if (cacheObject != null) { 112 if (cacheObject != null) {
  113 + JSONArray product_list = cacheObject.getJSONArray("product_list");
  114 + if(product_list!=null){
  115 +// Collections.shuffle(product_list);
  116 +// cacheObject.put("product_list", product_list);
  117 + }
106 return new SearchApiResult().setData(cacheObject); 118 return new SearchApiResult().setData(cacheObject);
107 } 119 }
108 // 6)查询ES 120 // 6)查询ES
@@ -123,43 +135,15 @@ public class GoodProductListService implements IGoodProductsService { @@ -123,43 +135,15 @@ public class GoodProductListService implements IGoodProductsService {
123 return new SearchApiResult().setData(dataMap); 135 return new SearchApiResult().setData(dataMap);
124 } 136 }
125 137
126 - private QueryBuilder builderGoodProductQueryBuilder(Map<String, String> paramMap, List<String> recommendedSknList) {  
127 - QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();  
128 - FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder);  
129 - // // 针对参数里第一个SKN加分  
130 - // String productSkns =  
131 - // paramMap.get(SearchRequestParams.PARAM_SYNC_SKN);  
132 - // if (!StringUtils.isBlank(productSkns)) {  
133 - // functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn",  
134 - // productSkns.split(",")[0]),  
135 - // ScoreFunctionBuilders.weightFactorFunction(firstSknScore));  
136 - // }  
137 - // 针对推荐出来的SKN做加分  
138 - if (recommendedSknList != null && !recommendedSknList.isEmpty()) {  
139 - Map<Integer, List<String>> recommondSknMap = this.splitProductSkns(recommendedSknList, maxCountPerGroup);  
140 - float currentGroupScore = recommendedSknMaxScore;  
141 - for (Map.Entry<Integer, List<String>> entry : recommondSknMap.entrySet()) {  
142 - functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", entry.getValue()), ScoreFunctionBuilders.weightFactorFunction(currentGroupScore));  
143 - currentGroupScore = currentGroupScore - 10;  
144 - }  
145 - }  
146 - // 加上个性化打分  
147 - if (searchCommonHelper.isNeedPersonalSearch(paramMap)) {  
148 - functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);  
149 - }  
150 - return functionScoreQueryBuilder;  
151 - }  
152 -  
153 /** 138 /**
154 - * 获取SKN相关的小分类 139 + * 获取skn的品类列表和品牌列表
155 * 140 *
156 * @param productSkns 141 * @param productSkns
157 * @return 142 * @return
158 */ 143 */
159 - private List<Integer> getProductSknSmallSortIds(Map<String, String> paramMap, int maxCount) {  
160 - String productSkns = paramMap.get(SearchRequestParams.PARAM_SYNC_SKN); 144 + private JSONObject querySknSortAndBrand(String productSkns) {
161 if (StringUtils.isBlank(productSkns)) { 145 if (StringUtils.isBlank(productSkns)) {
162 - return new ArrayList<Integer>(); 146 + return new JSONObject();
163 } 147 }
164 String[] productSknArray = productSkns.split(","); 148 String[] productSknArray = productSkns.split(",");
165 SearchParam searchParam = new SearchParam(); 149 SearchParam searchParam = new SearchParam();
@@ -167,102 +151,108 @@ public class GoodProductListService implements IGoodProductsService { @@ -167,102 +151,108 @@ public class GoodProductListService implements IGoodProductsService {
167 BoolQueryBuilder boolFilter = QueryBuilders.boolQuery(); 151 BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
168 boolFilter.must(QueryBuilders.termsQuery("productSkn", productSknArray)); 152 boolFilter.must(QueryBuilders.termsQuery("productSkn", productSknArray));
169 searchParam.setFiter(boolFilter); 153 searchParam.setFiter(boolFilter);
170 - // 2、设置聚合条件  
171 - final String aggName = "smallSortAgg";  
172 - TermsBuilder smallSortIdAgg = AggregationBuilders.terms(aggName).field("smallSortId").size(maxCount);  
173 - searchParam.setAggregationBuilders(Arrays.asList(smallSortIdAgg)); 154 + // 2、设置聚合条件,获取所有的品类和品牌
  155 + TermsBuilder smallSortIdAgg = AggregationBuilders.terms("smallSortAgg").field("smallSortId").size(100);
  156 + TermsBuilder brandIdAgg = AggregationBuilders.terms("brandAgg").field("brandId").size(100);
  157 + List<AbstractAggregationBuilder> aggregationBuilders = new ArrayList<AbstractAggregationBuilder>();
  158 + aggregationBuilders.add(smallSortIdAgg);
  159 + aggregationBuilders.add(brandIdAgg);
  160 + searchParam.setAggregationBuilders(aggregationBuilders);
174 // 3、设置分页 161 // 3、设置分页
175 searchParam.setPage(0); 162 searchParam.setPage(0);
176 searchParam.setSize(0); 163 searchParam.setSize(0);
177 searchParam.setOffset(0); 164 searchParam.setOffset(0);
178 165
179 // 4、先从缓存中获取,如果能取到,则直接返回 166 // 4、先从缓存中获取,如果能取到,则直接返回
180 - JSONArray sknSmallSortIdJSONArray = searchCacheService.getJSONArrayFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);  
181 - if (sknSmallSortIdJSONArray != null) {  
182 - return this.jsonArrayToList(sknSmallSortIdJSONArray, Integer.class); 167 + JSONObject sortAndBrandJSONObject = searchCacheService.getJSONObjectFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
  168 + if (sortAndBrandJSONObject != null) {
  169 + return sortAndBrandJSONObject;
183 } 170 }
184 // 5、执行搜索 171 // 5、执行搜索
185 SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam); 172 SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
186 - if (searchResult == null || searchResult.getAggMaps() == null || searchResult.getAggMaps().get("smallSortAgg") == null) { 173 + if (searchResult == null || searchResult.getAggMaps() == null) {
  174 + return new JSONObject();
  175 + }
  176 + Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
  177 +
  178 + // 6、从聚合结果中获取品牌和品类id
  179 + List<Integer> smallSortIds = this.getIdsFromAggMaps(aggMaps, "smallSortAgg");
  180 + List<Integer> brandIds = this.getIdsFromAggMaps(aggMaps, "brandAgg");
  181 +
  182 + // 7、构造返回结果并加入缓存
  183 + sortAndBrandJSONObject = new JSONObject();
  184 + sortAndBrandJSONObject.put("brandIds", brandIds);
  185 + sortAndBrandJSONObject.put("smallSortIds", smallSortIds);
  186 + searchCacheService.addJSONObjectToCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam, sortAndBrandJSONObject);
  187 + return sortAndBrandJSONObject;
  188 +
  189 + }
  190 +
  191 + private List<Integer> getIdsFromAggMaps(Map<String, Aggregation> aggMaps, String aggName) {
  192 + if (!aggMaps.containsKey(aggName)) {
187 return new ArrayList<Integer>(); 193 return new ArrayList<Integer>();
188 } 194 }
189 - sknSmallSortIdJSONArray = new JSONArray();  
190 - MultiBucketsAggregation aggregation = (MultiBucketsAggregation) searchResult.getAggMaps().get(aggName);  
191 - Iterator<? extends Bucket> smallSortIdIterator = aggregation.getBuckets().iterator();  
192 - while (smallSortIdIterator.hasNext()) {  
193 - Bucket smallSortIdBucket = smallSortIdIterator.next();  
194 - if (StringUtils.isNumeric(smallSortIdBucket.getKeyAsString())) {  
195 - sknSmallSortIdJSONArray.add(Integer.valueOf(smallSortIdBucket.getKeyAsString())); 195 + MultiBucketsAggregation aggregation = (MultiBucketsAggregation) aggMaps.get(aggName);
  196 + List<Integer> results = new ArrayList<Integer>();
  197 + Iterator<? extends Bucket> iterator = aggregation.getBuckets().iterator();
  198 + while (iterator.hasNext()) {
  199 + Bucket bucket = iterator.next();
  200 + if (StringUtils.isNumeric(bucket.getKeyAsString())) {
  201 + results.add(Integer.valueOf(bucket.getKeyAsString()));
196 } 202 }
197 } 203 }
198 - searchCacheService.addJSONArrayToCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam, sknSmallSortIdJSONArray);  
199 - return this.jsonArrayToList(sknSmallSortIdJSONArray, Integer.class); 204 + return results;
200 } 205 }
201 206
202 /** 207 /**
203 - * 有好货默认的过滤规则 208 + * 根据skn参数获取推荐出来的skn列表
204 * 209 *
205 * @return 210 * @return
206 */ 211 */
207 - private BoolQueryBuilder getDefaultBoolQueryBuilder() {  
208 - BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();  
209 - boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));  
210 - boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));  
211 - boolFilter.must(QueryBuilders.termQuery("status", 1));  
212 - boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));  
213 - boolFilter.must(QueryBuilders.termQuery("attribute", 1));  
214 - // 默认推库存>2,非断码,并且短评存在的数据  
215 - boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(2));  
216 - boolFilter.must(QueryBuilders.rangeQuery("breakingRate").lt(50));  
217 - boolFilter.must(QueryBuilders.rangeQuery("basePinRatio").lt(3.5));  
218 - boolFilter.must(QueryBuilders.termQuery("isPhraseExist", "Y"));  
219 - return boolFilter;  
220 - }  
221 -  
222 - /**  
223 - * 每个品类为用户推荐maxSize个SKN  
224 - *  
225 - * @param smallSortIds  
226 - * @param maxSize  
227 - * @param paramMap  
228 - * @return  
229 - */  
230 - private List<String> getRecommondedSkns(List<Integer> smallSortIds, int maxSizePerSort, Map<String, String> paramMap) {  
231 - // 1、如果品类为空,则直接返回  
232 - if (smallSortIds == null || smallSortIds.isEmpty()) { 212 + private List<String> recommondSknsBySortAndBrandInfo(JSONObject sortAndBrandInfo, Map<String, String> paramMap) {
  213 + // 1、获取品牌id和品类id
  214 + if (sortAndBrandInfo == null || sortAndBrandInfo.isEmpty()) {
  215 + return new ArrayList<String>();
  216 + }
  217 + JSONArray brandIdJSONArray = sortAndBrandInfo.getJSONArray("brandIds");
  218 + JSONArray smallSortIdJSONArray = sortAndBrandInfo.getJSONArray("smallSortIds");
  219 + List<Integer> brandIds = this.jsonArrayToList(brandIdJSONArray, Integer.class);
  220 + List<Integer> smallSortIds = this.jsonArrayToList(smallSortIdJSONArray, Integer.class);
  221 + if (brandIds.isEmpty() && smallSortIds.isEmpty()) {
233 return new ArrayList<String>(); 222 return new ArrayList<String>();
234 } 223 }
235 - SearchParam searchParam = new SearchParam();  
236 -  
237 // 2、构造filter 224 // 2、构造filter
  225 + SearchParam searchParam = new SearchParam();
238 BoolQueryBuilder boolFilter = this.getDefaultBoolQueryBuilder(); 226 BoolQueryBuilder boolFilter = this.getDefaultBoolQueryBuilder();
239 - boolFilter.must(QueryBuilders.termsQuery("smallSortId", smallSortIds)); 227 + BoolQueryBuilder brandAndSortFilter = new BoolQueryBuilder();
  228 + if (!brandIds.isEmpty()) {
  229 + brandAndSortFilter.should(QueryBuilders.termsQuery("brandId", brandIds));
  230 + }
  231 + if (!smallSortIds.isEmpty()) {
  232 + brandAndSortFilter.should(QueryBuilders.termsQuery("smallSortId", smallSortIds));
  233 + }
  234 + boolFilter.must(brandAndSortFilter);
240 searchParam.setFiter(boolFilter); 235 searchParam.setFiter(boolFilter);
241 236
242 // 3、构造query 237 // 3、构造query
243 FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(QueryBuilders.matchAllQuery()); 238 FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(QueryBuilders.matchAllQuery());
244 - // 针对看过的SKN做加分  
245 - String productSkns = paramMap.get(SearchRequestParams.PARAM_SYNC_SKN);  
246 - if (!StringUtils.isBlank(productSkns)) {  
247 - functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", productSkns.split(",")), ScoreFunctionBuilders.weightFactorFunction(100));  
248 - }  
249 // 强制加上个性化打分 239 // 强制加上个性化打分
250 functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap); 240 functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);
251 searchParam.setQuery(functionScoreQueryBuilder); 241 searchParam.setQuery(functionScoreQueryBuilder);
252 242
253 // 4、设置聚合条件 243 // 4、设置聚合条件
254 - final String firstAggName = "firstAgg";  
255 - String order = "_score:desc";  
256 - String sortField = order.split(":")[0];  
257 - SortOrder sortOrder = order.split(":")[1].equals("desc") ? SortOrder.DESC : SortOrder.ASC; 244 + String sortField = "_score";
  245 + SortOrder sortOrder = SortOrder.DESC;
258 List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>(); 246 List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>();
259 - // 4.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】  
260 - TermsBuilder parentAggregationBuilder = AggregationBuilders.terms(firstAggName).field("smallSortId").size(smallSortIds.size());  
261 - // 4.2)添加子聚合:取得分最大的值  
262 - parentAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField));  
263 - // 4.3)添加孙聚合:取打分最高的一个product  
264 - parentAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(maxSizePerSort));  
265 - list.add(parentAggregationBuilder); 247 + // 4.1)构造父聚合:品类聚合【同时按子聚合的sort字段排序】
  248 + TermsBuilder parentAggregationBuilder = AggregationBuilders.terms("smallSortAgg").field("smallSortId").size(smallSortIds.size());
  249 + // 4.2)构造子聚合:品牌或聚合【同时按子聚合的sort字段排序】
  250 + TermsBuilder sonAggregationBuilder = AggregationBuilders.terms("brandAgg").field("brandId").size(brandIds.size());
  251 + // 4.3)为子聚合添加孙聚合:取得分最大的值
  252 + sonAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField));
  253 + // 4.4)为子聚合孙聚合:取打分最高的一个product
  254 + sonAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(1));
  255 + list.add(parentAggregationBuilder.subAggregation(sonAggregationBuilder));
266 searchParam.setAggregationBuilders(list); 256 searchParam.setAggregationBuilders(list);
267 257
268 // 5、设置分页 258 // 5、设置分页
@@ -275,26 +265,123 @@ public class GoodProductListService implements IGoodProductsService { @@ -275,26 +265,123 @@ public class GoodProductListService implements IGoodProductsService {
275 if (recommendedSknJSONArray != null) { 265 if (recommendedSknJSONArray != null) {
276 return this.jsonArrayToList(recommendedSknJSONArray, String.class); 266 return this.jsonArrayToList(recommendedSknJSONArray, String.class);
277 } 267 }
278 - // 7、执行搜索,并构造返回结果 268 + // 7、执行搜索
279 final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX; 269 final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
280 SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam); 270 SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
281 if (searchResult == null || searchResult.getAggMaps() == null) { 271 if (searchResult == null || searchResult.getAggMaps() == null) {
282 return new ArrayList<String>(); 272 return new ArrayList<String>();
283 } 273 }
  274 + // 8、获取搜索结果
284 Map<String, Aggregation> aggMaps = searchResult.getAggMaps(); 275 Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
285 - if (!aggMaps.containsKey(firstAggName)) { 276 + if (!aggMaps.containsKey("smallSortAgg")) {
286 return new ArrayList<String>(); 277 return new ArrayList<String>();
287 } 278 }
288 - List<String> recommendedSknList = this.getRecommendedSknList((MultiBucketsAggregation) aggMaps.get(firstAggName)); 279 + List<GoodProductBO> goodProductLists = new ArrayList<GoodProductBO>();
  280 + Iterator<? extends Bucket> smallSortIterator = ((MultiBucketsAggregation) aggMaps.get("smallSortAgg")).getBuckets().iterator();
  281 + while (smallSortIterator.hasNext()) {
  282 + Bucket smallSortBucket = smallSortIterator.next();
  283 + Map<String, Aggregation> smallSortAggMap = smallSortBucket.getAggregations().getAsMap();
  284 + if (!smallSortAggMap.containsKey("brandAgg")) {
  285 + continue;
  286 + }
  287 + Iterator<? extends Bucket> brandIdIterator = ((MultiBucketsAggregation) smallSortAggMap.get("brandAgg")).getBuckets().iterator();
  288 + List<String> productSkns = new ArrayList<String>();
  289 + while (brandIdIterator.hasNext()) {
  290 + Bucket brandIdBucket = brandIdIterator.next();
  291 + if (brandIdBucket.getAggregations().getAsMap().containsKey("product")) {
  292 + TopHits topHits = brandIdBucket.getAggregations().get("product");
  293 + if (topHits != null) {
  294 + SearchHits hits = topHits.getHits();
  295 + for (SearchHit hit : hits.getHits()) {
  296 + productSkns.add("" + hit.getSource().get("productSkn"));
  297 + }
  298 + }
  299 + }
  300 + }
  301 + if (!productSkns.isEmpty()) {
  302 + Collections.shuffle(productSkns);
  303 + goodProductLists.add(new GoodProductBO(smallSortBucket.getKeyAsString(), productSkns));
  304 + }
  305 + }
  306 + Collections.shuffle(goodProductLists);
  307 + // 9、构造返回结果
289 recommendedSknJSONArray = new JSONArray(); 308 recommendedSknJSONArray = new JSONArray();
290 - for (String recommendedSkn : recommendedSknList) {  
291 - recommendedSknJSONArray.add(recommendedSkn); 309 + // 9.1、获取推荐出来的商品
  310 + recommendedSknJSONArray.addAll(this.getProductSkns(goodProductLists));
  311 + // 9.2、再加入用户看过的商品前面
  312 + for (String skn : paramMap.getOrDefault("product_skn", "").split(",")) {
  313 + if (!recommendedSknJSONArray.contains(skn)) {
  314 + recommendedSknJSONArray.add(skn);
  315 + }
292 } 316 }
293 searchCacheService.addJSONArrayToCache(indexName, searchParam, recommendedSknJSONArray); 317 searchCacheService.addJSONArrayToCache(indexName, searchParam, recommendedSknJSONArray);
294 return this.jsonArrayToList(recommendedSknJSONArray, String.class); 318 return this.jsonArrayToList(recommendedSknJSONArray, String.class);
295 } 319 }
296 320
  321 + private List<String> getProductSkns(List<GoodProductBO> goodProductLists) {
  322 + if (goodProductLists == null || goodProductLists.isEmpty()) {
  323 + return new ArrayList<String>();
  324 + }
  325 + int maxSknCount = 0;
  326 + for (GoodProductBO goodProductBO : goodProductLists) {
  327 + int productSknSize = goodProductBO.getProductSkn().size();
  328 + if (maxSknCount < productSknSize) {
  329 + maxSknCount = productSknSize;
  330 + }
  331 + }
  332 + List<String> results = new ArrayList<String>();
  333 + for (int i = 0; i < maxSknCount; i++) {
  334 + for (GoodProductBO goodProductBO : goodProductLists) {
  335 + int randomCount = 1 + (int) (Math.random() * 2);
  336 + results.addAll(goodProductBO.randomGetProductSkn(randomCount));
  337 + }
  338 + }
  339 + return results;
  340 + }
  341 +
  342 + private QueryBuilder builderGoodProductQueryBuilder(Map<String, String> paramMap, List<String> recommendedSknList) {
  343 + QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
  344 + FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder);
  345 + // 针对推荐出来的SKN做加分
  346 + if (recommendedSknList != null && !recommendedSknList.isEmpty()) {
  347 + Map<Integer, List<String>> recommondSknMap = this.splitProductSkns(recommendedSknList, 2);
  348 + float currentGroupScore = 1000;
  349 + for (Map.Entry<Integer, List<String>> entry : recommondSknMap.entrySet()) {
  350 + functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", entry.getValue()), ScoreFunctionBuilders.weightFactorFunction(currentGroupScore));
  351 + currentGroupScore = currentGroupScore - 10;
  352 + }
  353 + }
  354 + // 加上个性化打分
  355 + if (searchCommonHelper.isNeedPersonalSearch(paramMap)) {
  356 + functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);
  357 + }
  358 + return functionScoreQueryBuilder;
  359 + }
  360 +
  361 + /**
  362 + * 有好货默认的过滤规则
  363 + *
  364 + * @return
  365 + */
  366 + private BoolQueryBuilder getDefaultBoolQueryBuilder() {
  367 + BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
  368 + boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));
  369 + boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));
  370 + boolFilter.must(QueryBuilders.termQuery("status", 1));
  371 + boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));
  372 + boolFilter.must(QueryBuilders.termQuery("attribute", 1));
  373 + // 默认推库存>2,非断码,并且短评存在的数据
  374 + boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(2));
  375 + boolFilter.must(QueryBuilders.rangeQuery("breakingRate").lt(50));
  376 + boolFilter.must(QueryBuilders.rangeQuery("basePinRatio").lt(3.5));
  377 + boolFilter.must(QueryBuilders.termQuery("isPhraseExist", "Y"));
  378 + return boolFilter;
  379 + }
  380 +
297 private <T> List<T> jsonArrayToList(JSONArray jsonArray, Class<T> clazz) { 381 private <T> List<T> jsonArrayToList(JSONArray jsonArray, Class<T> clazz) {
  382 + if (jsonArray == null) {
  383 + return new ArrayList<T>();
  384 + }
298 return JSONObject.parseArray(jsonArray.toJSONString(), clazz); 385 return JSONObject.parseArray(jsonArray.toJSONString(), clazz);
299 } 386 }
300 387
@@ -320,40 +407,11 @@ public class GoodProductListService implements IGoodProductsService { @@ -320,40 +407,11 @@ public class GoodProductListService implements IGoodProductsService {
320 return result; 407 return result;
321 } 408 }
322 409
323 - /**  
324 - * 获取聚合出来的商品  
325 - *  
326 - * @param aggregation  
327 - * @return  
328 - */  
329 - private List<String> getRecommendedSknList(MultiBucketsAggregation aggregation) {  
330 - Iterator<? extends Bucket> itAgg = aggregation.getBuckets().iterator();  
331 - List<String> recommendedSknList = new ArrayList<String>();  
332 - while (itAgg.hasNext()) {  
333 - Bucket lt = itAgg.next();  
334 - if (lt.getAggregations().getAsMap().containsKey("product")) {  
335 - TopHits topHits = lt.getAggregations().get("product");  
336 - if (topHits != null) {  
337 - SearchHits hits = topHits.getHits();  
338 - for (SearchHit hit : hits.getHits()) {  
339 - recommendedSknList.add("" + hit.getSource().get("productSkn"));  
340 - }  
341 - }  
342 - } 410 + public static List<String> getNewList(List<String> oldList) {
  411 + List<String> newList = new ArrayList<String>();
  412 + for (String element : oldList) {
  413 + newList.add(element);
343 } 414 }
344 - Collections.shuffle(recommendedSknList);  
345 - return recommendedSknList; 415 + return newList;
346 } 416 }
347 -  
348 - public static void main(String[] args) {  
349 - List<String> list = new ArrayList<String>();  
350 - for (int i = 1; i <= 99; i++) {  
351 - list.add(i + "");  
352 - }  
353 - Map<Integer, List<String>> results = new GoodProductListService().splitProductSkns(list, 20);  
354 - for (Map.Entry<Integer, List<String>> entry : results.entrySet()) {  
355 - System.out.println(entry.getKey() + "_" + entry.getValue());  
356 - }  
357 - }  
358 -  
359 } 417 }
@@ -88,7 +88,7 @@ public class SearchSortGroupService implements ISearchSortGroupService, Applicat @@ -88,7 +88,7 @@ public class SearchSortGroupService implements ISearchSortGroupService, Applicat
88 // 性别 88 // 性别
89 if (paramMap.containsKey(SearchRequestParams.PARAM_SEARCH_GENDER)) { 89 if (paramMap.containsKey(SearchRequestParams.PARAM_SEARCH_GENDER)) {
90 String[] genders = paramMap.get(SearchRequestParams.PARAM_SEARCH_GENDER).split(","); 90 String[] genders = paramMap.get(SearchRequestParams.PARAM_SEARCH_GENDER).split(",");
91 - boolFilter.must(QueryBuilders.termQuery("gender", genders)); 91 + boolFilter.must(QueryBuilders.termsQuery("gender", genders));
92 } 92 }
93 // 如果contain_global!=Y,则过滤掉全球购的商品[全球购商品融合需求] 93 // 如果contain_global!=Y,则过滤掉全球购的商品[全球购商品融合需求]
94 if (!searchCommonHelper.containGlobal(paramMap)) { 94 if (!searchCommonHelper.containGlobal(paramMap)) {
  1 +package com.yoho.search.service.vo;
  2 +
  3 +import java.util.ArrayList;
  4 +import java.util.List;
  5 +
  6 +public class GoodProductBO {
  7 + private String smallSortId;
  8 + private List<String> productSkn;
  9 +
  10 + public GoodProductBO(String smallSortId, List<String> productSkn) {
  11 + super();
  12 + this.smallSortId = smallSortId;
  13 + this.productSkn = productSkn;
  14 + }
  15 +
  16 + public String getSmallSortId() {
  17 + return smallSortId;
  18 + }
  19 +
  20 + public List<String> getProductSkn() {
  21 + return productSkn;
  22 + }
  23 +
  24 + public List<String> randomGetProductSkn(int count) {
  25 + List<String> results = new ArrayList<String>();
  26 + if (productSkn == null || productSkn.isEmpty()) {
  27 + return results;
  28 + }
  29 + for (int i = 0; i < count; i++) {
  30 + if (!productSkn.isEmpty()) {
  31 + results.add(productSkn.remove(0));
  32 + }
  33 + }
  34 + return results;
  35 + }
  36 +
  37 +}