...
|
...
|
@@ -26,6 +26,8 @@ import org.elasticsearch.search.aggregations.metrics.tophits.TopHits; |
|
|
import org.elasticsearch.search.sort.SortBuilder;
|
|
|
import org.elasticsearch.search.sort.SortBuilders;
|
|
|
import org.elasticsearch.search.sort.SortOrder;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
...
|
...
|
@@ -37,19 +39,20 @@ import com.yoho.search.core.es.model.SearchResult; |
|
|
import com.yoho.search.service.cache.CacheEnum;
|
|
|
import com.yoho.search.service.service.SearchCacheService;
|
|
|
import com.yoho.search.service.service.SearchCommonService;
|
|
|
import com.yoho.search.service.service.base.ProductIndexBaseService;
|
|
|
import com.yoho.search.service.service.helper.AggProductListHelper;
|
|
|
import com.yoho.search.service.service.helper.FunctionScoreSearchHelper;
|
|
|
import com.yoho.search.service.service.helper.SearchCommonHelper;
|
|
|
import com.yoho.search.service.service.helper.SearchServiceHelper;
|
|
|
import com.yoho.search.service.service.helper.SearchSortHelper;
|
|
|
import com.yoho.search.service.servicenew.IGoodProductsService;
|
|
|
import com.yoho.search.service.utils.SearchRequestParams;
|
|
|
import com.yoho.search.service.vo.GoodProductBO;
|
|
|
import com.yoho.search.service.vo.SearchApiResult;
|
|
|
|
|
|
@Service
|
|
|
public class GoodProductListService implements IGoodProductsService {
|
|
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(GoodProductListService.class);
|
|
|
|
|
|
@Autowired
|
|
|
private SearchCommonService searchCommonService;
|
|
|
@Autowired
|
...
|
...
|
@@ -64,17 +67,13 @@ public class GoodProductListService implements IGoodProductsService { |
|
|
private SearchCommonHelper searchCommonHelper;
|
|
|
@Autowired
|
|
|
private FunctionScoreSearchHelper functionScoreSearchHelper;
|
|
|
@Autowired
|
|
|
private ProductIndexBaseService productIndexBaseService;
|
|
|
|
|
|
private final int maxSmallSortCount = 20;
|
|
|
private final int maxProductSknCountPerSort = 5;
|
|
|
private final int maxCountPerGroup = 3;
|
|
|
// private final float firstSknScore = 300;
|
|
|
private final float recommendedSknMaxScore = 200;
|
|
|
|
|
|
@Override
|
|
|
public SearchApiResult goodProductList(Map<String, String> paramMap) {
|
|
|
boolean openLog = "13420925".equals(paramMap.getOrDefault("uid", "0"));
|
|
|
if (openLog) {
|
|
|
logger.warn("goodProductList param is : [{}]", paramMap.toString());
|
|
|
}
|
|
|
// 1、检测分页参数
|
|
|
int pageSize = StringUtils.isBlank(paramMap.get("viewNum")) ? 30 : Integer.parseInt(paramMap.get("viewNum"));
|
|
|
int page = StringUtils.isBlank(paramMap.get("page")) ? 1 : Integer.parseInt(paramMap.get("page"));
|
...
|
...
|
@@ -82,9 +81,14 @@ public class GoodProductListService implements IGoodProductsService { |
|
|
return new SearchApiResult().setCode(400).setMessage("分页参数不合法");
|
|
|
}
|
|
|
// 2、先获取用户浏览的SKN对应的品类列表
|
|
|
List<Integer> smallSortIds = this.getProductSknSmallSortIds(paramMap, maxSmallSortCount);
|
|
|
// 3、再每个品类下获取5个SKN[每次随机打乱]
|
|
|
List<String> recommondSkns = this.getRecommondedSkns(smallSortIds, maxProductSknCountPerSort, paramMap);
|
|
|
JSONObject sortAndBrandInfo = this.querySknSortAndBrand(paramMap.getOrDefault("product_skn", ""));
|
|
|
|
|
|
// 3、再根据品类和品牌推荐SKN
|
|
|
List<String> recommondSkns = this.recommondSknsBySortAndBrandInfo(sortAndBrandInfo, paramMap);
|
|
|
|
|
|
if (paramMap.getOrDefault("uid", "0").equals("13420925")) {
|
|
|
logger.warn("goodProductList recommondSkns is : [{}]", recommondSkns);
|
|
|
}
|
|
|
|
|
|
// 4、构造搜索参数
|
|
|
SearchParam searchParam = new SearchParam();
|
...
|
...
|
@@ -106,6 +110,11 @@ public class GoodProductListService implements IGoodProductsService { |
|
|
CacheEnum cacheEnum = CacheEnum.EHCACHE;
|
|
|
JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(cacheEnum, indexName, searchParam);
|
|
|
if (cacheObject != null) {
|
|
|
JSONArray product_list = cacheObject.getJSONArray("product_list");
|
|
|
if(product_list!=null){
|
|
|
// Collections.shuffle(product_list);
|
|
|
// cacheObject.put("product_list", product_list);
|
|
|
}
|
|
|
return new SearchApiResult().setData(cacheObject);
|
|
|
}
|
|
|
// 6)查询ES
|
...
|
...
|
@@ -119,42 +128,22 @@ public class GoodProductListService implements IGoodProductsService { |
|
|
dataMap.put("page", searchResult.getPage());
|
|
|
dataMap.put("page_size", searchParam.getSize());
|
|
|
dataMap.put("page_total", searchResult.getTotalPage());
|
|
|
dataMap.put("product_list", productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList(), Arrays.asList("phrase")));
|
|
|
dataMap.put("product_list", searchServiceHelper.getProductMapList(searchResult.getResultList(), Arrays.asList("phrase")));
|
|
|
|
|
|
// 8)将结果存进缓存
|
|
|
searchCacheService.addJSONObjectToCache(cacheEnum, indexName, searchParam, dataMap);
|
|
|
return new SearchApiResult().setData(dataMap);
|
|
|
}
|
|
|
|
|
|
private QueryBuilder builderGoodProductQueryBuilder(Map<String, String> paramMap, List<String> recommendedSknList) {
|
|
|
QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
|
|
|
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder);
|
|
|
// 针对推荐出来的SKN做加分
|
|
|
if (recommendedSknList != null && !recommendedSknList.isEmpty()) {
|
|
|
Map<Integer, List<String>> recommondSknMap = this.splitProductSkns(recommendedSknList, maxCountPerGroup);
|
|
|
float currentGroupScore = recommendedSknMaxScore;
|
|
|
for (Map.Entry<Integer, List<String>> entry : recommondSknMap.entrySet()) {
|
|
|
functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", entry.getValue()), ScoreFunctionBuilders.weightFactorFunction(currentGroupScore));
|
|
|
currentGroupScore = currentGroupScore - 10;
|
|
|
}
|
|
|
}
|
|
|
// 加上个性化打分
|
|
|
if (searchCommonHelper.isNeedPersonalSearch(paramMap)) {
|
|
|
functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);
|
|
|
}
|
|
|
return functionScoreQueryBuilder;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取SKN相关的小分类
|
|
|
* 获取skn的品类列表和品牌列表
|
|
|
*
|
|
|
* @param productSkns
|
|
|
* @return
|
|
|
*/
|
|
|
private List<Integer> getProductSknSmallSortIds(Map<String, String> paramMap, int maxCount) {
|
|
|
String productSkns = paramMap.get(SearchRequestParams.PARAM_SEARCH_PRODUCT_SKN);
|
|
|
private JSONObject querySknSortAndBrand(String productSkns) {
|
|
|
if (StringUtils.isBlank(productSkns)) {
|
|
|
return new ArrayList<Integer>();
|
|
|
return new JSONObject();
|
|
|
}
|
|
|
String[] productSknArray = productSkns.split(",");
|
|
|
SearchParam searchParam = new SearchParam();
|
...
|
...
|
@@ -162,102 +151,108 @@ public class GoodProductListService implements IGoodProductsService { |
|
|
BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
|
|
|
boolFilter.must(QueryBuilders.termsQuery("productSkn", productSknArray));
|
|
|
searchParam.setFiter(boolFilter);
|
|
|
// 2、设置聚合条件
|
|
|
final String aggName = "smallSortAgg";
|
|
|
TermsBuilder smallSortIdAgg = AggregationBuilders.terms(aggName).field("smallSortId").size(maxCount);
|
|
|
searchParam.setAggregationBuilders(Arrays.asList(smallSortIdAgg));
|
|
|
// 2、设置聚合条件,获取所有的品类和品牌
|
|
|
TermsBuilder smallSortIdAgg = AggregationBuilders.terms("smallSortAgg").field("smallSortId").size(100);
|
|
|
TermsBuilder brandIdAgg = AggregationBuilders.terms("brandAgg").field("brandId").size(100);
|
|
|
List<AbstractAggregationBuilder> aggregationBuilders = new ArrayList<AbstractAggregationBuilder>();
|
|
|
aggregationBuilders.add(smallSortIdAgg);
|
|
|
aggregationBuilders.add(brandIdAgg);
|
|
|
searchParam.setAggregationBuilders(aggregationBuilders);
|
|
|
// 3、设置分页
|
|
|
searchParam.setPage(0);
|
|
|
searchParam.setSize(0);
|
|
|
searchParam.setOffset(0);
|
|
|
|
|
|
// 4、先从缓存中获取,如果能取到,则直接返回
|
|
|
JSONArray sknSmallSortIdJSONArray = searchCacheService.getJSONArrayFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
|
|
|
if (sknSmallSortIdJSONArray != null) {
|
|
|
return this.jsonArrayToList(sknSmallSortIdJSONArray, Integer.class);
|
|
|
JSONObject sortAndBrandJSONObject = searchCacheService.getJSONObjectFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
|
|
|
if (sortAndBrandJSONObject != null) {
|
|
|
return sortAndBrandJSONObject;
|
|
|
}
|
|
|
// 5、执行搜索
|
|
|
SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
|
|
|
if (searchResult == null || searchResult.getAggMaps() == null || searchResult.getAggMaps().get("smallSortAgg") == null) {
|
|
|
if (searchResult == null || searchResult.getAggMaps() == null) {
|
|
|
return new JSONObject();
|
|
|
}
|
|
|
Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
|
|
|
|
|
|
// 6、从聚合结果中获取品牌和品类id
|
|
|
List<Integer> smallSortIds = this.getIdsFromAggMaps(aggMaps, "smallSortAgg");
|
|
|
List<Integer> brandIds = this.getIdsFromAggMaps(aggMaps, "brandAgg");
|
|
|
|
|
|
// 7、构造返回结果并加入缓存
|
|
|
sortAndBrandJSONObject = new JSONObject();
|
|
|
sortAndBrandJSONObject.put("brandIds", brandIds);
|
|
|
sortAndBrandJSONObject.put("smallSortIds", smallSortIds);
|
|
|
searchCacheService.addJSONObjectToCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam, sortAndBrandJSONObject);
|
|
|
return sortAndBrandJSONObject;
|
|
|
|
|
|
}
|
|
|
|
|
|
private List<Integer> getIdsFromAggMaps(Map<String, Aggregation> aggMaps, String aggName) {
|
|
|
if (!aggMaps.containsKey(aggName)) {
|
|
|
return new ArrayList<Integer>();
|
|
|
}
|
|
|
sknSmallSortIdJSONArray = new JSONArray();
|
|
|
MultiBucketsAggregation aggregation = (MultiBucketsAggregation) searchResult.getAggMaps().get(aggName);
|
|
|
Iterator<? extends Bucket> smallSortIdIterator = aggregation.getBuckets().iterator();
|
|
|
while (smallSortIdIterator.hasNext()) {
|
|
|
Bucket smallSortIdBucket = smallSortIdIterator.next();
|
|
|
if (StringUtils.isNumeric(smallSortIdBucket.getKeyAsString())) {
|
|
|
sknSmallSortIdJSONArray.add(Integer.valueOf(smallSortIdBucket.getKeyAsString()));
|
|
|
MultiBucketsAggregation aggregation = (MultiBucketsAggregation) aggMaps.get(aggName);
|
|
|
List<Integer> results = new ArrayList<Integer>();
|
|
|
Iterator<? extends Bucket> iterator = aggregation.getBuckets().iterator();
|
|
|
while (iterator.hasNext()) {
|
|
|
Bucket bucket = iterator.next();
|
|
|
if (StringUtils.isNumeric(bucket.getKeyAsString())) {
|
|
|
results.add(Integer.valueOf(bucket.getKeyAsString()));
|
|
|
}
|
|
|
}
|
|
|
searchCacheService.addJSONArrayToCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam, sknSmallSortIdJSONArray);
|
|
|
return this.jsonArrayToList(sknSmallSortIdJSONArray, Integer.class);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 有好货默认的过滤规则
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
private BoolQueryBuilder getDefaultBoolQueryBuilder() {
|
|
|
BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
|
|
|
boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));
|
|
|
boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));
|
|
|
boolFilter.must(QueryBuilders.termQuery("status", 1));
|
|
|
boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));
|
|
|
boolFilter.must(QueryBuilders.termQuery("attribute", 1));
|
|
|
// 默认推库存>2,非断码,并且短评存在的数据
|
|
|
boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(2));
|
|
|
boolFilter.must(QueryBuilders.rangeQuery("breakingRate").lt(50));
|
|
|
boolFilter.must(QueryBuilders.rangeQuery("basePinRatio").lt(3.5));
|
|
|
boolFilter.must(QueryBuilders.termQuery("isPhraseExist", "Y"));
|
|
|
return boolFilter;
|
|
|
return results;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 每个品类为用户推荐maxSize个SKN
|
|
|
* 根据skn参数获取推荐出来的skn列表
|
|
|
*
|
|
|
* @param smallSortIds
|
|
|
* @param maxSize
|
|
|
* @param paramMap
|
|
|
* @return
|
|
|
*/
|
|
|
private List<String> getRecommondedSkns(List<Integer> smallSortIds, int maxSizePerSort, Map<String, String> paramMap) {
|
|
|
// 1、如果品类为空,则直接返回
|
|
|
if (smallSortIds == null || smallSortIds.isEmpty()) {
|
|
|
private List<String> recommondSknsBySortAndBrandInfo(JSONObject sortAndBrandInfo, Map<String, String> paramMap) {
|
|
|
// 1、获取品牌id和品类id
|
|
|
if (sortAndBrandInfo == null || sortAndBrandInfo.isEmpty()) {
|
|
|
return new ArrayList<String>();
|
|
|
}
|
|
|
JSONArray brandIdJSONArray = sortAndBrandInfo.getJSONArray("brandIds");
|
|
|
JSONArray smallSortIdJSONArray = sortAndBrandInfo.getJSONArray("smallSortIds");
|
|
|
List<Integer> brandIds = this.jsonArrayToList(brandIdJSONArray, Integer.class);
|
|
|
List<Integer> smallSortIds = this.jsonArrayToList(smallSortIdJSONArray, Integer.class);
|
|
|
if (brandIds.isEmpty() && smallSortIds.isEmpty()) {
|
|
|
return new ArrayList<String>();
|
|
|
}
|
|
|
SearchParam searchParam = new SearchParam();
|
|
|
|
|
|
// 2、构造filter
|
|
|
SearchParam searchParam = new SearchParam();
|
|
|
BoolQueryBuilder boolFilter = this.getDefaultBoolQueryBuilder();
|
|
|
boolFilter.must(QueryBuilders.termsQuery("smallSortId", smallSortIds));
|
|
|
BoolQueryBuilder brandAndSortFilter = new BoolQueryBuilder();
|
|
|
if (!brandIds.isEmpty()) {
|
|
|
brandAndSortFilter.should(QueryBuilders.termsQuery("brandId", brandIds));
|
|
|
}
|
|
|
if (!smallSortIds.isEmpty()) {
|
|
|
brandAndSortFilter.should(QueryBuilders.termsQuery("smallSortId", smallSortIds));
|
|
|
}
|
|
|
boolFilter.must(brandAndSortFilter);
|
|
|
searchParam.setFiter(boolFilter);
|
|
|
|
|
|
// 3、构造query
|
|
|
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(QueryBuilders.matchAllQuery());
|
|
|
// 针对看过的SKN做加分
|
|
|
String productSkns = paramMap.get(SearchRequestParams.PARAM_SEARCH_PRODUCT_SKN);
|
|
|
if (!StringUtils.isBlank(productSkns)) {
|
|
|
functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", productSkns.split(",")), ScoreFunctionBuilders.weightFactorFunction(100));
|
|
|
}
|
|
|
// 强制加上个性化打分
|
|
|
functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);
|
|
|
searchParam.setQuery(functionScoreQueryBuilder);
|
|
|
|
|
|
// 4、设置聚合条件
|
|
|
final String firstAggName = "firstAgg";
|
|
|
String order = "_score:desc";
|
|
|
String sortField = order.split(":")[0];
|
|
|
SortOrder sortOrder = order.split(":")[1].equals("desc") ? SortOrder.DESC : SortOrder.ASC;
|
|
|
String sortField = "_score";
|
|
|
SortOrder sortOrder = SortOrder.DESC;
|
|
|
List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>();
|
|
|
// 4.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】
|
|
|
TermsBuilder parentAggregationBuilder = AggregationBuilders.terms(firstAggName).field("smallSortId").size(smallSortIds.size());
|
|
|
// 4.2)添加子聚合:取得分最大的值
|
|
|
parentAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField));
|
|
|
// 4.3)添加孙聚合:取打分最高的一个product
|
|
|
parentAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(maxSizePerSort));
|
|
|
list.add(parentAggregationBuilder);
|
|
|
// 4.1)构造父聚合:品类聚合【同时按子聚合的sort字段排序】
|
|
|
TermsBuilder parentAggregationBuilder = AggregationBuilders.terms("smallSortAgg").field("smallSortId").size(smallSortIds.size());
|
|
|
// 4.2)构造子聚合:品牌或聚合【同时按子聚合的sort字段排序】
|
|
|
TermsBuilder sonAggregationBuilder = AggregationBuilders.terms("brandAgg").field("brandId").size(brandIds.size());
|
|
|
// 4.3)为子聚合添加孙聚合:取得分最大的值
|
|
|
sonAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(sortField));
|
|
|
// 4.4)为子聚合孙聚合:取打分最高的一个product
|
|
|
sonAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(1));
|
|
|
list.add(parentAggregationBuilder.subAggregation(sonAggregationBuilder));
|
|
|
searchParam.setAggregationBuilders(list);
|
|
|
|
|
|
// 5、设置分页
|
...
|
...
|
@@ -270,26 +265,123 @@ public class GoodProductListService implements IGoodProductsService { |
|
|
if (recommendedSknJSONArray != null) {
|
|
|
return this.jsonArrayToList(recommendedSknJSONArray, String.class);
|
|
|
}
|
|
|
// 7、执行搜索,并构造返回结果
|
|
|
// 7、执行搜索
|
|
|
final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
|
|
|
SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
|
|
|
if (searchResult == null || searchResult.getAggMaps() == null) {
|
|
|
return new ArrayList<String>();
|
|
|
}
|
|
|
// 8、获取搜索结果
|
|
|
Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
|
|
|
if (!aggMaps.containsKey(firstAggName)) {
|
|
|
if (!aggMaps.containsKey("smallSortAgg")) {
|
|
|
return new ArrayList<String>();
|
|
|
}
|
|
|
List<String> recommendedSknList = this.getRecommendedSknList((MultiBucketsAggregation) aggMaps.get(firstAggName));
|
|
|
List<GoodProductBO> goodProductLists = new ArrayList<GoodProductBO>();
|
|
|
Iterator<? extends Bucket> smallSortIterator = ((MultiBucketsAggregation) aggMaps.get("smallSortAgg")).getBuckets().iterator();
|
|
|
while (smallSortIterator.hasNext()) {
|
|
|
Bucket smallSortBucket = smallSortIterator.next();
|
|
|
Map<String, Aggregation> smallSortAggMap = smallSortBucket.getAggregations().getAsMap();
|
|
|
if (!smallSortAggMap.containsKey("brandAgg")) {
|
|
|
continue;
|
|
|
}
|
|
|
Iterator<? extends Bucket> brandIdIterator = ((MultiBucketsAggregation) smallSortAggMap.get("brandAgg")).getBuckets().iterator();
|
|
|
List<String> productSkns = new ArrayList<String>();
|
|
|
while (brandIdIterator.hasNext()) {
|
|
|
Bucket brandIdBucket = brandIdIterator.next();
|
|
|
if (brandIdBucket.getAggregations().getAsMap().containsKey("product")) {
|
|
|
TopHits topHits = brandIdBucket.getAggregations().get("product");
|
|
|
if (topHits != null) {
|
|
|
SearchHits hits = topHits.getHits();
|
|
|
for (SearchHit hit : hits.getHits()) {
|
|
|
productSkns.add("" + hit.getSource().get("productSkn"));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (!productSkns.isEmpty()) {
|
|
|
Collections.shuffle(productSkns);
|
|
|
goodProductLists.add(new GoodProductBO(smallSortBucket.getKeyAsString(), productSkns));
|
|
|
}
|
|
|
}
|
|
|
Collections.shuffle(goodProductLists);
|
|
|
// 9、构造返回结果
|
|
|
recommendedSknJSONArray = new JSONArray();
|
|
|
for (String recommendedSkn : recommendedSknList) {
|
|
|
recommendedSknJSONArray.add(recommendedSkn);
|
|
|
// 9.1、获取推荐出来的商品
|
|
|
recommendedSknJSONArray.addAll(this.getProductSkns(goodProductLists));
|
|
|
// 9.2、再加入用户看过的商品前面
|
|
|
for (String skn : paramMap.getOrDefault("product_skn", "").split(",")) {
|
|
|
if (!recommendedSknJSONArray.contains(skn)) {
|
|
|
recommendedSknJSONArray.add(skn);
|
|
|
}
|
|
|
}
|
|
|
searchCacheService.addJSONArrayToCache(indexName, searchParam, recommendedSknJSONArray);
|
|
|
return this.jsonArrayToList(recommendedSknJSONArray, String.class);
|
|
|
}
|
|
|
|
|
|
private List<String> getProductSkns(List<GoodProductBO> goodProductLists) {
|
|
|
if (goodProductLists == null || goodProductLists.isEmpty()) {
|
|
|
return new ArrayList<String>();
|
|
|
}
|
|
|
int maxSknCount = 0;
|
|
|
for (GoodProductBO goodProductBO : goodProductLists) {
|
|
|
int productSknSize = goodProductBO.getProductSkn().size();
|
|
|
if (maxSknCount < productSknSize) {
|
|
|
maxSknCount = productSknSize;
|
|
|
}
|
|
|
}
|
|
|
List<String> results = new ArrayList<String>();
|
|
|
for (int i = 0; i < maxSknCount; i++) {
|
|
|
for (GoodProductBO goodProductBO : goodProductLists) {
|
|
|
int randomCount = 1 + (int) (Math.random() * 2);
|
|
|
results.addAll(goodProductBO.randomGetProductSkn(randomCount));
|
|
|
}
|
|
|
}
|
|
|
return results;
|
|
|
}
|
|
|
|
|
|
private QueryBuilder builderGoodProductQueryBuilder(Map<String, String> paramMap, List<String> recommendedSknList) {
|
|
|
QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
|
|
|
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder);
|
|
|
// 针对推荐出来的SKN做加分
|
|
|
if (recommendedSknList != null && !recommendedSknList.isEmpty()) {
|
|
|
Map<Integer, List<String>> recommondSknMap = this.splitProductSkns(recommendedSknList, 2);
|
|
|
float currentGroupScore = 1000;
|
|
|
for (Map.Entry<Integer, List<String>> entry : recommondSknMap.entrySet()) {
|
|
|
functionScoreQueryBuilder.add(QueryBuilders.termsQuery("productSkn", entry.getValue()), ScoreFunctionBuilders.weightFactorFunction(currentGroupScore));
|
|
|
currentGroupScore = currentGroupScore - 10;
|
|
|
}
|
|
|
}
|
|
|
// 加上个性化打分
|
|
|
if (searchCommonHelper.isNeedPersonalSearch(paramMap)) {
|
|
|
functionScoreSearchHelper.addCommonPersonalizedScriptScore(functionScoreQueryBuilder, paramMap);
|
|
|
}
|
|
|
return functionScoreQueryBuilder;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 有好货默认的过滤规则
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
private BoolQueryBuilder getDefaultBoolQueryBuilder() {
|
|
|
BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
|
|
|
boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));
|
|
|
boolFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));
|
|
|
boolFilter.must(QueryBuilders.termQuery("status", 1));
|
|
|
boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));
|
|
|
boolFilter.must(QueryBuilders.termQuery("attribute", 1));
|
|
|
// 默认推库存>2,非断码,并且短评存在的数据
|
|
|
boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(2));
|
|
|
boolFilter.must(QueryBuilders.rangeQuery("breakingRate").lt(50));
|
|
|
boolFilter.must(QueryBuilders.rangeQuery("basePinRatio").lt(3.5));
|
|
|
boolFilter.must(QueryBuilders.termQuery("isPhraseExist", "Y"));
|
|
|
return boolFilter;
|
|
|
}
|
|
|
|
|
|
private <T> List<T> jsonArrayToList(JSONArray jsonArray, Class<T> clazz) {
|
|
|
if (jsonArray == null) {
|
|
|
return new ArrayList<T>();
|
|
|
}
|
|
|
return JSONObject.parseArray(jsonArray.toJSONString(), clazz);
|
|
|
}
|
|
|
|
...
|
...
|
@@ -315,40 +407,11 @@ public class GoodProductListService implements IGoodProductsService { |
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取聚合出来的商品
|
|
|
*
|
|
|
* @param aggregation
|
|
|
* @return
|
|
|
*/
|
|
|
private List<String> getRecommendedSknList(MultiBucketsAggregation aggregation) {
|
|
|
Iterator<? extends Bucket> itAgg = aggregation.getBuckets().iterator();
|
|
|
List<String> recommendedSknList = new ArrayList<String>();
|
|
|
while (itAgg.hasNext()) {
|
|
|
Bucket lt = itAgg.next();
|
|
|
if (lt.getAggregations().getAsMap().containsKey("product")) {
|
|
|
TopHits topHits = lt.getAggregations().get("product");
|
|
|
if (topHits != null) {
|
|
|
SearchHits hits = topHits.getHits();
|
|
|
for (SearchHit hit : hits.getHits()) {
|
|
|
recommendedSknList.add("" + hit.getSource().get("productSkn"));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
public static List<String> getNewList(List<String> oldList) {
|
|
|
List<String> newList = new ArrayList<String>();
|
|
|
for (String element : oldList) {
|
|
|
newList.add(element);
|
|
|
}
|
|
|
Collections.shuffle(recommendedSknList);
|
|
|
return recommendedSknList;
|
|
|
return newList;
|
|
|
}
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
List<String> list = new ArrayList<String>();
|
|
|
for (int i = 1; i <= 99; i++) {
|
|
|
list.add(i + "");
|
|
|
}
|
|
|
Map<Integer, List<String>> results = new GoodProductListService().splitProductSkns(list, 20);
|
|
|
for (Map.Entry<Integer, List<String>> entry : results.entrySet()) {
|
|
|
System.out.println(entry.getKey() + "_" + entry.getValue());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
} |
...
|
...
|
|