|
|
package com.yoho.search.service.recall;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
|
|
|
import org.apache.commons.collections.MapUtils;
|
|
|
import org.apache.commons.lang.StringUtils;
|
|
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
|
|
import org.elasticsearch.index.query.QueryBuilders;
|
|
|
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;
|
|
|
import com.yoho.search.core.es.model.SearchParam;
|
|
|
import com.yoho.search.core.es.model.SearchResult;
|
|
|
import com.yoho.search.core.personalized.PersonalizedSearch;
|
|
|
import com.yoho.search.models.SearchApiResult;
|
|
|
import com.yoho.search.service.base.ProductListSortService;
|
|
|
import com.yoho.search.service.base.SearchCommonService;
|
|
|
import com.yoho.search.service.base.SearchRequestParams;
|
|
|
import com.yoho.search.service.base.index.ProductIndexBaseService;
|
|
|
import com.yoho.search.service.helper.SearchParamHelper;
|
|
|
import com.yoho.search.service.helper.SearchSortHelper;
|
|
|
import com.yoho.search.service.recall.cacheable.CacheAbleServiceHelper;
|
|
|
import com.yoho.search.service.recall.cacheable.CommonPageRecallService;
|
|
|
import com.yoho.search.service.recall.model.CommonRecallParam;
|
|
|
import com.yoho.search.service.recall.model.CommonRecallResult;
|
|
|
import com.yoho.search.service.recall.model.CommonRecallSkn;
|
|
|
import com.yoho.search.service.scorer.personal.PersonalVectorFeatureSearch;
|
|
|
|
|
|
@Service
|
|
|
public class CommonSceneProductListService {
|
|
|
|
|
|
@Autowired
|
|
|
private CommonPageRecallService commonRecallSceneService;
|
|
|
@Autowired
|
|
|
private ProductListSortService productListSortService;
|
|
|
@Autowired
|
|
|
private SearchParamHelper searchParamHelper;
|
|
|
@Autowired
|
|
|
private SearchSortHelper searchSortHelper;
|
|
|
@Autowired
|
|
|
private ProductIndexBaseService productIndexBaseService;
|
|
|
@Autowired
|
|
|
private SearchCommonService searchCommonService;
|
|
|
@Autowired
|
|
|
private PersonalVectorFeatureSearch personalVectorFeatureSearch;
|
|
|
@Autowired
|
|
|
private ProductFeatureFactorHepler productFeatureFactorHepler;
|
|
|
@Autowired
|
|
|
private CacheAbleServiceHelper cacheAbleServiceHelper;
|
|
|
|
|
|
private long getTotalPage(long total, int viewNum) {
|
|
|
if (viewNum == 0) {
|
|
|
return total;
|
|
|
}
|
|
|
long totalPage = total / viewNum;
|
|
|
if (total % viewNum > 0) {
|
|
|
totalPage = totalPage + 1;
|
|
|
}
|
|
|
return totalPage;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 个性化的列表接口
|
|
|
*
|
|
|
* @order为空并且有uid
|
|
|
* @param paramMap
|
|
|
* @return
|
|
|
*/
|
|
|
public SearchApiResult productListForPersional(Map<String, String> paramMap) {
|
|
|
// 1、召回整数页个skn
|
|
|
CommonRecallResult commonRecallResult = commonRecallSceneService.doCommonPageRecallBatch(paramMap);
|
|
|
// 2、查询这整数页个skn的全部信息-包含了变价计划
|
|
|
SearchApiResult productInfoMapResult = cacheAbleServiceHelper.queryProductInfoMap(commonRecallResult);
|
|
|
// 3、计算这些skn得分并排序
|
|
|
commonRecallResult = this.callUserScoreAndRank(paramMap, commonRecallResult);
|
|
|
// 4、获取分页参数
|
|
|
int page = StringUtils.isBlank(paramMap.get("page")) ? 1 : Integer.parseInt(paramMap.get("page"));
|
|
|
int viewNum = StringUtils.isBlank(paramMap.get("viewNum")) ? 60 : Integer.parseInt(paramMap.get("viewNum"));
|
|
|
long total = commonRecallResult.getTotalCount();
|
|
|
// 5、为用户做列表截取-保留第一页数据
|
|
|
int recallMaxPage = (int) (commonRecallResult.listCount() / viewNum);
|
|
|
if (recallMaxPage == 0) {
|
|
|
recallMaxPage = 1;
|
|
|
}
|
|
|
// 6、构造分页结果参数
|
|
|
JSONObject results = new JSONObject();
|
|
|
results.put("total", total);
|
|
|
results.put("page", page);
|
|
|
results.put("page_size", viewNum);
|
|
|
results.put("page_total", this.getTotalPage(total, viewNum));
|
|
|
// 7、构造productList
|
|
|
if (page <= recallMaxPage) {
|
|
|
results.put("product_list", this.getProductList(productInfoMapResult, commonRecallResult, page, viewNum));
|
|
|
return new SearchApiResult().setData(results);
|
|
|
}
|
|
|
// 8、其他页面使用CommonRecallParamService去查询
|
|
|
CommonRecallParam commonRecallParam = new CommonRecallParam(paramMap);
|
|
|
BoolQueryBuilder extendMustFilter = QueryBuilders.boolQuery();
|
|
|
extendMustFilter.mustNot(QueryBuilders.termsQuery(ProductIndexEsField.productSkn, commonRecallResult.toSknList()));
|
|
|
commonRecallParam.setExtendMustFilter(extendMustFilter);
|
|
|
commonRecallParam.setPage(page - recallMaxPage);
|
|
|
commonRecallParam.setViewNum(viewNum);
|
|
|
results.put("product_list", cacheAbleServiceHelper.queryProductListByRecallParam(commonRecallParam).getData());
|
|
|
return new SearchApiResult().setData(results);
|
|
|
}
|
|
|
|
|
|
private List<? extends Map<?, ?>> getProductList(SearchApiResult productInfoMapResult, CommonRecallResult commonRecallResult, int page, int viewNum) {
|
|
|
JSONObject productInfoMap = (JSONObject) productInfoMapResult.getData();
|
|
|
List<JSONObject> results = new ArrayList<JSONObject>();
|
|
|
List<CommonRecallSkn> recallSknList = commonRecallResult.getRecallSknList();
|
|
|
int fromIndex = (page - 1) * viewNum;
|
|
|
int toIndex = page * viewNum;
|
|
|
if (toIndex > recallSknList.size()) {
|
|
|
toIndex = recallSknList.size();
|
|
|
}
|
|
|
recallSknList = recallSknList.subList(fromIndex, toIndex);
|
|
|
for (CommonRecallSkn commonRecallSkn : recallSknList) {
|
|
|
results.add(productInfoMap.getJSONObject(String.valueOf(commonRecallSkn.getProductSkn())));
|
|
|
}
|
|
|
return results;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 根据向量计算得分并排序
|
|
|
*
|
|
|
* @param paramMap
|
|
|
* @param commonRecallResult
|
|
|
* @return
|
|
|
*/
|
|
|
private CommonRecallResult callUserScoreAndRank(Map<String, String> paramMap, CommonRecallResult commonRecallResult) {
|
|
|
PersonalizedSearch personalizedSearch = personalVectorFeatureSearch.getPersonalizedSearch(paramMap);
|
|
|
if (personalizedSearch == null) {
|
|
|
return commonRecallResult;
|
|
|
}
|
|
|
String userVectorFeature = personalizedSearch.getUserVectorFeature();
|
|
|
String[] userFeatureFactorArr = userVectorFeature.split(",");
|
|
|
String vectorFeatureVersion = personalizedSearch.getVectorFeatureVersion();
|
|
|
List<String> firstProductSkns = Arrays.asList(MapUtils.getString(paramMap, SearchRequestParams.PARAM_SEARCH_FIRST_PRODUCRSKN, "").split(","));
|
|
|
for (CommonRecallSkn commonRecallSkn : commonRecallResult.getRecallSknList()) {
|
|
|
if (firstProductSkns.contains(String.valueOf(commonRecallSkn.getProductSkn()))) {
|
|
|
commonRecallSkn.setScore(10000);
|
|
|
} else {
|
|
|
commonRecallSkn.setScore(productFeatureFactorHepler.calProductFeatureFactor(userFeatureFactorArr, vectorFeatureVersion, commonRecallSkn.getProductFeatureFactor()));
|
|
|
}
|
|
|
}
|
|
|
Collections.sort(commonRecallResult.getRecallSknList());
|
|
|
return commonRecallResult;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 非个性化的列表接口
|
|
|
*
|
|
|
* @order不为空,或者无uid
|
|
|
* @param paramMap
|
|
|
* @return
|
|
|
*/
|
|
|
@SearchCacheAble(cacheName = "SCENE_PRODUCT_LIST_DEFAULT", cacheInMinute = 10, excludeParams = { "uid" })
|
|
|
public SearchApiResult productListForDefault(Map<String, String> paramMap) {
|
|
|
try {
|
|
|
// 1)验证查询条数
|
|
|
int pageSize = StringUtils.isBlank(paramMap.get("viewNum")) ? 10 : Integer.parseInt(paramMap.get("viewNum"));
|
|
|
int page = StringUtils.isBlank(paramMap.get("page")) ? 1 : Integer.parseInt(paramMap.get("page"));
|
|
|
if (page < 1 || pageSize < 0) {
|
|
|
throw new IllegalArgumentException("分页参数不合法");
|
|
|
}
|
|
|
if (pageSize > 100) {
|
|
|
pageSize = 100;
|
|
|
}
|
|
|
// 2)构建基本查询参数
|
|
|
SearchParam searchParam = searchParamHelper.buildWithPersional(paramMap, true);
|
|
|
searchParam.setAggregationBuilders(null);
|
|
|
searchParam.setOffset((page - 1) * pageSize);
|
|
|
searchParam.setSize(pageSize);
|
|
|
// 3)设置排序字段
|
|
|
searchParam.setSortBuilders(searchSortHelper.buildSortList(paramMap));
|
|
|
// 4)设置返回的参数【节省带宽】
|
|
|
List<String> includeFields = productIndexBaseService.getProductIndexIncludeFields();
|
|
|
searchParam.setIncludeFields(includeFields);
|
|
|
SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
|
|
|
// 5)构造返回结果
|
|
|
JSONObject dataMap = new JSONObject();
|
|
|
dataMap.put("total", searchResult.getTotal());
|
|
|
dataMap.put("page", searchResult.getPage());
|
|
|
dataMap.put("page_size", searchParam.getSize());
|
|
|
dataMap.put("page_total", searchResult.getTotalPage());
|
|
|
List<Map<String, Object>> product_list = productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList());
|
|
|
dataMap.put("product_list", productListSortService.sortProductList(product_list, paramMap));// 处理一下商品的顺序;
|
|
|
return new SearchApiResult().setData(dataMap);
|
|
|
} catch (Exception e) {
|
|
|
return new SearchApiResult().setData(null).setCode(500);
|
|
|
}
|
|
|
}
|
|
|
} |
...
|
...
|
|