Authored by unknown

找相似优化

... ... @@ -216,6 +216,47 @@ public class SearchLikeHelper {
return false;
}
public void append(StringBuilder stringBuilder, String word) {
if (StringUtils.isNotBlank(word)) {
stringBuilder.append(word).append(' ');
}
}
private StringBuilder genYohoDefaultQueryString(JSONObject productInfoInEs) {
StringBuilder query = new StringBuilder();
this.append(query, productInfoInEs.getString("productName"));
this.append(query, productInfoInEs.getString("standardOnlyNames"));
this.append(query, productInfoInEs.getString("attributeNames"));
this.append(query, productInfoInEs.getString("style"));
this.append(query, productInfoInEs.getString("pattern"));
return query;
}
public String genYohoQueryStringWithBrandName(JSONObject productInfoInEs) {
StringBuilder query = this.genYohoDefaultQueryString(productInfoInEs);
this.append(query, productInfoInEs.getString("brandName"));
return query.toString();
}
public String genYohoQueryStringWithOutBrandName(JSONObject productInfoInEs) {
StringBuilder query = this.genYohoDefaultQueryString(productInfoInEs);
String queryString = query.toString();
String brandName = productInfoInEs.getString("brandName");
if (StringUtils.isNotBlank(brandName)) {
queryString = queryString.replaceAll(brandName, "");
}
return queryString;
}
public String genGlobalQueryString(JSONObject productInfoInEs) {
StringBuilder query = new StringBuilder();
this.append(query, productInfoInEs.getString("productName"));
this.append(query, productInfoInEs.getString("smallSort"));
this.append(query, productInfoInEs.getString("middleSort"));
this.append(query, productInfoInEs.getString("maxSort"));
return query.toString();
}
public List<Map<String, Object>> queryProductList(SearchParam searchParam) {
SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
return searchResult.getResultList();
... ...
... ... @@ -80,7 +80,7 @@ public class SearchLikeInShopService {
pageSize = 30;
}
// 4、设置第一步SearchParam[]
// 4、设置第一步SearchParam
List<SearchParam> searchParams = new ArrayList<SearchParam>();
searchParams.add(this.builderSearchParam(productInfoInEs, Arrays.asList(productSkn), pageSize, true));
searchParams.add(this.builderSearchParam(productInfoInEs, Arrays.asList(productSkn), 10, false));// 防止数量不够,可以用不同品类的补全
... ... @@ -136,17 +136,6 @@ public class SearchLikeInShopService {
return searchParam;
}
private QueryBuilder builderQueryBuilder(JSONObject productInfoInEs, String minimumShouldMatch) {
String productName = productInfoInEs.getString("productName");
String standardOnlyNames = productInfoInEs.getString("standardOnlyNames");
String style = productInfoInEs.getString("style");
String brandName = productInfoInEs.getString("brandName");
String productFeatureFactor = productInfoInEs.getString("productFeatureFactor");
String query = brandName + "," + productName + "," + standardOnlyNames + "," + style;
QueryBuilder queryBuilder = searchLikeHelper.genSearchLikeQueryBuilder(query, minimumShouldMatch, productFeatureFactor);
return queryBuilder;
}
private QueryBuilder builderFilterBuilder(JSONObject productInfoInEs, List<String> notProductSkns, boolean inSmallSort) {
String isGlobalInEs = productInfoInEs.getString("isGlobal");
boolean isGlobal = "Y".equalsIgnoreCase(isGlobalInEs);
... ... @@ -174,5 +163,12 @@ public class SearchLikeInShopService {
}
return boolFilter;
}
private QueryBuilder builderQueryBuilder(JSONObject productInfoInEs, String minimumShouldMatch) {
String queryString = searchLikeHelper.genYohoQueryStringWithBrandName(productInfoInEs);
String productFeatureFactor = productInfoInEs.getString("productFeatureFactor");
QueryBuilder queryBuilder = searchLikeHelper.genSearchLikeQueryBuilder(queryString, minimumShouldMatch, productFeatureFactor);
return queryBuilder;
}
}
... ...
... ... @@ -83,11 +83,11 @@ public class SearchLikeNotInShopService {
if (pageSize > 60 || pageSize <= 0) {
pageSize = 60;
}
// 4、找相似参数
// 4、找相似参数[按比例寻找同品类和不同品类的数量]
List<SearchParam> searchParams = new ArrayList<SearchParam>();
int sameSortCount = searchDynamicConfigService.getSearchLikeNotInShopSameSortPercent()*pageSize / 100 ;
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));
searchParams.add(this.builderSearchParam(productInfoInEs, Arrays.asList(productSkn), pageSize - sameSortCount, false));
// 5、从缓存中获取数据,有则直接返回
String productIndexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
... ... @@ -158,19 +158,15 @@ public class SearchLikeNotInShopService {
boolFilter.must(QueryBuilders.termQuery("smallSortId", smallSortId));
} else {
boolFilter.mustNot(QueryBuilders.termQuery("smallSortId", smallSortId));
boolFilter.mustNot(QueryBuilders.termQuery("smallSort.smallSort_keyword", productInfoInEs.getString("smallSort")));//有些小分类同名,要排除这种情况。
boolFilter.mustNot(QueryBuilders.termQuery("smallSort.smallSort_keyword", productInfoInEs.getString("smallSort")));// 有些小分类同名,要排除这种情况。
}
return boolFilter;
}
private QueryBuilder builderQueryBuilder(JSONObject productInfoInEs, String minimumShouldMatch) {
String productName = productInfoInEs.getString("productName");
String standardOnlyNames = productInfoInEs.getString("standardOnlyNames");
String style = productInfoInEs.getString("style");
String brandName = productInfoInEs.getString("brandName");
String queryString = searchLikeHelper.genYohoQueryStringWithOutBrandName(productInfoInEs);
String productFeatureFactor = productInfoInEs.getString("productFeatureFactor");
String query = brandName + "," + productName + "," + standardOnlyNames + "," + style;
QueryBuilder queryBuilder = searchLikeHelper.genSearchLikeQueryBuilder(query, minimumShouldMatch, productFeatureFactor);
QueryBuilder queryBuilder = searchLikeHelper.genSearchLikeQueryBuilder(queryString, minimumShouldMatch, productFeatureFactor);
return queryBuilder;
}
... ...
... ... @@ -5,36 +5,25 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONArray;
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.SearchCacheFactory;
import com.yoho.search.common.cache.model.SearchCache;
import com.yoho.search.core.es.model.SearchParam;
import com.yoho.search.core.es.model.SearchResult;
import com.yoho.search.models.SearchApiResult;
import com.yoho.search.service.base.SearchCacheService;
import com.yoho.search.service.base.SearchCommonService;
... ... @@ -69,13 +58,13 @@ public class SearchLikeSceneService {
private SearchCache searchLikeSearchCache;
private ExecutorService executorService = Executors.newFixedThreadPool(100);
@PostConstruct
void init() {
searchLikeSearchCache = searchCacheFactory.getSearchLikeSearchCache();
}
private static final int sameBrandCount = 10;// 获取相同品牌的商品数量
/**
* @找相似功能
*/
... ... @@ -98,40 +87,36 @@ public class SearchLikeSceneService {
String isGlobalInEs = productInfoInEs.getString("isGlobal");
boolean isGlobal = "Y".equalsIgnoreCase(isGlobalInEs);
// 4、获取同一个品牌下最多5个商品
int maxCountInBrand = 5;
// 5.并行去取数据
CompletableFuture<JSONArray> inBrandProductListFuture = CompletableFuture.supplyAsync(
() -> this.getProductListInBrand(productInfoInEs, paramMap, pageSize > maxCountInBrand ? maxCountInBrand : pageSize, isGlobal), executorService);
CompletableFuture<JSONArray> notInBrandProductListFuture = CompletableFuture.supplyAsync(() -> this.getProductListNotInBrand(productInfoInEs, pageSize, isGlobal),
executorService);
JSONArray inBrandProductList = inBrandProductListFuture.get();
JSONArray notInBrandProductList = notInBrandProductListFuture.get();
// 4、设置第一步SearchParam
List<SearchParam> searchParams = new ArrayList<SearchParam>();
searchParams.add(this.buildSearchParam(productInfoInEs, sameBrandCount, true, isGlobal));
searchParams.add(this.buildSearchParam(productInfoInEs, pageSize, false, isGlobal));
// 6、构造返回结果
Map<String, Object> dataMap = new HashMap<String, Object>();
JSONArray productList = new JSONArray();
productList.addAll(inBrandProductList);
productList.addAll(notInBrandProductList);
// 5、从缓存中获取数据,有则直接返回
String productIndexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(searchLikeSearchCache, productIndexName, searchParams);
if (cacheObject != null) {
return new SearchApiResult().setData(cacheObject);
}
// 7、返回个数一共pageSize个
if (productList.size() > pageSize) {
List<Object> tempList = productList.subList(0, pageSize);
if (CollectionUtils.isNotEmpty(tempList)) {
productList = new JSONArray();
for (Object object : tempList) {
productList.add(object);
}
}
// 6、获取搜索结果[并截取条数]
List<Map<String, Object>> tempProductList = searchLikeHelper.queryProductList(searchParams);
if (tempProductList.size() > pageSize) {
tempProductList = tempProductList.subList(0, pageSize);
}
// 7、构造真实返回结果
List<Map<String, Object>> productListResults = new ArrayList<Map<String, Object>>();
if (!tempProductList.isEmpty()) {
productListResults = productIndexBaseService.getProductListWithPricePlan(tempProductList);
}
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("page", 1);
dataMap.put("page_total", 1);
dataMap.put("page_size", pageSize);
dataMap.put("total", productList.size());
dataMap.put("total", productListResults.size());
dataMap.put("product_info", searchLikeHelper.genProductInfoResult(productInfoInEs));
dataMap.put("product_list", productList);
dataMap.put("product_list", productListResults);
return new SearchApiResult().setData(dataMap);
} catch (Exception e) {
return new SearchApiResult().setData(null).setMessage("searchLike Exception").setCode(500);
... ... @@ -139,17 +124,23 @@ public class SearchLikeSceneService {
}
/**
* 找出该品牌下的最多X个相似商品
* 构造SearchParam
*
* @param productInfoInEs
* @param limitCount
* @param isGlobal
* @return
*/
private JSONArray getProductListInBrand(JSONObject productInfoInEs, Map<String, String> paramMap, int limitCount, boolean isGlobal) {
private SearchParam buildSearchParam(JSONObject productInfoInEs, int limitCount, boolean inBrand, boolean isGlobal) {
SearchParam searchParam = new SearchParam();
// 1、构建Query
QueryBuilder queryBuilder = this.genQueryBuilder(productInfoInEs, true, isGlobal);
QueryBuilder queryBuilder = this.genQueryBuilder(productInfoInEs, inBrand, isGlobal);
searchParam.setQuery(queryBuilder);
// 2、设置过滤条件
List<String> notProductSkns = Arrays.asList(paramMap.get(SearchRequestParams.PARAM_SEARCH_PRODUCT_SKN));
BoolQueryBuilder booleanQueryBuilder = this.genSearchLikeFilterBuilder(productInfoInEs, notProductSkns, true, isGlobal);
List<String> notProductSkns = Arrays.asList(productInfoInEs.getString(ProductIndexEsField.productSkn));
BoolQueryBuilder booleanQueryBuilder = this.genSearchLikeFilterBuilder(productInfoInEs, notProductSkns, inBrand, isGlobal);
searchParam.setFiter(booleanQueryBuilder);
// 3、设置排序规则[按打分排序]
... ... @@ -159,111 +150,21 @@ public class SearchLikeSceneService {
// 4、设置分页参数
searchParam.setOffset(0);
searchParam.setSize(limitCount);
// 5、从缓存中获取数据,有则直接返回
String productIndexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
JSONArray productJSONArray = searchCacheService.getJSONArrayFromCache(searchLikeSearchCache, productIndexName, searchParam);
if (productJSONArray != null) {
return productJSONArray;
}
// 6、执行搜索
SearchResult searchResult = searchCommonService.doSearch(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
if (searchResult == null) {
return new JSONArray();
}
// 7、构造返回结果并加入缓存
List<Map<String, Object>> productList = productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList());
productJSONArray = searchLikeHelper.listToJsonArray(productList);
searchCacheService.addJSONArrayToCache(searchLikeSearchCache, productIndexName, searchParam, productJSONArray);
return productJSONArray;
// 5、设置不包含的字段
List<String> excludeFields = productIndexBaseService.getProductIndexExcludeFields();
searchParam.setExcludeFields(excludeFields);
return searchParam;
}
/**
* 找出不同品牌下的余下商品数量的相似商品
* 构造filter
*
* @param productInfoInEs
* @param notProductSkns
* @param isInBrand
* @param isGlobal
* @return
*/
private JSONArray getProductListNotInBrand(JSONObject productInfoInEs, int limit, boolean isGlobal) {
if (limit <= 0) {
return new JSONArray();
}
SearchParam searchParam = new SearchParam();
// 1、构建Query
QueryBuilder queryBuilder = this.genQueryBuilder(productInfoInEs, false, isGlobal);
searchParam.setQuery(queryBuilder);
// 2、设置过滤条件
BoolQueryBuilder booleanQueryBuilder = this.genSearchLikeFilterBuilder(productInfoInEs, null, false, isGlobal);
searchParam.setFiter(booleanQueryBuilder);
// 3、设置聚合条件
final String firstAggName = "firstAgg";
String sortField = "_score";
SortOrder sortOrder = SortOrder.DESC;
List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>();
// 3.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】
TermsBuilder brandAggregation = AggregationBuilders.terms(firstAggName).field("brandId").order(Terms.Order.aggregation("sort", false)).size(200 + limit);
// 3.2)添加子聚合:取得分最大的值
brandAggregation.subAggregation(AggregationBuilders.max("sort").field(sortField));
// 3.3)添加孙聚合:取打分最高的一个product
brandAggregation.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(sortField).order(sortOrder)).setSize(2));
list.add(brandAggregation);
searchParam.setAggregationBuilders(list);
// 4、设置分页参数
searchParam.setOffset(0);
searchParam.setSize(0);
// 5、先从缓存中获取,如果能取到,则直接返回
JSONArray productJSONArray = searchCacheService.getJSONArrayFromCache(searchLikeSearchCache, ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
if (productJSONArray != null) {
return productJSONArray;
}
// 6、执行搜索,并构造返回结果
final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
if (searchResult == null || searchResult.getAggMaps() == null) {
return new JSONArray();
}
Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
if (!aggMaps.containsKey(firstAggName)) {
return new JSONArray();
}
List<Map<String, Object>> productList = aggProductListHelper.getProductListFromAggregation(((MultiBucketsAggregation) aggMaps.get(firstAggName)), limit, sortField,
sortOrder);
productJSONArray = searchLikeHelper.listToJsonArray(productList);
searchCacheService.addJSONArrayToCache(searchLikeSearchCache, indexName, searchParam, productJSONArray);
return productJSONArray;
}
private QueryBuilder genQueryBuilder(JSONObject productInfoInEs, boolean isInBrand, boolean isGlobal) {
StringBuilder query = new StringBuilder();
// 1、如果是全球购,则直接用商品名称+品类名称去查
if (isGlobal) {
this.append(query, productInfoInEs.getString("productName"));
this.append(query, productInfoInEs.getString("smallSort"));
this.append(query, productInfoInEs.getString("middleSort"));
this.append(query, productInfoInEs.getString("maxSort"));
return searchLikeHelper.genSearchLikeQueryBuilder(query.toString(), "25%", null);
}
// 2、设置有货的查询的值
this.append(query, productInfoInEs.getString("productName"));
this.append(query, productInfoInEs.getString("style"));
this.append(query, productInfoInEs.getString("pattern"));
this.append(query, productInfoInEs.getString("attributeNames"));
this.append(query, productInfoInEs.getString("standardOnlyNames"));
String queryString = query.toString();
String brandName = productInfoInEs.getString("brandName");
if (StringUtils.isNotBlank(brandName)) {
if (isInBrand) {
queryString = queryString + " " + brandName;
} else {
queryString = queryString.replaceAll(brandName, "");
}
}
// 3、生成QueryBuilder
return searchLikeHelper.genSearchLikeQueryBuilder(queryString, isInBrand ? "40%" : "30%", productInfoInEs.getString("productFeatureFactor"));
}
private BoolQueryBuilder genSearchLikeFilterBuilder(JSONObject productInfoInEs, List<String> notProductSkns, boolean isInBrand, boolean isGlobal) {
BoolQueryBuilder boolFilter = searchLikeHelper.genDefaultSearchLikeFilter(notProductSkns, isInBrand ? isGlobal : false);// 前几个是全球购,后面找有货的就好
// 1)设置此SKN相关的过滤条件
... ... @@ -296,9 +197,28 @@ public class SearchLikeSceneService {
return boolFilter;
}
private void append(StringBuilder stringBuilder, String word) {
if (StringUtils.isNotBlank(word)) {
stringBuilder.append(word).append(' ');
/**
* 构造query
*
* @param productInfoInEs
* @param notProductSkns
* @param isInBrand
* @param isGlobal
* @return
*/
private QueryBuilder genQueryBuilder(JSONObject productInfoInEs, boolean isInBrand, boolean isGlobal) {
// 1、如果是全球购,则直接用商品名称+品类名称去查
if (isGlobal) {
String queryString = searchLikeHelper.genGlobalQueryString(productInfoInEs);
return searchLikeHelper.genSearchLikeQueryBuilder(queryString, "25%", null);
}
// 2、设置有货的查询的值
if (isInBrand) {
String queryString = searchLikeHelper.genYohoQueryStringWithBrandName(productInfoInEs);
return searchLikeHelper.genSearchLikeQueryBuilder(queryString, "40%", productInfoInEs.getString("productFeatureFactor"));
} else {
String queryString = searchLikeHelper.genYohoQueryStringWithOutBrandName(productInfoInEs);
return searchLikeHelper.genSearchLikeQueryBuilder(queryString, "30%", productInfoInEs.getString("productFeatureFactor"));
}
}
... ...