文章目录

第 5 章 搜索解决方案 - 3 分页与排序

学习目标:

无新技术, 完成业务逻辑, 依旧 es
完成搜索分页功能
完成搜索排序功能
完成搜索高亮功能

1 搜索分页

1.1 需求分析

实现搜索分页功能,每页显示 30 条记录。实现商品列表下方分页栏。

1.2 分页语法与代码

1.2.1 分页语法

需求:每页显示 30 条记录,查询第 3 页内容。分页语法如下:

GET sku/_search
        {
        "from": 60,
        "size": 30
        }


1.2.2 分页代码

需求:每页显示 30 条记录,查询第 3 页内容。分页代码如下:

searchSourceBuilder.size(30);
        searchSourceBuilder.from(60)

1.3 代码实现

1.3.1 分页查询逻辑

实现思路:前端向后端传递参数 pageNo (页码)
(1)修改 SkuSearchServiceImpl 的 search 方法,添加代码

//分页
Integer pageNo = Integer.parseInt(searchMap.get("pageNo"));//页码
        Integer pageSize = 30;//页大小
//起始记录下标
        int fromIndex = (pageNo ‐ 1) * pageSize;
        searchSourceBuilder.from(fromIndex);//开始索引
        searchSourceBuilder.size(pageSize);//页大小
 
 

(2)修改 SearchController 的 search 方法,添加代码

//接受页码处理,如果不传递页码默认为第1页
if(searchMap.get("pageNo")==null){
        searchMap.put("pageNo","1");
        }
 

1.3.2 页码渲染

实现思路:后端逻辑根据总记录数和每页记录数计算总页数,模板根据总页数渲染生成页码。
(1)修改 SkuSearchServiceImpl 的 search 方法,添加代码

//计算总页数
long totalCount = searchHits.getTotalHits();//总记录数
        long pageCount = (totalCount % pageSize == 0) ? totalCount / pageSize :
        (totalCount / pageSize + 1);
        resultMap.put("totalPages", pageCount);
 
 

(2)修改 SearchController 的 search 方法,添加代码

int pageNo =Integer.parseInt(searchMap.get("pageNo")) ;//当前页
        model.addAttribute("pageNo",pageNo);
 
 

(3)修改 search.html 页码部分

<li th:class="${page==pageNo?'active':''}"
        th:each="page:${#numbers.sequence(1,result.totalPages)}">
<a
th:href="${#strings.replace(url,'&pageNo='+searchMap.pageNo,'&pageNo='+pa
        ge )}" th:text="${page}">
</a>
</li>
 

numbers.sequence(开始索引,截至索引)可以产生从开始索引到截至索引的数组

1.3.3 页码数量控制

我们刚才的代码显示页码会全部显示,如果页码很多,也不利于显示,所以,我们
可以显示当前页前后的 5 个页码即可
(1)修改 SearchController 的 search 方法,添加代码

Long totalPages= (Long) result.get("totalPages");//得到总页数
        int startPage=1;//开始页码
        int endPage =totalPages.intValue();//截至代码
        if(totalPages>5){
        startPage=pageNo‐2;
        if(startPage<1){
        startPage=1;
        }
        endPage=startPage+4;
        }
        model.addAttribute("startPage",startPage);
        model.addAttribute("endPage",endPage);
 
 

(2)修改 search.html 的页码部分

<li class="dotted" th:if="${startPage>1}"><span>...</span></li>
<li th:class="${page==pageNo?'active':''}"
        th:each="page:${#numbers.sequence(startPage,endPage)}">
<a
th:href="${#strings.replace(url,'&pageNo='+searchMap.pageNo,'&pageNo='+pa
        ge )}" th:text="${page}"></a>
</li>
<li class="dotted" th:if="${endPage<result.totalPages}"><span>...</span>
</li>
 
 

1.3.4 上一页与下一页

修改 search.html 上一页

<li class="prev" th:if="${pageNo>1}">
<a th:href="${#strings.replace(url,'&pageNo='+pageNo ,'&pageNo='+
        (pageNo‐1) )}">«</a>
</li>
<li class="prev disabled" th:if="${pageNo<=1}">
<a>«</a>
</li>
 

下一页

<li class="next" th:if="${pageNo<result.totalPages }">
<a th:href="${#strings.replace(url,'&pageNo='+pageNo, '&pageNo='+
        (pageNo+1) )}">»</a>
</li>
<li class="next disabled" th:if="${pageNo>=result.totalPages }">
<a>»</a>
</li>
 

2 搜索排序

2.1 需求分析

实现按销量排序、新品排序、评论排序和价格排序

2.2 排序语法与代码

2.2.1 排序语法

需求:按价格升序排序,语法如下:

GET sku/_search
        {
        "sort": [
        {
        "price": {
        "order": "asc"
        }
        }
        ]
        }
 

如果是降序,则指定 order 为 desc

2.2.2 排序代码

