springboot+springcloud + elasticsearch (电商平台搜索功能)

搜索关键字查询
0.1 构建查询条件
1). 需求

在这里插入图片描述
2). 接口定义
public interface SearchService {
//按照查询条件进行数据查询
Map search(Map<String,String> searchMap);
}

方法形参 Map : 关键字 , 品牌 , 规格 , 价格 , 排序, 分页参数 ;
返回值为 Map : 分页结果 , 结果列表 , 品牌 , 规格 … ;
3). 接口实现-条件封装
技术 : SpringDataElasticSearch ; ------> 并未使用原生API : RestHighLevelClient
1). 封装请求条件
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 多条件查询使用 , 通过该boolQuery组装多个条件

//根据关键字搜索
if (StringUtils.isNotEmpty(searchMap.get(“keywords”))){
boolQuery.must(QueryBuilders.matchQuery(“name”,searchMap.get(“keywords”)).operator(Operator.AND));//必须满足
}
//… boolQuery组装多个条件

nativeSearchQueryBuilder.withQuery(boolQuery);

2). 执行请求
AggregatedPage resultInfo = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
@Override
public AggregatedPage mapResults(SearchResponse searchResponse, Class aClass, Pageable pageable) {
//查询结果操作
List list = new ArrayList<>();

    //获取查询命中结果数据
    SearchHits hits = searchResponse.getHits();
    if (hits != null){
        //有查询结果
        for (SearchHit hit : hits) {
            //SearchHit转换为skuinfo
            SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
            list.add((T) skuInfo);
        }
    }
    return new AggregatedPageImpl<T>(list,pageable,hits.getTotalHits(),searchResponse.getAggregations());
}

});

//总记录数
resultMap.put(“total”,resultInfo.getTotalElements());
//总页数
resultMap.put(“totalPages”,resultInfo.getTotalPages());
//数据集合
resultMap.put(“rows”,resultInfo.getContent());

在这里插入图片描述
可以参考ElasticSearch的原始操作请求Rest接口, 理解 :
请求链接 : POST http://192.168.192.152:9200/skuinfo/docs/_search
请求参数 :
{“query”:{
“bool”:{
“must”:[
{“match”:{“name”:“手机”}}
]
}
},
“from”:0,
“size”:2
}

0.2 Controller
@RestController
@RequestMapping("/search")
public class SearchController {

@Autowired
private SearchService searchService;

@GetMapping //?key=value&key=value
public Map search(@RequestParam Map<String,String> searchMap){
    Map searchResult = searchService.search(searchMap);
    return searchResult;
}

}

1.搜索 - 条件筛选
1.1 介绍
在这里插入图片描述

1.2 品牌过滤查询
//按照品牌进行过滤查询
// 1.4 根据品牌进行过滤查询
// 如果请求参数中包含brand 品牌 且数据信息不为空
if (StringUtils.isNotEmpty(searchMap.get(“brand”))){
// 使用boolQuery进行过滤 因为是进行过滤查询,所以不进行分词,直接精确匹配
// 第一个参数为需要进行过滤查询的域 第二个字段为需要匹配的值
boolQuery.filter(QueryBuilders.termQuery(“brandName”,searchMap.get(“brand”)));
}

matchQuery : 会对查询的关键字进行分词 , 然后再进行匹配 ;
termQuery : 不会对关键字进行分词, 精准匹配 ;
1.3 品牌聚合查询
1). 品牌列表数据来源
在这里插入图片描述

2). 在数据库中应该如何实现
SELECT brand_name AS brandName FROM tb_sku WHERE NAME LIKE ‘%手机%’ GROUP BY brand_name;

在这里插入图片描述
3). ES中的实现方式
A. 设置分组条件 (根据那个域进行分组)
// 1.5.1 对于品牌进行聚合查询,关键字搜索不同时,所对应的品牌也不相同,所以对应品牌进行聚合查询
// 第一个参数为给分组查询起的别名。 第二个参数为需要对那个域进行分组查询
String skuBrand = “skuBrand”;
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuBrand).field(“brandName”));

B. 解析分组结果
// 1.5.2获取resultInfo的品牌分组数据,进行封装
StringTerms brandTerms = (StringTerms) resultInfo.getAggregation(skuBrand);
// 1.5.3使用stream流将brandTerms中的数据取出,并转换为list。封装在mapResult中
List brandList = brandTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
resultMap.put(“brandList”,brandList);

1.4 规格过滤查询
1). 参数传递
在这里插入图片描述

