Authored by 胡古飞

add goodProductList.json

@@ -156,23 +156,6 @@ public class SearchServiceHelper { @@ -156,23 +156,6 @@ public class SearchServiceHelper {
156 QueryBuilder queryBuilder = this.constructQueryBuilder(paramMap); 156 QueryBuilder queryBuilder = this.constructQueryBuilder(paramMap);
157 queryBuilder = functionScoreSearchHelper.buildFunctionScoreQueryBuild(queryBuilder, paramMap); 157 queryBuilder = functionScoreSearchHelper.buildFunctionScoreQueryBuild(queryBuilder, paramMap);
158 return queryBuilder; 158 return queryBuilder;
159 - // queryBuilder = this.buildPersonalSearch(queryBuilder, paramMap);  
160 - // queryBuilder = this.buildFunctionScoreQueryBuild(queryBuilder,  
161 - // paramMap);  
162 - // return queryBuilder;  
163 -  
164 - // String dynamicRuleValue =  
165 - // dynamicSearchRuleHelper.getDynamicRuleValue(paramMap);  
166 - // if (StringUtils.isEmpty(dynamicRuleValue) ||  
167 - // "-1".equals(dynamicRuleValue)) {  
168 - // queryBuilder = this.buildGlobalSearch(queryBuilder, paramMap);  
169 - // queryBuilder = this.buildDeScoreBrandSearch(queryBuilder, paramMap);  
170 - // } else {  
171 - // queryBuilder =  
172 - // dynamicSearchRuleHelper.buildDynamicSerach(queryBuilder, paramMap,  
173 - // dynamicRuleValue);  
174 - // }  
175 - // return queryBuilder;  
176 } 159 }
177 160
178 public QueryBuilder constructOrQueryBuilderForProductList(Map<String, String> paramMap) { 161 public QueryBuilder constructOrQueryBuilderForProductList(Map<String, String> paramMap) {
1 package com.yoho.search.service.servicenew.impl; 1 package com.yoho.search.service.servicenew.impl;
2 2
3 import java.util.ArrayList; 3 import java.util.ArrayList;
  4 +import java.util.Arrays;
  5 +import java.util.Collections;
  6 +import java.util.Iterator;
4 import java.util.List; 7 import java.util.List;
5 import java.util.Map; 8 import java.util.Map;
6 9
  10 +import org.apache.commons.lang.StringUtils;
7 import org.elasticsearch.index.query.BoolQueryBuilder; 11 import org.elasticsearch.index.query.BoolQueryBuilder;
  12 +import org.elasticsearch.index.query.QueryBuilder;
8 import org.elasticsearch.index.query.QueryBuilders; 13 import org.elasticsearch.index.query.QueryBuilders;
  14 +import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
  15 +import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
  16 +import org.elasticsearch.search.SearchHit;
  17 +import org.elasticsearch.search.SearchHits;
9 import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; 18 import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
  19 +import org.elasticsearch.search.aggregations.Aggregation;
10 import org.elasticsearch.search.aggregations.AggregationBuilders; 20 import org.elasticsearch.search.aggregations.AggregationBuilders;
11 -import org.elasticsearch.search.aggregations.bucket.terms.Terms; 21 +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
  22 +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket;
12 import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder; 23 import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
  24 +import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
  25 +import org.elasticsearch.search.sort.SortBuilder;
13 import org.elasticsearch.search.sort.SortBuilders; 26 import org.elasticsearch.search.sort.SortBuilders;
14 import org.elasticsearch.search.sort.SortOrder; 27 import org.elasticsearch.search.sort.SortOrder;
15 import org.springframework.beans.factory.annotation.Autowired; 28 import org.springframework.beans.factory.annotation.Autowired;
16 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
17 30
  31 +import com.alibaba.fastjson.JSONArray;
  32 +import com.alibaba.fastjson.JSONObject;
18 import com.yoho.search.base.utils.ISearchConstants; 33 import com.yoho.search.base.utils.ISearchConstants;
19 import com.yoho.search.core.es.model.SearchParam; 34 import com.yoho.search.core.es.model.SearchParam;
20 import com.yoho.search.core.es.model.SearchResult; 35 import com.yoho.search.core.es.model.SearchResult;
  36 +import com.yoho.search.service.cache.CacheEnum;
  37 +import com.yoho.search.service.personalized.PersonalVectorFeatureSearch;
  38 +import com.yoho.search.service.service.SearchCacheService;
21 import com.yoho.search.service.service.SearchCommonService; 39 import com.yoho.search.service.service.SearchCommonService;
  40 +import com.yoho.search.service.service.helper.AggProductListHelper;
  41 +import com.yoho.search.service.service.helper.FunctionScoreSearchHelper;
  42 +import com.yoho.search.service.service.helper.SearchCommonHelper;
  43 +import com.yoho.search.service.service.helper.SearchServiceHelper;
  44 +import com.yoho.search.service.service.helper.SearchSortHelper;
22 import com.yoho.search.service.servicenew.IGoodProductsService; 45 import com.yoho.search.service.servicenew.IGoodProductsService;
  46 +import com.yoho.search.service.utils.SearchRequestParams;
23 import com.yoho.search.service.vo.SearchApiResult; 47 import com.yoho.search.service.vo.SearchApiResult;
24 48
25 @Service 49 @Service
@@ -27,65 +51,241 @@ public class GoodProductListService implements IGoodProductsService { @@ -27,65 +51,241 @@ public class GoodProductListService implements IGoodProductsService {
27 51
28 @Autowired 52 @Autowired
29 private SearchCommonService searchCommonService; 53 private SearchCommonService searchCommonService;
  54 + @Autowired
  55 + private SearchServiceHelper searchServiceHelper;
  56 + @Autowired
  57 + private FunctionScoreSearchHelper functionScoreSearchHelper;
  58 + @Autowired
  59 + private SearchCacheService searchCacheService;
  60 + @Autowired
  61 + private AggProductListHelper aggProductListHelper;
  62 + @Autowired
  63 + private SearchSortHelper searchSortHelper;
  64 + @Autowired
  65 + private SearchCommonHelper searchCommonHelper;
  66 + @Autowired
  67 + private PersonalVectorFeatureSearch personalVectorFeatureSearch;
  68 +
  69 + private final int maxSmallSortCount = 20;
  70 + private final int maxProductSknCountPerSort = 5;
30 71
31 @Override 72 @Override
32 public SearchApiResult goodProductList(Map<String, String> paramMap) { 73 public SearchApiResult goodProductList(Map<String, String> paramMap) {
33 - //String productSkns = paramMap.get(SearchRequestParams.PARAM_SYNC_SKN);  
34 - BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();  
35 - boolFilter.must(QueryBuilders.termQuery("isPhraseExist","Y"));  
36 - boolFilter.must(QueryBuilders.termQuery("productSkn", "50002052")); 74 + // 1、检测分页参数
  75 + int pageSize = StringUtils.isBlank(paramMap.get("viewNum")) ? 30 : Integer.parseInt(paramMap.get("viewNum"));
  76 + int page = StringUtils.isBlank(paramMap.get("page")) ? 1 : Integer.parseInt(paramMap.get("page"));
  77 + if (page < 1 || pageSize < 0) {
  78 + return new SearchApiResult().setCode(400).setMessage("分页参数不合法");
  79 + }
  80 + // 2、先获取用户浏览的SKN对应的品类列表
  81 + List<Integer> smallSortIds = this.getProductSknSmallSortIds(paramMap, maxSmallSortCount);
  82 + // 3、再每个品类下获取5个SKN
  83 + List<String> recommondSkns = this.getRecommondedSkns(smallSortIds, maxProductSknCountPerSort, paramMap);
  84 +
  85 + // 4、构造搜索参数
37 SearchParam searchParam = new SearchParam(); 86 SearchParam searchParam = new SearchParam();
38 - searchParam.setFiter(boolFilter);  
39 - searchCommonService.doSearch("productindex", searchParam);  
40 - return null; 87 + searchParam.setFiter(this.getDefaultBoolQueryBuilder());
  88 + searchParam.setQuery(this.builderGoodProductQueryBuilder(paramMap, recommondSkns));
  89 + searchParam.setAggregationBuilders(null);
  90 + searchParam.setPage(page);
  91 + searchParam.setOffset((page - 1) * pageSize);
  92 + searchParam.setSize(pageSize);
  93 + List<SortBuilder> sortBuilders = new ArrayList<SortBuilder>();
  94 + sortBuilders.add(SortBuilders.fieldSort("_score").order(SortOrder.DESC));
  95 + sortBuilders.add(SortBuilders.fieldSort("salesNum").order(SortOrder.DESC));
  96 + sortBuilders.add(SortBuilders.fieldSort("firstShelveTime").order(SortOrder.DESC));
  97 + sortBuilders.add(SortBuilders.fieldSort("id").order(SortOrder.DESC));
  98 + searchParam.setSortBuilders(sortBuilders);
  99 +
  100 + // 5)从缓存中获取数据
  101 + final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
  102 + CacheEnum cacheEnum = CacheEnum.EHCACHE;
  103 + JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(cacheEnum, indexName, searchParam);
  104 + if (cacheObject != null) {
  105 + return new SearchApiResult().setData(cacheObject);
  106 + }
  107 + // 6)查询ES
  108 + SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
  109 + if (searchResult == null) {
  110 + return new SearchApiResult().setCode(500).setMessage("execption");
  111 + }
  112 + // 7)构造返回结果
  113 + JSONObject dataMap = new JSONObject();
  114 + dataMap.put("total", searchResult.getTotal());
  115 + dataMap.put("page", searchResult.getPage());
  116 + dataMap.put("page_size", searchParam.getSize());
  117 + dataMap.put("page_total", searchResult.getTotalPage());
  118 + dataMap.put("product_list", searchServiceHelper.getProductMapList(searchResult.getResultList()));
  119 +
  120 + // 8)将结果存进缓存
  121 + searchCacheService.addJSONObjectToCache(cacheEnum, indexName, searchParam, dataMap);
  122 + return new SearchApiResult().setData(dataMap);
  123 + }
  124 +
  125 + private QueryBuilder builderGoodProductQueryBuilder(Map<String, String> paramMap, List<String> recommondSkns) {
  126 + QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
  127 + FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder);
  128 + // 针对参数里第一个SKN加分
  129 + String productSkns = paramMap.get(SearchRequestParams.PARAM_SYNC_SKN);
  130 + if (!StringUtils.isBlank(productSkns)) {
  131 + functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", productSkns.split(",")[0]), ScoreFunctionBuilders.weightFactorFunction(100));
  132 + }
  133 + // 针对推荐出来的SKN做加分
  134 + if (recommondSkns != null && !recommondSkns.isEmpty()) {
  135 + functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", recommondSkns), ScoreFunctionBuilders.weightFactorFunction(50));
  136 + }
  137 + // 加上个性化打分
  138 + if (searchCommonHelper.isNeedPersonalSearch(paramMap)) {
  139 + personalVectorFeatureSearch.addPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);
  140 + }
  141 + return functionScoreQueryBuilder;
41 } 142 }
42 143
43 /** 144 /**
44 - * 获取skn的小物理分类列表 145 + * 获取SKN相关的小分类
45 * 146 *
46 * @param productSkns 147 * @param productSkns
47 * @return 148 * @return
48 */ 149 */
49 - private List<String> genProductSknSmallSortIds(String productSkns) { 150 + private List<Integer> getProductSknSmallSortIds(Map<String, String> paramMap, int maxCount) {
  151 + String productSkns = paramMap.get(SearchRequestParams.PARAM_SYNC_SKN);
  152 + if (StringUtils.isBlank(productSkns)) {
  153 + return new ArrayList<Integer>();
  154 + }
50 String[] productSknArray = productSkns.split(","); 155 String[] productSknArray = productSkns.split(",");
51 -  
52 SearchParam searchParam = new SearchParam(); 156 SearchParam searchParam = new SearchParam();
53 -  
54 // 1、设置过滤条件 157 // 1、设置过滤条件
55 BoolQueryBuilder boolFilter = QueryBuilders.boolQuery(); 158 BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
  159 + boolFilter.must(QueryBuilders.termsQuery("productSkn", productSknArray));
  160 + searchParam.setFiter(boolFilter);
  161 + // 2、设置聚合条件
  162 + final String aggName = "smallSortAgg";
  163 + TermsBuilder smallSortIdAgg = AggregationBuilders.terms(aggName).field("smallSortId").size(maxCount);
  164 + searchParam.setAggregationBuilders(Arrays.asList(smallSortIdAgg));
  165 + // 3、设置分页
  166 + searchParam.setPage(0);
  167 + searchParam.setSize(0);
  168 + searchParam.setOffset(0);
  169 + // 4、执行搜索
  170 + SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
  171 + if (searchResult == null || searchResult.getAggMaps() == null || searchResult.getAggMaps().get("smallSortAgg") == null) {
  172 + return new ArrayList<Integer>();
  173 + }
  174 + List<Integer> results = new ArrayList<Integer>();
  175 + MultiBucketsAggregation aggregation = (MultiBucketsAggregation) searchResult.getAggMaps().get(aggName);
  176 + Iterator<? extends Bucket> smallSortIdIterator = aggregation.getBuckets().iterator();
  177 + while (smallSortIdIterator.hasNext()) {
  178 + Bucket smallSortIdBucket = smallSortIdIterator.next();
  179 + if (StringUtils.isNumeric(smallSortIdBucket.getKeyAsString())) {
  180 + results.add(Integer.valueOf(smallSortIdBucket.getKeyAsString()));
  181 + }
  182 + }
  183 + return results;
  184 + }
  185 +
  186 + /**
  187 + * 有好货默认的过滤规则
  188 + *
  189 + * @return
  190 + */
  191 + private BoolQueryBuilder getDefaultBoolQueryBuilder() {
  192 + BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
56 boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y")); 193 boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));
57 boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y")); 194 boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));
58 boolFilter.must(QueryBuilders.termQuery("status", 1)); 195 boolFilter.must(QueryBuilders.termQuery("status", 1));
59 boolFilter.must(QueryBuilders.termQuery("isOutlets", 2)); 196 boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));
60 boolFilter.must(QueryBuilders.termQuery("attribute", 1)); 197 boolFilter.must(QueryBuilders.termQuery("attribute", 1));
61 - boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(1));  
62 - boolFilter.must(QueryBuilders.termsQuery("productSkn", productSknArray));  
63 - searchParam.setFiter(boolFilter); 198 + // 默认推库存>2,非断码,并且短评存在的数据
  199 + boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(2));
  200 + boolFilter.must(QueryBuilders.rangeQuery("breakingRate").lt(50));
  201 + boolFilter.must(QueryBuilders.rangeQuery("basePinRatio").lt(3.5));
  202 + boolFilter.must(QueryBuilders.termQuery("isPhraseExist", "Y"));
  203 + return boolFilter;
  204 + }
