对于课程搜索页面中,需要完成下面的功能,如下:
课程搜索功能
课程搜索功能需求:
1,分页数据查询
2,根据关键字进行查询
3,根据课程分类和课程等级条件查询
根据关键字进行查询后的内容要高亮显示
在上面的查询条件需求中,需要使用DSL不同的查询方式进行实现。
1,关键字查询,要查询name和description中的数据,对此要使用MatchQuery查询方式来进行查询,查询的数据会先分词再查询。
2,课程等级和课程分类查询,要使用精确匹配,对此使用termQuery查询方式来查询,查询数据不会进行分词
3,优于是多个查询方式共同进行查询,对此要使用BooleanQuery来组合多个查询方式
4,对于课程等级和课程分类精确匹配查询,课程使用BooleanQuery查询中的过滤器来查询数据,这样可以提高查询效率
课程检索交互流程如下:
步骤描述:
1,前端输入检索参数,发起课程检索请求
2,课程搜索服务封装检索参数为ES检索条件,向ES服务发起检索请求
3,ES服务执行检索,并返回检索结果
4,课程检索服务封装ES检索结果为课程信息列表,最终返回给前端
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 {
PageVO 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;
@Slf4j
@Service
public class CoursePubSearchServiceImpl implements CoursePubSearchService {
@Value("${xuecheng.elasticsearch.course.index}")
private String indexName;
@Autowired
private RestHighLevelClient client;
public PageVO queryCoursePubIndex(PageRequestParams params,
QueryCoursePubModel model) {
PageVO 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;
}
private PageVO parseResponse(SearchResponse searchResponse,PageRequestParams params) {
// 1.获得响应数据的大hits
SearchHits hits = searchResponse.getHits();
// 2.查询的总条数
long totalCount = hits.getTotalHits().value;
// 3.获得小hits
SearchHit[] hitsHits = hits.getHits();
ArrayList 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 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 pageVO = new PageVO<>(list,totalCount,params.getPageNo(),params.getPageSize());
return pageVO;
}
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("");
highlightBuilder.postTags("");
highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
sourceBuilder.highlighter(highlightBuilder);
// 3.构建查询方式
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 coursePubIndexByCondition(PageRequestParams pageParams, @RequestBody QueryCoursePubModel queryModel) {
PageVO pageVO = couresPubSearchService.queryCoursePubIndex(pageParams, queryModel);
return pageVO;
}
}



