第6章 商品搜索

学习目标

  • 条件筛选
  • 多条件搜索[品牌、规格条件搜索]
  • 规格过滤
  • 价格区间搜索
  • 搜索分页
  • 搜索排序
  • 搜索高亮

1. 品牌统计

用户搜索的时候,除了使用分类搜索外,还有可能使用品牌搜索,所以我们还需要显示品牌数据和规格数据,品牌数据和规格数据的显示比较容易,都可以考虑使用分类统计的方式进行分组实现。

1.1 品牌统计分析

看下面的SQL语句,我们在执行搜索的时候,第1条SQL语句是执行搜,第2条语句是根据品牌名字分组查看有多少品牌,大概执行了2个步骤就可以获取数据结果以及品牌统计,我们可以发现他们的搜索条件完全一样。

-- 查询所有
SELECT * FROM tb_sku WHERE name LIKE '%手机%';
-- 根据品牌名字分组查询
SELECT brand_name FROM  tb_sku WHERE name LIKE '%手机%' GROUP BY brand_name;

我们每次执行搜索的时候,需要显示商品品牌名称,这里要显示的品牌名称其实就是符合搜素条件的所有商品的品牌集合,我们可以按照上面的实现思路,使用ES根据分组名称做一次分组查询即可实现。

1.2 品牌分组统计实现

修改search微服务的com.changgou.search.service.impl.SkuServiceImpl类,添加一个品牌分组搜索,如图:

添加的代码如下:

//设置分组条件  商品品牌
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrandgroup").field("brandName").size(50));

执行获取分组结果:


整体代码如下:

public Map search(Map<String, String> searchMap) {//1.获取关键字的值String keywords = searchMap.get("keywords");if (StringUtils.isEmpty(keywords)) {keywords = "华为";//赋值给一个默认的值}//2.创建查询对象 的构建对象NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();//3.设置查询的条件//设置分组条件  商品分类nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategorygroup").field("categoryName").size(50));//设置分组条件  商品品牌nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrandgroup").field("brandName").size(50));nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));//4.构建查询对象NativeSearchQuery query = nativeSearchQueryBuilder.build();//5.执行查询AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(query, SkuInfo.class);//获取分组结果  商品分类StringTerms stringTermsCategory = (StringTerms) skuPage.getAggregation("skuCategorygroup");//获取分组结果  商品品牌StringTerms stringTermsBrand = (StringTerms) skuPage.getAggregation("skuBrandgroup");List<String> categoryList = getStringsCategoryList(stringTermsCategory);List<String> brandList = getStringsBrandList(stringTermsBrand);//6.返回结果Map resultMap = new HashMap<>();resultMap.put("categoryList", categoryList);resultMap.put("brandList", brandList);resultMap.put("rows", skuPage.getContent());resultMap.put("total", skuPage.getTotalElements());resultMap.put("totalPages", skuPage.getTotalPages());return resultMap;
}
/*** 获取品牌列表** @param stringTermsBrand* @return*/
private List<String> getStringsBrandList(StringTerms stringTermsBrand) {List<String> brandList = new ArrayList<>();if (stringTermsBrand != null) {for (StringTerms.Bucket bucket : stringTermsBrand.getBuckets()) {brandList.add(bucket.getKeyAsString());}}return brandList;
}/*** 获取分类列表数据** @param stringTerms* @return*/
private List<String> getStringsCategoryList(StringTerms stringTerms) {List<String> categoryList = new ArrayList<>();if (stringTerms != null) {for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {String keyAsString = bucket.getKeyAsString();//分组的值categoryList.add(keyAsString);}}return categoryList;
}

1.3 测试

使用PostMan请求http://localhost:18086/search

2. 规格统计

用户搜索的时候,除了使用分类、品牌搜索外,还有可能使用规格搜索,所以我们还需要显示规格数据,规格数据的显示相比上面2种实现略微较难一些,需要对数据进行处理,我们也可以考虑使用分类统计和品牌统计的方式进行分组实现。

2.1 规格统计分析

看下面的SQL语句,我们在执行搜索的时候,第1条SQL语句是执行搜,第2条语句是根据规格分组查看有多少规格,大概执行了2个步骤就可以获取数据结果以及规格统计,我们可以发现他们的搜索条件完全一样。

-- 查询所有
SELECT * FROM tb_sku WHERE name LIKE '%手机%';
-- 根据规格名字分组查询
SELECT spec FROM  tb_sku WHERE name LIKE '%手机%' GROUP BY spec;

上述SQL语句执行后的结果如下图:

获取到的规格数据我们发现有重复,不过也可以解决,解决思路如下:

1.获取所有规格数据
2.将所有规格数据转换成Map
3.定义一个Map<String,Set>,key是规格名字,防止重复所以用Map,valu是规格值,规格值有多个,所以用集合,为了防止规格重复,用Set去除重复
4.循环规格的Map,将数据填充到定义的Map<String,Set>中

我们每次执行搜索的时候,需要显示商品规格数据,这里要显示的规格数据其实就是符合搜素条件的所有商品的规格集合,我们可以按照上面的实现思路,使用ES根据分组名称做一次分组查询,并去除重复数据即可实现。

2.2 规格统计分组实现

修改search微服务的com.changgou.search.service.impl.SkuServiceImpl类,添加一个规格分组搜索

如图:添加规格分组条件

上图代码如下:

//设置分组条件  商品的规格
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpecgroup").field("spec.keyword").size(100));

如图:获取规格分组结果:

封装调用分组结果的方法:

上图代码如下:

/*** 获取规格列表数据** @param stringTermsSpec* @return*/
private Map<String, Set<String>> getStringSetMap(StringTerms stringTermsSpec) {Map<String, Set<String>> specMap = new HashMap<String, Set<String>>();Set<String> specList = new HashSet<>();if (stringTermsSpec != null) {for (StringTerms.Bucket bucket : stringTermsSpec.getBuckets()) {specList.add(bucket.getKeyAsString());}}for (String specjson : specList) {Map<String, String> map = JSON.parseObject(specjson, Map.class);for (Map.Entry<String, String> entry : map.entrySet()) {//String key = entry.getKey();        //规格名字String value = entry.getValue();    //规格选项值//获取当前规格名字对应的规格数据Set<String> specValues = specMap.get(key);if (specValues == null) {specValues = new HashSet<String>();}//将当前规格加入到集合中specValues.add(value);//将数据存入到specMap中specMap.put(key, specValues);}}return specMap;
}

整体代码如下:

public Map search(Map<String, String> searchMap) {//1.获取关键字的值String keywords = searchMap.get("keywords");if (StringUtils.isEmpty(keywords)) {keywords = "华为";//赋值给一个默认的值}//2.创建查询对象 的构建对象NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();//3.设置查询的条件//设置分组条件  商品分类nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategorygroup").field("categoryName").size(50));//设置分组条件  商品品牌nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrandgroup").field("brandName").size(50));//设置分组条件  商品的规格nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpecgroup").field("spec.keyword").size(100));nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));//4.构建查询对象NativeSearchQuery query = nativeSearchQueryBuilder.build();//5.执行查询AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(query, SkuInfo.class);//获取分组结果  商品分类StringTerms stringTermsCategory = (StringTerms) skuPage.getAggregation("skuCategorygroup");//获取分组结果  商品品牌StringTerms stringTermsBrand = (StringTerms) skuPage.getAggregation("skuBrandgroup");//获取分组结果  商品规格数据StringTerms stringTermsSpec = (StringTerms) skuPage.getAggregation("skuSpecgroup");List<String> categoryList = getStringsCategoryList(stringTermsCategory);List<String> brandList = getStringsBrandList(stringTermsBrand);Map<String, Set<String>> specMap = getStringSetMap(stringTermsSpec);//6.返回结果Map resultMap = new HashMap<>();resultMap.put("specMap", specMap);resultMap.put("categoryList", categoryList);resultMap.put("brandList", brandList);resultMap.put("rows", skuPage.getContent());resultMap.put("total", skuPage.getTotalElements());resultMap.put("totalPages", skuPage.getTotalPages());return resultMap;
}/*** 获取品牌列表** @param stringTermsBrand* @return*/
private List<String> getStringsBrandList(StringTerms stringTermsBrand) {List<String> brandList = new ArrayList<>();if (stringTermsBrand != null) {for (StringTerms.Bucket bucket : stringTermsBrand.getBuckets()) {brandList.add(bucket.getKeyAsString());}}return brandList;
}/*** 获取分类列表数据** @param stringTerms* @return*/
private List<String> getStringsCategoryList(StringTerms stringTerms) {List<String> categoryList = new ArrayList<>();if (stringTerms != null) {for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {String keyAsString = bucket.getKeyAsString();//分组的值categoryList.add(keyAsString);}}return categoryList;
}/*** 获取规格列表数据** @param stringTermsSpec* @return*/
private Map<String, Set<String>> getStringSetMap(StringTerms stringTermsSpec) {Map<String, Set<String>> specMap = new HashMap<String, Set<String>>();Set<String> specList = new HashSet<>();if (stringTermsSpec != null) {for (StringTerms.Bucket bucket : stringTermsSpec.getBuckets()) {specList.add(bucket.getKeyAsString());}}for (String specjson : specList) {Map<String, String> map = JSON.parseObject(specjson, Map.class);for (Map.Entry<String, String> entry : map.entrySet()) {//String key = entry.getKey();        //规格名字String value = entry.getValue();    //规格选项值//获取当前规格名字对应的规格数据Set<String> specValues = specMap.get(key);if (specValues == null) {specValues = new HashSet<String>();}//将当前规格加入到集合中specValues.add(value);//将数据存入到specMap中specMap.put(key, specValues);}}return specMap;
}

2.3 测试

使用Postman测试访问http://localhost:18086/search 效果如下:

3 条件筛选

用户有可能会根据分类搜索、品牌搜索,还有可能根据规格搜索,以及价格搜索和排序操作。根据分类和品牌搜索的时候,可以直接根据指定域搜索,而规格搜索的域数据是不确定的,价格是一个区间搜索,所以我们可以分为三段时间,先实现分类、品牌搜素,再实现规格搜索,然后实现价格区间搜索。

3.1 分类、品牌筛选

3.1.1 需求分析

页面每次向后台传入对应的分类和品牌,后台据分类和品牌进行条件过滤即可。

3.1.2 代码实现

修改搜索微服务com.changgou.search.service.impl.SkuServiceImpl的search方法,添加分类和品牌过滤,

添加过滤条件如下:

PS说明: 以上,我们建议使用filter ,它的搜索效率要优于must.可以参考官方文档说明:

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html

执行过滤查询如下:

上图整体代码如下:

@Override
public Map search(Map<String, String> searchMap) {//1.获取关键字的值String keywords = searchMap.get("keywords");if (StringUtils.isEmpty(keywords)) {keywords = "华为";//赋值给一个默认的值}//2.创建查询对象 的构建对象NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();//3.设置查询的条件//设置分组条件  商品分类nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategorygroup").field("categoryName").size(50));//设置分组条件  商品品牌nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrandgroup").field("brandName").size(50));//设置分组条件  商品的规格nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpecgroup").field("spec.keyword").size(1000));//设置主关键字查询nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();if (!StringUtils.isEmpty(searchMap.get("brand"))) {boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));}if (!StringUtils.isEmpty(searchMap.get("category"))) {boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", searchMap.get("category")));}//构建过滤查询nativeSearchQueryBuilder.withFilter(boolQueryBuilder);//4.构建查询对象NativeSearchQuery query = nativeSearchQueryBuilder.build();//5.执行查询AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(query, SkuInfo.class);//获取分组结果  商品分类StringTerms stringTermsCategory = (StringTerms) skuPage.getAggregation("skuCategorygroup");//获取分组结果  商品品牌StringTerms stringTermsBrand = (StringTerms) skuPage.getAggregation("skuBrandgroup");//获取分组结果  商品规格数据StringTerms stringTermsSpec = (StringTerms) skuPage.getAggregation("skuSpecgroup");List<String> categoryList = getStringsCategoryList(stringTermsCategory);List<String> brandList = getStringsBrandList(stringTermsBrand);Map<String, Set<String>> specMap = getStringSetMap(stringTermsSpec);//6.返回结果Map resultMap = new HashMap<>();resultMap.put("specMap", specMap);resultMap.put("categoryList", categoryList);resultMap.put("brandList", brandList);resultMap.put("rows", skuPage.getContent());resultMap.put("total", skuPage.getTotalElements());resultMap.put("totalPages", skuPage.getTotalPages());return resultMap;
}

3.1.3 测试

测试效果如下:

访问地址:http://localhost:18085/search

此时只能搜到华为手环设备

3.2 规格过滤

3.2.1 需求分析

规格这一块,需要向后台发送规格名字以及规格值,我们可以按照一定要求来发送数据,例如规格名字以特殊前缀提交到后台:spec_网络制式:电信4G、spec_显示屏尺寸:4.0-4.9英寸

后台接到数据后,可以根据前缀spec_来区分是否是规格,如果以spec_xxx开始的数据则为规格数据,需要根据指定规格找信息。

上图是规格的索引存储格式,真实数据在spechMap.规格名字.keyword中,所以找数据也是按照如下格式去找:

spechMap.规格名字.keyword

3.2.2 代码实现

修改com.changgou.search.service.impl.SkuServiceImpl的search方法,增加规格查询操作,代码如下:

//规格过滤查询
if (searchMap != null) {for (String key : searchMap.keySet()) {if (key.startsWith("spec_")) {boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", searchMap.get(key)));}}
}

3.2.3 测试

访问地址:http://localhost:18085/search

3.3 价格区间查询

3.3.1 需求分析

价格区间查询,每次需要将价格传入到后台,前端传入后台的价格大概是price=0-500或者price=500-1000依次类推,最后一个是price=3000,后台可以根据-分割,如果分割得到的结果最多有2个,第1个表示x<price,第2个表示price<=y

1.3.2 代码实现

修改com.changgou.search.service.impl.SkuServiceImpl的search方法,增加价格区间查询操作,代码如下:

上图代码如下:

//价格过滤查询
String price = searchMap.get("price");
if (!StringUtils.isEmpty(price)) {String[] split = price.split("-");if (!split[1].equalsIgnoreCase("*")) {boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").from(split[0], true).to(split[1], true));} else {boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(split[0]));}
}

3.3.3 测试

访问地址:http://localhost:18085/search

效果如下(部分数据):

 [{"id": 1088256019328536576,"name": "守护宝幼儿安全手环","price": 500,"num": 100,"image": "http://img10.360buyimg.com/n1/s450x450_jfs/t3457/294/236823024/102048/c97f5825/58072422Ndd7e66c4.jpg","status": "1","createTime": "2019-01-24T10:03:48.000+0000","updateTime": "2019-01-24T10:03:48.000+0000","isDefault": null,"spuId": 1088256019315953664,"categoryId": 1108,"categoryName": "户外工具","brandName": "守护宝","spec": "{\"颜色\":\"红\",\"机身内存\":\"64G\"}","specMap": {"颜色": "红","机身内存": "64G"}},{"id": 1088256014043713536,"name": "计步器小米手环,适用老人、小孩","price": 800,"num": 100,"image": "http://img10.360buyimg.com/n1/s450x450_jfs/t3457/294/236823024/102048/c97f5825/58072422Ndd7e66c4.jpg","status": "1","createTime": "2019-01-24T10:03:47.000+0000","updateTime": "2019-01-24T10:03:47.000+0000","isDefault": null,"spuId": 1088256014026936320,"categoryId": 1192,"categoryName": "小家电","brandName": "小米","spec": "{\"颜色\":\"红\",\"机身内存\":\"64G\"}","specMap": {"颜色": "红","机身内存": "64G"}}]

4 搜索分页

4.1 分页分析

页面需要实现分页搜索,所以我们后台每次查询的时候,需要实现分页。用户页面每次会传入当前页和每页查询多少条数据,当然如果不传入每页显示多少条数据,默认查询30条即可。

4.2 分页实现

分页使用PageRequest.of( pageNo- 1, pageSize);实现,第1个参数表示第N页,从0开始,第2个参数表示每页显示多少条,实现代码如下:

上图代码如下:

         //略//构建过滤查询nativeSearchQueryBuilder.withFilter(boolQueryBuilder);//构建分页查询Integer pageNum = 1;if (!StringUtils.isEmpty(searchMap.get("pageNum"))) {try {pageNum = Integer.valueOf(searchMap.get("pageNum"));} catch (NumberFormatException e) {e.printStackTrace();pageNum=1;}}Integer pageSize = 3;nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1, pageSize));//略//4.构建查询对象NativeSearchQuery query = nativeSearchQueryBuilder.build();//略

测试如下:

5 搜索排序

5.1 排序分析

排序这里总共有根据价格排序、根据评价排序、根据新品排序、根据销量排序,排序要想实现非常简单,只需要告知排序的域以及排序方式即可实现。

价格排序:只需要根据价格高低排序即可,降序价格高->低,升序价格低->高

评价排序:评价分为好评、中评、差评,可以在数据库中设计3个列,用来记录好评、中评、差评的量,每次排序的时候,好评的比例来排序,当然还要有条数限制,评价条数需要超过N条。

新品排序:直接根据商品的发布时间或者更新时间排序。

销量排序:销量排序除了销售数量外,还应该要有时间段限制。

5.2 排序实现

这里我们不单独针对某个功能实现排序,我们只需要在后台接收2个参数,分别是排序域名字和排序方式,代码如下:

解释: 前端页面传递要排序的字段(field)和要排序的类型(ASC,DESC),后台接收.

上图代码如下:

//构建排序查询
String sortRule = searchMap.get("sortRule");
String sortField = searchMap.get("sortField");
if (!StringUtils.isEmpty(sortRule) && !StringUtils.isEmpty(sortField)) {nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortRule.equals("DESC") ? SortOrder.DESC : SortOrder.ASC));
}

测试

根据价格降序:

{"keywords":"手机","pageNum":"1","sortRule":"DESC","sortField":"price"}

根据价格升序:

{"keywords":"手机","pageNum":"1","sortRule":"ASC","sortField":"price"}

6 高亮显示

6.1 高亮分析

高亮显示是指根据商品关键字搜索商品的时候,显示的页面对关键字给定了特殊样式,让它显示更加突出,如上图商品搜索中,关键字编程了红色,其实就是给定了红色样式。

6.2 高亮搜索实现步骤解析

将之前的搜索换掉,换成高亮搜索,我们需要做3个步骤:

1.指定高亮域,也就是设置哪个域需要高亮显示设置高亮域的时候,需要指定前缀和后缀,也就是关键词用什么html标签包裹,再给该标签样式
2.高亮搜索实现
3.将非高亮数据替换成高亮数据

第1点,例如在百度中搜索数据的时候,会有2个地方高亮显示,分别是标题和描述,商城搜索的时候,只是商品名称高亮显示了。而高亮显示其实就是添加了样式,例如<span style="color:red;">笔记本</span>,而其中span开始标签可以称为前缀,span结束标签可以称为后缀。

第2点,高亮搜索使用ElasticsearchTemplate实现。

第3点,高亮搜索后,会搜出非高亮数据和高亮数据,高亮数据会加上第1点中的高亮样式,此时我们需要将非高亮数据换成高亮数据即可。例如非高亮:华为笔记本性能超强悍 高亮数据:华为<span style="color:red;"笔记本</span>性能超强悍,将非高亮的换成高亮的,到页面就能显示样式了。

6.3 高亮代码实现

修改com.changgou.search.service.impl.SkuServiceImpl的search方法搜索代码,添加高亮显示的域:

上图代码如下:

 //设置高亮条件nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("name"));nativeSearchQueryBuilder.withHighlightBuilder(new HighlightBuilder().preTags("<em style=\"color:red\">").postTags("</em>"));//设置主关键字查询nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(keywords,"name","brandName","categoryName"));

修改 查询的方法,自定义结果映射器,入下图:

上图图片如下:

AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(query, SkuInfo.class, new SearchResultMapperImpl());

自定义一个映射结果类实现接口,作用就是:自定义映射结果集,获取高亮的数据展示,如下图:

代码如下:

public class SearchResultMapperImpl implements SearchResultMapper {@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {List<T> content = new ArrayList<>();//如果没有结果返回为空if (response.getHits() == null || response.getHits().getTotalHits() <= 0) {return new AggregatedPageImpl<T>(content);}for (SearchHit searchHit : response.getHits()) {String sourceAsString = searchHit.getSourceAsString();SkuInfo skuInfo = JSON.parseObject(sourceAsString, SkuInfo.class);Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();HighlightField highlightField = highlightFields.get("name");//有高亮则设置高亮的值if (highlightField != null) {StringBuffer stringBuffer = new StringBuffer();for (Text text : highlightField.getFragments()) {stringBuffer.append(text.string());}skuInfo.setName(stringBuffer.toString());}content.add((T) skuInfo);}return new AggregatedPageImpl<T>(content, pageable, response.getHits().getTotalHits(), response.getAggregations(), response.getScrollId());}
}

6.4 测试

效果如下:

"name": "HTC M8Sd (E8) 波尔多红 电信4G<span style=\"color:red\">手机</span> 双卡双待双通",

整体代码如下:

@Service
public class SkuServiceImpl implements SkuService {@Autowiredprivate SkuEsMapper skuEsMapper;@Autowiredprivate SkuFeign skuFeign;@Overridepublic void importSku() {Result<List<Sku>> listResult = skuFeign.findByStatus("1");List<Sku> data = listResult.getData();List<SkuInfo> skuInfos = JSON.parseArray(JSON.toJSONString(data), SkuInfo.class);for (SkuInfo skuInfo : skuInfos) {String spec = skuInfo.getSpec();Map map = JSON.parseObject(spec, Map.class);skuInfo.setSpecMap(map);}skuEsMapper.saveAll(skuInfos);}@Autowiredprivate ElasticsearchTemplate esTemplate;/**
* @param searchMap
* @return
*/@Overridepublic Map search(Map<String, String> searchMap) {//1.获取关键字的值String keywords = searchMap.get("keywords");if (StringUtils.isEmpty(keywords)) {keywords = "华为";//赋值给一个默认的值}//2.创建查询对象 的构建对象NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();//3.设置查询的条件//设置分组条件  商品分类nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategorygroup").field("categoryName").size(50));//设置分组条件  商品品牌nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrandgroup").field("brandName").size(50));//设置分组条件  商品的规格nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpecgroup").field("spec.keyword").size(500000));//设置高亮条件nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("name"));nativeSearchQueryBuilder.withHighlightBuilder(new HighlightBuilder().preTags("<em style=\"color:red\">").postTags("</em>"));//设置主关键字查询nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(keywords,"name","brandName","categoryName"));BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();if (!StringUtils.isEmpty(searchMap.get("brand"))) {boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));}if (!StringUtils.isEmpty(searchMap.get("category"))) {boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", searchMap.get("category")));}//规格过滤查询if (searchMap != null) {for (String key : searchMap.keySet()) {if (key.startsWith("spec_")) {boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", searchMap.get(key)));}}}//价格过滤查询String price = searchMap.get("price");if (!StringUtils.isEmpty(price)) {String[] split = price.split("-");if (!split[1].equalsIgnoreCase("*")) {boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").from(split[0], true).to(split[1], true));} else {boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(split[0]));}}//构建过滤查询nativeSearchQueryBuilder.withFilter(boolQueryBuilder);//构建分页查询Integer pageNum = 1;if (!StringUtils.isEmpty(searchMap.get("pageNum"))) {try {pageNum = Integer.valueOf(searchMap.get("pageNum"));} catch (NumberFormatException e) {e.printStackTrace();pageNum=1;}}Integer pageSize = 3;nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1, pageSize));//构建排序查询String sortRule = searchMap.get("sortRule");String sortField = searchMap.get("sortField");if (!StringUtils.isEmpty(sortRule) && !StringUtils.isEmpty(sortField)) {nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortRule.equals("DESC") ? SortOrder.DESC : SortOrder.ASC));}//4.构建查询对象NativeSearchQuery query = nativeSearchQueryBuilder.build();//5.执行查询AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(query, SkuInfo.class, new SearchResultMapperImpl());//获取分组结果  商品分类StringTerms stringTermsCategory = (StringTerms) skuPage.getAggregation("skuCategorygroup");//获取分组结果  商品品牌StringTerms stringTermsBrand = (StringTerms) skuPage.getAggregation("skuBrandgroup");//获取分组结果  商品规格数据StringTerms stringTermsSpec = (StringTerms) skuPage.getAggregation("skuSpecgroup");List<String> categoryList = getStringsCategoryList(stringTermsCategory);List<String> brandList = getStringsBrandList(stringTermsBrand);Map<String, Set<String>> specMap = getStringSetMap(stringTermsSpec);//6.返回结果Map resultMap = new HashMap<>();resultMap.put("specMap", specMap);resultMap.put("categoryList", categoryList);resultMap.put("brandList", brandList);resultMap.put("rows", skuPage.getContent());resultMap.put("total", skuPage.getTotalElements());resultMap.put("totalPages", skuPage.getTotalPages());return resultMap;}/**
* 获取品牌列表
*
* @param stringTermsBrand
* @return
*/private List<String> getStringsBrandList(StringTerms stringTermsBrand) {List<String> brandList = new ArrayList<>();if (stringTermsBrand != null) {for (StringTerms.Bucket bucket : stringTermsBrand.getBuckets()) {brandList.add(bucket.getKeyAsString());}}return brandList;}/**
* 获取分类列表数据
*
* @param stringTerms
* @return
*/private List<String> getStringsCategoryList(StringTerms stringTerms) {List<String> categoryList = new ArrayList<>();if (stringTerms != null) {for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {String keyAsString = bucket.getKeyAsString();//分组的值categoryList.add(keyAsString);}}return categoryList;}/**
* 获取规格列表数据
*
* @param stringTermsSpec
* @return
*/private Map<String, Set<String>> getStringSetMap(StringTerms stringTermsSpec) {Map<String, Set<String>> specMap = new HashMap<String, Set<String>>();Set<String> specList = new HashSet<>();if (stringTermsSpec != null) {for (StringTerms.Bucket bucket : stringTermsSpec.getBuckets()) {specList.add(bucket.getKeyAsString());}}for (String specjson : specList) {Map<String, String> map = JSON.parseObject(specjson, Map.class);for (Map.Entry<String, String> entry : map.entrySet()) {//String key = entry.getKey();        //规格名字String value = entry.getValue();    //规格选项值//获取当前规格名字对应的规格数据Set<String> specValues = specMap.get(key);if (specValues == null) {specValues = new HashSet<String>();}//将当前规格加入到集合中specValues.add(value);//将数据存入到specMap中specMap.put(key, specValues);}}return specMap;}
}

getStringSetMap(StringTerms stringTermsSpec) {
Map<String, Set> specMap = new HashMap<String, Set>();

    Set<String> specList = new HashSet<>();if (stringTermsSpec != null) {for (StringTerms.Bucket bucket : stringTermsSpec.getBuckets()) {specList.add(bucket.getKeyAsString());}}for (String specjson : specList) {Map<String, String> map = JSON.parseObject(specjson, Map.class);for (Map.Entry<String, String> entry : map.entrySet()) {//String key = entry.getKey();        //规格名字String value = entry.getValue();    //规格选项值//获取当前规格名字对应的规格数据Set<String> specValues = specMap.get(key);if (specValues == null) {specValues = new HashSet<String>();}//将当前规格加入到集合中specValues.add(value);//将数据存入到specMap中specMap.put(key, specValues);}}return specMap;
}

}


项目 cg day06相关推荐

  1. 项目 cg day04

    第4章 lua.Canal实现广告缓存 学习目标 Lua介绍 Lua语法 输出.变量定义.数据类型.流程控制(if..).循环操作.函数.表(数组).模块 OpenResty介绍(理解配置) 封装了N ...

  2. 项目 cg day05

    第5章 商品搜索 学习目标 Elasticsearch安装 docker安装Elasticsearch 系统参数问题 跨域操作 IK分词器配置 Kibana的使用->DSL语句 Kibana-& ...

  3. 项目cg day01

    第1章 框架搭建 学习目标 了解电商 了解畅购架构 了解畅购工程结构 畅购工程搭建 商品微服务搭建 品牌增删改查[通用的mapper实现DAO的操作] 1. 走进电商 1.1 电商行业分析 近年来,世 ...

  4. Django之爱鲜蜂项目开发 day06(一)

    1支付流程 当购物车商品筛选完毕点击结算按键时,跳转到支付宝支付流程 1.1支付宝支付时序流程图 1.2 沙箱模拟 这里支付并不是支付宝真正的交易,而是使用沙箱模拟交易,一般开发的时候,都是可以先去沙 ...

  5. Django之爱鲜蜂项目开发 day06(三)

    3.未支付订单模块 3.1 首先在views.py文件下写一下未支付订单的视图函数 取到未支付的订单,渲染到未支付页面 @check_login def order_unpay(request):or ...

  6. Django之爱鲜蜂项目开发 day06(二)

    2订单模块 2.1表的关联 一个用户可以有多个订单 1:n 一个订单中可以有多个商品 一个商品可以在多个订单中 订单----商品 m:n 2.2建立模型 做一下数据迁移 然后可以查看一下,数据库中多了 ...

  7. 电商项目(Day06)

    1.店铺信息编辑 编写shopDao //更新店铺信息 返回1成功 -1失败int updateShop(Shop shop);//通过shopid查询店铺Shop queryByShopId(lon ...

  8. 项目 cg day03

    第3章 商品发布 学习目标 SPU与SKU概念理解 SPU:某一款商品的公共属性 SKU:某款商品的不同参数对应的商品信息[某个商品] 新增商品.修改商品 增加:增加SPU和SKU 修改:修改SPU和 ...

  9. 畅购04——商品管理(分布式ID)

    电商项目--CG 为何使用分布式ID 项目中如何使用分布式ID 搞清SPU和SKU的区别 商品的CRUD 增加商品 根据id查询商品 修改商品 审核商品 商品上下架 删除商品 全局异常处理 为何使用分 ...

最新文章

  1. linux下eaccelerator,memcache,memcached安装
  2. OpenCV AKAZE本地特征匹配
  3. NA-NP-IE系列实验5:配置文件的备份和IOS 的备份
  4. 基础【循环】-----(枚举器)------(转)
  5. oracle查效能,【DataGuard】Oracle 11g物理Active Data Guard实时查询(Real-time query)特性...
  6. php跳转分站,根据访客所在城市ip地址自动跳转到分站的php代码
  7. Linux 命令(109)—— ping 命令
  8. 【编译原理笔记16】代码优化:流图,常用代码优化方法, 基本块的优化
  9. freetextbox java_FreeTextBox使用详解 (版本3.1.1)
  10. 【xinput1_3.dll下载】xinput1_3.dll丢失怎么修复win10
  11. 100 bugs in Open Source C/C++ projects
  12. 最完整最全面的汉化中文游戏列表
  13. 杨森翔人日诗词;人日书法
  14. 图像处理之颜色检测分类标记(Python OpenCV实现)
  15. 苹果在旧设备中修复了两个 iOS 零日漏洞
  16. 【论文 CCF C】Multi-DQN: An ensemble of Deep Q-learning agents for stock market forecasting
  17. 简单编程(十一)简单编程 判断并输出500以内既能够被3整除又能够被6整除的整数。
  18. java斗地主代码_实例解析java如何实现斗地主代码
  19. APAP-基础知识(内表SORT的使用)
  20. 模拟CSDN请求,做一点事

热门文章

  1. SimpleMind Pro中文版
  2. 夜光 带你走进微信小程序研发(三)
  3. nvm环境安装和 node 的基本使用
  4. [转贴]变态的C自增
  5. 转贴:华为加班死人了
  6. 转:作为一个HR,说说应届生及一两年往届生的注意情况。
  7. 使用C# 实现串口拨号器的SIM卡通信
  8. Unity3d简单的发牌效果
  9. 妥妥解决MySqL中文乱码问题,就这么任性
  10. 企业级前端项目组框架vue-fpg介绍