64 205
65 - // 2、设置聚合条件 206 + private List<String> getRecommondedSkns(List<Integer> smallSortIds, int maxSize, Map<String, String> paramMap) {
  207 + // 1、如果品类为空,则直接返回
  208 + if (smallSortIds == null || smallSortIds.isEmpty()) {
  209 + return new ArrayList<String>();
  210 + }
  211 + SearchParam searchParam = new SearchParam();
  212 +
  213 + // 2、构造filter
  214 + BoolQueryBuilder boolFilter = this.getDefaultBoolQueryBuilder();
  215 + boolFilter.must(QueryBuilders.termsQuery("smallSortId", smallSortIds));
  216 +
  217 + // 3、构造query[针对用户做个性化打分]
  218 + searchParam.setQuery(searchServiceHelper.constructQueryBuilderForProductList(paramMap));
  219 +
  220 + // 4、设置聚合条件
66 final String firstAggName = "firstAgg"; 221 final String firstAggName = "firstAgg";
67 String order = "_score:desc"; 222 String order = "_score:desc";
68 String sortField = order.split(":")[0]; 223 String sortField = order.split(":")[0];
69 SortOrder sortOrder = order.split(":")[1].equals("desc") ? SortOrder.DESC : SortOrder.ASC; 224 SortOrder sortOrder = order.split(":")[1].equals("desc") ? SortOrder.DESC : SortOrder.ASC;
70 List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>(); 225 List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>();
71 - // 3.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】  
72 - TermsBuilder brandAggregationBuilder = AggregationBuilders.terms(firstAggName).field("smallSortId").order(Terms.Order.aggregation("sort", sortOrder.equals(SortOrder.ASC)))  
73 - .size(200 + productSknArray.length);  
74 - // 3.2)添加子聚合:取得分最大的值  
75 - brandAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField));  
76 - // 3.3)添加孙聚合:取打分最高的一个product  
77 - brandAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(2));  
78 - list.add(brandAggregationBuilder); 226 + // 4.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】
  227 + TermsBuilder parentAggregationBuilder = AggregationBuilders.terms(firstAggName).field("smallSortId").size(smallSortIds.size());
  228 + // 4.2)添加子聚合:取得分最大的值
  229 + parentAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField));
  230 + // 4.3)添加孙聚合:取打分最高的一个product
  231 + parentAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(maxSize));
  232 + list.add(parentAggregationBuilder);
