Spring Boot 2.0 整合 ES 5 文章内容搜索实战

本章内容

  1. 文章内容搜索思路
  2. 搜索内容分词
  3. 搜索查询语句
  4. 筛选条件
  5. 分页、排序条件
  6. 小结

一、文章内容搜索思路

上一篇讲了在怎么在 Spring Boot 2.0 上整合ES5 ,这一篇聊聊具体实战。简单讲下如何实现文章、问答这些内容搜索的具体实现。实现思路很简单:

  • 基于「短语匹配」并设置最小匹配权重值
  • 哪来的短语,利用 IK 分词器分词
  • 基于 Fiter 实现筛选
  • 基于 Pageable 实现分页排序

这里直接调用搜索的话,容易搜出不尽人意的东西。因为内容搜索关注内容的连接性。所以这里处理方法比较 low ,希望多交流一起实现更好的搜索方法。就是通过分词得到很多短语,然后利用短语进行短语精准匹配。

ES 安装 IK 分词器插件很简单。第一步,在下载对应版本 https://github.com/medcl/elasticsearch-analysis-ik/releases。第二步,在 elasticsearch-5.5.3/plugins 目录下,新建一个文件夹 ik,把 elasticsearch-analysis-ik-5.5.3.zip 解压后的文件拷贝到 elasticsearch-5.1.1/plugins/ik 目录下。最后重启 ES 即可。

二、搜索内容分词

安装好 IK ,如何调用呢?

第一步,我这边搜搜内容会以 逗号 拼接传入。所以会先将逗号分割

第二步,在搜索词中加入自己本身,因为有些词经过 ik 分词后就没了... 这是个 bug

第三步,利用 AnalyzeRequestBuilder 对象获取 IK 分词后的返回值对象列表

第四步,优化分词结果,比如都为词,则保留全部;有词有字,则保留词;只有字,则保留字

核心实现代码如下:

   /**
     * 搜索内容分词
     */
    protected List<String>      handlingSearchContent(String searchContent) {

             List<String> searchTermResultList = new ArrayList<>();
             // 按逗号分割,获取搜索词列表
             List<String> searchTermList = Arrays.asList(searchContent.split(SearchConstant.STRING_TOKEN_SPLIT));

             // 如果搜索词大于 1 个字,则经过 IK 分词器获取分词结果列表
             searchTermList.forEach(searchTerm -> {
                 // 搜索词 TAG 本身加入搜索词列表,并解决 will 这种问题
                 searchTermResultList.add(searchTerm);
                 // 获取搜索词 IK 分词列表
                 searchTermResultList.addAll(getIkAnalyzeSearchTerms(searchTerm));
             });

             return searchTermResultList;
    }

    /**
     * 调用 ES 获取 IK 分词后结果
     */
    protected List<String>      getIkAnalyzeSearchTerms(String searchContent) {
             AnalyzeRequestBuilder ikRequest = new AnalyzeRequestBuilder(elasticsearchTemplate.getClient(),
                     AnalyzeAction.INSTANCE, SearchConstant.INDEX_NAME,      searchContent);
             ikRequest.setTokenizer(SearchConstant.TOKENIZER_IK_MAX);
             List<AnalyzeResponse.AnalyzeToken> ikTokenList =      ikRequest.execute().actionGet().getTokens();

             // 循环赋值
             List<String> searchTermList = new ArrayList<>();
             ikTokenList.forEach(ikToken -> {
                 searchTermList.add(ikToken.getTerm());
             });

             return handlingIkResultTerms(searchTermList);
    }

    /**
     * 如果分词结果:洗发水(洗发、发水、洗、发、水)
     * - 均为词,保留
     * - 词 + 字,只保留词
     * - 均为字,保留字
     */
    private List<String>      handlingIkResultTerms(List<String> searchTermList) {
             Boolean isPhrase = false;
             Boolean isWord = false;
             for (String term : searchTermList) {
                 if (term.length() > SearchConstant.SEARCH_TERM_LENGTH)      {
                     isPhrase = true;
                 } else {
                     isWord = true;
                 }
             }

             if (isWord & isPhrase) {
                 List<String> phraseList = new ArrayList<>();
                 searchTermList.forEach(term -> {
                     if (term.length() > SearchConstant.SEARCH_TERM_LENGTH)      {
                         phraseList.add(term);
                     }
                 });
                 return phraseList;
             }

             return searchTermList;
    }

三、搜索查询语句

构造内容枚举对象,罗列需要搜索的字段,ContentSearchTermEnum 代码如下:

import lombok.AllArgsConstructor;

@AllArgsConstructor
public enum ContentSearchTermEnum {

    // 标题
    TITLE("title"),
    // 内容
    CONTENT("content");

    /**
     * 搜索字段
     */
    private String      name;

    public String      getName() {
             return name;
    }

    public void      setName(String name) {
             this.name = name;
    }

}

