Authored by 胡古飞

Merge branch 'master' into 5.6-global

# Conflicts:
#	service/src/main/java/com/yoho/search/service/servicenew/impl/GoodProductListService.java
@@ -43,9 +43,9 @@ public class FunctionScoreSearchHelper { @@ -43,9 +43,9 @@ public class FunctionScoreSearchHelper {
43 43
44 // 普通个性化的时间维度 44 // 普通个性化的时间维度
45 private final int oneDaySecondCount = 24 * 60 * 60; 45 private final int oneDaySecondCount = 24 * 60 * 60;
46 - private FirstShelveTimeScore commonFirstShelveTimeScore = new FirstShelveTimeScore(90, 30, 60); 46 + private FirstShelveTimeScore commonFirstShelveTimeScore = new FirstShelveTimeScore(60, 30, 30);
47 // 新品到着的个性化时间维度 47 // 新品到着的个性化时间维度
48 - private FirstShelveTimeScore newRecShelveTimeScore = new FirstShelveTimeScore(30, 10, 20); 48 + private FirstShelveTimeScore newRecShelveTimeScore = new FirstShelveTimeScore(14, 7, 7);
49 private final float globalWeight = 0.50f; 49 private final float globalWeight = 0.50f;
50 50
51 private WeightBuilder genWeightFactorBuilder(float factor) { 51 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
@@ -37,19 +39,20 @@ import com.yoho.search.core.es.model.SearchResult; @@ -37,19 +39,20 @@ import com.yoho.search.core.es.model.SearchResult;
37 import com.yoho.search.service.cache.CacheEnum; 39 import com.yoho.search.service.cache.CacheEnum;
38 import com.yoho.search.service.service.SearchCacheService; 40 import com.yoho.search.service.service.SearchCacheService;
39 import com.yoho.search.service.service.SearchCommonService; 41 import com.yoho.search.service.service.SearchCommonService;
40 -import com.yoho.search.service.service.base.ProductIndexBaseService;  
41 import com.yoho.search.service.service.helper.AggProductListHelper; 42 import com.yoho.search.service.service.helper.AggProductListHelper;
42 import com.yoho.search.service.service.helper.FunctionScoreSearchHelper; 43 import com.yoho.search.service.service.helper.FunctionScoreSearchHelper;
43 import com.yoho.search.service.service.helper.SearchCommonHelper; 44 import com.yoho.search.service.service.helper.SearchCommonHelper;
44 import com.yoho.search.service.service.helper.SearchServiceHelper; 45 import com.yoho.search.service.service.helper.SearchServiceHelper;
45 import com.yoho.search.service.service.helper.SearchSortHelper; 46 import com.yoho.search.service.service.helper.SearchSortHelper;
46 import com.yoho.search.service.servicenew.IGoodProductsService; 47 import com.yoho.search.service.servicenew.IGoodProductsService;
47 -import com.yoho.search.service.utils.SearchRequestParams; 48 +import com.yoho.search.service.vo.GoodProductBO;
48 import com.yoho.search.service.vo.SearchApiResult; 49 import com.yoho.search.service.vo.SearchApiResult;
49 50
50 @Service 51 @Service
51 public class GoodProductListService implements IGoodProductsService { 52 public class GoodProductListService implements IGoodProductsService {
52 53
  54 + private static final Logger logger = LoggerFactory.getLogger(GoodProductListService.class);
  55 +
53 @Autowired 56 @Autowired
54 private SearchCommonService searchCommonService; 57 private SearchCommonService searchCommonService;
55 @Autowired 58 @Autowired
@@ -64,17 +67,13 @@ public class GoodProductListService implements IGoodProductsService { @@ -64,17 +67,13 @@ public class GoodProductListService implements IGoodProductsService {
64 private SearchCommonHelper searchCommonHelper; 67 private SearchCommonHelper searchCommonHelper;
65 @Autowired 68 @Autowired
66 private FunctionScoreSearchHelper functionScoreSearchHelper; 69 private FunctionScoreSearchHelper functionScoreSearchHelper;
67 - @Autowired  
68 - private ProductIndexBaseService productIndexBaseService;  
69 -  
70 - private final int maxSmallSortCount = 20;  
71 - private final int maxProductSknCountPerSort = 5;  
72 - private final int maxCountPerGroup = 3;  
73 - // private final float firstSknScore = 300;  
74 - private final float recommendedSknMaxScore = 200;  
75 70
76 @Override 71 @Override
77 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 + }
78 // 1、检测分页参数 77 // 1、检测分页参数
79 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"));
80 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"));
@@ -82,9 +81,14 @@ public class GoodProductListService implements IGoodProductsService { @@ -82,9 +81,14 @@ public class GoodProductListService implements IGoodProductsService {
82 return new SearchApiResult().setCode(400).setMessage("分页参数不合法"); 81 return new SearchApiResult().setCode(400).setMessage("分页参数不合法");
83 } 82 }
84 // 2、先获取用户浏览的SKN对应的品类列表 83 // 2、先获取用户浏览的SKN对应的品类列表
85 - List<Integer> smallSortIds = this.getProductSknSmallSortIds(paramMap, maxSmallSortCount);  
86 - // 3、再每个品类下获取5个SKN[每次随机打乱]  
87 - 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 + }
88 92
89 // 4、构造搜索参数 93 // 4、构造搜索参数
90 SearchParam searchParam = new SearchParam(); 94 SearchParam searchParam = new SearchParam();
@@ -106,6 +110,11 @@ public class GoodProductListService implements IGoodProductsService { @@ -106,6 +110,11 @@ public class GoodProductListService implements IGoodProductsService {
106 CacheEnum cacheEnum = CacheEnum.EHCACHE; 110 CacheEnum cacheEnum = CacheEnum.EHCACHE;
107 JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(cacheEnum, indexName, searchParam); 111 JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(cacheEnum, indexName, searchParam);
108 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 + }
109 return new SearchApiResult().setData(cacheObject); 118 return new SearchApiResult().setData(cacheObject);
110 } 119 }
111 // 6)查询ES 120 // 6)查询ES
@@ -119,42 +128,22 @@ public class GoodProductListService implements IGoodProductsService { @@ -119,42 +128,22 @@ public class GoodProductListService implements IGoodProductsService {
119 dataMap.put("page", searchResult.getPage()); 128 dataMap.put("page", searchResult.getPage());
120 dataMap.put("page_size", searchParam.getSize()); 129 dataMap.put("page_size", searchParam.getSize());
121 dataMap.put("page_total", searchResult.getTotalPage()); 130 dataMap.put("page_total", searchResult.getTotalPage());
122 - dataMap.put("product_list", productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList(), Arrays.asList("phrase"))); 131 + dataMap.put("product_list", searchServiceHelper.getProductMapList(searchResult.getResultList(), Arrays.asList("phrase")));
123 132
124 // 8)将结果存进缓存 133 // 8)将结果存进缓存
125 searchCacheService.addJSONObjectToCache(cacheEnum, indexName, searchParam, dataMap); 134 searchCacheService.addJSONObjectToCache(cacheEnum, indexName, searchParam, dataMap);
126 return new SearchApiResult().setData(dataMap); 135 return new SearchApiResult().setData(dataMap);
127 } 136 }
128 137
129 - private QueryBuilder builderGoodProductQueryBuilder(Map<String, String> paramMap, List<String> recommendedSknList) {  
130 - QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();  
131 - FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder);  
132 - // 针对推荐出来的SKN做加分  
133 - if (recommendedSknList != null && !recommendedSknList.isEmpty()) {  
134 - Map<Integer, List<String>> recommondSknMap = this.splitProductSkns(recommendedSknList, maxCountPerGroup);  
135 - float currentGroupScore = recommendedSknMaxScore;  
136 - for (Map.Entry<Integer, List<String>> entry : recommondSknMap.entrySet()) {  
137 - functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", entry.getValue()), ScoreFunctionBuilders.weightFactorFunction(currentGroupScore));  
138 - currentGroupScore = currentGroupScore - 10;  
139 - }  
140 - }  
141 - // 加上个性化打分  
142 - if (searchCommonHelper.isNeedPersonalSearch(paramMap)) {  
143 - functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);  
144 - }  
145 - return functionScoreQueryBuilder;  
146 - }  
147 -  
148 /** 138 /**
149 - * 获取SKN相关的小分类 139 + * 获取skn的品类列表和品牌列表
150 * 140 *
151 * @param productSkns 141 * @param productSkns
152 * @return 142 * @return
153 */ 143 */
154 - private List<Integer> getProductSknSmallSortIds(Map<String, String> paramMap, int maxCount) {  
155 - String productSkns = paramMap.get(SearchRequestParams.PARAM_SEARCH_PRODUCT_SKN); 144 + private JSONObject querySknSortAndBrand(String productSkns) {
156 if (StringUtils.isBlank(productSkns)) { 145 if (StringUtils.isBlank(productSkns)) {
157 - return new ArrayList<Integer>(); 146 + return new JSONObject();
158 } 147 }
159 String[] productSknArray = productSkns.split(","); 148 String[] productSknArray = productSkns.split(",");
160 SearchParam searchParam = new SearchParam(); 149 SearchParam searchParam = new SearchParam();
@@ -162,102 +151,108 @@ public class GoodProductListService implements IGoodProductsService { @@ -162,102 +151,108 @@ public class GoodProductListService implements IGoodProductsService {
162 BoolQueryBuilder boolFilter = QueryBuilders.boolQuery(); 151 BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
163 boolFilter.must(QueryBuilders.termsQuery("productSkn", productSknArray)); 152 boolFilter.must(QueryBuilders.termsQuery("productSkn", productSknArray));
164 searchParam.setFiter(boolFilter); 153 searchParam.setFiter(boolFilter);
165 - // 2、设置聚合条件  
166 - final String aggName = "smallSortAgg";  
167 - TermsBuilder smallSortIdAgg = AggregationBuilders.terms(aggName).field("smallSortId").size(maxCount);  
168 - 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);
169 // 3、设置分页 161 // 3、设置分页
170 searchParam.setPage(0); 162 searchParam.setPage(0);
171 searchParam.setSize(0); 163 searchParam.setSize(0);
172 searchParam.setOffset(0); 164 searchParam.setOffset(0);
173 165
174 // 4、先从缓存中获取,如果能取到,则直接返回 166 // 4、先从缓存中获取,如果能取到,则直接返回
175 - JSONArray sknSmallSortIdJSONArray = searchCacheService.getJSONArrayFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);  
176 - if (sknSmallSortIdJSONArray != null) {  
177 - return this.jsonArrayToList(sknSmallSortIdJSONArray, Integer.class); 167 + JSONObject sortAndBrandJSONObject = searchCacheService.getJSONObjectFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
  168 + if (sortAndBrandJSONObject != null) {
  169 + return sortAndBrandJSONObject;
178 } 170 }
179 // 5、执行搜索 171 // 5、执行搜索
180 SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam); 172 SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
181 - 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)) {
182 return new ArrayList<Integer>(); 193 return new ArrayList<Integer>();
183 } 194 }
184 - sknSmallSortIdJSONArray = new JSONArray();  
185 - MultiBucketsAggregation aggregation = (MultiBucketsAggregation) searchResult.getAggMaps().get(aggName);  
186 - Iterator<? extends Bucket> smallSortIdIterator = aggregation.getBuckets().iterator();  
187 - while (smallSortIdIterator.hasNext()) {  
188 - Bucket smallSortIdBucket = smallSortIdIterator.next();  
189 - if (StringUtils.isNumeric(smallSortIdBucket.getKeyAsString())) {  
190 - 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()));
191 } 202 }
192 } 203 }
193 - searchCacheService.addJSONArrayToCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam, sknSmallSortIdJSONArray);  
194 - return this.jsonArrayToList(sknSmallSortIdJSONArray, Integer.class); 204 + return results;
195 } 205 }
196 206
197 /** 207 /**
198 - * 有好货默认的过滤规则 208 + * 根据skn参数获取推荐出来的skn列表
199 * 209 *
200 * @return 210 * @return
201 */ 211 */
202 - private BoolQueryBuilder getDefaultBoolQueryBuilder() {  
203 - BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();  
204 - boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));  
205 - boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));  
206 - boolFilter.must(QueryBuilders.termQuery("status", 1));  
207 - boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));  
208 - boolFilter.must(QueryBuilders.termQuery("attribute", 1));  
209 - // 默认推库存>2,非断码,并且短评存在的数据  
210 - boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(2));  
211 - boolFilter.must(QueryBuilders.rangeQuery("breakingRate").lt(50));  
212 - boolFilter.must(QueryBuilders.rangeQuery("basePinRatio").lt(3.5));  
213 - boolFilter.must(QueryBuilders.termQuery("isPhraseExist", "Y"));  
214 - return boolFilter; 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>();
215 } 216 }
216 -  
217 - /**  
218 - * 每个品类为用户推荐maxSize个SKN  
219 - *  
220 - * @param smallSortIds  
221 - * @param maxSize  
222 - * @param paramMap  
223 - * @return  
224 - */  
225 - private List<String> getRecommondedSkns(List<Integer> smallSortIds, int maxSizePerSort, Map<String, String> paramMap) {  
226 - // 1、如果品类为空,则直接返回  
227 - if (smallSortIds == null || smallSortIds.isEmpty()) { 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()) {
228 return new ArrayList<String>(); 222 return new ArrayList<String>();
229 } 223 }
230 - SearchParam searchParam = new SearchParam();  
231 -  
232 // 2、构造filter 224 // 2、构造filter
  225 + SearchParam searchParam = new SearchParam();