需求:按价格升序排序,代码如下:

searchSourceBuilder.sort("price", SortOrder.ASC);
 
 

如果是降序,则指定为 SortOrder.DESC

2.3 代码实现

2.3.1 销量排序

(1)修改 SearchController 的 search 方法,添加代码

//排序参数容错处理
if(searchMap.get("sort")==null){
        searchMap.put("sort","");
        }
        if(searchMap.get("sortOrder")==null){
        searchMap.put("sortOrder","DESC");
        }
 

(2)修改 SkuSearchServiceImpl 的 searchList 方法,添加代码

//排序
String sort = searchMap.get("sort");//排序字段
        String sortOrder = searchMap.get("sortOrder");//排序规则
        if(!"".equals(sort)){
        searchSourceBuilder.sort(sort, SortOrder.valueOf(sortOrder));
        }
 
 

(3)修改 search.html 的排序标签

<li th:class="${searchMap.sort==''?'active':''}">
<a
th:href="${#strings.replace(url,'&sortOrder='+searchMap.sortOrder+'&sort=
        '+searchMap.sort,'&sortOrder=DESC&sort=' )}">综合</a>
</li>
<li th:class="${searchMap.sort=='saleNum'?'active':''}">
<a
th:href="${#strings.replace(url,'&sortOrder='+searchMap.sortOrder+'&sort=
        '+searchMap.sort,'&sortOrder=DESC&sort=saleNum' )}">销量</a>
</li>
 

2.3.2 新品排序

<li th:class="${searchMap.sortField=='createTime'}?'active':''">
<a th:href="|${#strings.replace(url,'&sortRule='+
        searchMap.get('sortRule') +'&sortField='+searchMap.get('sortField')
        ,'')}&sortRule=DESC&sortField=createTime|">新品</a>
</li>
 

2.3.3 评论排序

<li th:class="${searchMap.sortField=='commentNum'}?'active':''">
<a th:href="|${#strings.replace(url,'&sortRule='+
        searchMap.get('sortRule') +'&sortField='+searchMap.get('sortField')
        ,'')}&sortRule=DESC&sortField=commentNum|">评价</a>
</li>
 

2.3.4 价格排序

修改 search.html

3 搜索高亮

3.1 需求分析

所谓高亮,就是使用特别的样式修饰某字段中包含的搜索关键字。
需求:实现搜索高亮,商品名称使用红色显示搜索关键字。

3.2 高亮语法与代码

3.2.1 高亮语法

使用默认高亮显示来获取每个搜索命中 title 字段的高亮显示,在指定 title 字段的查询请求中包含高亮显示对象。
执行查询

GET /sku/doc/_search
{
  "query": {
    "match": {
      "name": "电视"
    }
  },
  "highlight": {
    "fields": {
      "categoryName": {}
    }
  },
  "size": 2
}
 

返回查询结果:可以看到关键字区域不同

自定义高亮 执行查询:

GET /sku/doc/_search
{
  "query": {
    "match": {
      "name": "电视"
    }
  },
  "highlight": {
    "fields": {
      "price": {
        "pre_tags": "<font style='color:red'>",
        "post_tags": "</font>"
      }
    }
  },
  "size": 2
}
 
 

返回结果(高亮部分)

"highlight" : {
        "name" : [
        "Apple 苹果 iPhone XR <font style='color:red'>手机</font> 全网通
        4G<font style='color:red'>手机</font> 黑色 128GB"
        ]
        }
 
 

3.2.2 高亮代码

高亮设置:

//设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name").preTags("<fontstyle='color:red'>").postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);
 

获取高亮结果:

for(SearchHit hit:hits){
//提取高亮内容
        Map<String, HighlightField> highlightFields =
        hit.getHighlightFields();
        HighlightField highlightFieldName = highlightFields.get("name");
        Text[] fragments = highlightFieldName.fragments();
        String name = fragments[0].toString();
        System.out.println(name);
        }
 

3.3 代码实现

(1)修改 SkuSearchServiceImpl 的 search 方法,在第一个代码块中添加高亮显示处理代

//设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name").preTags("<font
        style='color:red'>").postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);
 
 

修改 //2.1 商品列表部分代码

//2.1 商品列表
List<Map<String,Object>> resultList=new ArrayList<Map<String, Object>>();
        for(SearchHit hit:hits){
        Map<String, Object> skuMap = hit.getSourceAsMap();
//name高亮处理
        Map<String, HighlightField> highlightFields =
        hit.getHighlightFields();
        HighlightField name = highlightFields.get("name");
        Text[] fragments = name.fragments();
        skuMap.put("name",fragments[0].toString());//用高亮的内容替换原内容
        resultList.add(skuMap);
        }
        resultMap.put("rows",resultList);
 

(3)修改模板中商品名称部分

<div class="attr">
<a target="_blank" href="item.html" th:utext="${sku.name}"></a>
</div>