1
|
-package com.yoho.search.service.scene;
|
|
|
2
|
-
|
|
|
3
|
-import java.util.ArrayList;
|
|
|
4
|
-import java.util.Collections;
|
|
|
5
|
-import java.util.HashMap;
|
|
|
6
|
-import java.util.Iterator;
|
|
|
7
|
-import java.util.List;
|
|
|
8
|
-import java.util.Map;
|
|
|
9
|
-import java.util.Random;
|
|
|
10
|
-
|
|
|
11
|
-import javax.annotation.PostConstruct;
|
|
|
12
|
-
|
|
|
13
|
-import org.apache.commons.lang.StringUtils;
|
|
|
14
|
-import org.elasticsearch.index.query.BoolQueryBuilder;
|
|
|
15
|
-import org.elasticsearch.index.query.QueryBuilder;
|
|
|
16
|
-import org.elasticsearch.index.query.QueryBuilders;
|
|
|
17
|
-import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
|
|
18
|
-import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
|
|
|
19
|
-import org.elasticsearch.search.SearchHit;
|
|
|
20
|
-import org.elasticsearch.search.SearchHits;
|
|
|
21
|
-import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
|
|
22
|
-import org.elasticsearch.search.aggregations.Aggregation;
|
|
|
23
|
-import org.elasticsearch.search.aggregations.AggregationBuilders;
|
|
|
24
|
-import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
|
|
|
25
|
-import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket;
|
|
|
26
|
-import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
|
|
27
|
-import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
|
|
|
28
|
-import org.elasticsearch.search.sort.SortBuilder;
|
|
|
29
|
-import org.elasticsearch.search.sort.SortBuilders;
|
|
|
30
|
-import org.elasticsearch.search.sort.SortOrder;
|
|
|
31
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
32
|
-import org.springframework.stereotype.Service;
|
|
|
33
|
-
|
|
|
34
|
-import com.alibaba.fastjson.JSON;
|
|
|
35
|
-import com.alibaba.fastjson.JSONArray;
|
|
|
36
|
-import com.alibaba.fastjson.JSONObject;
|
|
|
37
|
-import com.yoho.search.base.utils.ISearchConstants;
|
|
|
38
|
-import com.yoho.search.base.utils.ProductIndexEsField;
|
|
|
39
|
-import com.yoho.search.common.cache.SearchCacheFactory;
|
|
|
40
|
-import com.yoho.search.common.cache.model.SearchCache;
|
|
|
41
|
-import com.yoho.search.core.es.model.SearchParam;
|
|
|
42
|
-import com.yoho.search.core.es.model.SearchResult;
|
|
|
43
|
-import com.yoho.search.models.GoodProductBO;
|
|
|
44
|
-import com.yoho.search.models.SearchApiResult;
|
|
|
45
|
-import com.yoho.search.models.YohoFilterFunctionBuilders;
|
|
|
46
|
-import com.yoho.search.service.base.SearchCacheService;
|
|
|
47
|
-import com.yoho.search.service.base.SearchCommonService;
|
|
|
48
|
-import com.yoho.search.service.base.index.ProductIndexBaseService;
|
|
|
49
|
-import com.yoho.search.service.helper.FunctionScoreSearchHelper;
|
|
|
50
|
-import com.yoho.search.service.helper.SearchCommonHelper;
|
|
|
51
|
-import com.yoho.search.service.helper.SearchServiceHelper;
|
|
|
52
|
-
|
|
|
53
|
-@Service
|
|
|
54
|
-public class GoodProductSceneService {
|
|
|
55
|
-
|
|
|
56
|
- // private static final Logger logger =
|
|
|
57
|
- // LoggerFactory.getLogger(GoodProductSceneService.class);
|
|
|
58
|
-
|
|
|
59
|
- @Autowired
|
|
|
60
|
- private SearchCommonService searchCommonService;
|
|
|
61
|
- @Autowired
|
|
|
62
|
- private SearchServiceHelper searchServiceHelper;
|
|
|
63
|
- @Autowired
|
|
|
64
|
- private SearchCommonHelper searchCommonHelper;
|
|
|
65
|
- @Autowired
|
|
|
66
|
- private FunctionScoreSearchHelper functionScoreSearchHelper;
|
|
|
67
|
- @Autowired
|
|
|
68
|
- private ProductIndexBaseService productIndexBaseService;
|
|
|
69
|
- @Autowired
|
|
|
70
|
- private SearchCacheService searchCacheService;
|
|
|
71
|
- @Autowired
|
|
|
72
|
- private SearchCacheFactory searchCacheFactory;
|
|
|
73
|
-
|
|
|
74
|
- private SearchCache productListSearchCache;
|
|
|
75
|
-
|
|
|
76
|
- @PostConstruct
|
|
|
77
|
- void init() {
|
|
|
78
|
- productListSearchCache = searchCacheFactory.getProductListSearchCache();
|
|
|
79
|
- }
|
|
|
80
|
-
|
|
|
81
|
- private static final String view_product_skn = "view_product_skn";
|
|
|
82
|
- private static final String cart_product_skn = "cart_product_skn";
|
|
|
83
|
- private static final String collect_product_skn = "collect_product_skn";
|
|
|
84
|
-
|
|
|
85
|
- public SearchApiResult goodProductList(Map<String, String> paramMap) throws Exception {
|
|
|
86
|
- // 1、检测分页参数
|
|
|
87
|
- int pageSize = StringUtils.isBlank(paramMap.get("viewNum")) ? 30 : Integer.parseInt(paramMap.get("viewNum"));
|
|
|
88
|
- int page = StringUtils.isBlank(paramMap.get("page")) ? 1 : Integer.parseInt(paramMap.get("page"));
|
|
|
89
|
- if (page < 1 || pageSize < 0) {
|
|
|
90
|
- return new SearchApiResult().setCode(400).setMessage("分页参数不合法");
|
|
|
91
|
- }
|
|
|
92
|
-
|
|
|
93
|
- // 1.1 SKN参数获取
|
|
|
94
|
- List<String> view_product_skns = stringToList(paramMap.getOrDefault(view_product_skn, ""), ",");
|
|
|
95
|
- List<String> cart_productskns = stringToList(paramMap.getOrDefault(cart_product_skn, ""), ",");
|
|
|
96
|
- List<String> collect_product_skns = stringToList(paramMap.getOrDefault(collect_product_skn, ""), ",");
|
|
|
97
|
-
|
|
|
98
|
- // 2、先获取用户浏览的SKN对应的品类列表
|
|
|
99
|
- List<String> allProductSkn = new ArrayList<String>();
|
|
|
100
|
- allProductSkn.addAll(view_product_skns);
|
|
|
101
|
- allProductSkn.addAll(cart_productskns);
|
|
|
102
|
- allProductSkn.addAll(collect_product_skns);
|
|
|
103
|
-
|
|
|
104
|
- JSONObject sortAndBrandInfo = this.querySknSortAndBrand(allProductSkn);
|
|
|
105
|
-
|
|
|
106
|
- // 3、再根据品类和品牌推荐出SKN
|
|
|
107
|
- List<String> recommondSkns = this.recommondSknsBySortAndBrandInfo(sortAndBrandInfo, paramMap, view_product_skns, cart_productskns, collect_product_skns);
|
|
|
108
|
-
|
|
|
109
|
- // 4、构造搜索参数
|
|
|
110
|
- SearchParam searchParam = new SearchParam();
|
|
|
111
|
- BoolQueryBuilder boolFilter = searchServiceHelper.constructFilterBuilder(paramMap, null);
|
|
|
112
|
- searchParam.setFiter(this.getDefaultBoolQueryBuilder(boolFilter));
|
|
|
113
|
- searchParam.setQuery(this.builderGoodProductQueryBuilder(paramMap, boolFilter, recommondSkns));
|
|
|
114
|
- searchParam.setOffset((page - 1) * pageSize);
|
|
|
115
|
- searchParam.setSize(pageSize);
|
|
|
116
|
- // 5、设置返回字段【节省带宽】
|
|
|
117
|
- List<String> includeFields = productIndexBaseService.getProductIndexIncludeFields();
|
|
|
118
|
- includeFields.add(ProductIndexEsField.phrase);
|
|
|
119
|
- searchParam.setIncludeFields(includeFields);
|
|
|
120
|
-
|
|
|
121
|
- List<SortBuilder<?>> sortBuilders = new ArrayList<SortBuilder<?>>();
|
|
|
122
|
- sortBuilders.add(SortBuilders.scoreSort().order(SortOrder.DESC));
|
|
|
123
|
- sortBuilders.add(SortBuilders.fieldSort(ProductIndexEsField.heatValue).order(SortOrder.DESC));
|
|
|
124
|
- sortBuilders.add(SortBuilders.fieldSort(ProductIndexEsField.id).order(SortOrder.DESC));
|
|
|
125
|
- searchParam.setSortBuilders(sortBuilders);
|
|
|
126
|
-
|
|
|
127
|
- // 5)从缓存中获取数据
|
|
|
128
|
- final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
|
|
|
129
|
- JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(productListSearchCache, indexName, searchParam);
|
|
|
130
|
- if (cacheObject != null) {
|
|
|
131
|
- return new SearchApiResult().setData(cacheObject);
|
|
|
132
|
- }
|
|
|
133
|
- // 6)查询ES
|
|
|
134
|
- SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
|
|
|
135
|
- if (searchResult == null) {
|
|
|
136
|
- return new SearchApiResult().setCode(500).setMessage("execption");
|
|
|
137
|
- }
|
|
|
138
|
- // 7)构造返回结果
|
|
|
139
|
- JSONObject dataMap = new JSONObject();
|
|
|
140
|
- dataMap.put("total", searchResult.getTotal());
|
|
|
141
|
- dataMap.put("page", searchResult.getPage());
|
|
|
142
|
- dataMap.put("page_size", searchParam.getSize());
|
|
|
143
|
- dataMap.put("page_total", searchResult.getTotalPage());
|
|
|
144
|
- dataMap.put("product_list", productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList()));
|
|
|
145
|
-
|
|
|
146
|
- // 8)将结果存进缓存
|
|
|
147
|
- searchCacheService.addJSONObjectToCache(productListSearchCache, indexName, searchParam, dataMap);
|
|
|
148
|
- return new SearchApiResult().setData(dataMap);
|
|
|
149
|
- }
|
|
|
150
|
-
|
|
|
151
|
- /**
|
|
|
152
|
- * 获取skn的品类列表和品牌列表
|
|
|
153
|
- *
|
|
|
154
|
- * @param productSkns
|
|
|
155
|
- * @return
|
|
|
156
|
- */
|
|
|
157
|
- private JSONObject querySknSortAndBrand(List<String> productSkns) {
|
|
|
158
|
- SearchParam searchParam = new SearchParam();
|
|
|
159
|
- // 1、设置过滤条件
|
|
|
160
|
- BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
|
|
|
161
|
- boolFilter.must(QueryBuilders.termsQuery("productSkn", productSkns));
|
|
|
162
|
- searchParam.setFiter(boolFilter);
|
|
|
163
|
- // 2、设置聚合条件,获取所有的品类和品牌
|
|
|
164
|
- TermsAggregationBuilder smallSortIdAgg = AggregationBuilders.terms("smallSortAgg").field("smallSortId").size(100);
|
|
|
165
|
- TermsAggregationBuilder brandIdAgg = AggregationBuilders.terms("brandAgg").field("brandId").size(100);
|
|
|
166
|
- List<AbstractAggregationBuilder<?>> aggregationBuilders = new ArrayList<AbstractAggregationBuilder<?>>();
|
|
|
167
|
- aggregationBuilders.add(smallSortIdAgg);
|
|
|
168
|
- aggregationBuilders.add(brandIdAgg);
|
|
|
169
|
- searchParam.setAggregationBuilders(aggregationBuilders);
|
|
|
170
|
- // 3、设置分页
|
|
|
171
|
- searchParam.setSize(0);
|
|
|
172
|
- searchParam.setOffset(0);
|
|
|
173
|
-
|
|
|
174
|
- // 4、先从缓存中获取,如果能取到,则直接返回
|
|
|
175
|
- JSONObject sortAndBrandJSONObject = searchCacheService.getJSONObjectFromCache(productListSearchCache, ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
|
|
|
176
|
- if (sortAndBrandJSONObject != null) {
|
|
|
177
|
- return sortAndBrandJSONObject;
|
|
|
178
|
- }
|
|
|
179
|
- // 5、执行搜索
|
|
|
180
|
- SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
|
|
|
181
|
- if (searchResult == null || searchResult.getAggMaps() == null) {
|
|
|
182
|
- return new JSONObject();
|
|
|
183
|
- }
|
|
|
184
|
- Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
|
|
|
185
|
-
|
|
|
186
|
- // 6、从聚合结果中获取品牌和品类id
|
|
|
187
|
- List<Integer> smallSortIds = this.getIdsFromAggMaps(aggMaps, "smallSortAgg");
|
|
|
188
|
- List<Integer> brandIds = this.getIdsFromAggMaps(aggMaps, "brandAgg");
|
|
|
189
|
-
|
|
|
190
|
- // 7、构造返回结果并加入缓存
|
|
|
191
|
- sortAndBrandJSONObject = new JSONObject();
|
|
|
192
|
- sortAndBrandJSONObject.put("brandIds", brandIds);
|
|
|
193
|
- sortAndBrandJSONObject.put("smallSortIds", smallSortIds);
|
|
|
194
|
- searchCacheService.addJSONObjectToCache(productListSearchCache, ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam, sortAndBrandJSONObject);
|
|
|
195
|
- return sortAndBrandJSONObject;
|
|
|
196
|
-
|
|
|
197
|
- }
|
|
|
198
|
-
|
|
|
199
|
- private List<Integer> getIdsFromAggMaps(Map<String, Aggregation> aggMaps, String aggName) {
|
|
|
200
|
- if (!aggMaps.containsKey(aggName)) {
|
|
|
201
|
- return new ArrayList<Integer>();
|
|
|
202
|
- }
|
|
|
203
|
- MultiBucketsAggregation aggregation = (MultiBucketsAggregation) aggMaps.get(aggName);
|
|
|
204
|
- List<Integer> results = new ArrayList<Integer>();
|
|
|
205
|
- Iterator<? extends Bucket> iterator = aggregation.getBuckets().iterator();
|
|
|
206
|
- while (iterator.hasNext()) {
|
|
|
207
|
- Bucket bucket = iterator.next();
|
|
|
208
|
- if (StringUtils.isNumeric(bucket.getKeyAsString())) {
|
|
|
209
|
- results.add(Integer.valueOf(bucket.getKeyAsString()));
|
|
|
210
|
- }
|
|
|
211
|
- }
|
|
|
212
|
- return results;
|
|
|
213
|
- }
|
|
|
214
|
-
|
|
|
215
|
- /**
|
|
|
216
|
- * 根据skn参数获取推荐出来的skn列表
|
|
|
217
|
- *
|
|
|
218
|
- * @return
|
|
|
219
|
- */
|
|
|
220
|
- private List<String> recommondSknsBySortAndBrandInfo(JSONObject sortAndBrandInfo, Map<String, String> paramMap, List<String> view_product_skns, List<String> cart_productskns,
|
|
|
221
|
- List<String> collect_product_skns) throws Exception {
|
|
|
222
|
- // 1、获取品牌id和品类id
|
|
|
223
|
- if (sortAndBrandInfo == null || sortAndBrandInfo.isEmpty()) {
|
|
|
224
|
- return new ArrayList<String>();
|
|
|
225
|
- }
|
|
|
226
|
- JSONArray brandIdJSONArray = sortAndBrandInfo.getJSONArray("brandIds");
|
|
|
227
|
- JSONArray smallSortIdJSONArray = sortAndBrandInfo.getJSONArray("smallSortIds");
|
|
|
228
|
- List<Integer> brandIds = this.jsonArrayToList(brandIdJSONArray, Integer.class);
|
|
|
229
|
- List<Integer> smallSortIds = this.jsonArrayToList(smallSortIdJSONArray, Integer.class);
|
|
|
230
|
- if (brandIds.isEmpty() && smallSortIds.isEmpty()) {
|
|
|
231
|
- return new ArrayList<String>();
|
|
|
232
|
- }
|
|
|
233
|
- // 2、构造filter
|
|
|
234
|
- SearchParam searchParam = new SearchParam();
|
|
|
235
|
- BoolQueryBuilder boolFilter = searchServiceHelper.constructFilterBuilder(paramMap, null);
|
|
|
236
|
- boolFilter = this.getDefaultBoolQueryBuilder(boolFilter);
|
|
|
237
|
- BoolQueryBuilder brandAndSortFilter = new BoolQueryBuilder();
|
|
|
238
|
- if (!brandIds.isEmpty()) {
|
|
|
239
|
- brandAndSortFilter.should(QueryBuilders.termsQuery("brandId", brandIds));
|
|
|
240
|
- }
|
|
|
241
|
- if (!smallSortIds.isEmpty()) {
|
|
|
242
|
- brandAndSortFilter.should(QueryBuilders.termsQuery("smallSortId", smallSortIds));
|
|
|
243
|
- }
|
|
|
244
|
- boolFilter.must(brandAndSortFilter);
|
|
|
245
|
- searchParam.setFiter(boolFilter);
|
|
|
246
|
-
|
|
|
247
|
- // 3、构造query
|
|
|
248
|
- FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(QueryBuilders.matchAllQuery());
|
|
|
249
|
- // 强制加上个性化打分
|
|
|
250
|
- functionScoreSearchHelper.buildFunctionScoreQueryBuild(functionScoreQueryBuilder, boolFilter, paramMap);
|
|
|
251
|
- searchParam.setQuery(functionScoreQueryBuilder);
|
|
|
252
|
-
|
|
|
253
|
- // 4、设置聚合条件
|
|
|
254
|
- String sortField = "_score";
|
|
|
255
|
- SortOrder sortOrder = SortOrder.DESC;
|
|
|
256
|
- List<AbstractAggregationBuilder<?>> list = new ArrayList<AbstractAggregationBuilder<?>>();
|
|
|
257
|
- // 4.1)构造父聚合:品类聚合【同时按子聚合的sort字段排序】
|
|
|
258
|
- TermsAggregationBuilder parentAggregationBuilder = AggregationBuilders.terms("smallSortAgg").field("smallSortId").size(smallSortIds.size());
|
|
|
259
|
- // 4.2)构造子聚合:品牌或聚合【同时按子聚合的sort字段排序】
|
|
|
260
|
- TermsAggregationBuilder sonAggregationBuilder = AggregationBuilders.terms("brandAgg").field("brandId").size(brandIds.size());
|
|
|
261
|
- // 4.3)为子聚合添加孙聚合:取得分最大的值
|
|
|
262
|
- sonAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField));
|
|
|
263
|
- // 4.4)为子聚合孙聚合:取打分最高的一个product
|
|
|
264
|
- sonAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").sort(SortBuilders.scoreSort().order(sortOrder)).size(1));
|
|
|
265
|
- list.add(parentAggregationBuilder.subAggregation(sonAggregationBuilder));
|
|
|
266
|
- searchParam.setAggregationBuilders(list);
|
|
|
267
|
-
|
|
|
268
|
- // 5、设置分页
|
|
|
269
|
- searchParam.setSize(0);
|
|
|
270
|
- searchParam.setOffset(0);
|
|
|
271
|
-
|
|
|
272
|
- // 6、先从缓存中获取,如果能取到,则直接返回
|
|
|
273
|
- JSONArray recommendedSknJSONArray = searchCacheService.getJSONArrayFromCache(productListSearchCache, ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
|
|
|
274
|
- if (recommendedSknJSONArray != null) {
|
|
|
275
|
- return this.jsonArrayToList(recommendedSknJSONArray, String.class);
|
|
|
276
|
- }
|
|
|
277
|
- // 7、执行搜索
|
|
|
278
|
- final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
|
|
|
279
|
- SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
|
|
|
280
|
- if (searchResult == null || searchResult.getAggMaps() == null) {
|
|
|
281
|
- return new ArrayList<String>();
|
|
|
282
|
- }
|
|
|
283
|
- // 8、获取搜索结果
|
|
|
284
|
- Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
|
|
|
285
|
- if (!aggMaps.containsKey("smallSortAgg")) {
|
|
|
286
|
- return new ArrayList<String>();
|
|
|
287
|
- }
|
|
|
288
|
- List<GoodProductBO> goodProductLists = new ArrayList<GoodProductBO>();
|
|
|
289
|
- Iterator<? extends Bucket> smallSortIterator = ((MultiBucketsAggregation) aggMaps.get("smallSortAgg")).getBuckets().iterator();
|
|
|
290
|
- while (smallSortIterator.hasNext()) {
|
|
|
291
|
- Bucket smallSortBucket = smallSortIterator.next();
|
|
|
292
|
- Map<String, Aggregation> smallSortAggMap = smallSortBucket.getAggregations().getAsMap();
|
|
|
293
|
- if (!smallSortAggMap.containsKey("brandAgg")) {
|
|
|
294
|
- continue;
|
|
|
295
|
- }
|
|
|
296
|
- Iterator<? extends Bucket> brandIdIterator = ((MultiBucketsAggregation) smallSortAggMap.get("brandAgg")).getBuckets().iterator();
|
|
|
297
|
- List<String> productSkns = new ArrayList<String>();
|
|
|
298
|
- while (brandIdIterator.hasNext()) {
|
|
|
299
|
- Bucket brandIdBucket = brandIdIterator.next();
|
|
|
300
|
- if (brandIdBucket.getAggregations().getAsMap().containsKey("product")) {
|
|
|
301
|
- TopHits topHits = brandIdBucket.getAggregations().get("product");
|
|
|
302
|
- if (topHits != null) {
|
|
|
303
|
- SearchHits hits = topHits.getHits();
|
|
|
304
|
- for (SearchHit hit : hits.getHits()) {
|
|
|
305
|
- productSkns.add("" + hit.getSource().get("productSkn"));
|
|
|
306
|
- }
|
|
|
307
|
- }
|
|
|
308
|
- }
|
|
|
309
|
- }
|
|
|
310
|
- if (!productSkns.isEmpty()) {
|
|
|
311
|
- Collections.shuffle(productSkns);
|
|
|
312
|
- goodProductLists.add(new GoodProductBO(smallSortBucket.getKeyAsString(), productSkns));
|
|
|
313
|
- }
|
|
|
314
|
- }
|
|
|
315
|
- Collections.shuffle(goodProductLists);
|
|
|
316
|
- // 9、构造返回结果
|
|
|
317
|
- recommendedSknJSONArray = new JSONArray();
|
|
|
318
|
- // 9.1、获取推荐出来的商品
|
|
|
319
|
- recommendedSknJSONArray.addAll(this.getProductSkns(goodProductLists));
|
|
|
320
|
- // 9.2、再加入用户购物车和收藏夹中的商品
|
|
|
321
|
- this.addListToJsonArray(recommendedSknJSONArray, cart_productskns);
|
|
|
322
|
- this.addListToJsonArray(recommendedSknJSONArray, collect_product_skns);
|
|
|
323
|
- if (cart_productskns.isEmpty() && collect_product_skns.isEmpty()) {
|
|
|
324
|
- this.addListToJsonArray(recommendedSknJSONArray, view_product_skns);
|
|
|
325
|
- }
|
|
|
326
|
- searchCacheService.addJSONArrayToCache(productListSearchCache, indexName, searchParam, recommendedSknJSONArray);
|
|
|
327
|
- return this.jsonArrayToList(recommendedSknJSONArray, String.class);
|
|
|
328
|
- }
|
|
|
329
|
-
|
|
|
330
|
- private void addListToJsonArray(JSONArray recommendedSknJSONArray, List<String> list) {
|
|
|
331
|
- for (String skn : list) {
|
|
|
332
|
- if (!recommendedSknJSONArray.contains(skn)) {
|
|
|
333
|
- recommendedSknJSONArray.add(skn);
|
|
|
334
|
- }
|
|
|
335
|
- }
|
|
|
336
|
- }
|
|
|
337
|
-
|
|
|
338
|
- private List<String> getProductSkns(List<GoodProductBO> goodProductLists) {
|
|
|
339
|
- if (goodProductLists == null || goodProductLists.isEmpty()) {
|
|
|
340
|
- return new ArrayList<String>();
|
|
|
341
|
- }
|
|
|
342
|
- int maxSknCount = 0;
|
|
|
343
|
- for (GoodProductBO goodProductBO : goodProductLists) {
|
|
|
344
|
- int productSknSize = goodProductBO.getProductSkn().size();
|
|
|
345
|
- if (maxSknCount < productSknSize) {
|
|
|
346
|
- maxSknCount = productSknSize;
|
|
|
347
|
- }
|
|
|
348
|
- }
|
|
|
349
|
- List<String> results = new ArrayList<String>();
|
|
|
350
|
- Random random = new Random();
|
|
|
351
|
- for (int i = 0; i < maxSknCount; i++) {
|
|
|
352
|
- for (GoodProductBO goodProductBO : goodProductLists) {
|
|
|
353
|
- int randomCount = 1 + random.nextInt(2);
|
|
|
354
|
- results.addAll(goodProductBO.randomGetProductSkn(randomCount));
|
|
|
355
|
- }
|
|
|
356
|
- }
|
|
|
357
|
- return results;
|
|
|
358
|
- }
|
|
|
359
|
-
|
|
|
360
|
- private QueryBuilder builderGoodProductQueryBuilder(Map<String, String> paramMap, BoolQueryBuilder boolFilter, List<String> recommendedSknList) {
|
|
|
361
|
- QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
|
|
|
362
|
- // 未推荐出商品则直接走个性化
|
|
|
363
|
- if (recommendedSknList == null || recommendedSknList.isEmpty()) {
|
|
|
364
|
- return functionScoreSearchHelper.buildFunctionScoreQueryBuild(queryBuilder, boolFilter, paramMap);
|
|
|
365
|
- } else {
|
|
|
366
|
- YohoFilterFunctionBuilders yohoFilterFunctionBuilders = new YohoFilterFunctionBuilders();
|
|
|
367
|
- Map<Integer, List<String>> recommondSknMap = this.splitProductSkns(recommendedSknList, 2);
|
|
|
368
|
- float currentGroupScore = 1000;
|
|
|
369
|
- for (Map.Entry<Integer, List<String>> entry : recommondSknMap.entrySet()) {
|
|
|
370
|
- yohoFilterFunctionBuilders.add(QueryBuilders.termsQuery("productSkn", entry.getValue()), ScoreFunctionBuilders.weightFactorFunction(currentGroupScore));
|
|
|
371
|
- currentGroupScore = currentGroupScore - 10;
|
|
|
372
|
- }
|
|
|
373
|
- FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder, yohoFilterFunctionBuilders.getFilterFunctionBuilders());
|
|
|
374
|
- return functionScoreQueryBuilder;
|
|
|
375
|
- }
|
|
|
376
|
- }
|
|
|
377
|
-
|
|
|
378
|
- /**
|
|
|
379
|
- * 有好货默认的过滤规则
|
|
|
380
|
- *
|
|
|
381
|
- * @return
|
|
|
382
|
- */
|
|
|
383
|
- private BoolQueryBuilder getDefaultBoolQueryBuilder(BoolQueryBuilder boolFilter) {
|
|
|
384
|
- boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));
|
|
|
385
|
- boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));
|
|
|
386
|
- boolFilter.must(QueryBuilders.termQuery("status", 1));
|
|
|
387
|
- boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));
|
|
|
388
|
- boolFilter.must(QueryBuilders.termQuery("attribute", 1));
|
|
|
389
|
- // 默认推库存>2,非断码,并且短评存在的数据
|
|
|
390
|
- boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(2));
|
|
|
391
|
- boolFilter.must(QueryBuilders.rangeQuery(ProductIndexEsField.breakSizePercent).lt(50));
|
|
|
392
|
- boolFilter.must(QueryBuilders.termQuery("isPhraseExist", "Y"));
|
|
|
393
|
- return boolFilter;
|
|
|
394
|
- }
|
|
|
395
|
-
|
|
|
396
|
- private <T> List<T> jsonArrayToList(JSONArray jsonArray, Class<T> clazz) {
|
|
|
397
|
- if (jsonArray == null) {
|
|
|
398
|
- return new ArrayList<T>();
|
|
|
399
|
- }
|
|
|
400
|
- return JSON.parseArray(jsonArray.toJSONString(), clazz);
|
|
|
401
|
- }
|
|
|
402
|
-
|
|
|
403
|
- /**
|
|
|
404
|
- * 分组
|
|
|
405
|
- *
|
|
|
406
|
- * @param recommendedSknList
|
|
|
407
|
- * @return
|
|
|
408
|
- */
|
|
|
409
|
- private Map<Integer, List<String>> splitProductSkns(List<String> recommendedSknList, int maxCountPerGroup) {
|
|
|
410
|
- int maxSize = recommendedSknList.size();
|
|
|
411
|
- int groupSize = maxSize / maxCountPerGroup;
|
|
|
412
|
- if (maxSize % maxCountPerGroup > 0) {
|
|
|
413
|
- groupSize = groupSize + 1;
|
|
|
414
|
- }
|
|
|
415
|
- Map<Integer, List<String>> result = new HashMap<Integer, List<String>>();
|
|
|
416
|
- for (int i = 0; i < groupSize; i++) {
|
|
|
417
|
- int fromIndex = i * maxCountPerGroup;
|
|
|
418
|
- int toIndex = (i + 1) * maxCountPerGroup;
|
|
|
419
|
- result.put(i, recommendedSknList.subList(fromIndex, toIndex > maxSize ? maxSize : toIndex));
|
|
|
420
|
- }
|
|
|
421
|
- return result;
|
|
|
422
|
- }
|
|
|
423
|
-
|
|
|
424
|
- private List<String> stringToList(String source, String split) {
|
|
|
425
|
- if (StringUtils.isBlank(source)) {
|
|
|
426
|
- return new ArrayList<String>();
|
|
|
427
|
- }
|
|
|
428
|
- List<String> result = new ArrayList<String>();
|
|
|
429
|
- for (String part : source.split(split)) {
|
|
|
430
|
- result.add(part);
|
|
|
431
|
- }
|
|
|
432
|
- return result;
|
|
|
433
|
- }
|
|
|
434
|
-
|
|
|
435
|
-} |
|
|