233 BoolQueryBuilder boolFilter = this.getDefaultBoolQueryBuilder(); 226 BoolQueryBuilder boolFilter = this.getDefaultBoolQueryBuilder();
234 - 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);
235 searchParam.setFiter(boolFilter); 235 searchParam.setFiter(boolFilter);
236 236
237 // 3、构造query 237 // 3、构造query
238 FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(QueryBuilders.matchAllQuery()); 238 FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(QueryBuilders.matchAllQuery());
239 - // 针对看过的SKN做加分  
240 - String productSkns = paramMap.get(SearchRequestParams.PARAM_SEARCH_PRODUCT_SKN);  
241 - if (!StringUtils.isBlank(productSkns)) {  
242 - functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", productSkns.split(",")), ScoreFunctionBuilders.weightFactorFunction(100));  
243 - }  
244 // 强制加上个性化打分 239 // 强制加上个性化打分
245 functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap); 240 functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);
246 searchParam.setQuery(functionScoreQueryBuilder); 241 searchParam.setQuery(functionScoreQueryBuilder);
247 242
248 // 4、设置聚合条件 243 // 4、设置聚合条件
249 - final String firstAggName = "firstAgg";  
250 - String order = "_score:desc";  
251 - String sortField = order.split(":")[0];  
252 - SortOrder sortOrder = order.split(":")[1].equals("desc") ? SortOrder.DESC : SortOrder.ASC; 244 + String sortField = "_score";
  245 + SortOrder sortOrder = SortOrder.DESC;
