在输入框输入一串带有关键词的字符串,后台可以将字符串分成若干个关键词,然后从数据库中查询出带有关键词的文章,在页面上显示,并且显示的时候关键词为红色
一开始分词搜素功能使用的是ElasticSearch
来实现的,最后发现只用使用一款分词工具就可以实现相同的功能,使用ElasticSearch
简直是是大材小用,这里分词工具选用的是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
时出现的错误。
@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);
}
}
我们
或者他们
为**
,可以这样实现:public class TestString {
public static void main(String[] args) {
String s1 = "我们都是好孩子,他们绝不会在代码里下毒";
String s2 = s1.replaceAll("我们|他们", "**");
System.out.println(s2);
}
}
我们
或者他们
作为关键词加粗,在关键词两边分别加上**
,就可以用如下代码实现:public class TestString {
public static void main(String[] args) {
String s1 = "我们都是好孩子,他们绝不会在代码里下毒";
String s2 = s1.replaceAll("(我|他)(们)", "**$0**");
System.out.println(s2);
}
}
运行结果:
这里的$0
含义如下图所示,$1
为第一个括号里匹配到的内容,$2
为第二个括号里匹配到的内容,$0
为整体匹配到的内容
/src/request/api/url.js
中添加根据含有关键字的字符串查询的URLconst 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("/");
}
}
需要注意的是:在
articles.vue
中,渲染标题时应该用v-html
才能显示关键字为红色的效果