实现高亮的全文分页检索

🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)

Sun Frame Banner

轻松高效的现代化开发体验

Sun Frame 是我个人开源的一款基于 SpringBoot 的轻量级框架,专为中小型企业设计。它提供了一种快速、简单且易于扩展的开发方式。

我们的开发文档记录了整个项目从0到1的任何细节,实属不易,请给我们一个Star!🌟
您的支持是我们持续改进的动力。

🌟 亮点功能

  • 组件化开发:灵活选择,简化流程。
  • 高性能:通过异步日志和 Redis 缓存提升性能。
  • 易扩展:支持多种数据库和消息队列。

📦 spring cloud模块概览

  • Nacos 服务:高效的服务注册与发现。
  • Feign 远程调用:简化服务间通信。
  • 强大网关:路由与限流。

常用工具

  • 日志管理:异步处理与链路追踪。
  • Redis 集成:支持分布式锁与缓存。
  • Swagger 文档:便捷的 API 入口。
  • 测试支持:SpringBoot-Test 集成。
  • EasyCode:自定义EasyCode模板引擎,一键生成CRUD。

🔗 更多信息


1.sun-club-infra 模块

SubjectEsServiceImpl.java
1.querySubjectList方法:根据查询请求进行查询
@Override
public PageResult<SubjectInfoEs> querySubjectList(SubjectInfoEs req) {
    // 构建一个返回的分页对象
    PageResult<SubjectInfoEs> pageResult = new PageResult<>();
    // 根据信息构建一个查询请求对象
    EsSearchRequest esSearchRequest = createSearchListQuery(req);
    // 获得分页查询结果
    SearchResponse searchResponse = EsRestClient.searchWithTermQuery(getEsIndexInfo(), esSearchRequest);
    // 构建一个要返回的数据对象
    List<SubjectInfoEs> subjectInfoEsList = new LinkedList<>();
    // 得到命中的数据
    SearchHits searchHits = searchResponse.getHits();
    if (searchHits == null || searchHits.getHits() == null) {
        pageResult.setPageNo(req.getPageNo());
        pageResult.setPageSize(req.getPageSize());
        pageResult.setRecords(subjectInfoEsList);
        pageResult.setTotal(0);
        return pageResult;
    }
    // 如果有命中的
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        // 将每个hit都转换为SubjectInfoEs,然后将其放到要返回的数据对象中
        SubjectInfoEs subjectInfoEs = convertHit2SubjectInfoEs(hit);
        subjectInfoEsList.add(subjectInfoEs);
    }

    // 构建分页对象
    pageResult.setPageNo(req.getPageNo());
    pageResult.setPageSize(req.getPageSize());
    pageResult.setRecords(subjectInfoEsList);
    pageResult.setTotal(Long.valueOf(searchHits.getTotalHits().value).intValue());

    return pageResult;
}
2.convertHit2SubjectInfoEs方法:将每一个hit转换为SubjectInfoEs
/**
 * 将每一个hit转换为SubjectInfoEs
 * @param hit
 * @return
 */
private SubjectInfoEs convertHit2SubjectInfoEs(SearchHit hit) {
    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
    if (CollectionUtils.isEmpty(sourceAsMap)) {
        return null;
    }
    SubjectInfoEs result = new SubjectInfoEs();
    // 封装基本数据
    result.setSubjectId(MapUtils.getLong(sourceAsMap, EsSubjectFields.SUBJECT_ID));
    result.setSubjectName(MapUtils.getString(sourceAsMap, EsSubjectFields.SUBJECT_NAME));

    result.setSubjectAnswer(MapUtils.getString(sourceAsMap, EsSubjectFields.SUBJECT_ANSWER));

    result.setDocId(MapUtils.getLong(sourceAsMap, EsSubjectFields.DOC_ID));
    result.setSubjectType(MapUtils.getInteger(sourceAsMap, EsSubjectFields.SUBJECT_TYPE));
    result.setScore(new BigDecimal(String.valueOf(hit.getScore())).multiply(new BigDecimal("100.00")
            .setScale(2, RoundingMode.HALF_UP)));

    //处理name的高亮
    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
    HighlightField subjectNameField = highlightFields.get(EsSubjectFields.SUBJECT_NAME);
    if(Objects.nonNull(subjectNameField)){
        Text[] fragments = subjectNameField.getFragments();
        StringBuilder subjectNameBuilder = new StringBuilder();
        for (Text fragment : fragments) {
            subjectNameBuilder.append(fragment);
        }
        result.setSubjectName(subjectNameBuilder.toString());
    }

    //处理答案高亮
    HighlightField subjectAnswerField = highlightFields.get(EsSubjectFields.SUBJECT_ANSWER);
    if(Objects.nonNull(subjectAnswerField)){
        Text[] fragments = subjectAnswerField.getFragments();
        StringBuilder subjectAnswerBuilder = new StringBuilder();
        for (Text fragment : fragments) {
            subjectAnswerBuilder.append(fragment);
        }
        result.setSubjectAnswer(subjectAnswerBuilder.toString());
    }

    return result;
}
3.根据信息构建一个查询请求对象
    /**
     * 根据信息构建一个查询请求对象
     * @param req
     * @return
     */
    private EsSearchRequest createSearchListQuery(SubjectInfoEs req) {
        EsSearchRequest esSearchRequest = new EsSearchRequest();
        // bq用来组合查询条件
        BoolQueryBuilder bq = new BoolQueryBuilder();
        // 查询条件1是根据题目名称查询
        MatchQueryBuilder subjectNameQueryBuilder =
                QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_NAME, req.getKeyWord());
        // 查询条件2是根据题目的答案查询
        MatchQueryBuilder subjectAnswerQueryBuilder =
                QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_ANSWER, req.getKeyWord());
        // 使用bq关联两个查询条件,should就相当于或者,就是两个都查
        bq.should(subjectNameQueryBuilder);
        // 让题目名的权重高2
        subjectNameQueryBuilder.boost(2);
        bq.should(subjectAnswerQueryBuilder);
        // 查询条件3是只查询简答题
        MatchQueryBuilder subjectTypeQueryBuilder =
                QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_TYPE, SubjectInfoTypeEnum.BRIEF.getCode());
        // 必须是简答题
        bq.must(subjectTypeQueryBuilder);
        // 至少命中一个should
        bq.minimumShouldMatch(1);

        // 增加高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
        highlightBuilder.preTags("<span style = \"color:red\">");
        highlightBuilder.postTags("</span>");

        // 组装请求
        // bq
        esSearchRequest.setBq(bq);
        // 高亮
        esSearchRequest.setHighlightBuilder(highlightBuilder);
        // 匹配字段(一般是全部)
        esSearchRequest.setFields(EsSubjectFields.FIELD_QUERY);

        // ==========返回分页查询的结果==========
        // 当前页是从第几条记录开始,下标从1开始
        esSearchRequest.setFrom((req.getPageNo() - 1) * req.getPageSize());
        // 页面大小
        esSearchRequest.setSize(req.getPageSize());
        // ==========返回分页查询的结果==========

        // 不需要快照缓存
        esSearchRequest.setNeedScroll(false);
        return esSearchRequest;
    }

