课程检索功能开发

对于课程搜索页面中,需要完成下面的功能,如下:
在这里插入图片描述
课程搜索功能
课程搜索功能需求:
1,分页数据查询
2,根据关键字进行查询
3,根据课程分类和课程等级条件查询
根据关键字进行查询后的内容要高亮显示
在上面的查询条件需求中,需要使用DSL不同的查询方式进行实现。
1,关键字查询,要查询name和description中的数据,对此要使用MatchQuery查询方式来进行查询,查询的数据会先分词再查询。
2,课程等级和课程分类查询,要使用精确匹配,对此使用termQuery查询方式来查询,查询数据不会进行分词
3,优于是多个查询方式共同进行查询,对此要使用BooleanQuery来组合多个查询方式
4,对于课程等级和课程分类精确匹配查询,课程使用BooleanQuery查询中的过滤器来查询数据,这样可以提高查询效率
课程检索交互流程如下:
在这里插入图片描述
步骤描述:
1,前端输入检索参数,发起课程检索请求
2,课程搜索服务封装检索参数为ES检索条件,向ES服务发起检索请求
3,ES服务执行检索,并返回检索结果
4,课程检索服务封装ES检索结果为课程信息列表,最终返回给前端

1.2 课程检索接口定义

1.接口参数列表

根据前后端传入参数列表来定义接口
在这里插入图片描述
接口传入传出列表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.传入传出参数封装类

  • 传入参数封装

在 xc-api 工程的 com.xuecheng.api.search.model.qo 包下创建类,如下:

QueryCoursePubModel为课程检索参数

@ApiModel(value = "QueryCoursePubModel",description = "课程索引搜索条件查询对象")
public class QueryCoursePubModel {

    @ApiModelProperty("查询关键字")
    private String keyword;

    @ApiModelProperty("课程二级分类")
    private String mt;

    @ApiModelProperty("课程三级分类")
    private String st;

    @ApiModelProperty("课程等级")
    private String grade;

    @ApiModelProperty("排序字段, 推荐/最新/热评")
    private String sortFiled;
}
  • 传出参数封装类

在 xc-api 工程的 com.xuecheng.api.search.model 包下创建类,如下:

CoursePubIndexDTO为检索返回的课程信息

@Data
@ApiModel(value="CoursePubIndexDTO", description="课程发布")
public class CoursePubIndexDTO implements Serializable {

    @ApiModelProperty(value = "主键")
    private Long indexId;

    @ApiModelProperty(hidden = true)
    private Long course_id;

    @ApiModelProperty(value = "课程标识",example = "1")
    public Long getCourseId() {
        return course_id;
    }

    @ApiModelProperty(hidden = true)
    private Long company_id;

    @ApiModelProperty(value = "机构ID",example = "1")
    public Long getCompanyId() {
        return company_id;
    }

    @ApiModelProperty(hidden = true)
    private String company_name;

    @ApiModelProperty(value = "公司名称")
    public String getCompanyName() {
        return company_name;
    }

    @ApiModelProperty(value = "课程名称")
    private String name;

    @ApiModelProperty(value = "适用人群")
    private String users;

    @ApiModelProperty(value = "标签")
    private String tags;

    @ApiModelProperty(value = "大分类")
    private String mt;

    @ApiModelProperty(value = "大分类名称")
    private String mtName;

    @ApiModelProperty(value = "小分类")
    private String st;

    @ApiModelProperty(value = "小分类名称")
    private String stName;

    @ApiModelProperty(value = "课程等级")
    private String grade;

    @ApiModelProperty(value = "教育模式(common普通,record 录播,live直播等)")
    private String teachmode;

    @ApiModelProperty(value = "课程图片")
    private String pic;

    @ApiModelProperty(value = "课程介绍")
    private String description;

    @ApiModelProperty(value = "所有课程计划,json格式")
    private String teachplan;

    @ApiModelProperty(hidden = true)
    private Date create_date;

    @ApiModelProperty(value = "发布时间")
    public Date getCreateDate() {
        return create_date;
    }

    @ApiModelProperty(hidden = true)
    private Date change_date;

    @ApiModelProperty(value = "修改时间")
    public Date getChangeDate() {
        return change_date;
    }

    @ApiModelProperty(hidden = true)
    private Integer is_latest;

    @ApiModelProperty(value = "是否最新课程(1最新)")
    public Integer getIsLatest() {
        return is_latest;
    }

    @ApiModelProperty(hidden = true)
    private Integer is_pub;

