Authored by wangnan9279

Merge branch '0522' into wn_fix_promotion

Showing 23 changed files with 874 additions and 302 deletions
... ... @@ -22,18 +22,6 @@ public class HttpServletRequestUtils {
return resultMap;
}
public static String transParamParamStr(HttpServletRequest request) {
if (request.getParameterMap() != null) {
StringBuilder paramStringBuilder = new StringBuilder();
for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
String value = ArrayUtils.isEmpty(entry.getValue()) ? null : entry.getValue()[0];
paramStringBuilder.append('&').append(entry.getKey()).append('=').append(value);
}
return paramStringBuilder.toString().replaceFirst("&", "");
}
return "";
}
public static String getRequestUrl(HttpServletRequest request) {
StringBuffer sb = new StringBuffer();
sb.append(request.getRequestURI());
... ... @@ -48,26 +36,6 @@ public class HttpServletRequestUtils {
return sb.toString();
}
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
public static String genParamString(Map<?, ?> paramMap) {
if (paramMap == null || paramMap.isEmpty()) {
return "";
... ...
package com.yoho.search.common.utils;
import com.yoho.search.base.utils.MD5Util;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.utils.SearchParamUtils;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import java.util.List;
import java.util.Set;
/**
* Created by henry on 2016/9/21.
*/
public class LocalCacheKeyUtils {
public static String guavaCacheKey(String indexName,SearchParam searchParam){
StringBuilder sb = new StringBuilder();
// 拼装索引名称
sb.append("indexName:").append(indexName).append(';');
// 拼装报文
SearchSourceBuilder searchSourceBuilder = SearchParamUtils.genSearchSourceBuilderFromSearchParam(searchParam);
sb.append("searchSource:").append(searchSourceBuilder.toString()).append(';');
String cacheKey = "YOHOSEARCH:" + MD5Util.string2MD5(sb.toString());
return cacheKey;
}
public static String genMultiGetParamKey(String indexName, Set<String> idList, List<String> fields) {
StringBuilder sb = new StringBuilder();
// 拼装索引名称
sb.append("indexName:").append(indexName).append(';');
// 拼装搜索类型
sb.append("idList:").append(idList.toString()).append(';');
// 拼装报文
sb.append("fields:").append(fields == null ? "" : fields.toString()).append(';');
// 打印拼装结果
return "YOHOSEARCH:" + MD5Util.string2MD5(sb.toString());
}
}
... ... @@ -17,8 +17,7 @@ public class SnappyUtils {
/**
* 压缩
*
* @param text
* @param unCompressedVal
* @return
*/
public static String compress(String unCompressedVal) {
... ... @@ -61,7 +60,7 @@ public class SnappyUtils {
/**
* 解压缩
*
* @param compressVal
* @param compressedVal
* @return
*/
public static String uncompress(String compressedVal) {
... ...
... ... @@ -22,6 +22,14 @@ public class UserRecallRequestBuilder {
@Autowired
private SearchServiceHelper searchServiceHelepr;
public ParamQueryFilter buildParamQueryFilter(Map<String, String> paramMap) throws Exception{
//1、获取链接中的query和filter参数
QueryBuilder query = searchServiceHelepr.constructQueryBuilder(paramMap);
BoolQueryBuilder filter = searchServiceHelepr.constructFilterBuilder(paramMap, null);
return new ParamQueryFilter(query, filter);
}
/**
* 为普通列表构造召回参数
* @param paramMap
... ... @@ -31,10 +39,8 @@ public class UserRecallRequestBuilder {
*/
public UserRecallRequest buildUserRecallRequest(Map<String, String> paramMap, int pageSize) throws Exception {
//1、获取链接中的query和filter参数
QueryBuilder query = searchServiceHelepr.constructQueryBuilder(paramMap);
BoolQueryBuilder filter = searchServiceHelepr.constructFilterBuilder(paramMap, null);
ParamQueryFilter paramQueryFilter = new ParamQueryFilter(query, filter);
//2、构造paramQueryFilter
ParamQueryFilter paramQueryFilter = this.buildParamQueryFilter(paramMap);
//2、构造UserRecallRequest
return this.innerBuildUserRecallRequest(paramQueryFilter,paramMap,pageSize);
}
... ...
package com.yoho.search.restapi;
import com.yoho.search.common.utils.HttpServletRequestUtils;
import com.yoho.search.models.SearchApiResult;
import com.yoho.search.service.scene.activity.ActivityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@Controller
public class ActivityConroller {
@Autowired
private ActivityService activityService;
/**
* 活动推荐店铺
*
* @param request
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = "/activity/recommendShop")
@ResponseBody
public SearchApiResult recommendShop(HttpServletRequest request) {
Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request);
return activityService.recommendShop(paramMap);
}
}
... ...
... ... @@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import com.yoho.search.common.utils.HttpServletRequestUtils;
import com.yoho.search.models.SearchApiResult;
import com.yoho.search.service.scene.searchlike.SearchLikeInShopService;
import com.yoho.search.service.scene.searchlike.SearchLikeNotInShopService;
import com.yoho.search.service.scene.searchlike.SearchLikeSceneService;
import com.yoho.search.service.service.IProductListWithSupplyService;
import com.yoho.search.service.service.ISimilarProductService;
... ... @@ -34,15 +33,11 @@ public class SearchLikeSecneController {
@Autowired
private SearchLikeInShopService searchLikeInShopService;
@Autowired
private SearchLikeNotInShopService searchLikeNotInShopService;
@Autowired
private IProductListWithSupplyService productListWithSupplyService;
@Autowired
private ISimilarProductService similarProductService;
@Autowired
private BigdataSimilarSknService bigdataSimilarSknService;
@Autowired
private SearchDynamicConfigService searchDynamicConfigService;
@RequestMapping(method = RequestMethod.GET, value = "/searchLike")
@ResponseBody
... ... @@ -51,13 +46,6 @@ public class SearchLikeSecneController {
return searchLikeService.searchLike(paramMap);
}
@RequestMapping(method = RequestMethod.GET, value = "/searchLikeForSaleOut")
@ResponseBody
public SearchApiResult searchLikeForSaleOut(HttpServletRequest request) {
Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request);
return searchLikeService.searchLikeForSaleOut(paramMap);
}
@RequestMapping(method = RequestMethod.GET, value = "/searchLikeInShop")
@ResponseBody
public SearchApiResult searchLikeInShop(HttpServletRequest request) {
... ... @@ -69,12 +57,17 @@ public class SearchLikeSecneController {
@ResponseBody
public SearchApiResult searchLikeNotInShop(HttpServletRequest request) {
Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request);
if (searchDynamicConfigService.isSearchLikeSimilarSknOpen()) {
return bigdataSimilarSknService.searchLikeSimilarSknNotInShop(paramMap);
}
return searchLikeNotInShopService.searchLikeNotInShop(paramMap);
return bigdataSimilarSknService.searchLikeSimilarSknNotInShop(paramMap);
}
@RequestMapping(method = RequestMethod.GET, value = "/searchLikeForSaleOut")
@ResponseBody
public SearchApiResult searchLikeForSaleOut(HttpServletRequest request) {
Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request);
return searchLikeService.searchLikeForSaleOut(paramMap);
}
/**
* 获取商品列表,支持如果传入skn无效,补充相似skn
*/
... ...
package com.yoho.search.restapi.tools;
import com.alibaba.fastjson.JSONObject;
import com.yoho.search.base.utils.CollectionUtils;
import com.yoho.search.base.utils.Transfer;
import com.yoho.search.base.utils.DateUtil;
import com.yoho.search.base.utils.SearchPageIdDefine;
import com.yoho.search.common.utils.HttpServletRequestUtils;
import com.yoho.search.common.utils.SearchKeyWordUtils;
import com.yoho.search.models.SearchApiResult;
import com.yoho.search.recall.beans.builder.UserRecallRequestBuilder;
import com.yoho.search.recall.beans.cache.SknReturnInfoCacheBean;
import com.yoho.search.recall.beans.cache.UserRecallCacheBean;
import com.yoho.search.recall.beans.strategy.StrategyEnum;
import com.yoho.search.recall.models.common.RecallSknInfo;
import com.yoho.search.recall.models.req.UserRecallRequest;
import com.yoho.search.recall.models.req.UserRecallResponse;
import com.yoho.search.service.base.SearchRequestParams;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
@RestController
@RequestMapping(value = "/tools")
public class RecallResultController {
private static final Logger LOGGER = LoggerFactory.getLogger("RecallResultController");
@Autowired
private UserRecallRequestBuilder userRecallRequestBuilder;
@Autowired
private UserRecallCacheBean userRecallCacheBean;
@Autowired
private SknReturnInfoCacheBean sknReturnInfoCacheBean;
private List<String> supportPageIdList = Arrays.asList(SearchPageIdDefine.PAGE_ID_SORT, SearchPageIdDefine.PAGE_ID_NEW, SearchPageIdDefine.PAGE_ID_ZQ, SearchPageIdDefine.PAGE_ID_POOL);
@RequestMapping(method = RequestMethod.GET, value = "/getRecallResult")
public SearchApiResult userVectorSortBrand(HttpServletRequest request) {
Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request);
if (paramMap.get("pageId") == null || !supportPageIdList.contains(paramMap.get("pageId"))) {
return new SearchApiResult().setCode(400).setMessage("不支持的pageId");
}
switch (paramMap.get("pageId")) {
case SearchPageIdDefine.PAGE_ID_SORT:
if (!checkSortParam(paramMap)) {
return new SearchApiResult().setCode(400).setMessage("缺少品类参数");
}
addSortParams(paramMap);
break;
case SearchPageIdDefine.PAGE_ID_NEW:
addNewParams(paramMap);
break;
case SearchPageIdDefine.PAGE_ID_ZQ:
if (!checkZqParam(paramMap)) {
return new SearchApiResult().setCode(400).setMessage("缺少专区ID");
}
addDefaultParamsToParamMap(paramMap);
break;
case SearchPageIdDefine.PAGE_ID_POOL:
if (!checkPoolParam(paramMap)) {
return new SearchApiResult().setCode(400).setMessage("缺少商品池ID");
}
addDefaultParamsToParamMap(paramMap);
break;
}
return recallProductList(paramMap);
}
private boolean checkSortParam(Map<String, String> paramMap) {
if (StringUtils.isNotBlank(paramMap.get(SearchRequestParams.PARAM_SEARCH_MAXSORT))) {
return true;
}
if (StringUtils.isNotBlank(paramMap.get(SearchRequestParams.PARAM_SEARCH_MIDDLESORT))) {
return true;
}
if (StringUtils.isNotBlank(paramMap.get(SearchRequestParams.PARAM_SEARCH_SMALLSORT))) {
return true;
}
if (StringUtils.isNotBlank(paramMap.get(SearchRequestParams.PARAM_SEARCH_COMMONSORT))) {
return true;
}
return false;
}
private void addSortParams(Map<String, String> paramMap) {
addDefaultParamsToParamMap(paramMap);
paramMap.put(SearchRequestParams.PARAM_SEARCH_CONTAIN_GLOBAL, "Y");// 包含全球购
}
private void addNewParams(Map<String, String> paramMap) {
addDefaultParamsToParamMap(paramMap);
// 默认30天内假上新
long end = DateUtil.getLastTimeSecond(new Date());
long begin = DateUtil.getFirstTimeSecond(DateUtil.addDay(new Date(), -30));
paramMap.put(SearchRequestParams.PARAM_SEARCH_SHELVETIME, begin + "," + end);
}
private boolean checkZqParam(Map<String, String> paramMap) {
if (StringUtils.isBlank(paramMap.get(SearchRequestParams.PARAM_SEARCH_ISPROMOTION))) {
return false;
}
return true;
}
private boolean checkPoolParam(Map<String, String> paramMap) {
if (StringUtils.isBlank(paramMap.get(SearchRequestParams.PARAM_SEARCH_FILTER_POOLID))) {
return false;
}
return true;
}
private void addDefaultParamsToParamMap(Map<String, String> paramMap) {
paramMap.put(SearchRequestParams.PARAM_SEARCH_PAGEID, paramMap.get("pageId"));// 根据场景划分的页面id
paramMap.put(SearchRequestParams.PARAM_SEARCH_GLOBAL_FILTER_BRAND, "Y");// 页面屏蔽
paramMap.put(SearchRequestParams.PARAM_SEARCH_STATUS, "1");// 上架
paramMap.put(SearchRequestParams.PARAM_SEARCH_STOCKNUM, "1");// 有库存
paramMap.put(SearchRequestParams.PARAM_SEARCH_SHOWSOLDOUT, "1");// 显示延期显示的商品
paramMap.put(SearchRequestParams.PARAM_SEARCH_ISOUTLETS, "2");// 非奥莱
paramMap.put(SearchRequestParams.PARAM_SEARCH_ATTRIBUTE_NOT, "2");// 非赠品
paramMap.put(SearchRequestParams.PARAM_SEARCH_NEEDSMALLSORT, "1");// 品类聚合时带上小分类
paramMap.put(SearchRequestParams.PARAM_SEARCH_AGG_WITH_PARAM_BRAND, "Y");// 聚合时使用参数中自带的参数
// 关键词反转码
String keyword = SearchKeyWordUtils.getParamKeyword(paramMap, SearchRequestParams.PARAM_SEARCH_QUERY);// 转码
if (!StringUtils.isBlank(keyword)) {
paramMap.put(SearchRequestParams.PARAM_SEARCH_QUERY, keyword);
paramMap.put("is_encode", "0");
}
}
private SearchApiResult recallProductList(Map<String, String> paramMap) {
try {
UserRecallRequest userRecallRequest = userRecallRequestBuilder.buildUserRecallRequest(paramMap, 300);
UserRecallResponse userRecallResponse = userRecallCacheBean.queryRecallResult(userRecallRequest, false);
List<RecallSknInfo> recallSknInfos = userRecallResponse.getSknList();
List<Integer> productSknList = recallSknInfos.stream().map(RecallSknInfo::getProductSkn).collect(Collectors.toList());
List<Map<String, Object>> productInfoList = sknReturnInfoCacheBean.queryProductListBySkn(productSknList, productSknList.size());
Map<Integer, RecallSknInfo> sknRecallTypeMap = CollectionUtils.toMap(recallSknInfos, (Transfer<RecallSknInfo, Integer>)(recallSknInfo) -> {return recallSknInfo.getProductSkn();});
List<Map<String, Object>> briefProductInfoList = new ArrayList<>();
for (Map<String, Object> productInfo : productInfoList) {
Map<String, Object> briefProductInfo = new HashMap<>();
briefProductInfo.put("product_skn", productInfo.get("product_skn"));
briefProductInfo.put("brand_name", productInfo.get("brand_name"));
briefProductInfo.put("small_sort_name", productInfo.get("small_sort_name"));
briefProductInfo.put("middle_sort_name", productInfo.get("middle_sort_name"));
briefProductInfo.put("product_name", productInfo.get("product_name"));
int productSkn = MapUtils.getIntValue(productInfo, "product_skn", 0);
RecallSknInfo recallSknInfo = sknRecallTypeMap.get(productSkn);
if (recallSknInfo == null || recallSknInfo.getRecallType() == null) {
briefProductInfo.put("recall_type", StrategyEnum.DEFAULT_HEAT_VALUE.name());
} else {
briefProductInfo.put("recall_type", recallSknInfo.getRecallType());
}
briefProductInfoList.add(briefProductInfo);
}
//4、构造返回结果
JSONObject dataMap = new JSONObject();
dataMap.put("product_list", briefProductInfoList);
return new SearchApiResult().setData(dataMap);
} catch (Exception e){
LOGGER.error(e.getMessage(), e);
return new SearchApiResult().setData(null).setCode(500).setMessage("Exception");
}
}
}
... ...
... ... @@ -84,4 +84,16 @@ public class SearchExplainerController {
return map;
}
@RequestMapping(value = "/tools/multiFieldAnalyzeList")
@ResponseBody
public Map<String, Object> multiFieldAnalyzeList(HttpServletRequest request) {
Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request);
try {
return searchExplainerService.multiFieldAnalyzeList(paramMap);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
return errorResult(t.getMessage());
}
}
}
... ...
... ... @@ -80,13 +80,6 @@ public class SearchDynamicConfigService {
}
/**
* 商品详情页-店铺外推荐同品类的比例
*/
public int getSearchLikeNotInShopSameSortPercent() {
return configReader.getInt("search.searchlike.not_in_shop.same_sort_percent", 60);
}
/**
* 个性化限流配置
*/
public PersionalRateLimitConfig getPersionalRateLimitConfig(String rateLimitName, PersionalRateLimit persionalRateLimit) {
... ... @@ -129,12 +122,6 @@ public class SearchDynamicConfigService {
return configReader.getBoolean("search.persional.newstrategy.first_page_cache.open", false);
}
/**
* 新的的召回策略-最多预测的品牌品类
*/
public int directTrainIndexInterval() {
return configReader.getInt("search.persional.newstrategy.directtrain.index.interval", 4);
}
/**
* 相关推荐否使用个性化-【双11关闭】
... ... @@ -144,13 +131,6 @@ public class SearchDynamicConfigService {
}
/**
* 找相似是否使用bigdatasimilarskn
*/
public boolean isSearchLikeSimilarSknOpen() {
return configReader.getBoolean("search.searchlike.similarskn", true);
}
/**
* 超时日志是否打开
*/
public boolean isControllerCostLogOpen() {
... ... @@ -158,13 +138,6 @@ public class SearchDynamicConfigService {
}
/**
* 推荐品牌是否要使用大数据的推荐
*/
public boolean isRecommendBrandUserPersonalOpen() {
return configReader.getBoolean("search.recommendbrand.userPersonal.open", false);
}
/**
* A策略是否打开
*/
public boolean isAStrategyOpen() {
... ... @@ -178,7 +151,6 @@ public class SearchDynamicConfigService {
return configReader.getBoolean("search.persional.abTest.b.strategy.open", true);
}
/**
* 全球购查新索引
*/
... ...
package com.yoho.search.service.base.index;
import com.yoho.search.base.utils.ISearchConstants;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.model.SearchResult;
import com.yoho.search.dal.model.SimilarSkn;
import com.yoho.search.service.base.SearchCommonService;
import org.apache.commons.beanutils.BeanUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
@Service
public class BigdataSimilarSknIndexBaseService {
private static final Logger logger = LoggerFactory.getLogger(BigdataSimilarSknIndexBaseService.class);
@Autowired
private SearchCommonService searchCommonService;
public SimilarSkn querySimilarSkn(String productSkn) {
try {
SearchParam searchParam = new SearchParam();
BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
boolFilter.must(QueryBuilders.termsQuery(ProductIndexEsField.productSkn, productSkn));
searchParam.setFiter(boolFilter);
searchParam.setAggregationBuilders(null);
searchParam.setSize(1);
String productIndexName = ISearchConstants.INDEX_NAME_BIGDATA_SIMILAR_SKN_INDEX;
SearchResult searchResult = searchCommonService.doSearch(productIndexName, searchParam);
if (searchResult == null) {
return null;
}
List<Map<String, Object>> similarSknResults = searchResult.getResultList();
if (CollectionUtils.isEmpty(similarSknResults) || similarSknResults.get(0) == null) {
return null;
}
Map<String, Object> result = similarSknResults.get(0);
SimilarSkn similarSkn = new SimilarSkn();
BeanUtils.populate(similarSkn, result);
return similarSkn;
}catch (Exception e){
logger.error(e.getMessage(),e);
return null;
}
}
}
... ...
package com.yoho.search.service.helper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.*;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.SearchHit;
... ... @@ -211,4 +206,53 @@ public class AggCommonHelper {
return results;
}
/**
* 从聚合结果中获取参数,仅支持二层聚合
*
* @param aggregationMap
* @param firstAggName
* @param secondAggName
* @return
*/
public static List<AggKeyCountTwoLevel> getAggKeyCountTwoLevelList(Map<String, Aggregation> aggregationMap, String firstAggName, String secondAggName) {
List<AggKeyCountTwoLevel> result = new ArrayList<>();
if (!aggregationMap.containsKey(firstAggName)) {
return result;
}
MultiBucketsAggregation firstAggregation = (MultiBucketsAggregation) aggregationMap.get(firstAggName);
Iterator<? extends MultiBucketsAggregation.Bucket> firstAggregationIterator = firstAggregation.getBuckets().iterator();
while (firstAggregationIterator.hasNext()) {
MultiBucketsAggregation.Bucket firstAggregationBucket = firstAggregationIterator.next();
Map<String, Aggregation> secondAggregationMap = firstAggregationBucket.getAggregations().asMap();
if (secondAggregationMap == null || !secondAggregationMap.containsKey(secondAggName)) {
continue;
}
AggKeyCount firstKeyCount = genAggKeyCount(firstAggregationBucket);
List<AggKeyCount> secondKeyCountList = genAggKeyCountList((MultiBucketsAggregation) secondAggregationMap.get(secondAggName));
result.add(new AggKeyCountTwoLevel(firstKeyCount,secondKeyCountList));
}
return result;
}
private static List<AggKeyCount> genAggKeyCountList(MultiBucketsAggregation aggregation) {
List<AggKeyCount> results = new ArrayList<>();
if (aggregation == null) {
return results;
}
Iterator<? extends MultiBucketsAggregation.Bucket> bucketsIterator = aggregation.getBuckets().iterator();
while (bucketsIterator.hasNext()) {
MultiBucketsAggregation.Bucket bucket = bucketsIterator.next();
results.add(genAggKeyCount(bucket));
}
return results;
}
private static AggKeyCount genAggKeyCount(MultiBucketsAggregation.Bucket bucket){
Integer value = Integer.valueOf(bucket.getKeyAsString());
long count = bucket.getDocCount();
return new AggKeyCount(value,count);
}
}
... ...
package com.yoho.search.service.helper;
public class AggKeyCount {
private Integer key;
private Long count;
public AggKeyCount(Integer key, long count) {
this.key = key;
this.count = count;
}
public Integer getKey() {
return key;
}
public void setKey(Integer key) {
this.key = key;
}
public Long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
}
... ...
package com.yoho.search.service.helper;
import java.util.List;
public class AggKeyCountTwoLevel {
private AggKeyCount firstAggKeyCount;
private List<AggKeyCount> secondAggKeyCountList;
public AggKeyCountTwoLevel(AggKeyCount firstAggKeyCount, List<AggKeyCount> secondAggKeyCountList) {
this.firstAggKeyCount = firstAggKeyCount;
this.secondAggKeyCountList = secondAggKeyCountList;
}
public AggKeyCount getFirstAggKeyCount() {
return firstAggKeyCount;
}
public void setFirstAggKeyCount(AggKeyCount firstAggKeyCount) {
this.firstAggKeyCount = firstAggKeyCount;
}
public List<AggKeyCount> getSecondAggKeyCountList() {
return secondAggKeyCountList;
}
public void setSecondAggKeyCountList(List<AggKeyCount> secondAggKeyCountList) {
this.secondAggKeyCountList = secondAggKeyCountList;
}
}
... ...
package com.yoho.search.service.scene.activity;
import com.yoho.search.base.helper.RnnVectorCalculator;
import com.yoho.search.base.helper.Word2VectorCalculator;
import com.yoho.search.base.utils.CollectionUtils;
import com.yoho.search.core.personalized.models.UserPersonalFactorRspNew;
import com.yoho.search.models.SearchApiResult;
import com.yoho.search.recall.beans.persional.UserPersionalFactorComponent;
import com.yoho.search.recall.beans.vector.BrandVectorCacheBean;
import com.yoho.search.service.base.SearchRequestParams;
import com.yoho.search.service.base.index.ShopsIndexBaseService;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class ActivityService{
@Autowired
private ActivityShopBrandListService activityShopBrandListService;
@Autowired
private UserPersionalFactorComponent userComponent;
@Autowired
private BrandVectorCacheBean brandVectorCacheBean;
@Autowired
private ShopsIndexBaseService shopsIndexBaseService;
public SearchApiResult recommendShop(Map<String, String> paramMap) {
//1、查询页面上的所有品牌*店铺
ActivityShopBrandList activityShopBrandList = activityShopBrandListService.queryActivityShopBrandList(paramMap);
if (activityShopBrandList == null || activityShopBrandList.getShopBrandList() == null || activityShopBrandList.getShopBrandList().isEmpty()) {
return this.buildShopList(200, "success", new ArrayList<>());
}
//2、查询用户的品牌向量
int uid = MapUtils.getIntValue(paramMap, "uid", 0);
String udid = MapUtils.getString(paramMap, "udid", "");
UserPersonalFactorRspNew userPersonalFactorRspNew = userComponent.queryUserPersionalFactor(uid, udid, null);
//3、按相关性聚合品牌
List<Integer> shopIds = this.buildShopIds(activityShopBrandList,userPersonalFactorRspNew);
//4、查询店铺
List<Map<String, Object>> shopsList = shopsIndexBaseService.getShopListByIdsWithSortAndStatus(shopIds);
//5、数量截取
int viewNum = MapUtils.getIntValue(paramMap, SearchRequestParams.PARAM_SEARCH_VIEWNUM, 10);
shopsList = CollectionUtils.safeSubList(shopsList,0,viewNum);
//6、构造返回结果
return this.buildShopList(200, "success",shopsList);
}
private List<Integer> buildShopIds(ActivityShopBrandList activityShopBrandList, UserPersonalFactorRspNew userPersonalFactorRspNew){
//1、计算店铺得分
List<Double> brandVector = userPersonalFactorRspNew.getBrandVector();
List<Double> brandVectorW2v = userPersonalFactorRspNew.getBrandVectorW2v();
List<ActivityShopBrandList.ShopBrand> shopBrandList = activityShopBrandList.getShopBrandList();
if(brandVectorW2v!=null && !brandVectorW2v.isEmpty()){
this.calScore(shopBrandList,brandVector,false);
}else{
this.calScore(shopBrandList,brandVectorW2v,true);
}
//2、按得分排序
Collections.sort(shopBrandList, (o1, o2) -> (o2.score).compareTo(o1.score));
//3、截取shopId
List<Integer> shopIds = new ArrayList<>();
for (ActivityShopBrandList.ShopBrand shopBrand: shopBrandList) {
if(shopIds.size()>=50){
break;
}
shopIds.add(shopBrand.getShopId());
}
return shopIds;
}
/**
* 按向量计算品牌得分
*/
private void calScore(List<ActivityShopBrandList.ShopBrand> shopBrandList , List<Double> userBrandVector, boolean isRnn) {
double userBrandVectorNorm = Word2VectorCalculator.getVectorListNorm(userBrandVector);
for (ActivityShopBrandList.ShopBrand shopBrand: shopBrandList) {
Integer brandId = shopBrand.getBrandId();
List<Double> brandVector = brandVectorCacheBean.queryBrandVector(brandId, isRnn);
if (brandVector == null || brandVector.isEmpty()) {
shopBrand.score = 0d;
continue;
}
double score;
if (isRnn) {
score = RnnVectorCalculator.calScore(userBrandVector, brandVector);
} else {
score = Word2VectorCalculator.calScore(userBrandVector, userBrandVectorNorm, brandVector);
}
shopBrand.score = score;
}
}
private SearchApiResult buildShopList(int code, String message, List<Map<String, Object>> shopList) {
SearchApiResult result = new SearchApiResult().setCode(code).setMessage(message);
Map<String, Object> data = new HashMap<>();
data.put("rec_shop_list", shopList);
result.setData(data);
return result;
}
}
... ...
package com.yoho.search.service.scene.activity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class ActivityShopBrandList implements Serializable {
private static final long serialVersionUID = 2995429365708206688L;
private List<ShopBrand> shopBrandList;
public ActivityShopBrandList() {
this.shopBrandList = new ArrayList<>();
}
public ActivityShopBrandList(List<ShopBrand> shopsListBrand) {
this.shopBrandList = shopsListBrand;
}
public List<ShopBrand> getShopBrandList() {
return shopBrandList;
}
public void setShopBrandList(List<ShopBrand> shopsListBrand) {
this.shopBrandList = shopsListBrand;
}
public static class ShopBrand implements Serializable {
private static final long serialVersionUID = 199617092092734433L;
private Integer shopId;
private Integer brandId;
public Double score;
public ShopBrand() {
}
public ShopBrand(Integer shopId,Integer brandId) {
this.shopId = shopId;
this.brandId = brandId;
}
public Integer getBrandId() {
return brandId;
}
public void setBrandId(Integer brandId) {
this.brandId = brandId;
}
public Integer getShopId() {
return shopId;
}
public void setShopId(Integer shopId) {
this.shopId = shopId;
}
}
}
... ...
package com.yoho.search.service.scene.activity;
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
import com.yoho.search.base.utils.ISearchConstants;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.model.SearchResult;
import com.yoho.search.recall.beans.builder.UserRecallRequestBuilder;
import com.yoho.search.recall.beans.persional.AbstractPageComponent;
import com.yoho.search.recall.config.CacheTimeConstants;
import com.yoho.search.recall.models.common.ParamQueryFilter;
import com.yoho.search.service.base.SearchCommonService;
import com.yoho.search.service.helper.AggCommonHelper;
import com.yoho.search.service.helper.AggKeyCount;
import com.yoho.search.service.helper.AggKeyCountTwoLevel;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class ActivityShopBrandListService extends AbstractPageComponent<ActivityShopBrandList> {
private static final Logger logger = LoggerFactory.getLogger(ActivityShopBrandListService.class);
@Autowired
private UserRecallRequestBuilder userRecallRequestBuilder;
@Autowired
private SearchCommonService searchCommonService;
/**
* 查询页面上的品牌*店铺元素
*
* @param paramMap
* @return
*/
public ActivityShopBrandList queryActivityShopBrandList(Map<String, String> paramMap) {
try {
ParamQueryFilter paramQueryFilter = userRecallRequestBuilder.buildParamQueryFilter(paramMap);
Object value = super.queryWithCache(paramQueryFilter, ActivityShopBrandList.class);
return value == null ? null : (ActivityShopBrandList) value;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return new ActivityShopBrandList();
}
}
@Override
protected RedisKeyBuilder genRedisKeyBuilder(ParamQueryFilter paramQueryFilter) {
return RedisKeyBuilder.newInstance().appendFixed("YOHOSEARCH:").appendFixed("PAGE_BRAND_SHOPS:").appendVar(paramQueryFilter.getParamMd5Key());
}
@Override
protected int cacheTimeInMinute() {
return CacheTimeConstants.CACHE_60_MINUTE;
}
@Override
protected ActivityShopBrandList doRealQuery(ParamQueryFilter paramQueryFilter) {
//1、构造参数
SearchParam searchParam = new SearchParam();
searchParam.setQuery(paramQueryFilter.getParamQuery());
searchParam.setFiter(paramQueryFilter.getParamFilter());
searchParam.setSize(0);
//2、构造聚合参数
List<AbstractAggregationBuilder<?>> aggregationBuilders = new ArrayList<>();
aggregationBuilders.add(brandShopAggBuilder());//品牌*店铺聚合
searchParam.setAggregationBuilders(aggregationBuilders);
//3、执行查询
SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
//4、构造结果
Map<String, Aggregation> aggregationMap = searchResult.getAggMaps();
return this.genActivityBrandShops(aggregationMap);
}
/**
* 品类+品牌聚合
*
* @return
*/
private TermsAggregationBuilder brandShopAggBuilder() {
TermsAggregationBuilder middleSortAggBuilder = AggregationBuilders.terms("shopAgg").field(ProductIndexEsField.shopId).size(1000);
middleSortAggBuilder.subAggregation(AggregationBuilders.terms("brandAgg").field(ProductIndexEsField.brandId).size(200));
return middleSortAggBuilder;
}
private ActivityShopBrandList genActivityBrandShops(Map<String, Aggregation> aggregationMap) {
//1、获取二层聚合的结果
List<AggKeyCountTwoLevel> aggKeyCountTwoLevelList = AggCommonHelper.getAggKeyCountTwoLevelList(aggregationMap, "shopAgg", "brandAgg");
//2、使用第一层聚合的总数量排序
//Collections.sort(aggKeyCountTwoLevelList, (o1, o2) -> ((o2.getFirstAggKeyCount().getCount()).compareTo((o1.getFirstAggKeyCount().getCount()))));
List<ActivityShopBrandList.ShopBrand> shopBrandList = new ArrayList<>();
Set<Integer> filterBrandIds = new HashSet<Integer>();
for (AggKeyCountTwoLevel aggKeyCountTwoLevel : aggKeyCountTwoLevelList) {
AggKeyCount first = aggKeyCountTwoLevel.getFirstAggKeyCount();
List<AggKeyCount> secondList = aggKeyCountTwoLevel.getSecondAggKeyCountList();
//3、对第二层聚合结果排序
//Collections.sort(secondList, (o1, o2) -> ((o2.getCount()).compareTo((o1.getCount()))));
Integer shopId = first.getKey();
if (shopId.equals(0)) {
continue;
}
for (AggKeyCount brandAggKeyCount : secondList) {
Integer brandId = brandAggKeyCount.getKey();
if (brandId.equals(0)) {
continue;
}
if (filterBrandIds.contains(brandId)) {
continue;
}
shopBrandList.add(new ActivityShopBrandList.ShopBrand(shopId, brandId));//取商品数最多的一个店铺即可,防止多品店的问题
filterBrandIds.add(brandId);//每个品牌只赋给一个店铺
break;
}
}
return new ActivityShopBrandList(shopBrandList);
}
}
... ...
... ... @@ -5,10 +5,12 @@ import com.yoho.search.base.utils.CollectionUtils;
import com.yoho.search.base.utils.ProductIndexEsField;
import com.yoho.search.base.utils.Transfer;
import com.yoho.search.common.cache.aop.SearchCacheAble;
import com.yoho.search.common.productlist.aop.ProductListWithSknRetention;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.dal.model.SimilarSkn;
import com.yoho.search.models.SearchApiResult;
import com.yoho.search.service.base.SearchRequestParams;
import com.yoho.search.service.base.index.BigdataSimilarSknIndexBaseService;
import com.yoho.search.service.base.index.ProductIndexBaseService;
import com.yoho.search.service.helper.ProductListHelper;
import org.apache.commons.beanutils.BeanUtils;
... ... @@ -34,6 +36,8 @@ public class BigdataSimilarSknService {
private ProductIndexBaseService productIndexBaseService;
@Autowired
private ProductListHelper productListHelper;
@Autowired
private BigdataSimilarSknIndexBaseService bigdataSimilarSknIndexBaseService;
/**
* 获取非重复skn
... ... @@ -51,7 +55,6 @@ public class BigdataSimilarSknService {
return results;
}
@SearchCacheAble(cacheInMinute = 600, cacheName = "SEARCH_LIKE_NOT_IN_SHOP_SIMILAR", includeParams = { "product_skn", "viewNum" })
public SearchApiResult searchLikeSimilarSknNotInShop(Map<String, String> paramMap) {
try {
... ... @@ -68,27 +71,26 @@ public class BigdataSimilarSknService {
return new SearchApiResult().setCode(400).setMessage("SKN不存在");
}
//4、获取similarskn
SimilarSkn similarSkn = new SimilarSkn();
BeanUtils.populate(similarSkn, searchLikeHelper.searchBigdataSimilarSkn(productSkn));
SimilarSkn similarSkn = bigdataSimilarSknIndexBaseService.querySimilarSkn(productSkn);
//5、构造searchParams
List<SearchParam> searchParams = new ArrayList<>();
//5.1)图片[性别+不同店铺]
List<String> diffShopImgSimilarSkns = new ArrayList<>();
if (StringUtils.isNotEmpty(similarSkn.getDiffShopImgSimilarSkns())) {
if (similarSkn != null && StringUtils.isNotEmpty(similarSkn.getDiffShopImgSimilarSkns())) {
diffShopImgSimilarSkns = this.getDistinctSknList(similarSkn.getDiffShopImgSimilarSkns());
}
searchParams.add(this.builderSimilarSknSearchParam(productInfoInEs, diffShopImgSimilarSkns, true));
//5.2)行为[性别+不同店铺+同品类]
List<String> diffShopActionSimilarSkns = new ArrayList<>();
if (StringUtils.isNotEmpty(similarSkn.getDiffShopActionSimilarSkns())) {
if (similarSkn != null && StringUtils.isNotEmpty(similarSkn.getDiffShopActionSimilarSkns())) {
diffShopActionSimilarSkns = this.getDistinctSknList(similarSkn.getDiffShopActionSimilarSkns());
}
searchParams.add(this.builderSimilarSknSearchParam(productInfoInEs, diffShopActionSimilarSkns, true));
//5.3)文字兜底[性别+不同店铺+文字相似性]
searchParams.add(this.builderCharactersSearchParam(productInfoInEs, Arrays.asList(productSkn), pageSize, true));
searchParams.add( searchLikeHelper.builderSearchLikeNotInShopCharactersSearchParam(productInfoInEs, Arrays.asList(productSkn), pageSize,true));
// 6、获取搜索结果
List<List<Map<String, Object>>> queryResults = searchLikeHelper.queryProductLists(searchParams);
... ... @@ -143,12 +145,7 @@ public class BigdataSimilarSknService {
if(sortedProductSkns==null || sortedProductSkns.isEmpty() || size<=0){
return results;
}
Map<String, Map<String, Object>> productMap = CollectionUtils.toMap(esProductList, new Transfer<Map<String,Object>, String>() {
@Override
public String transfer (Map<String,Object> product){
return MapUtils.getString(product,ProductIndexEsField.productSkn,"");
};
});
Map<String, Map<String, Object>> productMap = CollectionUtils.toMap(esProductList,(product)->MapUtils.getString(product,ProductIndexEsField.productSkn,""));
for (String productSkn : sortedProductSkns){
if(productMap.containsKey(productSkn)){
results.add(new HashMap<>(productMap.get(productSkn)));//注意循环引用
... ... @@ -160,10 +157,6 @@ public class BigdataSimilarSknService {
return results;
}
private SearchParam builderCharactersSearchParam(JSONObject productInfoInEs, List<String> notProductSkns, int pageSize, boolean inSameSort) {
return searchLikeHelper.builderSearchLikeNotInShopCharactersSearchParam(productInfoInEs, notProductSkns, pageSize,inSameSort);
}
/**
* 构建SimilarSknSearchParam[考虑productSkns为空的情况]
... ...
... ... @@ -286,36 +286,6 @@ public class SearchLikeHelper {
return results;
}
public Map<String, Object> searchBigdataSimilarSkn(String productSkn) {
SearchParam searchParam = new SearchParam();
BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
boolFilter.must(QueryBuilders.termsQuery(ProductIndexEsField.productSkn, productSkn));
searchParam.setFiter(boolFilter);
searchParam.setAggregationBuilders(null);
searchParam.setSize(1);
String productIndexName = ISearchConstants.INDEX_NAME_BIGDATA_SIMILAR_SKN_INDEX;
SearchResult searchResult = searchCommonService.doSearch(productIndexName, searchParam);
if (searchResult == null) {
return null;
}
List<Map<String, Object>> similarSknResults = searchResult.getResultList();
if (CollectionUtils.isEmpty(similarSknResults) || similarSknResults.get(0) == null) {
return null;
}
return similarSknResults.get(0);
}
/**
* 找相似相关的匹配打分策略
* @param productFeatureFactor
* @return
*/
public QueryBuilder genProductFeatureQueryBuilder(String productFeatureFactor) {
return functionScoreSearchHelper.buildFunctionScoreQueryBuildWithProductFeature(QueryBuilders.matchAllQuery(), productFeatureFactor);
}
/**
* 店铺外按文字找相似
* @param productInfoInEs
... ...
package com.yoho.search.service.scene.searchlike;
import com.alibaba.fastjson.JSONObject;
import com.yoho.search.base.utils.CollectionUtils;
import com.yoho.search.common.cache.aop.SearchCacheAble;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.models.SearchApiResult;
import com.yoho.search.service.base.SearchDynamicConfigService;
import com.yoho.search.service.base.SearchRequestParams;
import com.yoho.search.service.base.index.ProductIndexBaseService;
import com.yoho.search.service.helper.ProductListHelper;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 店铺外找相似
*
* @author gufei.hu
*
*/
@Service
public class SearchLikeNotInShopService{
private static final Logger logger = LoggerFactory.getLogger(SearchLikeNotInShopService.class);
@Autowired
private SearchLikeHelper searchLikeHelper;
@Autowired
private ProductIndexBaseService productIndexBaseService;
@Autowired
private SearchDynamicConfigService searchDynamicConfigService;
@Autowired
private ProductListHelper productListHelper;
/**
* 店铺外推荐
*
* @param paramMap
* @return
*/
@SearchCacheAble(cacheInMinute = 600, cacheName = "SEARCH_LIKE_NOT_IN_SHOP", includeParams = { "product_skn", "viewNum" })
public SearchApiResult searchLikeNotInShop(Map<String, String> paramMap) {
try {
// 1、获取参数
String productSkn = paramMap.get(SearchRequestParams.PARAM_SEARCH_PRODUCT_SKN);
if (StringUtils.isBlank(productSkn)) {
return new SearchApiResult().setCode(400).setMessage("请输入SKN");
}
// 2、检测分页参数【默认30条,最多60条】
int pageSize = searchLikeHelper.getPageSize(paramMap);
// 2、获取当前查询的SKN的基本信息
JSONObject productInfoInEs = searchLikeHelper.getProductInfoInEs(productSkn);
if (productInfoInEs == null) {
return new SearchApiResult().setCode(400).setMessage("SKN不存在");
}
// 3、设置SearchParams
List<SearchParam> searchParams = new ArrayList<SearchParam>();
int sameSortCount = searchDynamicConfigService.getSearchLikeNotInShopSameSortPercent() * pageSize / 100;
searchParams.add(this.builderSearchParam(productInfoInEs, Arrays.asList(productSkn), sameSortCount, true));
searchParams.add(this.builderSearchParam(productInfoInEs, Arrays.asList(productSkn), pageSize - sameSortCount, false));
// 4、获取搜索结果[截取条数]
List<Map<String, Object>> tempProductList = searchLikeHelper.queryProductList(searchParams);
if (tempProductList.size() > pageSize) {
tempProductList = CollectionUtils.safeSubList(tempProductList,0, pageSize);
}
// 5、构造真实返回结果
List<Map<String, Object>> productListResults = new ArrayList<Map<String, Object>>();
if (!tempProductList.isEmpty()) {
productListResults = productListHelper.buildReturnInfoByEsSourceList(tempProductList);
}
JSONObject result = new JSONObject();
result.put("page", 1);
result.put("page_total", 1);
result.put("page_size", pageSize);
result.put("total", productListResults.size());
result.put("product_info", searchLikeHelper.genProductInfoResult(productInfoInEs));
result.put("product_list", productListResults);
// 8、结果加入缓存
return new SearchApiResult().setData(result);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return new SearchApiResult().setData(null).setMessage("searchLikeNotInShop Exception").setCode(500);
}
}
private SearchParam builderSearchParam(JSONObject productInfoInEs, List<String> notProductSkns, int pageSize, boolean inSameSort) {
return searchLikeHelper.builderSearchLikeNotInShopCharactersSearchParam(productInfoInEs, notProductSkns, pageSize,inSameSort);
}
}
... ... @@ -753,4 +753,119 @@ public class SearchExplainerService {
}
}
public Map<String, Object> multiFieldAnalyzeList(Map<String, String> paramMap) throws Exception {
Map<String, Object> map = new LinkedHashMap<>();
String skn = paramMap.get("skn");
if (StringUtils.isEmpty(skn)) {
map.put("code", "400");
map.put("message", "skn is null");
return map;
}
String keyword = paramMap.get("query");
long start = System.currentTimeMillis();
logger.info("Begin to show skn tokens. skn={}, keyword={}.", skn, keyword);
// 1. 获取document
Map<String, Object> document = getDocumentBySkn(skn);
logger.info("Get the document for the product. document: \n{}", document);
// 2. 解析mapping文件获取字段元数据
Map<String, FieldDesc> fieldDescMap = parseMapping();
logger.info("Get the field description by mapping. fieldDescMap: \n{}", fieldDescMap);
// 3. 处理多字段和copy_to的字段并赋值
processMultiFields(fieldDescMap, document);
processCopiedFields(fieldDescMap, document);
logger.info("Set the value of copied field succeeded.");
// 4. 分析每个multi-match的每个字段
List<String> sortedSearchFields = getSortedSearchFields();
logger.info("Get the search fields. fields: {}", sortedSearchFields);
Map<String, FieldDesc> multiMatchFieldMap = new HashMap<>();
sortedSearchFields.forEach(e -> {
if (document.get(e) != null && StringUtils.isNotBlank(document.get(e).toString())) {
multiMatchFieldMap.put(e, fieldDescMap.get(e));
}
});
List<String> multiMatchFieldSearchAnalyzes = multiMatchFieldMap.values().stream().map(e -> e.search_analyzer).distinct().collect(Collectors.toList());
Map<String, List<FieldDesc>> multiMatchFieldAnalyzeFieldsMap = multiMatchFieldMap.values().stream().collect(Collectors.groupingBy(e -> e.analyzer));
List<SearchFieldResult> resultList = getSearchFieldResult(keyword, multiMatchFieldSearchAnalyzes, multiMatchFieldAnalyzeFieldsMap, document);
Collections.sort(resultList);
map.put("code", "200");
map.put("message", "show skn tokens");
map.put("data", resultList);
logger.info("End to show skn tokens. skn={}, keyword={}, cost={}.", skn, keyword, System.currentTimeMillis() - start);
return map;
}
private List<SearchFieldResult> getSearchFieldResult(String keyword, List<String> multiMatchFieldSearchAnalyzes, Map<String, List<FieldDesc>> multiMatchFieldAnalyzeFieldsMap, Map<String, Object> document) throws Exception {
Map<String, List<AnalyzeResponse.AnalyzeToken>> keywordSearchAnalyzesMap = new HashMap<>();
multiMatchFieldSearchAnalyzes.forEach(e -> keywordSearchAnalyzesMap.put(e, getAnalyzerResultWithMultiValues(e, keyword)));
List<SearchFieldResult> searchFieldResultList = new ArrayList<>();
Map<String, FieldWithBoost> localMutilFieldWithBoostMap = localMutilFieldWithBoostList.stream().collect(Collectors.toMap((e -> e.fieldName), p -> p));
multiMatchFieldAnalyzeFieldsMap.entrySet().forEach(e -> {
List<FieldDesc> analyzeFields = e.getValue();
List<String> fieldValueList = analyzeFields.stream().map(f -> document.get(f.field).toString()).collect(Collectors.toList());
List<AnalyzeResponse.AnalyzeToken> analyzeResponse = getAnalyzerResultWithMultiValues(e.getKey(), fieldValueList.toArray(new String[fieldValueList.size()]));
List<List<String>> tokensGroupList = new ArrayList<>();
int position = 0;
int analyzeResponseIndex = 0;
for (int i = 0; i < analyzeFields.size(); i++) {
List<String> tokenList = new ArrayList<>();
tokensGroupList.add(tokenList);
for (int j = analyzeResponseIndex; j < analyzeResponse.size(); j++) {
if (analyzeResponse.get(j).getPosition() - position < 100) {
tokenList.add(analyzeResponse.get(j).getTerm());
position = analyzeResponse.get(j).getPosition();
} else {
analyzeResponseIndex = j;
position = analyzeResponse.get(j).getPosition();
break;
}
}
}
for (int i = 0; i < analyzeFields.size(); i++) {
SearchFieldResult searchFieldResult = new SearchFieldResult();
searchFieldResult.setFieldName(analyzeFields.get(i).field);
searchFieldResult.setBoost(localMutilFieldWithBoostMap.get(analyzeFields.get(i).field).boost);
searchFieldResult.setIndexAnalyzer(analyzeFields.get(i).analyzer);
searchFieldResult.setSearchAnalyzer(analyzeFields.get(i).search_analyzer);
searchFieldResult.setFieldValue(document.get(analyzeFields.get(i).field).toString());
List<String> tokens = tokensGroupList.get(i);
searchFieldResult.setTokens(Arrays.asList(StringUtils.join(tokens, ";")));
List<String> keywordtokens;
if (StringUtils.isBlank(analyzeFields.get(i).search_analyzer)) {
keywordtokens = Arrays.asList(keyword);
} else {
List<AnalyzeResponse.AnalyzeToken> keywordSearchAnalyzes = keywordSearchAnalyzesMap.get(analyzeFields.get(i).search_analyzer);
if (CollectionUtils.isNotEmpty(keywordSearchAnalyzes)) {
keywordtokens = keywordSearchAnalyzes.stream().map(AnalyzeResponse.AnalyzeToken::getTerm).collect(Collectors.toList());
} else {
keywordtokens = Arrays.asList(keyword);
}
}
searchFieldResult.setKeywordtokens(Arrays.asList(StringUtils.join(keywordtokens, ";")));
List<String> matchTokens = keywordtokens.stream().filter(t -> tokens.contains(t)).collect(Collectors.toList());
searchFieldResult.addAllMatchToken(Arrays.asList(StringUtils.join(matchTokens, ";")));
searchFieldResult.setMatchResult(CollectionUtils.isNotEmpty(matchTokens));
searchFieldResultList.add(searchFieldResult);
}
});
return searchFieldResultList;
}
private List<AnalyzeResponse.AnalyzeToken> getAnalyzerResultWithMultiValues(String analyzer, String... value) {
IElasticsearchClient client = esClientMgr.getClient(ISearchConstants.INDEX_NAME_PRODUCT_INDEX);
List<AnalyzeResponse.AnalyzeToken> analyzeResponse = client.getAnalyzeResponseWithMultiValues(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, analyzer, value).getTokens();
return analyzeResponse;
}
}
... ...
... ... @@ -6,7 +6,7 @@ import java.util.List;
/**
* Created by ginozhang on 2017/2/13.
*/
public class SearchFieldResult {
public class SearchFieldResult implements Comparable<SearchFieldResult>{
private String fieldName;
... ... @@ -20,14 +20,25 @@ public class SearchFieldResult {
private List<String> tokens;
private List<String> keywordtokens;
private List<String> matchTokens = new ArrayList<>();
private boolean matchResult;
@Override
public int compareTo(SearchFieldResult o) {
return o.boost - boost;
}
public void addMatchToken(String searchToken) {
matchTokens.add(searchToken);
}
public void addAllMatchToken(List<String> searchTokenList) {
matchTokens.addAll(searchTokenList);
}
public List<String> getMatchTokens() {
return matchTokens;
}
... ... @@ -87,4 +98,12 @@ public class SearchFieldResult {
public void setMatchResult(boolean matchResult) {
this.matchResult = matchResult;
}
public List<String> getKeywordtokens() {
return keywordtokens;
}
public void setKeywordtokens(List<String> keywordtokens) {
this.keywordtokens = keywordtokens;
}
}
... ...
... ... @@ -10,7 +10,7 @@ import java.util.Map;
public interface IProductListWithSupplyService {
/**
* 获取商品列表,支持如果传入skn无效,补充相似skn[使用本地缓存]
* 获取商品列表,支持如果传入skn无效,补充相似skn
*/
public SearchApiResult productListWithSupply(Map<String, String> paramMap);
}
... ...
... ... @@ -30,20 +30,20 @@
</list>
</property>
</bean>
<!-- controller耗时的监听器 -->
<bean id="controllerCostInterceptor" class="com.yoho.search.common.interceptor.ControllerCostInterceptor"/>
<mvc:interceptors>
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 需排除拦截的地址 -->
<mvc:exclude-mapping path="/" />
<mvc:exclude-mapping path="/test" />
<ref bean="controllerCostInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<mvc:interceptors>
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 需排除拦截的地址 -->
<mvc:exclude-mapping path="/" />
<mvc:exclude-mapping path="/test" />
<ref bean="controllerCostInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<mvc:interceptors>
<ref bean="threadProfileInterceptor"/>
</mvc:interceptors>
... ... @@ -62,10 +62,10 @@
<!-- json转换器 application/json -->
<bean id="jsonConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
<property name="features">
... ... @@ -75,7 +75,7 @@
</array>
</property>
</bean>
<!-- 启动SpringMVC的注解功能,完成请求和注解POJO的映射 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="false">
... ... @@ -83,8 +83,8 @@
<ref bean="jsonConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="serviceGlobalExceptionHandler" class="com.yoho.error.exception.handler.ServiceGlobalExceptionHandler" />
<bean id="applicationContextUtil" class="com.yoho.search.base.utils.ApplicationContextUtil"></bean>
</beans>
\ No newline at end of file
... ...