循环进行「短语搜索匹配」搜索字段,然后并设置最低权重值为 1。核心代码如下:

   /**
     * 构造查询条件
     */
    private void      buildMatchQuery(BoolQueryBuilder queryBuilder, List<String>      searchTermList) {
             for (String searchTerm : searchTermList) {
                 for (ContentSearchTermEnum searchTermEnum : ContentSearchTermEnum.values())      {
                     queryBuilder.should(QueryBuilders.matchPhraseQuery(searchTermEnum.getName(),      searchTerm));
                 }
             }
             queryBuilder.minimumShouldMatch(SearchConstant.MINIMUM_SHOULD_MATCH);
    }

四、筛选条件

搜到东西不止,有时候需求是这样的。需要在某个品类下搜索,比如电商需要在某个 品牌 下搜索商品。那么需要构造一些 fitler 进行筛选。对应 SQL 语句的 Where 下的 OR 和 AND 两种语句。在 ES 中使用 filter 方法添加过滤。代码如下:

   /**
     * 构建筛选条件
     */
    private void      buildFilterQuery(BoolQueryBuilder boolQueryBuilder, Integer type, String      category) {
             // 内容类型筛选
             if (type != null) {
                 BoolQueryBuilder typeFilterBuilder = QueryBuilders.boolQuery();
                 typeFilterBuilder.should(QueryBuilders.matchQuery(SearchConstant.TYPE_NAME,      type).lenient(true));
                 boolQueryBuilder.filter(typeFilterBuilder);
             }

             // 内容类别筛选
             if (!StringUtils.isEmpty(category)) {
                 BoolQueryBuilder categoryFilterBuilder = QueryBuilders.boolQuery();
                 categoryFilterBuilder.should(QueryBuilders.matchQuery(SearchConstant.CATEGORY_NAME,      category).lenient(true));
                 boolQueryBuilder.filter(categoryFilterBuilder);
             }
    }

type 是大类,category 是小类,这样就可以支持 大小类 筛选。但是如果需要在 type = 1 或者 type = 2 中搜索呢?具体实现代码很简单:

typeFilterBuilder
    .should(QueryBuilders.matchQuery(SearchConstant.TYPE_NAME,      1)
    .should(QueryBuilders.matchQuery(SearchConstant.TYPE_NAME,      2)
    .lenient(true));

通过链式表达式,两个 should 实现或,即 SQL 对应的 OR 语句。通过两个 BoolQueryBuilder 实现与,即 SQL 对应的 AND 语句。

五、分页、排序条件

分页排序代码就很简单了:

  @Override
    public PageBean      searchContent(ContentSearchBean contentSearchBean) {

             Integer pageNumber = contentSearchBean.getPageNumber();
             Integer pageSize = contentSearchBean.getPageSize();

             PageBean<ContentEntity> resultPageBean = new PageBean<>();
             resultPageBean.setPageNumber(pageNumber);
             resultPageBean.setPageSize(pageSize);

             // 构建搜索短语
             String searchContent = contentSearchBean.getSearchContent();
             List<String> searchTermList =      handlingSearchContent(searchContent);

             // 构建查询条件
             BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
             buildMatchQuery(boolQueryBuilder, searchTermList);

             // 构建筛选条件
             buildFilterQuery(boolQueryBuilder, contentSearchBean.getType(),      contentSearchBean.getCategory());

             // 构建分页、排序条件
             Pageable pageable = PageRequest.of(pageNumber, pageSize);
             if (!StringUtils.isEmpty(contentSearchBean.getOrderName())) {
                 pageable = PageRequest.of(pageNumber, pageSize, Sort.Direction.DESC,      contentSearchBean.getOrderName());
             }
             SearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageable)
                     .withQuery(boolQueryBuilder).build();

             // 搜索
             LOGGER.info("\n ContentServiceImpl.searchContent() [" +      searchContent
                     + "] \n DSL  = \n " +      searchQuery.getQuery().toString());
             Page<ContentEntity> contentPage =      contentRepository.search(searchQuery);

             resultPageBean.setResult(contentPage.getContent());
             resultPageBean.setTotalCount((int) contentPage.getTotalElements());
             resultPageBean.setTotalPage((int) contentPage.getTotalElements() /      resultPageBean.getPageSize() + 1);
             return resultPageBean;
    }

利用 Pageable 对象,构造分页参数以及指定对应的 排序字段、排序顺序(DESC ASC)即可。

文章来源:http://mp.weixin.qq.com/s/ZoJzF9VpynUBSQWlJJjmEw

springboot视频教程:http://www.roncoo.com/course/list.html?courseName=spring+boot

原文地址:https://www.cnblogs.com/tpniu/p/8625152.html

时间: 2024-08-20 13:14:43

Spring Boot 2.0 整合 ES 5 文章内容搜索实战的相关文章