    @ApiModelProperty(value = "是否发布(1发布 0取消发布)")
    public Integer getIsPub() {
        return is_pub;
    }

    @ApiModelProperty(value = "状态(1正常  0删除)")
    private String status;

    @ApiModelProperty(value = "备注")
    private String remark;
    
    @ApiModelProperty(value = "课程营销数据")
    private String market;
    
    @ApiModelProperty(value = "收费规则,对应数据字典--203")
    private String charge;

    @ApiModelProperty(value = "现价")
    private Float price;

    @ApiModelProperty(value = "有效性,对应数据字典--204")
    private String valid;

    @ApiModelProperty(value = "学习人数")
    private Long learners;
    
    @ApiModelProperty(value = "课程评论数")
    private Long comment_num;
}

1.3 接口实现

(1)服务层实现

接口定义:

在 xc-content-search-service 工程的 **com.xuecheng.search.service**包下新增以下接口:

/**
 *  课程搜索服务层
 */
public interface CoursePubSearchService {

    /**
     * 根据条件分页查询
     * @param pageRequestParams {@link PageRequestParams} 分页数据封装对象
     * @param queryModel {@link QueryCoursePubModel} 条件查询对象
     * @return PageVO 分页封装数据
     */
    PageVO<CoursePubIndexDTO> queryCoursePubIndex(PageRequestParams pageRequestParams, QueryCoursePubModel queryModel);
}

服务层实现:

在 xc-content-search-service 工程的 **com.xuecheng.search.service.impl**包下新增以下实现类:

package com.xuecheng.search.service.impl;

import com.xuecheng.api.search.model.dto.CoursePubIndexDTO;
import com.xuecheng.api.search.model.qo.QueryCoursePubIndexModel;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.search.common.constant.ContentSearchErrorCode;
import com.xuecheng.search.service.CoursePubSearchService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;

/**
 *  课程搜索服务实现层(es原始Api实现)
 */
@Slf4j
@Service
public class CoursePubSearchServiceImpl implements CoursePubSearchService {
  @Value("${xuecheng.elasticsearch.course.index}")
    private String indexName;

    @Autowired
    private RestHighLevelClient client;

    /*
    * 主方法的分析:
    *   1.创建出SearchRequest
    *       1.1 SearchRequest 设置索引库的名称
    *       1.2 创建SearchSourceBuilder
    *           分页
    *           查询方式
    *           高亮
    *           排序(作业)
    *   2.获得响应数据SearchResponse
    *       通过Client方法Search方法来获得Response
    *   3.解析结果数据并封装PageVO中
    *       获得大Hits
    *       从大Hits获得总条数
    *       从大Hits获得小Htis数组
    *       遍历小Hits
    *       从小Hist中获得
    *           文档id
    *           文档的源数据_source
    *           文档的高亮
    * */
    public PageVO<CoursePubIndexDTO> queryCoursePubIndex(PageRequestParams params,
                                                             QueryCoursePubModel model) {
        PageVO<CoursePubIndexDTO> pageVO = null;


        try {
            // 1.创建出SearchRequest
            SearchRequest request = getSearchRequest(params,model);

            // 2.获得响应数据SearchResponse
            SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);

            // 3.解析结果数据并封装PageVO中
            pageVO = parseResponse(searchResponse,params);
        } catch (IOException e) {
            log.error("课程搜索数据失败:{}", e.getMessage());
            ExceptionCast.cast(ContentSearchErrorCode.E_150001);
        }

