Authored by Gino Zhang

优化重构搜索推荐词

... ... @@ -183,6 +183,14 @@ public class SearchKeyWordService {
}
}
public Double getKeywordCount(String redisKeyTemplate, String queryWord){
try {
return yhNoSyncZSetOperations.score(RedisKeys.getRedisKey4Yesterday(redisKeyTemplate), queryWord);
}catch (Exception e){
return null;
}
}
// 获取【热搜】toplist
public Map<String, Object> getHotkeyWords(int limit, boolean isReturnTodayRecords) {
return this.getListByScoreDesc(RedisKeys.YOHO_SEARCH_KEYWORDS_HOT, limit, isReturnTodayRecords);
... ...
... ... @@ -3,6 +3,7 @@ package com.yoho.search.service.servicenew.impl;
import com.alibaba.fastjson.JSONObject;
import com.yoho.search.base.utils.CharUtils;
import com.yoho.search.base.utils.ISearchConstants;
import com.yoho.search.base.utils.RedisKeys;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.model.SearchResult;
import com.yoho.search.service.service.SearchCacheService;
... ... @@ -85,33 +86,53 @@ public class SearchRecommendServiceImpl implements ISearchRecommendService {
@Override
public JSONObject recommend(SearchApiResult searchResult, Map<String, String> paramMap) {
String queryWord = paramMap.get(SearchRequestParams.PARAM_SEARCH_KEYWORD);
long begin = System.currentTimeMillis();
logger.info("[func=recommend][queryWord={}][begin={}]", queryWord, begin);
try {
Assert.isTrue(StringUtils.isNotEmpty(queryWord), "query keyword cannot be empty.");
Assert.isTrue(searchResult != null && searchResult.getData() != null, "SearchResult is invalid.");
String keywordsToSearch = queryWord;
String spellingCorrentWord = null, dest = null;
// 搜索推荐分两部分
// 1) 第一部分是最常见的情况,包括有结果、根据SKN搜索、关键词未出现在空结果的REDIS ZSet里
if (containsProductInSearchResult(searchResult)) {
// 1) 搜索有结果的 优先从搜索结果聚合出品牌等关键词进行查询
// 1.1) 搜索有结果的 优先从搜索结果聚合出品牌等关键词进行查询
JSONObject dataMap = ((JSONObject) searchResult.getData());
List<Map<String, Object>> productList = (List<Map<String, Object>>) dataMap.get("product_list");
String aggKeywords = aggKeywordsByProductList(productList);
keywordsToSearch = keywordsToSearch + " " + aggKeywords;
} else if (isQuerySkn(queryWord)) {
// 2) 如果是查询SKN 没有查询到的 按照情况3)也无法推荐 所以直接到ES里去获取关键词
// 1.2) 如果是查询SKN 没有查询到的 后续的逻也无法推荐 所以直接到ES里去获取关键词
keywordsToSearch = aggKeywordsBySkns(queryWord);
if (StringUtils.isEmpty(keywordsToSearch)) {
return defaultSuggestRecommendation();
}
}
Double count = searchKeyWordService.getKeywordCount(RedisKeys.YOHO_SEARCH_KEYWORDS_EMPTY, queryWord);
if (count == null || queryWord.length() >= 5) {
// 1.3) 如果该关键词一次都没有出现在空结果列表或者长度大于5 则该词很有可能是可以搜索出结果的 因此优先取suggest去搜索一把 减少后面的查询动作
JSONObject recommendResult = recommendBySuggestIndex(paramMap, keywordsToSearch);
if (recommendResult == null) {
return defaultSuggestRecommendation();
} else if (CollectionUtils.isNotEmpty((List) recommendResult.get("terms_suggestion"))) {
logger.info("[func=recommend][srcQueryWord={}][queryWord={}][cost={}]", queryWord, System.currentTimeMillis() - begin);
return recommendResult;
}
}
// 2) 第二部分是通过Conversion和拼写纠错去获取关键词 由于很多品牌的拼写可能比较相近 因此先走Conversion然后再拼写检查
String spellingCorrentWord = null, dest = null;
if (allowGetingDest(queryWord) && StringUtils.isNotEmpty((dest = getSuggestConversionDestBySource(queryWord)))) {
// 2.1) 爬虫和自定义的Conversion处理
keywordsToSearch = dest;
} else if (allowSpellingCorrent(queryWord) && StringUtils.isNotEmpty((spellingCorrentWord = suggestService.getSpellingCorrectKeyword(queryWord)))) {
// 3) 执行拼写检查 由于在搜索建议的时候会进行拼写检查 所以缓存命中率高
// 2.2) 执行拼写检查 由于在搜索建议的时候会进行拼写检查 所以缓存命中率高
keywordsToSearch = spellingCorrentWord;
} else if (allowGetingDest(queryWord) && StringUtils.isNotEmpty((dest = getSuggestConversionDestBySource(queryWord)))) {
// 4) 爬虫和自定义的Conversion处理
keywordsToSearch = dest;
} else {
// 5) 如果两者都没有 则还是使用原先的用户输入queryWord
// 2.3) 如果两者都没有 则还是使用返回
return defaultSuggestRecommendation();
}
JSONObject recommendResult = recommendBySuggestIndex(paramMap, keywordsToSearch);
... ... @@ -119,6 +140,7 @@ public class SearchRecommendServiceImpl implements ISearchRecommendService {
recommendResult = defaultSuggestRecommendation();
}
logger.info("[func=recommend][queryWord={}][cost={}]", queryWord, System.currentTimeMillis() - begin);
return recommendResult;
} catch (Exception e) {
logger.error("[func=recommend][queryWord=" + queryWord + "]", e);
... ... @@ -128,7 +150,16 @@ public class SearchRecommendServiceImpl implements ISearchRecommendService {
private boolean allowGetingDest(String queryWord) {
// 对于conversion长度要求在[2,10]之间
return queryWord.length() >= 2 && queryWord.length() <= 10;
if (queryWord.length() < 2 || queryWord.length() > 10) {
return false;
}
// 不能有空格、加号、逗号等符号
if (queryWord.contains(" ") || queryWord.contains("+") || queryWord.contains(",") || queryWord.contains("%")) {
return false;
}
return true;
}
private boolean allowSpellingCorrent(String queryWord) {
... ... @@ -158,7 +189,7 @@ public class SearchRecommendServiceImpl implements ISearchRecommendService {
long begin = System.currentTimeMillis();
logger.info("[func=recommendBySuggestIndex][srcQueryWord={}][keywordsToSearch={}][begin={}]", srcQueryWord, keywordsToSearch, begin);
// 1) 如果是查询SKN 则返回空结果去走聚合
// 1) 如果是查询SKN 直接返回 前面已经处理过了
if (StringUtils.isEmpty(keywordsToSearch)) {
return null;
}
... ... @@ -166,7 +197,7 @@ public class SearchRecommendServiceImpl implements ISearchRecommendService {
// 2) 先对keywordsToSearch进行分词
List<String> terms = searchKeyWordService.getAnalyzeTerms(keywordsToSearch, "ik_smart", true);
if (CollectionUtils.isEmpty(terms)) {
return null;
return new JSONObject();
}
Set<String> termSet = terms.stream().collect(Collectors.toSet());
... ... @@ -234,7 +265,8 @@ public class SearchRecommendServiceImpl implements ISearchRecommendService {
List<String> resultTerms = searchResult.getResultList().stream().map(map -> (String) map.get("keyword")).collect(Collectors.toList());
suggestResult.put("terms_suggestion", resultTerms);
searchCacheService.addJSONObjectToCache(indexName, searchParam, suggestResult);
logger.info("[func=recommendBySuggestIndex][srcQueryWord={}][keywordsToSearch={}][cost={}]", srcQueryWord, keywordsToSearch, System.currentTimeMillis() - begin);
logger.info("[func=recommendBySuggestIndex][srcQueryWord={}][keywordsToSearch={}][resultTerms={}][cost={}]",
srcQueryWord, keywordsToSearch, resultTerms, System.currentTimeMillis() - begin);
return suggestResult;
}
... ...