79 searchParam.setAggregationBuilders(list); 233 searchParam.setAggregationBuilders(list);
80 234
81 - // 3、设置分页 235 + // 5、设置分页
82 searchParam.setPage(0); 236 searchParam.setPage(0);
83 searchParam.setSize(0); 237 searchParam.setSize(0);
84 searchParam.setOffset(0); 238 searchParam.setOffset(0);
85 -  
86 - //4、执行搜索  
87 - SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);  
88 - return null; 239 +
  240 + // 6、先从缓存中获取,如果能取到,则直接返回
  241 + JSONArray recommendedSknJSONArray = searchCacheService.getJSONArrayFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
  242 + if (recommendedSknJSONArray != null) {
  243 + return this.jsonArrayToList(recommendedSknJSONArray);
  244 + }
  245 + // 7、执行搜索,并构造返回结果
  246 + final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
  247 + SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
  248 + if (searchResult == null || searchResult.getAggMaps() == null) {
  249 + return new ArrayList<String>();
  250 + }
  251 + Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
  252 + if (!aggMaps.containsKey(firstAggName)) {
  253 + return new ArrayList<String>();
  254 + }
  255 + List<String> recommendedSknList = this.getRecommendedSknList((MultiBucketsAggregation) aggMaps.get(firstAggName));
  256 + recommendedSknJSONArray = new JSONArray();
  257 + for (String recommendedSkn : recommendedSknList) {
  258 + recommendedSknJSONArray.add(recommendedSkn);
  259 + }
  260 + searchCacheService.addJSONArrayToCache(indexName, searchParam, recommendedSknJSONArray);
  261 + return this.jsonArrayToList(recommendedSknJSONArray);
