Authored by 胡古飞

fix

1 package com.yoho.search.service.restapi; 1 package com.yoho.search.service.restapi;
2 2
3 -import com.yoho.search.service.downgrade.aop.DownGradeAble;  
4 -import com.yoho.search.service.servicenew.IShopsService;  
5 -import com.yoho.search.service.utils.HttpServletRequestUtils;  
6 -import com.yoho.search.service.vo.SearchApiResult; 3 +import java.util.Map;
  4 +
  5 +import javax.servlet.http.HttpServletRequest;
7 6
8 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.beans.factory.annotation.Autowired;
9 import org.springframework.stereotype.Controller; 8 import org.springframework.stereotype.Controller;
@@ -11,9 +10,11 @@ import org.springframework.web.bind.annotation.RequestMapping; @@ -11,9 +10,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
11 import org.springframework.web.bind.annotation.RequestMethod; 10 import org.springframework.web.bind.annotation.RequestMethod;
12 import org.springframework.web.bind.annotation.ResponseBody; 11 import org.springframework.web.bind.annotation.ResponseBody;
13 12
14 -import javax.servlet.http.HttpServletRequest;  
15 -  
16 -import java.util.Map; 13 +import com.yoho.search.service.downgrade.aop.DownGradeAble;
  14 +import com.yoho.search.service.servicenew.IShopListService;
  15 +import com.yoho.search.service.servicenew.IShopsService;
  16 +import com.yoho.search.service.utils.HttpServletRequestUtils;
  17 +import com.yoho.search.service.vo.SearchApiResult;
17 18
18 /** 19 /**
19 * Created by wangnan on 2016/12/14. 20 * Created by wangnan on 2016/12/14.
@@ -23,6 +24,8 @@ public class ShopsController { @@ -23,6 +24,8 @@ public class ShopsController {
23 24
24 @Autowired 25 @Autowired
25 private IShopsService shopsService; 26 private IShopsService shopsService;
  27 + @Autowired
  28 + private IShopListService shopListService;
26 29
27 /** 30 /**
28 * 搜索店铺与品牌 31 * 搜索店铺与品牌
@@ -65,4 +68,19 @@ public class ShopsController { @@ -65,4 +68,19 @@ public class ShopsController {
65 Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request); 68 Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request);
66 return shopsService.group_shops(paramMap); 69 return shopsService.group_shops(paramMap);
67 } 70 }
  71 +
  72 +
  73 + /**
  74 + * 按关键字搜出一堆店铺
  75 + *
  76 + * @param request
  77 + * @return
  78 + */
  79 + @DownGradeAble(key = "shopList")
  80 + @RequestMapping(method = RequestMethod.GET, value = "/shopList")
  81 + @ResponseBody
  82 + public SearchApiResult shopList(HttpServletRequest request) {
  83 + Map<String, String> paramMap = HttpServletRequestUtils.transParamType(request);
  84 + return shopListService.searchShopList(paramMap);
  85 + }
68 } 86 }
  1 +package com.yoho.search.service.servicenew;
  2 +
  3 +import java.util.Map;
  4 +
  5 +import com.yoho.search.service.vo.SearchApiResult;
  6 +
  7 +public interface IShopListService {
  8 +
  9 + /**
  10 + * 搜索店铺列表
  11 + *
  12 + * @param paramMap
  13 + * @return
  14 + */
  15 + public SearchApiResult searchShopList(Map<String, String> paramMap);
  16 +
  17 +}