2.简单测试

1.TestFeignController.java
    @RequestMapping("/querySubjectByKeyword")
    public void querySubjectByKeyword() {
        SubjectInfoEs subjectInfoEs = new SubjectInfoEs();
        subjectInfoEs.setKeyWord("简答题目");
        PageResult<SubjectInfoEs> subjectInfoEsPageResult = subjectEsService.querySubjectList(subjectInfoEs);
        // 打印
        log.info("querySubjectByKeyword:" + JSON.toJSONString(subjectInfoEsPageResult));
    }
}
2.新增几条记录

image-20240621131902122

2.查询

image-20240621131919068

3.结果高亮显示

image-20240621131958040

3.加上控制层的实现

1.DTO和BO都加上keyWord属性

image-20240621134333208

image-20240621134345542

2.SubjectController.java
/**
 * 全文检索
 * @param subjectInfoDTO
 * @return
 */
@PostMapping("/getSubjectPageBySearch")
public Result<SubjectInfoEs> getSubjectPageBySearch(@RequestBody SubjectInfoDTO subjectInfoDTO) {
    try {
        // 打印日志
        if (log.isInfoEnabled()) {
            log.info("SubjectController getSubjectPageBySearch SubjectInfoDTO, subjectInfoDTO:{}", JSON.toJSONString(subjectInfoDTO));
        }
        // 参数校验
        Preconditions.checkArgument(StringUtils.isNotBlank(subjectInfoDTO.getKeyWord()), "关键字不能为空");
        // DTO转BO
        SubjectInfoBO subjectInfoBO = SubjectInfoDTOConverter.INSTANCE.convertDTO2BO(subjectInfoDTO);
        // 设置分页参数
        subjectInfoBO.setPageNo(subjectInfoDTO.getPageNo());
        subjectInfoBO.setPageSize(subjectInfoDTO.getPageSize());
        // 全文检索
        PageResult<SubjectInfoEs> boPageResult = subjectInfoDomainService.getSubjectPageBySearch(subjectInfoBO);
        return Result.ok(boPageResult);

    } catch (Exception e) {
        log.error("SubjectController getSubjectPageBySearch error, subjectInfoDTO:{}", e.getMessage(), e);
        return Result.fail("全文检索失败");
    }
}
3.sun-club-domain
1.SubjectInfoDomainService.java
/**
 * 全文检索
 * @param subjectInfoBO
 * @return
 */
PageResult<SubjectInfoEs> getSubjectPageBySearch(SubjectInfoBO subjectInfoBO);
2.SubjectInfoDomainServiceImpl.java
@Override
public PageResult<SubjectInfoEs> getSubjectPageBySearch(SubjectInfoBO subjectInfoBO) {
    // 将bo转换为entity
    SubjectInfoEs subjectInfoEs = new SubjectInfoEs();
    subjectInfoEs.setKeyWord(subjectInfoBO.getKeyWord());
    subjectInfoEs.setPageNo(subjectInfoBO.getPageNo());
    subjectInfoEs.setPageSize(subjectInfoBO.getPageSize());
    // 全文检索
    PageResult<SubjectInfoEs> subjectInfoEsPageResult = subjectEsService.querySubjectList(subjectInfoEs);
    return subjectInfoEsPageResult;
}
4.测试
1.es报错,requires query value

image-20240621134034359

2.发现转换器没有将keyWord转换,所以clean-package一下maven即可

image-20240621134110243

image-20240621134223762

3.重新测试

image-20240621134708128

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

S-X-S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值