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

简介: 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);
    }
}


大功告成

相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。 &nbsp;
目录
相关文章
存储 JSON Java
655 0
|
6月前
|
JSON 分布式计算 大数据
springboot项目集成大数据第三方dolphinscheduler调度器
springboot项目集成大数据第三方dolphinscheduler调度器
397 3
|
分布式计算 大数据 Java
springboot项目集成大数据第三方dolphinscheduler调度器 执行/停止任务
springboot项目集成大数据第三方dolphinscheduler调度器 执行/停止任务
151 0
|
6月前
|
缓存 JSON 前端开发
第07课:Spring Boot集成Thymeleaf模板引擎
第07课:Spring Boot集成Thymeleaf模板引擎
630 0
第07课:Spring Boot集成Thymeleaf模板引擎
|
6月前
|
存储 人工智能 Java
Springboot集成AI Springboot3 集成阿里云百炼大模型CosyVoice2 实现Ai克隆语音(未持久化存储)
本项目基于Spring Boot 3.5.3与Java 17,集成阿里云百炼大模型CosyVoice2实现音色克隆与语音合成。内容涵盖项目搭建、音色创建、音频合成、音色管理等功能,适用于希望快速掌握Spring Boot集成语音AI技术的开发者。需提前注册阿里云并获取API Key。
|
6月前
|
Java 关系型数据库 MySQL
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
706 2
|
6月前
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
394 2
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 项目管理
springboot项目集成dolphinscheduler调度器 项目管理
221 0
|
7月前
|
前端开发
SpringBoot2.3.1集成Knife4j接口文档
SpringBoot2.3.1集成Knife4j接口文档
838 44

热门文章

最新文章