springboot 2.0集成elasticsearch 7.6.2 (集群)关键字高亮显示(下)

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: springboot 2.0集成elasticsearch 7.6.2 (集群)关键字高亮显示(下)

正文


四、filter


查询在老寇云交流群发了关于生日快乐的内容(两种方式)


GET /message/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "data": "生日快乐"
          }
        },
        {
          "match": {
            "remark": "老寇云交流群"
          }
        }
      ]
    }
  }
}


GET /message/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "data": "生日快乐"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "remark": "老寇云交流群"
          }
        }
      ]
    }
  }
}


query 与 filter 对比


filter:只会按照搜索条件过滤出所需要的数据,不会计算相关度分数,因此对相关度没有影响,同时内置自动缓存最常使用filter的数据


query:会计算每个文档对搜索条件的相关度,并按相关度排序,然而需要计算相关度,所以无法缓存结果


应用场景:在进行搜索时,需要根据一些条件筛选部分数据,而且不关注其排序,建议使用filter,反之,使用query


五、定制排序规则


constant_score - 举个栗子


GET /message/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "data": "生日快乐"
        }
      },
      "boost": 1.2
    }
  },
  "sort": [
    {
      "createDate": {
        "order": "desc"
      }
    }
  ]
}


六、代码实现


1.引入依赖


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency> 
       <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.6.2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.elasticsearch</groupId>
                    <artifactId>elasticsearch</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.elasticsearch.client</groupId>
                    <artifactId>elasticsearch-rest-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>7.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.6.2</version>
        </dependency>


2.增加实体类


@Data
@ApiModel(description = "查询表单实体类")
public class QueryForm implements Serializable {
    /**
     * 页码
     */
    private Integer pageNum = 1;
    /**
     * 条数
     */
    private Integer pageSize = 10;
    /**
     * 是否分页
     */
    private boolean needPage = false;
    /**
     * 查询索引名称
     */
    private String[] indexNames;
    /**
     * 分词搜索
     */
    private List<SearchDTO> queryStringList;
    /**
     * 排序
     */
    private List<SearchDTO> sortFieldList;
    /**
     * 高亮搜索字段
     */
    private List<String> highlightFieldList;
    /**
     * or搜索-精准匹配
     */
    private List<SearchDTO> orSearchList;
}


/**
 * 搜索DTO
 * @author Kou Shenhai 2413176044@leimingtech.com
 * @version 1.0
 * @date 2022/3/15 0015 上午 9:45
 */
@Data
public class SearchDTO implements Serializable {
    private String field;
    private String value;
}


3.分词查询