@@ -29,4 +29,5 @@ public interface IShopsService { @@ -29,4 +29,5 @@ public interface IShopsService {
29 * @return 29 * @return
30 */ 30 */
31 public SearchApiResult searchShopsNew(Map<String, String> paramMap); 31 public SearchApiResult searchShopsNew(Map<String, String> paramMap);
  32 +
32 } 33 }
  1 +package com.yoho.search.service.servicenew.impl;
  2 +
  3 +import java.io.UnsupportedEncodingException;
  4 +import java.net.URLDecoder;
  5 +import java.util.ArrayList;
  6 +import java.util.Collections;
  7 +import java.util.Comparator;
  8 +import java.util.HashMap;
  9 +import java.util.Iterator;
  10 +import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +import org.apache.commons.lang.StringUtils;
  14 +import org.elasticsearch.index.query.BoolQueryBuilder;
  15 +import org.elasticsearch.index.query.MatchQueryBuilder;
  16 +import org.elasticsearch.index.query.MultiMatchQueryBuilder;
  17 +import org.elasticsearch.index.query.QueryBuilders;
  18 +import org.elasticsearch.search.SearchHit;
  19 +import org.elasticsearch.search.SearchHits;
  20 +import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
  21 +import org.elasticsearch.search.aggregations.Aggregation;
  22 +import org.elasticsearch.search.aggregations.AggregationBuilders;
  23 +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
  24 +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket;
  25 +import org.elasticsearch.search.aggregations.bucket.terms.Terms;
  26 +import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
  27 +import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
  28 +import org.elasticsearch.search.sort.SortBuilders;
  29 +import org.elasticsearch.search.sort.SortOrder;
  30 +import org.slf4j.Logger;
  31 +import org.slf4j.LoggerFactory;
  32 +import org.springframework.beans.factory.annotation.Autowired;
  33 +import org.springframework.stereotype.Service;
  34 +
  35 +import com.alibaba.fastjson.JSONObject;
  36 +import com.yoho.search.base.utils.ISearchConstants;
  37 +import com.yoho.search.core.es.model.SearchParam;
  38 +import com.yoho.search.core.es.model.SearchResult;
  39 +import com.yoho.search.service.service.SearchCacheService;
  40 +import com.yoho.search.service.service.SearchCommonService;
  41 +import com.yoho.search.service.service.helper.SearchServiceHelper;
  42 +import com.yoho.search.service.servicenew.IShopListService;
  43 +import com.yoho.search.service.utils.SearchRequestParams;
  44 +import com.yoho.search.service.vo.SearchApiResult;
  45 +import com.yoho.search.service.vo.SearchSort;
  46 +
  47 +@Service
  48 +public class IShopListServiceImpl implements IShopListService {
  49 +
  50 + private static final Logger logger = LoggerFactory.getLogger(ShopsServiceImpl.class);
  51 +
  52 + private String getLegalKeyWord(Map<String, String> paramMap) {
  53 + String keyword = paramMap.get(SearchRequestParams.PARAM_SEARCH_SHOPS_KEYWORD);
  54 + if (StringUtils.isBlank(keyword)) {
  55 + return null;
  56 + }
  57 + if (keyword.contains("%")) {
  58 + keyword.replace("%", "percent");// 特殊处理
  59 + }
  60 + // 编码转换
  61 + String is_encode = paramMap.get("is_encode");
  62 + if (StringUtils.isNotBlank(is_encode) && is_encode.equals("1")) {
  63 + try {
  64 + keyword = URLDecoder.decode(keyword, "UTF-8");
  65 + } catch (UnsupportedEncodingException e) {
  66 + logger.warn(e.getMessage(), e);
  67 + }
  68 + }
  69 + return keyword;
  70 + }
  71 +
  72 + @Autowired
  73 + private SearchCacheService searchCacheService;
  74 + @Autowired
  75 + private SearchCommonService searchCommonService;
  76 + @Autowired
  77 + private SearchServiceHelper searchServiceHelper;
  78 +
  79 + @Override
  80 + public SearchApiResult searchShopList(Map<String, String> paramMap) {
  81 + logger.info("[func=searchShops][param={}][begin={}]", paramMap.toString(), System.currentTimeMillis());
  82 + // 1、获取搜索店铺的关键词
  83 + String keyword = this.getLegalKeyWord(paramMap);
  84 + if (StringUtils.isBlank(keyword)) {
  85 + return new SearchApiResult().setCode(400).setMessage("请传keyword");
  86 + }
  87 + SearchParam searchParam = new SearchParam();
  88 + searchParam.setSize(0);
  89 +
  90 + // 2、构建query
  91 + MultiMatchQueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(keyword);
  92 + queryBuilder.operator(MatchQueryBuilder.Operator.OR);
  93 + StringBuilder searchField = new StringBuilder();
  94 + searchField.append("brandName.brandName_lowercase^4000,brandName^900").append(",");
  95 + searchField.append("shopName.shopName_lowercase^4000,shopName^900").append(",");
  96 + searchField.append("brandNameCn^850").append(",").append("brandNameCn.brandNameCn_pinyin^850").append(",");
  97 + searchField.append("brandNameEn^800");
  98 + String[] fields = searchField.toString().split(",");
  99 + for (String field : fields) {
  100 + String[] fieldBoost = field.split("\\^");
  101 + if (fieldBoost.length == 2) {
  102 + queryBuilder.field(fieldBoost[0], Float.parseFloat(fieldBoost[1]));
  103 + } else if (fieldBoost.length == 1) {
  104 + queryBuilder.field(fieldBoost[0]);
  105 + }
  106 + }
  107 + queryBuilder.minimumShouldMatch("95%");
  108 + searchParam.setQuery(queryBuilder);
  109 +
  110 + // 3、构建filter
  111 + // 3.1默认条件
  112 + BoolQueryBuilder boolFilter = QueryBuilders.boolQuery();
  113 + boolFilter.must(QueryBuilders.termQuery("status", 1));
  114 + boolFilter.must(QueryBuilders.rangeQuery("storageNum").gte(1));
  115 + boolFilter.must(QueryBuilders.termQuery("isOutlets", 2));
  116 + boolFilter.must(QueryBuilders.termQuery("attribute", 1));
  117 + boolFilter.mustNot(QueryBuilders.termsQuery("isSeckill", "Y"));
  118 + // 3.2店铺过滤器
  119 + BoolQueryBuilder shopFilter = QueryBuilders.boolQuery();
  120 + BoolQueryBuilder globalShopFilter = QueryBuilders.boolQuery();
  121 + globalShopFilter.must(QueryBuilders.termsQuery("isGlobal", "Y"));
  122 + globalShopFilter.must(QueryBuilders.termQuery("shopId", "0"));
  123 + BoolQueryBuilder yohoShopFilter = QueryBuilders.boolQuery();
  124 + yohoShopFilter.mustNot(QueryBuilders.termsQuery("isGlobal", "Y"));
  125 + yohoShopFilter.must(QueryBuilders.rangeQuery("shopId").gt(0));
  126 + boolFilter.must(shopFilter.should(globalShopFilter).should(yohoShopFilter));
  127 + searchParam.setFiter(boolFilter);
  128 +
  129 + // 4、构建聚合条件
  130 + final String firstAggName = "firstAgg";
  131 + SearchSort aggSort = new SearchSort("_score", SortOrder.DESC);
  132 + List<AbstractAggregationBuilder> list = new ArrayList<AbstractAggregationBuilder>();
  133 + // 2.1)构造父聚合:品牌或品类聚合【同时按子聚合的sort字段排序】
  134 + TermsBuilder firstAggregationBuilder = AggregationBuilders.terms(firstAggName).field("shopId").order(Terms.Order.aggregation("sort", aggSort.asc())).size(50);
  135 + // 2.2)添加子聚合:取得分最大的值
  136 + firstAggregationBuilder.subAggregation(AggregationBuilders.max("sort").field(aggSort.getSortField()));
  137 + // 2.3)添加孙聚合:取打分最高的一个product
  138 + firstAggregationBuilder.subAggregation(AggregationBuilders.topHits("product").addSort(SortBuilders.fieldSort(aggSort.getSortField()).order(aggSort.getSortOrder()))
  139 + .setSize(1));
  140 + list.add(firstAggregationBuilder);
  141 + searchParam.setAggregationBuilders(list);
  142 +
  143 + // 5、根据searchParam查询ES
  144 + // 3、先从缓存中获取,如果能取到,则直接返回
  145 + JSONObject jsonObject = searchCacheService.getJSONObjectFromCache(ISearchConstants.INDEX_NAME_PRODUCT_INDEX, searchParam);
  146 + if (jsonObject != null) {
  147 + return new SearchApiResult().setData(jsonObject);
  148 + }
  149 + // 4、执行搜索,并构造返回结果
  150 + final String indexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
  151 + SearchResult searchResult = searchCommonService.doSearch(indexName, searchParam);
  152 + if (searchResult == null || searchResult.getAggMaps() == null) {
  153 + return null;
  154 + }
  155 + Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
  156 + if (!aggMaps.containsKey(firstAggName)) {
  157 + return null;
  158 + }
  159 + // 5、构造返回结果
  160 + List<Map<String, Object>> shop_list = this.getShopList(((MultiBucketsAggregation) aggMaps.get(firstAggName)));
  161 + jsonObject = new JSONObject();
  162 + jsonObject.put("shop_list", shop_list);
  163 + searchCacheService.addJSONObjectToCache(indexName, searchParam, jsonObject);
  164 + return new SearchApiResult().setData(jsonObject);
  165 + }
  166 +
  167 + /**
  168 + * 从聚合结果中获取原生的商品列表
  169 + *
  170 + * @param aggregation
  171 + * @return
  172 + */
  173 + private List<Map<String, Object>> getShopList(final MultiBucketsAggregation aggregation) {
  174 + Iterator<? extends Bucket> itAgg = aggregation.getBuckets().iterator();
  175 + List<Map<String, Object>> productList = new ArrayList<Map<String, Object>>();
  176 + while (itAgg.hasNext()) {
  177 + Bucket lt = itAgg.next();
  178 + if (lt.getAggregations().getAsMap().containsKey("product")) {
  179 + TopHits topHits = lt.getAggregations().get("product");
  180 + if (topHits != null) {
  181 + SearchHits hits = topHits.getHits();
  182 + for (SearchHit hit : hits.getHits()) {
  183 + Map<String, Object> source = hit.getSource();
  184 + float _score = hit.getScore();
  185 + source.put("_score", _score);
  186 + productList.add(source);
  187 + }
  188 + }
  189 + }
  190 + }
  191 + productList = this.sortListBySortField(productList);
  192 +
  193 + List<Map<String, Object>> shopsInfos = new ArrayList<Map<String, Object>>();
  194 + for (Map<String, Object> map : productList) {
  195 + Map<String, Object> shopInfo = new HashMap<String, Object>();
  196 + shopInfo.put("shop_id", map.get("shopId"));
  197 + shopInfo.put("shop_name", map.get("shopName"));
  198 + shopInfo.put("brand_id", map.get("brandId"));
  199 + shopInfo.put("brand_name", map.get("brandName"));
  200 + shopInfo.put("is_global", map.get("isGlobal"));
  201 + shopsInfos.add(shopInfo);
  202 + }
  203 + return shopsInfos;
  204 + }
  205 +
  206 + private double getDouble(Object value) {
  207 + if (value == null) {
  208 + return 0;
  209 + }
  210 + if (value instanceof Float) {
  211 + return ((Float) value).floatValue();
  212 + }
  213 + if (value instanceof Integer) {
  214 + return ((Integer) value);
  215 + }
  216 + if (value instanceof Long) {
  217 + return ((Long) value);
  218 + }
  219 + if (value instanceof String) {
  220 + return Double.valueOf(value.toString());
  221 + }
  222 + if (value instanceof Double) {
  223 + return Double.valueOf(value.toString());
  224 + }
  225 + return 0;
  226 + }
  227 +
  228 + private List<Map<String, Object>> sortListBySortField(List<Map<String, Object>> productList) {
  229 + if (productList == null || productList.isEmpty()) {
  230 + return productList;
  231 + }
  232 + // 再按照某个字段对商品排序
  233 + boolean asc = false;
  234 + Collections.sort(productList, new Comparator<Map<String, Object>>() {
  235 + public int compare(Map<String, Object> o1, Map<String, Object> o2) {
  236 + try {
  237 + double value1 = getDouble(o1.get("_score"));
  238 + double value2 = getDouble(o2.get("_score"));
  239 + if (value1 == value2) {
  240 + return 0;
  241 + }
  242 + if (asc) {
  243 + return value1 - value2 > 0 ? 1 : -1;
  244 + } else {
  245 + return value1 - value2 > 0 ? -1 : 1;
  246 + }
  247 + } catch (Exception e) {
  248 + logger.error(e.getMessage(), e);
  249 + return -1;
  250 + }
  251 + }
  252 + });
  253 + return productList;
  254 + }
  255 +
  256 +}