89 } 262 }
90 263
  264 + private List<String> jsonArrayToList(JSONArray jsonArray) {
  265 + List<String> results = new ArrayList<String>();
  266 + for (int i = 0; i < jsonArray.size(); i++) {
  267 + results.add(jsonArray.getString(i));
  268 + }
  269 + return results;
  270 + }
  271 +
  272 + private List<String> getRecommendedSknList(MultiBucketsAggregation aggregation) {
  273 + Iterator<? extends Bucket> itAgg = aggregation.getBuckets().iterator();
  274 + // 获取聚合出来的商品
  275 + List<String> recommendedSknList = new ArrayList<String>();
  276 + while (itAgg.hasNext()) {
  277 + Bucket lt = itAgg.next();
  278 + if (lt.getAggregations().getAsMap().containsKey("product")) {
  279 + TopHits topHits = lt.getAggregations().get("product");
  280 + if (topHits != null) {
  281 + SearchHits hits = topHits.getHits();
  282 + for (SearchHit hit : hits.getHits()) {
  283 + recommendedSknList.add("" + hit.getSource().get("productSkn"));
  284 + }
  285 + }
  286 + }
  287 + }
  288 + Collections.shuffle(recommendedSknList);
  289 + return recommendedSknList;
  290 + }
91 } 291 }