@Slf4j
@Component
public class ElasticsearchUtil {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    private static final String HIGHLIGHT_PRE_TAGS = "<span class='highlight'>";
    private static final String HIGHLIGHT_POST_TAGS = "</span>";
    private static final String PINYIN_SUFFIX = ".pinyin";
    /**
     * 关键字高亮显示
     * @param queryForm 查询实体类
     * @return
     * @throws IOException
     */
    public HttpResultUtil<SearchVO> search(QueryForm queryForm) throws IOException {
        long startTime = System.currentTimeMillis();
        final String[] indexName = queryForm.getIndexNames();
        final List<SearchDTO> orSearchList = queryForm.getOrSearchList();
        final List<SearchDTO> sortFieldList = queryForm.getSortFieldList();
        final List<String> highlightFieldList = queryForm.getHighlightFieldList();
        final List<SearchDTO> queryStringList = queryForm.getQueryStringList();
        final Integer pageNum = queryForm.getPageNum();
        final Integer pageSize = queryForm.getPageSize();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //用于搜索文档,聚合,定制查询有关操作
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(indexName);
        //or查询
        BoolQueryBuilder orQuery = QueryBuilders.boolQuery();
        for (SearchDTO dto : orSearchList) {
            orQuery.should(QueryBuilders.termQuery(dto.getField(),dto.getValue()));
        }
        boolQueryBuilder.must(orQuery);
        //分词查询
        BoolQueryBuilder analysisQuery = QueryBuilders.boolQuery();
        for (SearchDTO dto : queryStringList) {
            final String field = dto.getField();
            //清除左右空格
            String keyword = dto.getValue().trim();
            //处理特殊字符
            keyword = QueryParser.escape(keyword);
            analysisQuery.should(QueryBuilders.queryStringQuery(keyword).field(field).field(field.concat(PINYIN_SUFFIX)));
        }
        boolQueryBuilder.must(analysisQuery);
        //高亮显示数据
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //设置关键字显示颜色
        highlightBuilder.preTags(HIGHLIGHT_PRE_TAGS);
        highlightBuilder.postTags(HIGHLIGHT_POST_TAGS);
        //设置显示的关键字
        for (String field : highlightFieldList) {
            highlightBuilder.field(field, 0, 0).field(field.concat(PINYIN_SUFFIX), 0, 0);
        }
        highlightBuilder.requireFieldMatch(false);
        //分页
        int start = 0;
        int end = 10000;
        if (queryForm.isNeedPage()) {
            start = (pageNum - 1) * pageSize;
            end = pageSize;
        }
        //设置高亮
        searchSourceBuilder.highlighter(highlightBuilder);
        searchSourceBuilder.from(start);
        searchSourceBuilder.size(end);
        //追踪分数开启
        searchSourceBuilder.trackScores(true);
        //注解
        searchSourceBuilder.explain(true);
        //排序
        for (SearchDTO dto : sortFieldList) {
            SortOrder sortOrder;
            final String desc = "desc";
            final String value = dto.getValue();
            final String field = dto.getField();
            if (desc.equalsIgnoreCase(value)) {
                sortOrder = SortOrder.DESC;
            } else {
                sortOrder = SortOrder.ASC;
            }
            searchSourceBuilder.sort(field,sortOrder);
        }
        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchHits hits = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT).getHits();
        List<Map<String,Object>> data = new ArrayList<>();
        for (SearchHit hit : hits){
            Map<String,Object> sourceData = hit.getSourceAsMap();
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            for (String key : highlightFields.keySet()){
                sourceData.put(key,highlightFields.get(key).getFragments()[0].string());
            }
            data.add(sourceData);
        }
        long endTime = System.currentTimeMillis();
        HttpResultUtil<SearchVO> result = new HttpResultUtil();
        SearchVO vo = new SearchVO();
        final String searchData = queryStringList.stream().map(i -> i.getValue()).collect(Collectors.joining(","));
        final List<String> searchFieldList = queryStringList.stream().map(i -> i.getField()).collect(Collectors.toList());
        vo.setRecords(handlerData(data,searchFieldList));
        vo.setTotal(hits.getTotalHits().value);
        vo.setPageNum(queryForm.getPageSize());
        vo.setPageSize(queryForm.getPageSize());
        result.setMsg("搜索 <span class='highlight'>" + searchData + "</span> 找到 " + vo.getTotal() + " 个与之相关的内容,耗时:" + (endTime - startTime) +"ms");
        //处理数据
        result.setData(vo);
        return result;
    }
    /**
     * 处理高亮后的数据
     * @param data ES查询结果集
     */
    private List<Map<String,Object>> handlerData(List<Map<String,Object>> data,List<String> fieldList) {
        log.info("查询结果:{}",data);
        if (CollectionUtils.isEmpty(data)) {
            return Lists.newArrayList();
        }
        if (CollectionUtils.isEmpty(fieldList)) {
            return data;
        }
        for (Map<String, Object> map : data) {
            for (String field : fieldList) {
                if (map.containsKey(field.concat(PINYIN_SUFFIX))) {
                    String result1 = map.get(field).toString();
                    String result2 = map.get(field.concat(PINYIN_SUFFIX)).toString();
                    //将同义词合并
                    for (;;) {
                        int start = result1.indexOf(HIGHLIGHT_PRE_TAGS);
                        int end = result1.indexOf(HIGHLIGHT_POST_TAGS);
                        if (start == -1 || end == -1) {
                            break;
                        }
                        String replaceKeyword = result1.substring(start, end).replace(HIGHLIGHT_PRE_TAGS, "");
                        result2 = result2.replaceAll(replaceKeyword, HIGHLIGHT_PRE_TAGS + replaceKeyword + HIGHLIGHT_POST_TAGS);
                        result1 = result1.substring(end + 1);
                    }
                    map.put(field, result2);
                    map.remove(field.concat(PINYIN_SUFFIX));
                }
            }
        }
        return data;
    }
}


