基于Spring Boot+Vue的博客系统 15——实现文章搜索功能(分词搜索)

需求

在输入框输入一串带有关键词的字符串,后台可以将字符串分成若干个关键词,然后从数据库中查询出带有关键词的文章,在页面上显示,并且显示的时候关键词为红色

后端实现

一开始分词搜素功能使用的是ElasticSearch来实现的,最后发现只用使用一款分词工具就可以实现相同的功能,使用ElasticSearch简直是是大材小用,这里分词工具选用的是HanLP

使用HanLP

HanLP是一系列模型与算法组成的NLP工具包,由大快搜索主导并完全开源,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点。
HanLP官网:http://hanlp.com/

  • 引入依赖
<dependency>
    <groupId>com.hankcsgroupId>
    <artifactId>hanlpartifactId>
    <version>portable-1.7.4version>
dependency>
  • ArticleRepository里面添加按照正则表达式查询的方法

注意这里如果使用SQL语句的话,就要将nativeQuery属性设为true,第二个参数Pageable为分页信息

package com.qianyucc.blog.repository;

import com.qianyucc.blog.model.entity.*;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.repository.*;
import org.springframework.data.repository.query.*;

import java.util.*;

/**
 * @author lijing
 * @date 2019-10-11 10:39
 * @description 访问数据库中文章
 */
public interface ArticleRepository extends JpaRepository<ArticleDO, Long>, JpaSpecificationExecutor<ArticleDO> {
    // 正则匹配
    @Query(value = "select * from article where title regexp :regex",nativeQuery = true)
    Page<ArticleDO> findByTitleWithRegex(@Param("regex") String regex, Pageable pageable);
}
  • ArticleService中添加按照查询字符串查询的方法
/**
 * 根据关键词查询文章
 *
 * @param queryString
 * @return
 */
public PageInfoVO<SimpleArticleVO> findArticlesByQueryString(String queryString, Integer pageNumber) {
    // 容错
    pageNumber = pageNumber < 1 ? 0 : pageNumber - 1;

    // 使用HanLP分词
    List<Term> termList = StandardTokenizer.segment(queryString);
    // 拼接正则字符串
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < termList.size(); i++) {
        String word = termList.get(i).word;
        if (i != termList.size() - 1) {
            sb.append(word + "|");
        } else {
            sb.append(word);
        }
    }
    String regex = sb.toString();
    sb.insert(0, ".*(");
    sb.append(").*");
    // 利用正则查找,这里的函数实现是使用SQL语句查询的所以在Sort里面要使用gmt_update而不能使用gmtUpdate
    Pageable pageable = new PageRequest(pageNumber, PAGE_SIZE, new Sort(Sort.Direction.DESC, "gmt_update"));
    Page<ArticleDO> page = articleRepository.findByTitleWithRegex(sb.toString(), pageable);
    List<ArticleDO> articleDOS = page.getContent();
    // 将标题中关键字替换为红色
    articleDOS.forEach(article -> {
        String title = article.getTitle();
        // 利用正则替换
        String newTitle = title.replaceAll(regex, "$0");
        article.setTitle(newTitle);
    });

    PageInfoVO<SimpleArticleVO> pageInfoVO = new PageInfoVO<>();
    pageInfoVO.setTotalCount(page.getTotalElements());
    pageInfoVO.setCurrentPage(pageNumber + 1);
    pageInfoVO.setTotalPages(page.getTotalPages());
    pageInfoVO.setData(ArticleUtil.jpaDosToSimpleArticleVOs(articleDOS));
    return pageInfoVO;
}

注意这里的Sort对象的第二个参数不能为gmtUpdate,因为这里我们使用的是SQL语句查询,数据库中并没有gmtUpdate字段,而是gmt_update字段,所以这里应该改为gmt_update,下图为使用gmtUpdate时出现的错误。

使用gmtUpdate时报错

  • 编写Controller
@GetMapping("/getArticlesByQueryString")
public PageInfoVO<SimpleArticleVO> getArticlesByQueryString(
        @RequestParam(name = "queryString") String queryString,
        @RequestParam(name = "pageNumber", required = false, defaultValue = "1") Integer pageNumber) {
    PageInfoVO<SimpleArticleVO> pageInfoVO = articleService.findArticlesByQueryString(queryString, pageNumber);
    return pageInfoVO;
}

正则替换

在上面的ArticleService类中使用了正则替换对文章标题进行操作,代码如下:

String newTitle = title.replaceAll(regex, "$0");

