Authored by hugufei

优化similarProductList

... ... @@ -12,4 +12,9 @@ public interface ISimilarProductService {
* 相似skn推荐
*/
public SearchApiResult similarProductList(Map<String, String> paramMap);
/**
* 相似skn推荐
*/
public SearchApiResult similarProductList(String product_skn,int viewNum);
}
... ...
package com.yoho.search.service.service.impl;
import static java.util.stream.Collectors.joining;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
... ... @@ -19,7 +17,6 @@ import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import com.yoho.search.base.utils.ISearchConstants;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.common.cache.aop.SearchCacheAble;
import com.yoho.search.common.utils.SearchApiResultUtils;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.model.SearchResult;
... ... @@ -58,26 +55,46 @@ public class ProductListWithSupplyServiceImpl implements IProductListWithSupplyS
* 不支持分页,最多返回100个
*/
@Override
@SearchCacheAble(cacheInMinute = 30, cacheName = "PRODUCT_LIST_WITH_SUPPLY")
//@SearchCacheAble(cacheInMinute = 30, cacheName = "PRODUCT_LIST_WITH_SUPPLY", includeParams = { "product_skn" })
public SearchApiResult productListWithSupply(Map<String, String> paramMap) {
try {
// 1)构建基本查询参数
SearchParam searchParam = searchParamHelper.buildDefault(paramMap);
// 1)参数校验
String inputSknListString = paramMap.getOrDefault(SearchRequestParams.PARAM_SEARCH_PRODUCT_SKN, "");
if (inputSknListString == null || inputSknListString.isEmpty()) {
return new SearchApiResult().setCode(400).setMessage("请传SKN参数");
}
// 2)构建searchParam
SearchParam searchParam = new SearchParam();
String[] productSkns = inputSknListString.split(",");
BoolQueryBuilder filter = new BoolQueryBuilder();
// 2.1)查询这些skn
filter.must(QueryBuilders.termsQuery(ProductIndexEsField.productSkn, productSkns));
// 2.2)保留【上架有库存】或【限购】
BoolQueryBuilder shouldFilter = new BoolQueryBuilder();
shouldFilter.should(QueryBuilders.termQuery(ProductIndexEsField.isLimitbuy, "Y"));
shouldFilter.should(QueryBuilders.boolQuery().must(QueryBuilders.termQuery(ProductIndexEsField.status, 1))
.must(QueryBuilders.rangeQuery(ProductIndexEsField.storageNum).gte(1)));
filter.must(shouldFilter);
// 3)设置其他参数
searchParam.setAggregationBuilders(null);
searchParam.setOffset(0);
searchParam.setSize(100);
// 2)查询ES
searchParam.setSize(productSkns.length);
searchParam.setIncludeFields(productIndexBaseService.getProductIndexIncludeFields());
// 4)查询ES
SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
if (searchResult == null) {
return new SearchApiResult().setCode(500).setMessage("execption");
}
// 3)补充SKN逻辑:记录传入的skn列表,调用搜索,把返回的product列表中的skn记录到一个列表,找出无效的skn,用这些skn查找相似的,加入到之前的返回列表中返回
List<Map<String, Object>> productList = supplySkn(paramMap, searchResult, searchParam);
// 4)构造返回结果
// 5)补充SKN逻辑:记录传入的skn列表,调用搜索,把返回的product列表中的skn记录到一个列表,找出无效的skn,用这些skn查找相似的,加入到之前的返回列表中返回
List<Map<String, Object>> productList = supplySkn(searchResult, productSkns);
// 6)构造返回结果
JSONObject dataMap = new JSONObject();
dataMap.put("total", productList.size());
dataMap.put("page", 1);
dataMap.put("page_size", searchParam.getSize());
dataMap.put("page_size", productSkns.length);
dataMap.put("page_total", 1);
dataMap.put("product_list", productList);
return new SearchApiResult().setData(dataMap);
... ... @@ -90,85 +107,34 @@ public class ProductListWithSupplyServiceImpl implements IProductListWithSupplyS
/**
* 补充相似的skn
*/
private List<Map<String, Object>> supplySkn(Map<String, String> paramMap, SearchResult searchResult, SearchParam searchParam) {
String inputSknListString = paramMap.getOrDefault(SearchRequestParams.PARAM_SEARCH_PRODUCT_SKN, "");
// 输入skn参数列表
String[] inputSknList = inputSknListString.split(",");
@SuppressWarnings("unchecked")
private List<Map<String, Object>> supplySkn(SearchResult searchResult, String[] productSkns) {
// 1、先获取有效skn的变价计划
List<Map<String, Object>> productList = productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList());
// 搜索结果skn列表
// 2、数量一致,不需要补充
if (productList.size() == productSkns.length) {
return productList;
}
// 3、获取已经存在的skn
Set<String> outputSknSet = new HashSet<>();
for (Map<String, Object> product : productList) {
String skn = product.get("product_skn").toString();
outputSknSet.add(skn);
}
// 无效的skn列表(比如不满足库存和状态等)
// 3、获取无效的skn列表
List<String> invalidSknList = new ArrayList<>();
for (int i = 0; i < inputSknList.length; i++) {
if (!outputSknSet.contains(inputSknList[i])) {
invalidSknList.add(inputSknList[i]);
}
}
// 如果无效的skn满足isLimitbuy则保留
invalidSknList = validateSkn(invalidSknList, productList);
String sknString = invalidSknList.stream().collect(joining(","));
int pageSize = searchParam.getSize();
// 需要补充的skn个数
int supplySknNum = 0;
// 如果pageSize等于搜索返回的skn个数,不需要补充
int validSknCount = inputSknList.length - invalidSknList.size();
if (validSknCount < pageSize) {
// 补充的skn数要根据pageSize的大小来确定
// 如果pageSize大于等于输入skn数,补充skn数等于输入skn数减去有效skn数
// 如果pageSize小于输入skn数,补充数等于pageSize减去返回数
if (pageSize >= inputSknList.length) {
supplySknNum = inputSknList.length - validSknCount;
} else {
supplySknNum = pageSize - validSknCount;
for (int i = 0; i < productSkns.length; i++) {
if (!outputSknSet.contains(productSkns[i])) {
invalidSknList.add(productSkns[i]);
}
}
// 如果需要补充数大于0,去调用找相似服务,获取补充数个结果
if (supplySknNum > 0) {
paramMap.put("viewNum", supplySknNum + "");
paramMap.put("product_skn", sknString);
SearchApiResult searchApiResult = similarProductService.similarProductList(paramMap);
JSONObject supplyDataMap = (JSONObject) searchApiResult.getData();
@SuppressWarnings("unchecked")
List<Map<String, Object>> supplyProductList = (List<Map<String, Object>>) supplyDataMap.get("product_list");
productList.addAll(supplyProductList);
}
// 4、执行找相似进行补充
int viewNum = productSkns.length - productList.size();
String productSkn = StringUtils.join(invalidSknList, ",");
SearchApiResult searchApiResult = similarProductService.similarProductList(productSkn,viewNum);
Map<String,Object> dataMap = (Map<String,Object> )searchApiResult.getData();
List<Map<String, Object>> supplyProductList = (List<Map<String, Object>>) dataMap.get("product_list");
productList.addAll(supplyProductList);
return productList;
}
/**
* 如果无效的skn列表中有isLimitbuy的skn,则保留
*/
private List<String> validateSkn(List<String> invalidSknList, List<Map<String, Object>> productList) {
String sknString = invalidSknList.stream().collect(joining(","));
SearchParam searchParam = new SearchParam();
BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
boolFilter.must(QueryBuilders.termsQuery(ProductIndexEsField.productSkn, sknString.split(",")));
boolFilter.must(QueryBuilders.termQuery(ProductIndexEsField.isLimitbuy, "Y"));
searchParam.setFiter(boolFilter);
searchParam.setQuery(QueryBuilders.matchAllQuery());
searchParam.setSize(invalidSknList.size());
final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
List<Map<String, Object>> productListValidate = productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList());
// 无效的skn列表(比如不满足库存和状态等)
List<String> trueInvalidSknList = new ArrayList<>();
Set<String> outputSknSet = new HashSet<>();
if (CollectionUtils.isNotEmpty(productListValidate)) {
productList.addAll(productListValidate);
for (Map<String, Object> product : productListValidate) {
String skn = product.get("product_skn").toString();
outputSknSet.add(skn);
}
}
for (int i = 0; i < invalidSknList.size(); i++) {
if (!outputSknSet.contains(invalidSknList.get(i))) {
trueInvalidSknList.add(invalidSknList.get(i));
}
}
return trueInvalidSknList;
}
}
... ...
... ... @@ -20,7 +20,6 @@ import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import com.yoho.search.base.utils.ISearchConstants;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.common.cache.aop.SearchCacheAble;
... ... @@ -50,20 +49,25 @@ public class SimilarProductServiceImpl implements ISimilarProductService {
@Override
@SearchCacheAble(cacheInMinute = 30, cacheName = "SIMILAR_PRODUCT_LIST", includeParams = { "product_skn", "viewNum" })
public SearchApiResult similarProductList(Map<String, String> paramMap) {
String product_skn = paramMap.getOrDefault(PRODUCT_SKN, "");
int viewNum = StringUtils.isBlank(paramMap.get("viewNum")) ? 30 : Integer.parseInt(paramMap.get("viewNum"));
if (viewNum > 50) {
viewNum = 50;
}
return this.similarProductList(product_skn, viewNum);
}
@Override
public SearchApiResult similarProductList(String product_skn, int viewNum) {
// 1、 获取入参SKN列表
List<String> productSknList = this.stringToList(paramMap.getOrDefault(PRODUCT_SKN, ""));
List<String> productSknList = this.stringToList(product_skn);
if (productSknList == null || productSknList.isEmpty()) {
return new SearchApiResult().setCode(400).setMessage("请传product_skn参数");
}
// 2.检测数量
int pageSize = StringUtils.isBlank(paramMap.get("viewNum")) ? 30 : Integer.parseInt(paramMap.get("viewNum"));
if (pageSize > 50) {
pageSize = 50;
}
// 3.获取skn的品类列表和品牌列表
// 2.获取skn的品类列表和品牌列表
Map<String, List<Integer>> sortAndBrandInfo = this.querySknSortAndBrand(productSknList);
// 4.根据品牌和品类,随机推荐X个商品
SearchApiResult searchApiResult = this.searchProductList(productSknList, sortAndBrandInfo, pageSize);
// 3.根据品牌和品类,随机推荐X个商品
SearchApiResult searchApiResult = this.searchProductList(productSknList, sortAndBrandInfo, viewNum);
return searchApiResult;
}
... ... @@ -114,6 +118,7 @@ public class SimilarProductServiceImpl implements ISimilarProductService {
boolQueryBuilder.mustNot(QueryBuilders.termsQuery(ProductIndexEsField.isSeckill, "Y"));
boolQueryBuilder.mustNot(QueryBuilders.termQuery(ProductIndexEsField.isFobbiden, 1));
boolQueryBuilder.mustNot(QueryBuilders.termQuery(ProductIndexEsField.attribute, 2));
boolQueryBuilder.mustNot(QueryBuilders.termQuery(ProductIndexEsField.isGlobal, "Y"));
boolQueryBuilder.must(QueryBuilders.termQuery(ProductIndexEsField.status, 1));
boolQueryBuilder.must(QueryBuilders.termQuery(ProductIndexEsField.isOutlets, 2));
boolQueryBuilder.must(QueryBuilders.rangeQuery(ProductIndexEsField.storageNum).gte(1));
... ... @@ -152,13 +157,13 @@ public class SimilarProductServiceImpl implements ISimilarProductService {
return new SearchApiResult().setCode(500).setMessage("exception");
}
// 6.构造返回结果
JSONObject dataMap = new JSONObject();
dataMap.put("total", searchResult.getResultList().size());
dataMap.put("page", 1);
dataMap.put("page_size", pageSize);
dataMap.put("page_total", 1);
dataMap.put("product_list", productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList()));
return new SearchApiResult().setData(dataMap);
Map<String,Object> result = new HashMap<String, Object>();
result.put("total", searchResult.getResultList().size());
result.put("page", 1);
result.put("page_size", pageSize);
result.put("page_total", 1);
result.put("product_list", productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList()));
return new SearchApiResult().setData(result);
}
private List<String> stringToList(String source) {
... ...