spring boot 2.0 整合 elasticsearch NoNodeAvailableException

原文地址:spring boot 2.0 整合 elasticsearch NoNodeAvailableException 原文说的有点问题,下面贴出我的配置: 码云项目地址:https://gitee.com/11230595/springboot-elasticsearch elasticsearch.yml cluster.name: my-applicationnetwork.host: 0.0.0.0 http.port: 9200transport.tcp.port: 9300tr

Spring Boot 2.0.4整合Spring Data JPA和Druid,双数据源

最近Team开始尝试使用Spring Boot + Spring Data JPA作为数据层的解决方案,在网上逛了几圈之后发现大家并不待见JPA,理由是(1)MyBatis简单直观够用,(2)以Hibernate为底层的Spring Data JPA复杂且性能一般. 但是当我们来到Spring Boot的世界后发现,相较于Spring Data JPA,MyBatis对Spring Boot的支持有限,Spring Data JPA与Spring Boot结合可以让dao变得非常简单,比如(1)

Spring Boot 2.0(一):【重磅】Spring Boot 2.0权威发布

就在昨天Spring Boot2.0.0.RELEASE正式发布,今天早上在发布Spring Boot2.0的时候还出现一个小插曲,将Spring Boot2.0同步到Maven仓库的时候出现了错误,然后Spring Boot官方又赶紧把 GitHub 上发布的 v2.0.0.RELEASE 版本进行了撤回.到了下午将问题修复后,又重新进行了上传,至此Spring Boot2.0正式推出! 要知道这是Spring Boot1.0发布4年之后第一次重大修订,因此有多的新功能和特性值得大家期待!在S

Spring Boot 2.0 迁移指南

![img](https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6Rs7yEJ6ItV43JZMS7AJWoMSZtxicnG0iaE0AvpUHI8oM7lxz1rRsmaa4IfbolVRG2WQwhXrchmVWS8Q/640?tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) ### 前提 本文档将帮助您把应用程序迁移到 Spring Boot 2.0. ### 在你开始之前 首先,**Spring Boot 2.0需要Ja

阿里P9告诉你 Spring Boot 2.0正式发布,升还是不升呢?

Spring帝国Spring几乎是每一位Java开发人员都耳熟能详的开发框架,不论您是一名初出茅庐的程序员还是经验丰富的老司机,都会对其有一定的了解或使用经验.在现代企业级应用架构中,Spring技术栈几乎成为了Java语言的代名词,那么Spring为什么能够在众多开源框架中脱颖而出,成为业内一致认可的技术解决方案呢?我们不妨从最初的Spring Framework开始,看看它为什么能够横扫千军,一统江湖! 挑战权威,一战成名 2004年3月,Spring的第一个版本以及其创始人Rod John

Spring Boot 2.0(六):使用 Docker 部署 Spring Boot 开源软件云收

只需三步即可部署开源项目云收藏,打造专属个人的收藏系统,就是这么简单! 云收藏项目已经开源2年多了,作为当初刚开始学习 Spring Boot 的练手项目,使用了很多当时很新的技术,现在看来其实很多新技术是没有必要使用的,但做为学习案例来讲确实是一个绝佳的 Spring Boot 实践. 从开源到现在,写了一些教程给大家介绍如何部署云收藏,如何在IDE中运行云收藏,但是仍然有很多的朋友不知道如何使用,如何部署?就像"请提供一份云收藏数据结构" 这样的问题我至少都回答了一百多次,并且在

Spring Boot 2.0——SpringApplication 深入探索

前言 在 Spring Boot 项目的启动类中常见代码如下: @SpringBootApplicationpublic class SpringbotApplication {    public static void main(String[] args) {         SpringApplication.run(SpringbotApplication.class, args);     } } 其中也就两个比较引人注意的地方: @SpringBootApplication Spr

深度实践Spring Boot 2.0之核心技术篇

第1章 系列总览总览 Spring Boot 2.0 深度实践系列课程的整体议程,包括 Spring Boot 三大核心特性(组件自动装配.嵌入式Web容器.生产准备特性).Web 应用(传统 Servlet.Spring Web MVC.Spring WebFlux).数据相关(JDBC.JPA.事务).功能扩展(SpringApplication.Spring Boot 配置.Spring Boot Starter)以及... 1-1 -课程导学1-2 为什么说Spring Boot 2.0

【SFA官方翻译】使用 Kubernetes、Spring Boot 2.0 和 Docker 的微服务快速指南

[SFA官方翻译]使用 Kubernetes.Spring Boot 2.0 和 Docker 的微服务快速指南 原创: Darren Luo SpringForAll社区 今天 原文链接:https://dzone.com/articles/quick-guide-to-microservices-with-kubernetes-sprin 作者:Piotr Mińkowski 译者:Darren Luo 在本教程中你将学习如何使用 Kubernetes 和 Docker 快速启动并运行 Sp