4.api测试


@RestController
@RequestMapping("/api")
@Api(tags = "Elasticsearch API 服务")
public class ElasticsearchController {
    @Autowired
    private ElasticsearchUtil elasticsearchUtil;
    @PostMapping("/search")
    @ApiOperation("ES关键字搜索-高亮显示")
    @ResponseBody
    public HttpResultUtil<SearchVO> search(@RequestBody @Validated final QueryForm queryForm, BindingResult bindingResult) throws IOException {
        if (bindingResult.hasErrors()) {
            return new HttpResultUtil<SearchVO>().error(bindingResult.getFieldError().getDefaultMessage());
        }
        return elasticsearchUtil.search(queryForm);
    }
}


大功告成

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
24天前
|
网络协议 Java API
SpringBoot整合Elasticsearch-Rest-Client、测试保存、复杂检索
这篇文章介绍了如何在SpringBoot中整合Elasticsearch-Rest-Client,并提供了保存数据和进行复杂检索的测试示例。
SpringBoot整合Elasticsearch-Rest-Client、测试保存、复杂检索
|
9天前
|
机器学习/深度学习 存储 数据采集
Elasticsearch 与机器学习的集成
【9月更文第3天】Elasticsearch 不仅仅是一个强大的分布式搜索和分析引擎,它还是一个完整的数据平台,通过与 Kibana、Logstash 等工具结合使用,能够提供从数据采集、存储到分析的一站式解决方案。特别是,Elasticsearch 集成了机器学习(ML)功能,使得在实时数据流中进行异常检测和趋势预测成为可能。本文将详细介绍如何利用 Elasticsearch 的 ML 功能来检测异常行为或预测趋势。
23 4
|
14天前
|
机器学习/深度学习 存储 搜索推荐
Elasticsearch与深度学习框架的集成案例研究
Elasticsearch 是一个强大的搜索引擎和分析引擎,广泛应用于实时数据处理和全文搜索。深度学习框架如 TensorFlow 和 PyTorch 则被用来构建复杂的机器学习模型。本文将探讨如何将 Elasticsearch 与这些深度学习框架集成,以实现高级的数据分析和预测任务。
17 0
|
14天前
|
机器学习/深度学习 数据采集 缓存
Elasticsearch与机器学习集成的最佳实践
【8月更文第28天】Elasticsearch 提供了强大的搜索和分析能力,而机器学习则能够通过识别模式和预测趋势来增强这些能力。将两者结合可以实现更智能的搜索体验、异常检测等功能。
31 0
|
28天前
|
自然语言处理 Java 关系型数据库
ElasticSearch 实现分词全文检索 - SpringBoot 完整实现 Demo 附源码【完结篇】
ElasticSearch 实现分词全文检索 - SpringBoot 完整实现 Demo 附源码【完结篇】
24 0
|
28天前
|
自然语言处理 Java 索引
ElasticSearch 实现分词全文检索 - Java SpringBoot ES 文档操作
ElasticSearch 实现分词全文检索 - Java SpringBoot ES 文档操作
27 0
|
2月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
159 6
|
2月前
|
Java 关系型数据库 MySQL
如何实现Springboot+camunda+mysql的集成
【7月更文挑战第2天】集成Spring Boot、Camunda和MySQL的简要步骤: 1. 初始化Spring Boot项目,添加Camunda和MySQL驱动依赖。 2. 配置`application.properties`,包括数据库URL、用户名和密码。 3. 设置Camunda引擎属性,指定数据源。 4. 引入流程定义文件(如`.bpmn`)。 5. 创建服务处理流程操作,创建控制器接收请求。 6. Camunda自动在数据库创建表结构。 7. 启动应用,测试流程启动,如通过服务和控制器开始流程实例。 示例代码包括服务类启动流程实例及控制器接口。实际集成需按业务需求调整。
180 4
|
2月前
|
消息中间件 Java 测试技术
【RocketMQ系列八】SpringBoot集成RocketMQ-实现普通消息和事务消息
【RocketMQ系列八】SpringBoot集成RocketMQ-实现普通消息和事务消息
132 1
|
3月前
|
消息中间件 Java Kafka
springboot集成kafka
springboot集成kafka
118 2