        return pageVO;
    }

    /*
    * 解析结果数据并封装PageVO中
    * */
    private PageVO<CoursePubIndexDTO> parseResponse(SearchResponse searchResponse,PageRequestParams params) {

        // 1.获得响应数据的大hits
        SearchHits hits = searchResponse.getHits();

        // 2.查询的总条数
        long totalCount = hits.getTotalHits().value;

        // 3.获得小hits
        SearchHit[] hitsHits = hits.getHits();


        ArrayList<CoursePubIndexDTO> list = new ArrayList<>();

        // 4.遍历小hits封装数据到PageVO中
        for (SearchHit hitsHit : hitsHits) {

            // 获得文档的源数据内容
            String id = hitsHit.getId();

            String jsonString = hitsHit.getSourceAsString();

            CoursePubIndexDTO dto = JsonUtil.jsonToObject(jsonString, CoursePubIndexDTO.class);

            dto.setIndexId(new Long(id));

            // 获得高亮数据
            Map<String, HighlightField> highlightFields = hitsHit.getHighlightFields();

            HighlightField highlightField = highlightFields.get("name");

            if (!(ObjectUtils.isEmpty(highlightField))) {

                Text[] fragments = highlightField.getFragments();

                StringBuilder stringBuilder = new StringBuilder();

                for (Text fragment : fragments) {
                    stringBuilder.append(fragment);
                }

                String highLightName = stringBuilder.toString();

                dto.setName(highLightName);
            }

            list.add(dto);
        }

        PageVO<CoursePubIndexDTO> pageVO = new PageVO<>(list,totalCount,params.getPageNo(),params.getPageSize());

        return pageVO;
    }

    /*
        课程搜索功能需求:

            1.分页数据查询

            2.根据关键字进行查询

            3.根据课程分类和课程等级条件查询

            4.根据关键字进行查询后的内容要高亮显示。

    * 创建出SearchRequest
    *
    *
    *   1 SearchRequest 设置索引库的名称
        2 创建SearchSourceBuilder
            分页
            查询方式
            高亮
            排序(作业)
        3.构建查询方式
             Boolean
                must:
                    MultiMatchQuery
                filter:
                    TermQuery
        4.设置查询源数据对象
    *
    * */
    private SearchRequest getSearchRequest(PageRequestParams params,
                                           QueryCoursePubModel model) {

        // 0.判断分页数据
        if (params.getPageNo() < 1) {
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }
        if (params.getPageSize() < 1) {
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }


        // 1.创建SearchRequest对象
        SearchRequest request = new SearchRequest(indexName);

        // 2.创建搜索源数据对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 2.1 设置分页数据

        Long pageNo = params.getPageNo();

        Integer pageSize = params.getPageSize();


        int from = (pageNo.intValue() - 1) * pageSize;

        sourceBuilder.from(from);
        sourceBuilder.size(pageSize);



        // 2.2 设置高亮数据
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color='red'><b>");
        highlightBuilder.postTags("</b></font>");
        highlightBuilder.fields().add(new HighlightBuilder.Field("name"));

        sourceBuilder.highlighter(highlightBuilder);


        // 3.构建查询方式
        /*
        *   根据关键字进行查询
        *       1.对关键字内容需要进行分词
        *       2.需要对多个字段进行查询
        *
        *       MultiMatchQuery
        *           1.匹配查询--> 对查询的内容先分词后查询
        *           2.可以这是对个字段
        *
            根据课程分类和课程等级条件查询
               课程分类--> TermQuery
               课程等级--> TermQuery

             对于不分词的查询方式,我们可以使用过滤器来提高查询效率

            上面查询是多种查询方式来构成:BooleanQuery可以对多种查询方式来一同查询

        * */
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();



        String keyword = model.getKeyword();
        if (StringUtil.isNotBlank(keyword)) {
            MultiMatchQueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(keyword, "name", "description")
                    .minimumShouldMatch("80%").field("name",3);
            boolQueryBuilder.must(queryBuilder);
        }


        String grade = model.getGrade();
        String mt = model.getMt();
        String st = model.getSt();


        if (StringUtil.isNotBlank(grade)) {
            TermQueryBuilder gradeTerm = QueryBuilders.termQuery("grade", grade);
            boolQueryBuilder.filter(gradeTerm);
        }

        if (StringUtil.isNotBlank(mt)) {
            TermQueryBuilder mtTerm = QueryBuilders.termQuery("mt", mt);
            boolQueryBuilder.filter(mtTerm);
        }

        if (StringUtil.isNotBlank(st)) {
            TermQueryBuilder stTerm = QueryBuilders.termQuery("st", st);
            boolQueryBuilder.filter(stTerm);
        }


        sourceBuilder.query(boolQueryBuilder);


        // 将查询源数据对象存放到Request
        request.source(sourceBuilder);


        return request;
    }
}

(2)Controller实现

CoursePubSearchController中实现课程检索接口

/**
 * 课程搜索服务控制层
 */
@RestController
@RequestMapping
public class CoursePubSearchController implements CoursePubSearchApi {

    @Resource 
    private CoursePubSearchService couresPubSearchService;


    @PostMapping("course_index")
    public PageVO<CoursePubIndexDTO> coursePubIndexByCondition(PageRequestParams pageParams, @RequestBody QueryCoursePubModel queryModel) {
        PageVO<CoursePubIndexDTO> pageVO = couresPubSearchService.queryCoursePubIndex(pageParams, queryModel);
        return pageVO;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值