这句代码实现的功能是将标题中的所有关键词替换为带有红色样式的关键词,下面主要解释一下java中的replaceAll()函数的具体使用方法

  • 首先说一下普通的正则替换,比如将下面字符串中的我们替换为**,我们可以这样写:
public class TestString {
    public static void main(String[] args) {
        String s1 = "我们都是好孩子,他们绝不会在代码里下毒";
        String s2 = s1.replaceAll("我们", "**");
        System.out.println(s2);
    }
}

运行结果如下:
基于Spring Boot+Vue的博客系统 15——实现文章搜索功能(分词搜索)_第1张图片

  • 如果我们想要替换我们或者他们**,可以这样实现:
public class TestString {
    public static void main(String[] args) {
        String s1 = "我们都是好孩子,他们绝不会在代码里下毒";
        String s2 = s1.replaceAll("我们|他们", "**");
        System.out.println(s2);
    }
}

运行结果如下:
基于Spring Boot+Vue的博客系统 15——实现文章搜索功能(分词搜索)_第2张图片

  • 假如我们替换的的时候需要用到匹配到的字符串,例如我们想把我们或者他们作为关键词加粗,在关键词两边分别加上**,就可以用如下代码实现:
public class TestString {
    public static void main(String[] args) {
        String s1 = "我们都是好孩子,他们绝不会在代码里下毒";
        String s2 = s1.replaceAll("(我|他)(们)", "**$0**");
        System.out.println(s2);
    }
}

运行结果:
基于Spring Boot+Vue的博客系统 15——实现文章搜索功能(分词搜索)_第3张图片
这里的$0含义如下图所示,$1为第一个括号里匹配到的内容,$2为第二个括号里匹配到的内容,$0为整体匹配到的内容
基于Spring Boot+Vue的博客系统 15——实现文章搜索功能(分词搜索)_第4张图片

前端实现

  • /src/request/api/url.js中添加根据含有关键字的字符串查询的URL
const getArticlesByQueryStringUrl = baseUrl + 'api/comm/article/getArticlesByQueryString';
  • /src/request/api/article.js里面封装根据含有关键字字符串查询的方法
getArticlesByQueryString(queryString, pageNumber, callback) {
  axios
    .get(url.getArticlesByQueryStringUrl, {
      params: {
        queryString: queryString,
        pageNumber: pageNumber
      }
    })
    .then(callback)
    .catch(err => {
      console.log("getArticlesByQueryString Error");
    });
},
getArticles(callback) {
  let pageInfo = store.state.articles;
  if (pageInfo.condition) {
    switch (pageInfo.condition) {
      case 'all':
        this.getPageArticles(pageInfo.currentPage, callback);
        break;
      case 'tag':
        // 根据tag查询
        break;
      case 'category':
        // 根据分类查询
        break;
      case 'queryString':
        this.getArticlesByQueryString(pageInfo.queryString, pageInfo.currentPage, callback);
        break;
    }
  }
}
  • navBar.vue中定义点击搜索键之后的回调函数search()和点击博客名时的回调函数toIndex()
methods: {
  ...mapMutations([
    "setIsLogin",
    "setPageInfo",
    "setQueryString",
    "setCurrentPage",
    "setCondition"
  ]),
  // 退出登录
  exit() {
    window.localStorage.removeItem("token");
    this.setIsLogin(false);
  },
  search() {
    if (this.queryString != null && this.queryString != "") {
      this.setCondition({
        condition: "queryString",
        info: this.queryString
      });
      this.setCurrentPage(1);
      this.$api.article.getArticles(resp => {
        this.setPageInfo(resp.data);
      });
    }
  },
  toIndex() {
    this.queryString = "";
    this.setCondition({
      condition: "all"
    });
    this.setCurrentPage(1);
    this.$api.article.getArticles(resp => {
      this.setPageInfo(resp.data);
    });
    router.push("/");
  }
}
  • 在指定元素上绑定回调函数,这里注意不要将b-button的属性设置为submit,否则会提交请求并刷新页面
    基于Spring Boot+Vue的博客系统 15——实现文章搜索功能(分词搜索)_第5张图片

需要注意的是:在articles.vue中,渲染标题时应该用v-html才能显示关键字为红色的效果

基于Spring Boot+Vue的博客系统 15——实现文章搜索功能(分词搜索)_第6张图片

运行项目效果如下

你可能感兴趣的:(基于Spring Boot+Vue的博客系统 15——实现文章搜索功能(分词搜索))