2). 代码实现
// 1.6 对于规格信息进行过滤查询
// 1.6.1在前台发送数据的时候因为规格的数据也跟随着品牌的变化,而不相同,所以约定规格数据发送的时候为spec_开头
// 1.6.2遍历所有接收到的请求参数
for (String key : searchMap.keySet()) {
// 1.6.3如果key的开头为"spec_"则证明是传递的规格参数 例如spec_尺寸
if (key.startsWith(“spec_”)){
// 1.6.4获取到所对应的值
String value = searchMap.get(key);
//1.6.5 对指定的域进行过滤
boolQuery.filter(QueryBuilders.termQuery((“specMap.” + key.substring(5) + “.keyword”),value));
}
}

URL编解码:
浏览器在请求后端接口时, 会对url中的中文 , 特殊符号 , 进行URL编码;
服务端获取到数据之后, 会自动对url进行解码 ;

  • -----------> 2B%
    1.5 规格分组聚合
    A. 设置分组条件

// 1.6.6 对于规格进行聚合查询,关键字搜索不同,所对应的规格也不同,
String skuSpec = “skuSpec”;
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuSpec).field(“spec.keywords”));

B. 解析分组结果
// 1.6.7 获取resultInfo的规格分组数据,进行封装
StringTerms specTerms = (StringTerms) resultInfo.getAggregation(skuSpec);
// 1.6.8 使用 stream流将specTerms中的数据取出,并转换为list,封装在mapResult中
List specList = specTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
resultMap.put(“specList”,specList);

1.6 价格区间过滤查询
1). 前端参数传递

在这里插入图片描述
price : 0-500 , 500-1000 , 3000
2). 代码实现
// 1.7对于价格进行过滤查询
// 价格传递过来的关键字为 0-500 或者3000+ 这样的。所以进行对字符串进行切割
if (StringUtils.isNotEmpty(searchMap.get(“price”))) {
String[] prices = searchMap.get(“price”).split("-");
// 1.7.1如果prices的长度为2 则证明里面封装了一个范围, 否则为大于该价格
if (prices.length == 2) {
// 1.7.2 定义价格区间为小于 prices[1]
boolQuery.filter(QueryBuilders.rangeQuery(“price”).lte(prices[1]));
}
// 1.7.3定义价格区间大于prices[0]
boolQuery.filter(QueryBuilders.rangeQuery(“price”).gte(prices[0]));
}

2.搜索 - 分页
// 1.8对应页面进行分页设置
// 1.8.1定义当前页的页码
String pageNum = searchMap.get(“pageNum”);
// 1.8.2定义每页显示的页码
String pageSize = searchMap.get(“pageSize”);
// 1.8.3 如果客户端每页传递分页的条件,则自定义一个
if (StringUtils.isEmpty(searchMap.get(“pageNum”))) {
pageNum = “1”;
}
if (StringUtils.isEmpty(searchMap.get(“pageSize”))) {
pageSize = “50”;
}
// 1.8.4 进行分页查询的设置
// 第一个参数为当前页:索引从0开始
// 第二个参数为每页显示的数据数,
nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum) - 1,Integer.parseInt(pageSize)));

注意: 查询的页面时从0开始的 ;
3.搜索 - 排序
1). 前端传递参数

在这里插入图片描述
sortField : price
sortRule : 升序ASC / 降序DESC
2). 代码实现
// 1.当前域 2.当前的排序操作(升序ASC,降序DESC)
// 1.9 对于价格栏进行排序
// 1.9.1如果分组字段sortFiled 以及分组的条件sortRule 不为空,则进行排序
if (StringUtils.isNotEmpty(searchMap.get(“sortField”))&&StringUtils.isNotEmpty(searchMap.get(“sortRule”))){
if (“ASC”.equals(searchMap.get(“sortRule”))){
//升序
// 第一个参数为需要进行排序的字段,第二个参数为排序的规则
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort((searchMap.get(“sortField”))).order(SortOrder.ASC));
}else{
//降序
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort((searchMap.get(“sortField”))).order(SortOrder.DESC));
}
}

4.搜索 - 高亮展示
4.1 高亮原理
手机
手机
手机
手机
高亮三要素 : 前缀 , 后缀 , 高亮域
4.2 高亮代码实现
1). 设置高亮参数
// 1.10 对关键字进行高亮查询
// 1.10.1设置高亮域
HighlightBuilder.Field filed = new HighlightBuilder.Field(“name”)
// 1.10.2这是高亮样式的前缀
.preTags("")
// 1.10.3这是高亮样式的后缀
.postTags("
");
nativeSearchQueryBuilder.withHighlightFields(filed);

2). 解析高亮结果
// 1.10.4解析高亮结果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
// 1.10.5非空校验,进行替换原来的内容
if (null != highlightFields && highlightFields.size() > 0) {
skuInfo.setName(highlightFields.get(“name”).getFragments()[0].toString());
}

在这里插入图片描述

6.1 搜索接口开发

在这里插入图片描述
API :

在这里插入图片描述

发布了92 篇原创文章 · 获赞 3 · 访问量 2796

猜你喜欢

转载自blog.csdn.net/weixin_44993313/article/details/104402113