|
|
package com.yoho.search.service.servicenew.impl;
|
|
|
|
|
|
import java.io.UnsupportedEncodingException;
|
|
|
import java.net.URLDecoder;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
|
import java.util.Comparator;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.Iterator;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
|
|
|
import org.apache.commons.lang.StringUtils;
|
|
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
|
|
import org.elasticsearch.index.query.MatchQueryBuilder;
|
|
|
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
|
|
|
import org.elasticsearch.index.query.QueryBuilders;
|
|
|
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
|
|
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
|
|
|
import org.elasticsearch.search.SearchHit;
|
|
|
import org.elasticsearch.search.SearchHits;
|
|
|
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.MultiBucketsAggregation.Bucket;
|
|
|
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
|
|
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
|
|
|
import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
|
|
|
import org.elasticsearch.search.sort.SortBuilders;
|
|
|
import org.elasticsearch.search.sort.SortOrder;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
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.core.es.model.SearchParam;
|
|
|
import com.yoho.search.core.es.model.SearchResult;
|
|
|
import com.yoho.search.service.service.SearchCacheService;
|
|
|
import com.yoho.search.service.service.SearchCommonService;
|
|
|
import com.yoho.search.service.service.base.BrandIndexBaseService;
|
|
|
import com.yoho.search.service.service.base.ShopsIndexBaseService;
|
|
|
import com.yoho.search.service.service.helper.SearchCommonHelper;
|
|
|
import com.yoho.search.service.service.helper.SearchServiceHelper;
|
|
|
import com.yoho.search.service.servicenew.IShopListService;
|
|
|
import com.yoho.search.service.utils.SearchRequestParams;
|
|
|
import com.yoho.search.service.vo.SearchApiResult;
|
|
|
import com.yoho.search.service.vo.SearchSort;
|
|
|
|
|
|
@Service
|
|
|
public class ShopListServiceImpl implements IShopListService {
|
|
|
|
|
|
@Autowired
|
|
|
private SearchCacheService searchCacheService;
|
|
|
@Autowired
|
|
|
private SearchCommonService searchCommonService;
|
|
|
@Autowired
|
|
|
private SearchServiceHelper searchServiceHelper;
|
|
|
@Autowired
|
|
|
private ShopsIndexBaseService shopsIndexBaseService;
|
|
|
@Autowired
|
|
|
private BrandIndexBaseService brandIndexBaseService;
|
|
|
@Autowired
|
|
|
private SearchCommonHelper searchCommonHelper;
|
|
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(ShopsServiceImpl.class);
|
|
|
|
|
|
private String getLegalKeyWord(Map<String, String> paramMap) {
|
|
|
String keyword = paramMap.get(SearchRequestParams.PARAM_SEARCH_SHOPS_KEYWORD);
|
|
|
if (StringUtils.isBlank(keyword)) {
|
|
|
return null;
|
|
|
}
|
|
|
if (keyword.contains("%")) {
|
|
|
keyword.replace("%", "percent");// 特殊处理
|
|
|
}
|
|
|
// 编码转换
|
|
|
String is_encode = paramMap.get("is_encode");
|
|
|
if (StringUtils.isNotBlank(is_encode) && is_encode.equals("1")) {
|
|
|
try {
|
|
|
keyword = URLDecoder.decode(keyword, "UTF-8");
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
logger.warn(e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
return keyword;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public SearchApiResult searchShopList(Map<String, String> paramMap) {
|
|
|
logger.info("[func=searchShops][param={}][begin={}]", paramMap.toString(), System.currentTimeMillis());
|
|
|
// 1、获取搜索店铺的关键词
|
|
|
String keyword = this.getLegalKeyWord(paramMap);
|
|
|
if (StringUtils.isBlank(keyword)) {
|
|
|
return new SearchApiResult().setCode(400).setMessage("请传keyword");
|
|
|
}
|
|
|
SearchParam searchParam = new SearchParam();
|
|
|
searchParam.setSize(0);
|
|
|
|
|
|
// 2、构建query
|
|
|
MultiMatchQueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(keyword);
|
|
|
queryBuilder.operator(MatchQueryBuilder.Operator.OR);
|
|
|
StringBuilder searchField = new StringBuilder();
|
|
|
searchField.append("brandName.brandName_lowercase^4000,brandName^900").append(",");
|
|
|
searchField.append("shopName.shopName_lowercase^4000,shopName^900").append(",");
|
|
|
searchField.append("brandNameCn^850").append(",").append("brandNameCn.brandNameCn_pinyin^850").append(",");
|
|
|
searchField.append("brandNameEn^800");
|
|
|
String[] fields = searchField.toString().split(",");
|
|
|
for (String field : fields) {
|
|
|
String[] fieldBoost = field.split("\\^");
|
|
|
if (fieldBoost.length == 2) {
|
|
|
queryBuilder.field(fieldBoost[0], Float.parseFloat(fieldBoost[1]));
|
|
|
} else if (fieldBoost.length == 1) {
|
|
|
queryBuilder.field(fieldBoost[0]);
|
|
|
}
|
|
|
}
|
|
|
queryBuilder.minimumShouldMatch("95%");
|
|
|
// 2.1 全球购得分减半
|
|
|
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder);
|
|
|
functionScoreQueryBuilder.add(QueryBuilders.termQuery("isGlobal", "Y"), ScoreFunctionBuilders.weightFactorFunction(0.5f));
|
|
|
searchParam.setQuery(functionScoreQueryBuilder);
|
|
|
|
|
|
// 3、构建filter
|
|
|
// 3.1默认条件
|
|
|
BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
|
|
|
boolFilter.must(QueryBuilders.termQuery("status", 1));
|
|
|
boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(1));
|
|
|
boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));
|
|
|
boolFilter.must(QueryBuilders.termQuery("attribute", 1));
|
|
|
boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));
|
|
|
|
|
|
// 3.2店铺过滤器
|
|
|
BoolQueryBuilder shopFilter = QueryBuilders.boolQuery();
|
|
|
// 3.2.1 全球购店铺过滤器
|
|
|
BoolQueryBuilder globalShopFilter = QueryBuilders.boolQuery();
|
|
|
globalShopFilter.must(QueryBuilders.termsQuery("isGlobal", "Y"));
|
|
|
globalShopFilter.must(QueryBuilders.termQuery("shopId", "0"));
|
|
|
// 3.2.2 有货店铺过滤器
|
|
|
BoolQueryBuilder yohoShopFilter = QueryBuilders.boolQuery();
|
|
|
yohoShopFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));
|
|
|
yohoShopFilter.must(QueryBuilders.rangeQuery("shopId").gt(0));
|
|
|
// 判断是否需要包含全球购
|
|
|
if (!searchCommonHelper.containGlobal(paramMap)) {
|
|
|
shopFilter.must(yohoShopFilter);
|
|
|
} else {
|
|
|
shopFilter.should(yohoShopFilter);
|
|
|
shopFilter.should(globalShopFilter);
|
|
|
}
|
|
|
boolFilter.must(shopFilter);
|
|
|
searchParam.setFiter(boolFilter);
|
|
|
|
|
|
// 4、构建聚合条件
|
|
|
final String firstAggName = "firstAgg";
|
|
|
SearchSort aggSort = new SearchSort("_score", SortOrder.DESC);
|
|
|
List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>();
|
|
|
// 2.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】
|
|
|
TermsBuilder firstAggregationBuilder = AggregationBuilders.terms(firstAggName).field("shopId").order(Terms.Order.aggregation("sort", aggSort.asc())).size(50);
|
|
|
// 2.2)添加子聚合:取得分最大的值
|
|
|
firstAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(aggSort.getSortField()));
|
|
|
// 2.3)添加孙聚合:取打分最高的一个product
|
|
|
firstAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(aggSort.getSortField()).order(aggSort.getSortOrder()))
|
|
|
.setSize(1));
|
|
|
list.add(firstAggregationBuilder);
|
|
|
searchParam.setAggregationBuilders(list);
|
|
|
|
|
|
// 5、根据searchParam查询ES
|
|
|
// 3、先从缓存中获取,如果能取到,则直接返回
|
|
|
JSONObject jsonObject = searchCacheService.getJSONObjectFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
|
|
|
if (jsonObject != null) {
|
|
|
return new SearchApiResult().setData(jsonObject);
|
|
|
}
|
|
|
// 4、执行搜索,并构造返回结果
|
|
|
final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
|
|
|
SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
|
|
|
if (searchResult == null || searchResult.getAggMaps() == null) {
|
|
|
return null;
|
|
|
}
|
|
|
Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
|
|
|
if (!aggMaps.containsKey(firstAggName)) {
|
|
|
return null;
|
|
|
}
|
|
|
// 5、构造返回结果
|
|
|
List<Map<String, Object>> shop_list = this.getShopList(((MultiBucketsAggregation) aggMaps.get(firstAggName)));
|
|
|
jsonObject = new JSONObject();
|
|
|
jsonObject.put("shop_list", shop_list);
|
|
|
searchCacheService.addJSONObjectToCache(indexName, searchParam, jsonObject);
|
|
|
return new SearchApiResult().setData(jsonObject);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 从聚合结果中获取原生的商品列表
|
|
|
*
|
|
|
* @param aggregation
|
|
|
* @return
|
|
|
*/
|
|
|
private List<Map<String, Object>> getShopList(final MultiBucketsAggregation aggregation) {
|
|
|
Iterator<? extends Bucket> itAgg = aggregation.getBuckets().iterator();
|
|
|
List<Map<String, Object>> productList = new ArrayList<Map<String, Object>>();
|
|
|
while (itAgg.hasNext()) {
|
|
|
Bucket lt = itAgg.next();
|
|
|
if (lt.getAggregations().getAsMap().containsKey("product")) {
|
|
|
TopHits topHits = lt.getAggregations().get("product");
|
|
|
if (topHits != null) {
|
|
|
SearchHits hits = topHits.getHits();
|
|
|
for (SearchHit hit : hits.getHits()) {
|
|
|
Map<String, Object> source = hit.getSource();
|
|
|
float _score = hit.getScore();
|
|
|
source.put("_score", _score);
|
|
|
productList.add(source);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (productList == null || productList.isEmpty()) {
|
|
|
return new ArrayList<Map<String, Object>>();
|
|
|
}
|
|
|
productList = this.sortListBySortField(productList);
|
|
|
|
|
|
List<Integer> yohoShopIds = new ArrayList<Integer>();
|
|
|
List<Integer> globalBrandIds = new ArrayList<Integer>();
|
|
|
|
|
|
for (Map<String, Object> map : productList) {
|
|
|
Integer shopId = (Integer) map.get("shopId");
|
|
|
Integer brandId = (Integer) map.get("brandId");
|
|
|
String isGlobal = (String) map.get("isGlobal");
|
|
|
if ("Y".equals(isGlobal) && brandId != null) {
|
|
|
globalBrandIds.add(brandId);
|
|
|
}
|
|
|
if ("N".equals(isGlobal) && shopId != null && shopId > 0) {
|
|
|
yohoShopIds.add(shopId);
|
|
|
}
|
|
|
}
|
|
|
Map<String, Map<String, Object>> yohoShopMap = shopsIndexBaseService.getShopsMapByIds(yohoShopIds);
|
|
|
Map<String, Map<String, Object>> globalBrandMap = brandIndexBaseService.getGlobalBrandMapByIds(globalBrandIds);
|
|
|
|
|
|
List<Map<String, Object>> shops_info = new ArrayList<Map<String, Object>>();
|
|
|
for (Map<String, Object> map : productList) {
|
|
|
String shopId = map.get("shopId") == null ? "" : map.get("shopId").toString();
|
|
|
String brandId = map.get("brandId") == null ? "" : map.get("brandId").toString();
|
|
|
String isGlobal = (String) map.get("isGlobal");
|
|
|
Map<String, Object> shop_info = new HashMap<String, Object>();
|
|
|
if ("Y".equals(isGlobal) && brandId != null) {
|
|
|
Map<String, Object> globalBrand = globalBrandMap.get(brandId);
|
|
|
if (globalBrand != null) {
|
|
|
shop_info.put("tbl_brand", globalBrand);
|
|
|
shop_info.put("yoho_shop", null);
|
|
|
shops_info.add(shop_info);
|
|
|
}
|
|
|
}
|
|
|
if ("N".equals(isGlobal) && shopId != null && Integer.valueOf(shopId) > 0) {
|
|
|
Map<String, Object> yohoShopInfo = yohoShopMap.get(shopId);
|
|
|
if (yohoShopInfo != null) {
|
|
|
shop_info.put("tbl_brand", null);
|
|
|
shop_info.put("yoho_shop", yohoShopInfo);
|
|
|
shops_info.add(shop_info);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return shops_info;
|
|
|
}
|
|
|
|
|
|
private double getDouble(Object value) {
|
|
|
if (value == null) {
|
|
|
return 0;
|
|
|
}
|
|
|
if (value instanceof Float) {
|
|
|
return ((Float) value).floatValue();
|
|
|
}
|
|
|
if (value instanceof Integer) {
|
|
|
return ((Integer) value);
|
|
|
}
|
|
|
if (value instanceof Long) {
|
|
|
return ((Long) value);
|
|
|
}
|
|
|
if (value instanceof String) {
|
|
|
return Double.valueOf(value.toString());
|
|
|
}
|
|
|
if (value instanceof Double) {
|
|
|
return Double.valueOf(value.toString());
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
private List<Map<String, Object>> sortListBySortField(List<Map<String, Object>> productList) {
|
|
|
if (productList == null || productList.isEmpty()) {
|
|
|
return productList;
|
|
|
}
|
|
|
// 再按照某个字段对商品排序
|
|
|
boolean asc = false;
|
|
|
Collections.sort(productList, new Comparator<Map<String, Object>>() {
|
|
|
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
|
|
|
try {
|
|
|
double value1 = getDouble(o1.get("_score"));
|
|
|
double value2 = getDouble(o2.get("_score"));
|
|
|
if (value1 == value2) {
|
|
|
return 0;
|
|
|
}
|
|
|
if (asc) {
|
|
|
return value1 - value2 > 0 ? 1 : -1;
|
|
|
} else {
|
|
|
return value1 - value2 > 0 ? -1 : 1;
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
logger.error(e.getMessage(), e);
|
|
|
return -1;
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
return productList;
|
|
|
}
|
|
|
|
|
|
} |
...
|
...
|
|