253 List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>(); 246 List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>();
254 - // 4.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】  
255 - TermsBuilder parentAggregationBuilder = AggregationBuilders.terms(firstAggName).field("smallSortId").size(smallSortIds.size());  
256 - // 4.2)添加子聚合:取得分最大的值  
257 - parentAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField));  
258 - // 4.3)添加孙聚合:取打分最高的一个product  
259 - parentAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(maxSizePerSort));  
260 - 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));
261 searchParam.setAggregationBuilders(list); 256 searchParam.setAggregationBuilders(list);
262 257
263 // 5、设置分页 258 // 5、设置分页
@@ -270,26 +265,123 @@ public class GoodProductListService implements IGoodProductsService { @@ -270,26 +265,123 @@ public class GoodProductListService implements IGoodProductsService {
270 if (recommendedSknJSONArray != null) { 265 if (recommendedSknJSONArray != null) {
271 return this.jsonArrayToList(recommendedSknJSONArray, String.class); 266 return this.jsonArrayToList(recommendedSknJSONArray, String.class);
272 } 267 }
273 - // 7、执行搜索,并构造返回结果 268 + // 7、执行搜索
274 final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX; 269 final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
275 SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam); 270 SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
276 if (searchResult == null || searchResult.getAggMaps() == null) { 271 if (searchResult == null || searchResult.getAggMaps() == null) {
277 return new ArrayList<String>(); 272 return new ArrayList<String>();
278 } 273 }
  274 + // 8、获取搜索结果
279 Map<String, Aggregation> aggMaps = searchResult.getAggMaps(); 275 Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
280 - if (!aggMaps.containsKey(firstAggName)) { 276 + if (!aggMaps.containsKey("smallSortAgg")) {
281 return new ArrayList<String>(); 277 return new ArrayList<String>();
282 } 278 }
283 - 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、构造返回结果
284 recommendedSknJSONArray = new JSONArray(); 308 recommendedSknJSONArray = new JSONArray();
285 - for (String recommendedSkn : recommendedSknList) {  
286 - 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 + }
287 } 316 }
288 searchCacheService.addJSONArrayToCache(indexName, searchParam, recommendedSknJSONArray); 317 searchCacheService.addJSONArrayToCache(indexName, searchParam, recommendedSknJSONArray);
289 return this.jsonArrayToList(recommendedSknJSONArray, String.class); 318 return this.jsonArrayToList(recommendedSknJSONArray, String.class);
290 } 319 }
291 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 +
292 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 + }
293 return JSONObject.parseArray(jsonArray.toJSONString(), clazz); 385 return JSONObject.parseArray(jsonArray.toJSONString(), clazz);
294 } 386 }
295 387
@@ -315,40 +407,11 @@ public class GoodProductListService implements IGoodProductsService { @@ -315,40 +407,11 @@ public class GoodProductListService implements IGoodProductsService {
315 return result; 407 return result;
316 } 408 }
317 409
318 - /**  
319 - * 获取聚合出来的商品  
320 - *  
321 - * @param aggregation  
322 - * @return  
323 - */  
324 - private List<String> getRecommendedSknList(MultiBucketsAggregation aggregation) {  
325 - Iterator<? extends Bucket> itAgg = aggregation.getBuckets().iterator();  
326 - List<String> recommendedSknList = new ArrayList<String>();  
327 - while (itAgg.hasNext()) {  
328 - Bucket lt = itAgg.next();  
329 - if (lt.getAggregations().getAsMap().containsKey("product")) {  
330 - TopHits topHits = lt.getAggregations().get("product");  
331 - if (topHits != null) {  
332 - SearchHits hits = topHits.getHits();  
333 - for (SearchHit hit : hits.getHits()) {  
334 - recommendedSknList.add("" + hit.getSource().get("productSkn"));  
335 - }  
336 - }  
337 - }  
338 - }  
339 - Collections.shuffle(recommendedSknList);  
340 - return recommendedSknList;  
341 - }  
342 -  
343 - public static void main(String[] args) {  
344 - List<String> list = new ArrayList<String>();  
345 - for (int i = 1; i <= 99; i++) {  
346 - list.add(i + ""); 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);
347 } 414 }
348 - Map<Integer, List<String>> results = new GoodProductListService().splitProductSkns(list, 20);  
349 - for (Map.Entry<Integer, List<String>> entry : results.entrySet()) {  
350 - System.out.println(entry.getKey() + "_" + entry.getValue()); 415 + return newList;
351 } 416 }
352 - }  
353 -  
354 } 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 +}