|
@@ -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
|
} |