Merge branch 'master' into zf_smart_search3
# Conflicts: # service/src/main/java/com/yoho/search/service/restapi/ProductListController.java
Showing
9 changed files
with
518 additions
and
37 deletions
@@ -23,7 +23,7 @@ public class PersonalVectorFeatureSearch { | @@ -23,7 +23,7 @@ public class PersonalVectorFeatureSearch { | ||
23 | 23 | ||
24 | private static final Double BASE_CONSTANT = 1.0D; | 24 | private static final Double BASE_CONSTANT = 1.0D; |
25 | 25 | ||
26 | - private static final Double FACTOR_CONSTANT = 1.0D; | 26 | + private static final Double FACTOR_CONSTANT = 0.8D; |
27 | 27 | ||
28 | @Autowired | 28 | @Autowired |
29 | private SearchDynamicConfigService searchDynamicConfigService; | 29 | private SearchDynamicConfigService searchDynamicConfigService; |
1 | +package com.yoho.search.service.restapi; | ||
2 | + | ||
3 | +import java.util.Map; | ||
4 | + | ||
5 | +import javax.servlet.http.HttpServletRequest; | ||
6 | + | ||
7 | +import org.springframework.beans.factory.annotation.Autowired; | ||
8 | +import org.springframework.stereotype.Controller; | ||
9 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
10 | +import org.springframework.web.bind.annotation.RequestMethod; | ||
11 | +import org.springframework.web.bind.annotation.ResponseBody; | ||
12 | + | ||
13 | +import com.yoho.search.service.servicenew.IGoodProductsService; | ||
14 | +import com.yoho.search.service.utils.HttpServletRequestUtils; | ||
15 | +import com.yoho.search.service.vo.SearchApiResult; | ||
16 | + | ||
17 | +@Controller | ||
18 | +public class GoodProductListController { | ||
19 | + | ||
20 | + @Autowired | ||
21 | + private IGoodProductsService goodProductsService; | ||
22 | + | ||
23 | + @RequestMapping(method = RequestMethod.GET, value = "/productindex/goodProductList") | ||
24 | + @ResponseBody | ||
25 | + public SearchApiResult goodProductList(HttpServletRequest request) { | ||
26 | + Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request); | ||
27 | + return goodProductsService.goodProductList(paramMap); | ||
28 | + } | ||
29 | + | ||
30 | +} |
1 | package com.yoho.search.service.service.helper; | 1 | package com.yoho.search.service.service.helper; |
2 | 2 | ||
3 | import java.util.ArrayList; | 3 | import java.util.ArrayList; |
4 | +import java.util.Date; | ||
4 | import java.util.List; | 5 | import java.util.List; |
5 | import java.util.Map; | 6 | import java.util.Map; |
6 | 7 | ||
@@ -17,10 +18,12 @@ import org.slf4j.LoggerFactory; | @@ -17,10 +18,12 @@ import org.slf4j.LoggerFactory; | ||
17 | import org.springframework.beans.factory.annotation.Autowired; | 18 | import org.springframework.beans.factory.annotation.Autowired; |
18 | import org.springframework.stereotype.Component; | 19 | import org.springframework.stereotype.Component; |
19 | 20 | ||
21 | +import com.yoho.search.base.utils.DateUtil; | ||
20 | import com.yoho.search.service.personalized.PersonalVectorFeatureSearch; | 22 | import com.yoho.search.service.personalized.PersonalVectorFeatureSearch; |
21 | import com.yoho.search.service.personalized.PersonalizedRedisService; | 23 | import com.yoho.search.service.personalized.PersonalizedRedisService; |
22 | import com.yoho.search.service.service.SearchDynamicConfigService; | 24 | import com.yoho.search.service.service.SearchDynamicConfigService; |
23 | import com.yoho.search.service.utils.SearchRequestParams; | 25 | import com.yoho.search.service.utils.SearchRequestParams; |
26 | +import com.yoho.search.service.vo.FirstShelveTimeScore; | ||
24 | import com.yoho.search.service.vo.PhysicalChannelScore; | 27 | import com.yoho.search.service.vo.PhysicalChannelScore; |
25 | 28 | ||
26 | @Component | 29 | @Component |
@@ -37,7 +40,12 @@ public class FunctionScoreSearchHelper { | @@ -37,7 +40,12 @@ public class FunctionScoreSearchHelper { | ||
37 | @Autowired | 40 | @Autowired |
38 | private PersonalizedRedisService personalizedRedisService; | 41 | private PersonalizedRedisService personalizedRedisService; |
39 | 42 | ||
40 | - static float globalWeight = 0.50f; | 43 | + // 普通个性化的时间维度 |
44 | + private final int oneDaySecondCount = 24 * 60 * 60; | ||
45 | + private FirstShelveTimeScore commonFirstShelveTimeScore = new FirstShelveTimeScore(90,30,60); | ||
46 | + // 新品到着的个性化时间维度 | ||
47 | + private FirstShelveTimeScore newRecShelveTimeScore = new FirstShelveTimeScore(30,10,20); | ||
48 | + private final float globalWeight = 0.50f; | ||
41 | 49 | ||
42 | private WeightBuilder genWeightFactorBuilder(float factor) { | 50 | private WeightBuilder genWeightFactorBuilder(float factor) { |
43 | return ScoreFunctionBuilders.weightFactorFunction(factor); | 51 | return ScoreFunctionBuilders.weightFactorFunction(factor); |
@@ -48,29 +56,57 @@ public class FunctionScoreSearchHelper { | @@ -48,29 +56,57 @@ public class FunctionScoreSearchHelper { | ||
48 | // 将某些SKN展示到前面 | 56 | // 将某些SKN展示到前面 |
49 | if (searchCommonHelper.isFirstProductSknSearch(paramMap)) { | 57 | if (searchCommonHelper.isFirstProductSknSearch(paramMap)) { |
50 | String[] productSkns = paramMap.get(SearchRequestParams.FIRST_PRODUCRSKN).split(","); | 58 | String[] productSkns = paramMap.get(SearchRequestParams.FIRST_PRODUCRSKN).split(","); |
51 | - functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn",productSkns),ScoreFunctionBuilders.weightFactorFunction(1000)); | 59 | + functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", productSkns), ScoreFunctionBuilders.weightFactorFunction(1000)); |
52 | } | 60 | } |
61 | + // 个性化搜索相关 | ||
53 | if (searchCommonHelper.isNeedPersonalSearch(paramMap)) { | 62 | if (searchCommonHelper.isNeedPersonalSearch(paramMap)) { |
54 | personalVectorFeatureSearch.addPersonalizedScriptScore(functionScoreQueryBuilder, paramMap); | 63 | personalVectorFeatureSearch.addPersonalizedScriptScore(functionScoreQueryBuilder, paramMap); |
64 | + this.addFirstShelveTimeFunctionScore(functionScoreQueryBuilder, paramMap); | ||
55 | } | 65 | } |
66 | + // 针对全球购降分 | ||
56 | if (searchCommonHelper.containGlobal(paramMap)) { | 67 | if (searchCommonHelper.containGlobal(paramMap)) { |
57 | functionScoreQueryBuilder.add(QueryBuilders.termQuery("isGlobal", "Y"), genWeightFactorBuilder(globalWeight)); | 68 | functionScoreQueryBuilder.add(QueryBuilders.termQuery("isGlobal", "Y"), genWeightFactorBuilder(globalWeight)); |
58 | } | 69 | } |
59 | - if (searchCommonHelper.isNeedDeScoreBrandSearch(paramMap)) { | ||
60 | - functionScoreQueryBuilder.add(QueryBuilders.termQuery("isForbiddenSortBrand", "1"), ScoreFunctionBuilders.weightFactorFunction(0)); | ||
61 | - } | ||
62 | - // 针对频道降分 | 70 | + // 模糊搜索针对频道降分 |
63 | if (searchCommonHelper.isNeedDeScoreForChannel(paramMap)) { | 71 | if (searchCommonHelper.isNeedDeScoreForChannel(paramMap)) { |
64 | List<PhysicalChannelScore> physicalChannelScores = this.getPhysicalChannelQueryBuilder(paramMap); | 72 | List<PhysicalChannelScore> physicalChannelScores = this.getPhysicalChannelQueryBuilder(paramMap); |
65 | for (PhysicalChannelScore physicalChannelScore : physicalChannelScores) { | 73 | for (PhysicalChannelScore physicalChannelScore : physicalChannelScores) { |
66 | functionScoreQueryBuilder.add(physicalChannelScore.getQueryBuilder(), ScoreFunctionBuilders.weightFactorFunction(physicalChannelScore.getWeight())); | 74 | functionScoreQueryBuilder.add(physicalChannelScore.getQueryBuilder(), ScoreFunctionBuilders.weightFactorFunction(physicalChannelScore.getWeight())); |
67 | } | 75 | } |
68 | } | 76 | } |
77 | + // 针对屏蔽的品牌降分【目前没用】 | ||
78 | + if (searchCommonHelper.isNeedDeScoreBrandSearch(paramMap)) { | ||
79 | + functionScoreQueryBuilder.add(QueryBuilders.termQuery("isForbiddenSortBrand", "1"), ScoreFunctionBuilders.weightFactorFunction(0)); | ||
80 | + } | ||
69 | functionScoreQueryBuilder.boostMode(CombineFunction.MULT); | 81 | functionScoreQueryBuilder.boostMode(CombineFunction.MULT); |
70 | return functionScoreQueryBuilder; | 82 | return functionScoreQueryBuilder; |
71 | } | 83 | } |
72 | 84 | ||
73 | /** | 85 | /** |
86 | + * 个性化时添加时间维度的降分 | ||
87 | + * | ||
88 | + * @param functionScoreQueryBuilder | ||
89 | + * @param paramMap | ||
90 | + */ | ||
91 | + private void addFirstShelveTimeFunctionScore(FunctionScoreQueryBuilder functionScoreQueryBuilder, Map<String, String> paramMap) { | ||
92 | + if (searchCommonHelper.isNewRecPageDefault(paramMap)) { | ||
93 | + this.addFirstShelveTimeScore(functionScoreQueryBuilder, newRecShelveTimeScore); | ||
94 | + } else { | ||
95 | + this.addFirstShelveTimeScore(functionScoreQueryBuilder, commonFirstShelveTimeScore); | ||
96 | + } | ||
97 | + } | ||
98 | + | ||
99 | + private void addFirstShelveTimeScore(FunctionScoreQueryBuilder functionScoreQueryBuilder, FirstShelveTimeScore firstShelveTimeScore) { | ||
100 | + int todayLastSecond = DateUtil.getLastTimeSecond(new Date()); | ||
101 | + int limitSecondValue = todayLastSecond - firstShelveTimeScore.getLimitDayCount() * oneDaySecondCount; | ||
102 | + int scaleSecondTime = firstShelveTimeScore.getScaleDayCount() * oneDaySecondCount; | ||
103 | + int offsetSecondValue = firstShelveTimeScore.getOffsetDayCount() * oneDaySecondCount; | ||
104 | + functionScoreQueryBuilder.add(QueryBuilders.rangeQuery("firstShelveTime").lt(limitSecondValue), ScoreFunctionBuilders.weightFactorFunction(0.5f)); | ||
105 | + functionScoreQueryBuilder.add(QueryBuilders.rangeQuery("firstShelveTime").from(limitSecondValue), | ||
106 | + ScoreFunctionBuilders.linearDecayFunction("firstShelveTime", todayLastSecond, scaleSecondTime).setOffset(offsetSecondValue)); | ||
107 | + } | ||
108 | + | ||
109 | + /** | ||
74 | * 直接使用商品特征的向量用来做个性化打分 | 110 | * 直接使用商品特征的向量用来做个性化打分 |
75 | * | 111 | * |
76 | * @param queryBuilder | 112 | * @param queryBuilder |
@@ -91,7 +127,7 @@ public class FunctionScoreSearchHelper { | @@ -91,7 +127,7 @@ public class FunctionScoreSearchHelper { | ||
91 | * @param descoreGender | 127 | * @param descoreGender |
92 | * @return | 128 | * @return |
93 | */ | 129 | */ |
94 | - public float getDescoreGenderWeight(String uid, float baseScore, String descoreGender) { | 130 | + private float getDescoreGenderWeight(String uid, float baseScore, String descoreGender) { |
95 | try { | 131 | try { |
96 | Map<String, Float> userGenderFloat = personalizedRedisService.getUserGenderFeature(uid); | 132 | Map<String, Float> userGenderFloat = personalizedRedisService.getUserGenderFeature(uid); |
97 | Float userGenderWeight = userGenderFloat.get(descoreGender); | 133 | Float userGenderWeight = userGenderFloat.get(descoreGender); |
@@ -116,7 +152,7 @@ public class FunctionScoreSearchHelper { | @@ -116,7 +152,7 @@ public class FunctionScoreSearchHelper { | ||
116 | 152 | ||
117 | // 潮童频道,对非潮童频道的和成人的商品降分 | 153 | // 潮童频道,对非潮童频道的和成人的商品降分 |
118 | if (physicalChannel.equals("3")) { | 154 | if (physicalChannel.equals("3")) { |
119 | - results.add(new PhysicalChannelScore(notPhysicalChannelsQueryBuilder,physicalChannelWeight)); | 155 | + results.add(new PhysicalChannelScore(notPhysicalChannelsQueryBuilder, physicalChannelWeight)); |
120 | results.add(new PhysicalChannelScore(QueryBuilders.termsQuery("ageLevel", "1"), physicalChannelWeight)); | 156 | results.add(new PhysicalChannelScore(QueryBuilders.termsQuery("ageLevel", "1"), physicalChannelWeight)); |
121 | return results; | 157 | return results; |
122 | } | 158 | } |
@@ -132,14 +168,14 @@ public class FunctionScoreSearchHelper { | @@ -132,14 +168,14 @@ public class FunctionScoreSearchHelper { | ||
132 | } | 168 | } |
133 | // 男生频道,对非男生频道的商品和性别女的降分 | 169 | // 男生频道,对非男生频道的商品和性别女的降分 |
134 | if (physicalChannel.equals("1")) { | 170 | if (physicalChannel.equals("1")) { |
135 | - results.add(new PhysicalChannelScore(notPhysicalChannelsQueryBuilder,physicalChannelWeight)); | 171 | + results.add(new PhysicalChannelScore(notPhysicalChannelsQueryBuilder, physicalChannelWeight)); |
136 | results.add(new PhysicalChannelScore(QueryBuilders.termsQuery("gender", "2"), this.getDescoreGenderWeight(uid, physicalChannelWeight, "2"))); | 172 | results.add(new PhysicalChannelScore(QueryBuilders.termsQuery("gender", "2"), this.getDescoreGenderWeight(uid, physicalChannelWeight, "2"))); |
137 | return results; | 173 | return results; |
138 | } | 174 | } |
139 | // 女生频道,对非女生频道的商品和性别男的降分 | 175 | // 女生频道,对非女生频道的商品和性别男的降分 |
140 | if (physicalChannel.equals("2")) { | 176 | if (physicalChannel.equals("2")) { |
141 | - results.add(new PhysicalChannelScore(notPhysicalChannelsQueryBuilder,physicalChannelWeight)); | ||
142 | - results.add(new PhysicalChannelScore(QueryBuilders.termsQuery("gender", "1"),this.getDescoreGenderWeight(uid, physicalChannelWeight, "1"))); | 177 | + results.add(new PhysicalChannelScore(notPhysicalChannelsQueryBuilder, physicalChannelWeight)); |
178 | + results.add(new PhysicalChannelScore(QueryBuilders.termsQuery("gender", "1"), this.getDescoreGenderWeight(uid, physicalChannelWeight, "1"))); | ||
143 | return results; | 179 | return results; |
144 | } | 180 | } |
145 | return new ArrayList<PhysicalChannelScore>(); | 181 | return new ArrayList<PhysicalChannelScore>(); |
@@ -117,13 +117,13 @@ public class SearchCommonHelper { | @@ -117,13 +117,13 @@ public class SearchCommonHelper { | ||
117 | */ | 117 | */ |
118 | public boolean isNeedDeScoreForChannel(Map<String, String> paramMap) { | 118 | public boolean isNeedDeScoreForChannel(Map<String, String> paramMap) { |
119 | String physicalChannel = paramMap.get(SearchRequestParams.PHYSICAL_CHANNEL); | 119 | String physicalChannel = paramMap.get(SearchRequestParams.PHYSICAL_CHANNEL); |
120 | - if(StringUtils.isBlank(physicalChannel)){ | 120 | + if (StringUtils.isBlank(physicalChannel)) { |
121 | return false; | 121 | return false; |
122 | } | 122 | } |
123 | - if(!isFuzzySearchDefault(paramMap)){ | 123 | + if (!isFuzzySearchDefault(paramMap)) { |
124 | return false; | 124 | return false; |
125 | } | 125 | } |
126 | - if(!dynamicConfig.isDeScorePhysicalChannelOpen()){ | 126 | + if (!dynamicConfig.isDeScorePhysicalChannelOpen()) { |
127 | return false; | 127 | return false; |
128 | } | 128 | } |
129 | return true; | 129 | return true; |
@@ -187,6 +187,25 @@ public class SearchCommonHelper { | @@ -187,6 +187,25 @@ public class SearchCommonHelper { | ||
187 | } | 187 | } |
188 | 188 | ||
189 | /** | 189 | /** |
190 | + * 是否是新品到着默认页 | ||
191 | + * | ||
192 | + * @param paramMap | ||
193 | + * @return | ||
194 | + */ | ||
195 | + public boolean isNewRecPageDefault(Map<String, String> paramMap) { | ||
196 | + String pageId = paramMap.get("pageId"); | ||
197 | + if (StringUtils.isBlank(pageId)|| !pageId.equals("4")){ | ||
198 | + return false; | ||
199 | + } | ||
200 | + String order = paramMap.get("order"); | ||
201 | + if (!StringUtils.isBlank(order)) { | ||
202 | + return false; | ||
203 | + } | ||
204 | + return true; | ||
205 | + } | ||
206 | + | ||
207 | + | ||
208 | + /** | ||
190 | * 关键字中含性别,则加上性别的过滤条件 | 209 | * 关键字中含性别,则加上性别的过滤条件 |
191 | * | 210 | * |
192 | * @param keyword | 211 | * @param keyword |
@@ -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) { |
@@ -649,13 +632,17 @@ public class SearchServiceHelper { | @@ -649,13 +632,17 @@ public class SearchServiceHelper { | ||
649 | } | 632 | } |
650 | } | 633 | } |
651 | 634 | ||
635 | + public List<Map<String, Object>> getProductMapList(List<Map<String, Object>> resultList) { | ||
636 | + return this.getProductMapList(resultList, null); | ||
637 | + } | ||
638 | + | ||
652 | /** | 639 | /** |
653 | * 获取商品列表返回结果 | 640 | * 获取商品列表返回结果 |
654 | * | 641 | * |
655 | * @param resultList | 642 | * @param resultList |
656 | * @return | 643 | * @return |
657 | */ | 644 | */ |
658 | - public List<Map<String, Object>> getProductMapList(List<Map<String, Object>> resultList) { | 645 | + public List<Map<String, Object>> getProductMapList(List<Map<String, Object>> resultList, List<String> extendedFields) { |
659 | // 获取搜索结果的skn,根据它们来获取product_price_plan | 646 | // 获取搜索结果的skn,根据它们来获取product_price_plan |
660 | String[] sknStr = new String[resultList.size()]; | 647 | String[] sknStr = new String[resultList.size()]; |
661 | for (int i = 0; i < resultList.size(); i++) { | 648 | for (int i = 0; i < resultList.size(); i++) { |
@@ -664,7 +651,7 @@ public class SearchServiceHelper { | @@ -664,7 +651,7 @@ public class SearchServiceHelper { | ||
664 | List<Map<String, Object>> pageList = new ArrayList<Map<String, Object>>(); | 651 | List<Map<String, Object>> pageList = new ArrayList<Map<String, Object>>(); |
665 | Map<String, List<Map<String, Object>>> productPricePlanMap = this.searchProductPricePlan(sknStr); | 652 | Map<String, List<Map<String, Object>>> productPricePlanMap = this.searchProductPricePlan(sknStr); |
666 | for (Map<String, Object> map : resultList) { | 653 | for (Map<String, Object> map : resultList) { |
667 | - Map<String, Object> productMap = getProductMap(map); | 654 | + Map<String, Object> productMap = getProductMap(map, extendedFields); |
668 | productMap.put("product_price_plan_list", productPricePlanMap.get("" + map.get("productSkn"))); | 655 | productMap.put("product_price_plan_list", productPricePlanMap.get("" + map.get("productSkn"))); |
669 | pageList.add(productMap); | 656 | pageList.add(productMap); |
670 | } | 657 | } |
@@ -677,8 +664,12 @@ public class SearchServiceHelper { | @@ -677,8 +664,12 @@ public class SearchServiceHelper { | ||
677 | return result; | 664 | return result; |
678 | } | 665 | } |
679 | 666 | ||
680 | - @SuppressWarnings("unchecked") | ||
681 | public Map<String, Object> getProductMap(Map<String, Object> map) { | 667 | public Map<String, Object> getProductMap(Map<String, Object> map) { |
668 | + return this.getProductMap(map, null); | ||
669 | + } | ||
670 | + | ||
671 | + @SuppressWarnings("unchecked") | ||
672 | + public Map<String, Object> getProductMap(Map<String, Object> map, List<String> extendedFields) { | ||
682 | Map<String, Object> productMap = new HashMap<String, Object>(); | 673 | Map<String, Object> productMap = new HashMap<String, Object>(); |
683 | productMap.put("cn_alphabet", map.get("cnAlphabet") == null ? "" : map.get("cnAlphabet")); | 674 | productMap.put("cn_alphabet", map.get("cnAlphabet") == null ? "" : map.get("cnAlphabet")); |
684 | if (map.containsKey("_highlight") && map.get("_highlight") != null) { | 675 | if (map.containsKey("_highlight") && map.get("_highlight") != null) { |
@@ -727,7 +718,6 @@ public class SearchServiceHelper { | @@ -727,7 +718,6 @@ public class SearchServiceHelper { | ||
727 | productMap.put("sales_num", map.get("salesNum")); | 718 | productMap.put("sales_num", map.get("salesNum")); |
728 | productMap.put("status", map.get("status")); | 719 | productMap.put("status", map.get("status")); |
729 | productMap.put("is_promotion", map.get("ispromotion")); | 720 | productMap.put("is_promotion", map.get("ispromotion")); |
730 | - productMap.put("is_promotion", map.get("ispromotion")); | ||
731 | String yohoodIdFromMap = (String) map.get("yohoodId"); | 721 | String yohoodIdFromMap = (String) map.get("yohoodId"); |
732 | if (yohoodIdFromMap != null && yohoodIdFromMap.length() > 0) { | 722 | if (yohoodIdFromMap != null && yohoodIdFromMap.length() > 0) { |
733 | productMap.put("yohood_id", yohoodIdFromMap); | 723 | productMap.put("yohood_id", yohoodIdFromMap); |
@@ -761,6 +751,12 @@ public class SearchServiceHelper { | @@ -761,6 +751,12 @@ public class SearchServiceHelper { | ||
761 | String tbl_plane = (tbl_country_id != null && tbl_country_id != 86) ? "Y" : "N"; | 751 | String tbl_plane = (tbl_country_id != null && tbl_country_id != 86) ? "Y" : "N"; |
762 | productMap.put("tbl_plane", tbl_plane); | 752 | productMap.put("tbl_plane", tbl_plane); |
763 | productMap.put("skn_default_img", map.get("sknDefaultImg")); | 753 | productMap.put("skn_default_img", map.get("sknDefaultImg")); |
754 | + // 一些特殊场景需要额外返回一些参数 | ||
755 | + if (extendedFields != null) { | ||
756 | + for (String extendedField : extendedFields) { | ||
757 | + productMap.put(extendedField, map.get(extendedField)); | ||
758 | + } | ||
759 | + } | ||
764 | return productMap; | 760 | return productMap; |
765 | } | 761 | } |
766 | } | 762 | } |
1 | +package com.yoho.search.service.servicenew; | ||
2 | + | ||
3 | +import java.util.Map; | ||
4 | + | ||
5 | +import com.yoho.search.service.vo.SearchApiResult; | ||
6 | + | ||
7 | +public interface IGoodProductsService { | ||
8 | + | ||
9 | + /** | ||
10 | + * 有好货商品列表接口 | ||
11 | + * @param paramMap | ||
12 | + * @return | ||
13 | + */ | ||
14 | + public SearchApiResult goodProductList(Map<String, String> paramMap); | ||
15 | + | ||
16 | +} |
service/src/main/java/com/yoho/search/service/servicenew/impl/GoodProductListService.java
0 → 100644
1 | +package com.yoho.search.service.servicenew.impl; | ||
2 | + | ||
3 | +import java.util.ArrayList; | ||
4 | +import java.util.Arrays; | ||
5 | +import java.util.Collections; | ||
6 | +import java.util.HashMap; | ||
7 | +import java.util.Iterator; | ||
8 | +import java.util.List; | ||
9 | +import java.util.Map; | ||
10 | + | ||
11 | +import org.apache.commons.lang.StringUtils; | ||
12 | +import org.elasticsearch.index.query.BoolQueryBuilder; | ||
13 | +import org.elasticsearch.index.query.QueryBuilder; | ||
14 | +import org.elasticsearch.index.query.QueryBuilders; | ||
15 | +import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; | ||
16 | +import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders; | ||
17 | +import org.elasticsearch.search.SearchHit; | ||
18 | +import org.elasticsearch.search.SearchHits; | ||
19 | +import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; | ||
20 | +import org.elasticsearch.search.aggregations.Aggregation; | ||
21 | +import org.elasticsearch.search.aggregations.AggregationBuilders; | ||
22 | +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; | ||
23 | +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket; | ||
24 | +import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder; | ||
25 | +import org.elasticsearch.search.aggregations.metrics.tophits.TopHits; | ||
26 | +import org.elasticsearch.search.sort.SortBuilder; | ||
27 | +import org.elasticsearch.search.sort.SortBuilders; | ||
28 | +import org.elasticsearch.search.sort.SortOrder; | ||
29 | +import org.springframework.beans.factory.annotation.Autowired; | ||
30 | +import org.springframework.stereotype.Service; | ||
31 | + | ||
32 | +import com.alibaba.fastjson.JSONArray; | ||
33 | +import com.alibaba.fastjson.JSONObject; | ||
34 | +import com.yoho.search.base.utils.ISearchConstants; | ||
35 | +import com.yoho.search.core.es.model.SearchParam; | ||
36 | +import com.yoho.search.core.es.model.SearchResult; | ||
37 | +import com.yoho.search.service.cache.CacheEnum; | ||
38 | +import com.yoho.search.service.personalized.PersonalVectorFeatureSearch; | ||
39 | +import com.yoho.search.service.service.SearchCacheService; | ||
40 | +import com.yoho.search.service.service.SearchCommonService; | ||
41 | +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.SearchCommonHelper; | ||
44 | +import com.yoho.search.service.service.helper.SearchServiceHelper; | ||
45 | +import com.yoho.search.service.service.helper.SearchSortHelper; | ||
46 | +import com.yoho.search.service.servicenew.IGoodProductsService; | ||
47 | +import com.yoho.search.service.utils.SearchRequestParams; | ||
48 | +import com.yoho.search.service.vo.SearchApiResult; | ||
49 | + | ||
50 | +@Service | ||
51 | +public class GoodProductListService implements IGoodProductsService { | ||
52 | + | ||
53 | + @Autowired | ||
54 | + private SearchCommonService searchCommonService; | ||
55 | + @Autowired | ||
56 | + private SearchServiceHelper searchServiceHelper; | ||
57 | + @Autowired | ||
58 | + private FunctionScoreSearchHelper functionScoreSearchHelper; | ||
59 | + @Autowired | ||
60 | + private SearchCacheService searchCacheService; | ||
61 | + @Autowired | ||
62 | + private AggProductListHelper aggProductListHelper; | ||
63 | + @Autowired | ||
64 | + private SearchSortHelper searchSortHelper; | ||
65 | + @Autowired | ||
66 | + private SearchCommonHelper searchCommonHelper; | ||
67 | + @Autowired | ||
68 | + private PersonalVectorFeatureSearch personalVectorFeatureSearch; | ||
69 | + | ||
70 | + private final int maxSmallSortCount = 20; | ||
71 | + private final int maxProductSknCountPerSort = 5; | ||
72 | + private final int maxCountPerGroup = 10; | ||
73 | + private final float firstSknScore = 300; | ||
74 | + private final float recommendedSknMaxScore = 200; | ||
75 | + | ||
76 | + @Override | ||
77 | + public SearchApiResult goodProductList(Map<String, String> paramMap) { | ||
78 | + // 1、检测分页参数 | ||
79 | + 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")); | ||
81 | + if (page < 1 || pageSize < 0) { | ||
82 | + return new SearchApiResult().setCode(400).setMessage("分页参数不合法"); | ||
83 | + } | ||
84 | + // 2、先获取用户浏览的SKN对应的品类列表 | ||
85 | + List<Integer> smallSortIds = this.getProductSknSmallSortIds(paramMap, maxSmallSortCount); | ||
86 | + // 3、再每个品类下获取5个SKN | ||
87 | + List<String> recommondSkns = this.getRecommondedSkns(smallSortIds, maxProductSknCountPerSort, paramMap); | ||
88 | + | ||
89 | + // 4、构造搜索参数 | ||
90 | + SearchParam searchParam = new SearchParam(); | ||
91 | + searchParam.setFiter(this.getDefaultBoolQueryBuilder()); | ||
92 | + searchParam.setQuery(this.builderGoodProductQueryBuilder(paramMap, recommondSkns)); | ||
93 | + searchParam.setAggregationBuilders(null); | ||
94 | + searchParam.setPage(page); | ||
95 | + searchParam.setOffset((page - 1) * pageSize); | ||
96 | + searchParam.setSize(pageSize); | ||
97 | + List<SortBuilder> sortBuilders = new ArrayList<SortBuilder>(); | ||
98 | + sortBuilders.add(SortBuilders.fieldSort("_score").order(SortOrder.DESC)); | ||
99 | + sortBuilders.add(SortBuilders.fieldSort("salesNum").order(SortOrder.DESC)); | ||
100 | + sortBuilders.add(SortBuilders.fieldSort("firstShelveTime").order(SortOrder.DESC)); | ||
101 | + sortBuilders.add(SortBuilders.fieldSort("id").order(SortOrder.DESC)); | ||
102 | + searchParam.setSortBuilders(sortBuilders); | ||
103 | + | ||
104 | + // 5)从缓存中获取数据 | ||
105 | + final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX; | ||
106 | + CacheEnum cacheEnum = CacheEnum.EHCACHE; | ||
107 | + JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(cacheEnum, indexName, searchParam); | ||
108 | + if (cacheObject != null) { | ||
109 | + return new SearchApiResult().setData(cacheObject); | ||
110 | + } | ||
111 | + // 6)查询ES | ||
112 | + SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam); | ||
113 | + if (searchResult == null) { | ||
114 | + return new SearchApiResult().setCode(500).setMessage("execption"); | ||
115 | + } | ||
116 | + // 7)构造返回结果 | ||
117 | + JSONObject dataMap = new JSONObject(); | ||
118 | + dataMap.put("total", searchResult.getTotal()); | ||
119 | + dataMap.put("page", searchResult.getPage()); | ||
120 | + dataMap.put("page_size", searchParam.getSize()); | ||
121 | + dataMap.put("page_total", searchResult.getTotalPage()); | ||
122 | + dataMap.put("product_list", searchServiceHelper.getProductMapList(searchResult.getResultList(), Arrays.asList("phrase"))); | ||
123 | + | ||
124 | + // 8)将结果存进缓存 | ||
125 | + searchCacheService.addJSONObjectToCache(cacheEnum, indexName, searchParam, dataMap); | ||
126 | + return new SearchApiResult().setData(dataMap); | ||
127 | + } | ||
128 | + | ||
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 | + String productSkns = paramMap.get(SearchRequestParams.PARAM_SYNC_SKN); | ||
134 | + if (!StringUtils.isBlank(productSkns)) { | ||
135 | + functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", productSkns.split(",")[0]), ScoreFunctionBuilders.weightFactorFunction(firstSknScore)); | ||
136 | + } | ||
137 | + // 针对推荐出来的SKN做加分 | ||
138 | + if (recommendedSknList != null && !recommendedSknList.isEmpty()) { | ||
139 | + Map<Integer, List<String>> recommondSknMap = this.splitProductSkns(recommendedSknList, maxCountPerGroup); | ||
140 | + float currentGroupScore = recommendedSknMaxScore; | ||
141 | + for (Map.Entry<Integer, List<String>> entry: recommondSknMap.entrySet()) { | ||
142 | + functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", entry.getValue()), ScoreFunctionBuilders.weightFactorFunction(currentGroupScore)); | ||
143 | + currentGroupScore = currentGroupScore - 10; | ||
144 | + } | ||
145 | + } | ||
146 | + // 加上个性化打分 | ||
147 | + if (searchCommonHelper.isNeedPersonalSearch(paramMap)) { | ||
148 | + personalVectorFeatureSearch.addPersonalizedScriptScore(functionScoreQueryBuilder, paramMap); | ||
149 | + } | ||
150 | + return functionScoreQueryBuilder; | ||
151 | + } | ||
152 | + | ||
153 | + /** | ||
154 | + * 获取SKN相关的小分类 | ||
155 | + * | ||
156 | + * @param productSkns | ||
157 | + * @return | ||
158 | + */ | ||
159 | + private List<Integer> getProductSknSmallSortIds(Map<String, String> paramMap, int maxCount) { | ||
160 | + String productSkns = paramMap.get(SearchRequestParams.PARAM_SYNC_SKN); | ||
161 | + if (StringUtils.isBlank(productSkns)) { | ||
162 | + return new ArrayList<Integer>(); | ||
163 | + } | ||
164 | + String[] productSknArray = productSkns.split(","); | ||
165 | + SearchParam searchParam = new SearchParam(); | ||
166 | + // 1、设置过滤条件 | ||
167 | + BoolQueryBuilder boolFilter = QueryBuilders.boolQuery(); | ||
168 | + boolFilter.must(QueryBuilders.termsQuery("productSkn", productSknArray)); | ||
169 | + searchParam.setFiter(boolFilter); | ||
170 | + // 2、设置聚合条件 | ||
171 | + final String aggName = "smallSortAgg"; | ||
172 | + TermsBuilder smallSortIdAgg = AggregationBuilders.terms(aggName).field("smallSortId").size(maxCount); | ||
173 | + searchParam.setAggregationBuilders(Arrays.asList(smallSortIdAgg)); | ||
174 | + // 3、设置分页 | ||
175 | + searchParam.setPage(0); | ||
176 | + searchParam.setSize(0); | ||
177 | + searchParam.setOffset(0); | ||
178 | + | ||
179 | + // 4、先从缓存中获取,如果能取到,则直接返回 | ||
180 | + JSONArray sknSmallSortIdJSONArray = searchCacheService.getJSONArrayFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam); | ||
181 | + if (sknSmallSortIdJSONArray != null) { | ||
182 | + return this.jsonArrayToList(sknSmallSortIdJSONArray, Integer.class); | ||
183 | + } | ||
184 | + // 5、执行搜索 | ||
185 | + SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam); | ||
186 | + if (searchResult == null || searchResult.getAggMaps() == null || searchResult.getAggMaps().get("smallSortAgg") == null) { | ||
187 | + return new ArrayList<Integer>(); | ||
188 | + } | ||
189 | + sknSmallSortIdJSONArray = new JSONArray(); | ||
190 | + MultiBucketsAggregation aggregation = (MultiBucketsAggregation) searchResult.getAggMaps().get(aggName); | ||
191 | + Iterator<? extends Bucket> smallSortIdIterator = aggregation.getBuckets().iterator(); | ||
192 | + while (smallSortIdIterator.hasNext()) { | ||
193 | + Bucket smallSortIdBucket = smallSortIdIterator.next(); | ||
194 | + if (StringUtils.isNumeric(smallSortIdBucket.getKeyAsString())) { | ||
195 | + sknSmallSortIdJSONArray.add(Integer.valueOf(smallSortIdBucket.getKeyAsString())); | ||
196 | + } | ||
197 | + } | ||
198 | + searchCacheService.addJSONArrayToCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam, sknSmallSortIdJSONArray); | ||
199 | + return this.jsonArrayToList(sknSmallSortIdJSONArray, Integer.class); | ||
200 | + } | ||
201 | + | ||
202 | + /** | ||
203 | + * 有好货默认的过滤规则 | ||
204 | + * | ||
205 | + * @return | ||
206 | + */ | ||
207 | + private BoolQueryBuilder getDefaultBoolQueryBuilder() { | ||
208 | + BoolQueryBuilder boolFilter = QueryBuilders.boolQuery(); | ||
209 | + boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y")); | ||
210 | + boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y")); | ||
211 | + boolFilter.must(QueryBuilders.termQuery("status", 1)); | ||
212 | + boolFilter.must(QueryBuilders.termQuery("isOutlets", 2)); | ||
213 | + boolFilter.must(QueryBuilders.termQuery("attribute", 1)); | ||
214 | + // 默认推库存>2,非断码,并且短评存在的数据 | ||
215 | + boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(2)); | ||
216 | + boolFilter.must(QueryBuilders.rangeQuery("breakingRate").lt(50)); | ||
217 | + boolFilter.must(QueryBuilders.rangeQuery("basePinRatio").lt(3.5)); | ||
218 | + boolFilter.must(QueryBuilders.termQuery("isPhraseExist", "Y")); | ||
219 | + return boolFilter; | ||
220 | + } | ||
221 | + | ||
222 | + /** | ||
223 | + * 每个品类为用户推荐maxSize个SKN | ||
224 | + * | ||
225 | + * @param smallSortIds | ||
226 | + * @param maxSize | ||
227 | + * @param paramMap | ||
228 | + * @return | ||
229 | + */ | ||
230 | + private List<String> getRecommondedSkns(List<Integer> smallSortIds, int maxSizePerSort, Map<String, String> paramMap) { | ||
231 | + // 1、如果品类为空,则直接返回 | ||
232 | + if (smallSortIds == null || smallSortIds.isEmpty()) { | ||
233 | + return new ArrayList<String>(); | ||
234 | + } | ||
235 | + SearchParam searchParam = new SearchParam(); | ||
236 | + | ||
237 | + // 2、构造filter | ||
238 | + BoolQueryBuilder boolFilter = this.getDefaultBoolQueryBuilder(); | ||
239 | + boolFilter.must(QueryBuilders.termsQuery("smallSortId", smallSortIds)); | ||
240 | + searchParam.setFiter(boolFilter); | ||
241 | + | ||
242 | + // 3、构造query | ||
243 | + FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(QueryBuilders.matchAllQuery()); | ||
244 | + // 针对看过的SKN做加分 | ||
245 | + String productSkns = paramMap.get(SearchRequestParams.PARAM_SYNC_SKN); | ||
246 | + if (!StringUtils.isBlank(productSkns)) { | ||
247 | + functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", productSkns.split(",")), ScoreFunctionBuilders.weightFactorFunction(100)); | ||
248 | + } | ||
249 | + // 加上个性化打分 | ||
250 | + if (searchCommonHelper.isNeedPersonalSearch(paramMap)) { | ||
251 | + personalVectorFeatureSearch.addPersonalizedScriptScore(functionScoreQueryBuilder, paramMap); | ||
252 | + } | ||
253 | + searchParam.setQuery(functionScoreQueryBuilder); | ||
254 | + | ||
255 | + // 4、设置聚合条件 | ||
256 | + final String firstAggName = "firstAgg"; | ||
257 | + String order = "_score:desc"; | ||
258 | + String sortField = order.split(":")[0]; | ||
259 | + SortOrder sortOrder = order.split(":")[1].equals("desc") ? SortOrder.DESC : SortOrder.ASC; | ||
260 | + List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>(); | ||
261 | + // 4.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】 | ||
262 | + TermsBuilder parentAggregationBuilder = AggregationBuilders.terms(firstAggName).field("smallSortId").size(smallSortIds.size()); | ||
263 | + // 4.2)添加子聚合:取得分最大的值 | ||
264 | + parentAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField)); | ||
265 | + // 4.3)添加孙聚合:取打分最高的一个product | ||
266 | + parentAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(maxSizePerSort)); | ||
267 | + list.add(parentAggregationBuilder); | ||
268 | + searchParam.setAggregationBuilders(list); | ||
269 | + | ||
270 | + // 5、设置分页 | ||
271 | + searchParam.setPage(0); | ||
272 | + searchParam.setSize(0); | ||
273 | + searchParam.setOffset(0); | ||
274 | + | ||
275 | + // 6、先从缓存中获取,如果能取到,则直接返回 | ||
276 | + JSONArray recommendedSknJSONArray = searchCacheService.getJSONArrayFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam); | ||
277 | + if (recommendedSknJSONArray != null) { | ||
278 | + return this.jsonArrayToList(recommendedSknJSONArray, String.class); | ||
279 | + } | ||
280 | + // 7、执行搜索,并构造返回结果 | ||
281 | + final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX; | ||
282 | + SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam); | ||
283 | + if (searchResult == null || searchResult.getAggMaps() == null) { | ||
284 | + return new ArrayList<String>(); | ||
285 | + } | ||
286 | + Map<String, Aggregation> aggMaps = searchResult.getAggMaps(); | ||
287 | + if (!aggMaps.containsKey(firstAggName)) { | ||
288 | + return new ArrayList<String>(); | ||
289 | + } | ||
290 | + List<String> recommendedSknList = this.getRecommendedSknList((MultiBucketsAggregation) aggMaps.get(firstAggName)); | ||
291 | + recommendedSknJSONArray = new JSONArray(); | ||
292 | + for (String recommendedSkn : recommendedSknList) { | ||
293 | + recommendedSknJSONArray.add(recommendedSkn); | ||
294 | + } | ||
295 | + searchCacheService.addJSONArrayToCache(indexName, searchParam, recommendedSknJSONArray); | ||
296 | + return this.jsonArrayToList(recommendedSknJSONArray, String.class); | ||
297 | + } | ||
298 | + | ||
299 | + private <T> List<T> jsonArrayToList(JSONArray jsonArray, Class<T> clazz) { | ||
300 | + return JSONObject.parseArray(jsonArray.toJSONString(), clazz); | ||
301 | + } | ||
302 | + | ||
303 | + /** | ||
304 | + * 分组 | ||
305 | + * | ||
306 | + * @param recommendedSknList | ||
307 | + * @param maxGroupCount | ||
308 | + * @return | ||
309 | + */ | ||
310 | + private Map<Integer, List<String>> splitProductSkns(List<String> recommendedSknList, int maxCountPerGroup) { | ||
311 | + int maxSize = recommendedSknList.size(); | ||
312 | + int groupSize = maxSize / maxCountPerGroup; | ||
313 | + if (maxSize % maxCountPerGroup > 0) { | ||
314 | + groupSize = groupSize + 1; | ||
315 | + } | ||
316 | + Map<Integer, List<String>> result = new HashMap<Integer, List<String>>(); | ||
317 | + for (int i = 0; i < groupSize; i++) { | ||
318 | + int fromIndex = i * maxCountPerGroup; | ||
319 | + int toIndex = (i + 1) * maxCountPerGroup; | ||
320 | + result.put(i, recommendedSknList.subList(fromIndex, toIndex > maxSize ? maxSize : toIndex)); | ||
321 | + } | ||
322 | + return result; | ||
323 | + } | ||
324 | + | ||
325 | + private List<String> getRecommendedSknList(MultiBucketsAggregation aggregation) { | ||
326 | + Iterator<? extends Bucket> itAgg = aggregation.getBuckets().iterator(); | ||
327 | + // 获取聚合出来的商品 | ||
328 | + List<String> recommendedSknList = new ArrayList<String>(); | ||
329 | + while (itAgg.hasNext()) { | ||
330 | + Bucket lt = itAgg.next(); | ||
331 | + if (lt.getAggregations().getAsMap().containsKey("product")) { | ||
332 | + TopHits topHits = lt.getAggregations().get("product"); | ||
333 | + if (topHits != null) { | ||
334 | + SearchHits hits = topHits.getHits(); | ||
335 | + for (SearchHit hit : hits.getHits()) { | ||
336 | + recommendedSknList.add("" + hit.getSource().get("productSkn")); | ||
337 | + } | ||
338 | + } | ||
339 | + } | ||
340 | + } | ||
341 | + Collections.shuffle(recommendedSknList); | ||
342 | + return recommendedSknList; | ||
343 | + } | ||
344 | + | ||
345 | + public static void main(String[] args) { | ||
346 | + List<String> list = new ArrayList<String>(); | ||
347 | + for (int i = 1; i <= 99; i++) { | ||
348 | + list.add(i+""); | ||
349 | + } | ||
350 | + Map<Integer, List<String>> results = new GoodProductListService().splitProductSkns(list, 20); | ||
351 | + for (Map.Entry<Integer, List<String>> entry : results.entrySet()) { | ||
352 | + System.out.println(entry.getKey() + "_" + entry.getValue()); | ||
353 | + } | ||
354 | + } | ||
355 | + | ||
356 | +} |
@@ -197,7 +197,7 @@ public class PromotionServiceImpl implements IPromotionService { | @@ -197,7 +197,7 @@ public class PromotionServiceImpl implements IPromotionService { | ||
197 | public SearchApiResult list(PromotionConditions promotionConditions, Map<String, String> paramMap) throws Exception { | 197 | public SearchApiResult list(PromotionConditions promotionConditions, Map<String, String> paramMap) throws Exception { |
198 | SearchParam searchParam = new SearchParam(); | 198 | SearchParam searchParam = new SearchParam(); |
199 | // 1、设置查询条件 | 199 | // 1、设置查询条件 |
200 | - searchParam.setQuery(searchServiceHelper.constructQueryBuilder(paramMap)); | 200 | + searchParam.setQuery(searchServiceHelper.constructQueryBuilderForProductList(paramMap)); |
201 | // 2、设置过滤条件 | 201 | // 2、设置过滤条件 |
202 | BoolQueryBuilder boolQueryBuilder = searchServiceHelper.constructFilterBuilder(paramMap, null); | 202 | BoolQueryBuilder boolQueryBuilder = searchServiceHelper.constructFilterBuilder(paramMap, null); |
203 | BoolQueryBuilder mustFilterByPromotion = this.getMustFilterByPromotion(promotionConditions); | 203 | BoolQueryBuilder mustFilterByPromotion = this.getMustFilterByPromotion(promotionConditions); |
1 | +package com.yoho.search.service.vo; | ||
2 | + | ||
3 | +public class FirstShelveTimeScore { | ||
4 | + | ||
5 | + private int limitDayCount; | ||
6 | + private int offsetDayCount; | ||
7 | + private int scaleDayCount; | ||
8 | + | ||
9 | + public FirstShelveTimeScore(int limitDayCount, int offsetDayCount, int scaleDayCount) { | ||
10 | + super(); | ||
11 | + this.limitDayCount = limitDayCount; | ||
12 | + this.offsetDayCount = offsetDayCount; | ||
13 | + this.scaleDayCount = scaleDayCount; | ||
14 | + } | ||
15 | + | ||
16 | + public int getLimitDayCount() { | ||
17 | + return limitDayCount; | ||
18 | + } | ||
19 | + | ||
20 | + public int getOffsetDayCount() { | ||
21 | + return offsetDayCount; | ||
22 | + } | ||
23 | + | ||
24 | + public int getScaleDayCount() { | ||
25 | + return scaleDayCount; | ||
26 | + } | ||
27 | + | ||
28 | +} |
-
Please register or login to post a comment