使用elasticsearch和springboot开发博客搜索服务,可分为两个部分:
- 将博客数据同步到elasticsearch:包括全量同步和增量同步
- springboot整合elasticsearch开发搜索功能
elasticsearch 数据同步
先将mysql中的数据同步到es中,才能进行搜索。es的数据同步,一般有一些工具可以完成,其中官方推荐的logstash,通过配置 input、filter 和output,再使用logstash就可以进行数据的同步。
但是,我们也可以手动实现这个功能,通过从mysql中读取数据,再写入elasticsearch中,完成同步。
初次全量同步
将历史数据一次性同步到mysql中。
/**
* 全量同步
*/
public boolean syncAll() {
int page = 0, size = 10;
List<Post> postList;
do {
postList = postsClient.page(page, size);
postList.forEach(this::parse);
postRepository.saveAll(postList);
page++;
} while (postList.size() >= size);
return true;
}
后续增量同步
以后新增的博客数据,陆续进行同步。设置定时器,每天定时自动同步新增的博客。
/**
* 将博客定时同步到elasticsearch中(增量同步)
*/
@Scheduled(cron = "0 0 0 3 * *")
public void syncBlog() {
long maxPostId = postService.maxPostId();
List<Post> postList = postsClient.page(maxPostId);
if (postList.isEmpty()) {
return;
}
postList.forEach(this::parse);
postRepository.saveAll(postList);
}
Springboot整合elasticsearch搜索
import static org.elasticsearch.index.query.QueryBuilders.*;
/**
* @author yawn http://jvm123.com
* 2019/12/1 10:17
*/
@Service
public class PostService {
private final Logger logger = LoggerFactory.getLogger(PostService.class);
private static final Pageable DEFAULT_PAGE = PageRequest.of(0, 24);
@Autowired
ElasticsearchTemplate esTemplate;
@Autowired
PostRepository postRepository;
/**
* 根据标题和内容搜索
* @param t 标题
* @param c 内容
*/
public List<Post> search(String t, String c) {
BoolQueryBuilder queryBuilder = boolQuery();
if (StringUtils.isNotBlank(t)) {
// .boost(2) 表示权重是2
// matchPhraseQuery 如果查询词是‘java excel’ 会把整体作为一个词来查询
// matchQuery 如果查询词是‘java excel’ 不会作为一个整体词来查询,默认是or关系
// queryBuilder.must(matchPhraseQuery("postTitle", t).boost(2));
queryBuilder.must(matchQuery("postTitle", t).operator(Operator.AND).boost(1.5F));
// queryBuilder.must(termQuery("postTitle", t));
}
if (StringUtils.isNotBlank(c)) {
queryBuilder.must(matchQuery("postContent", c).boost(1));
}
logger.debug("查询:\n{}", queryBuilder.toString());
Page<Post> posts = postRepository.search(queryBuilder, DEFAULT_PAGE);
return posts.getContent();
}
/**
* 根据关键词搜索
* @param wd 关键词
*/
public List<Post> search(String wd) {
if (StringUtils.isBlank(wd)) {
return null;
}
// SearchQuery sQuery = new NativeSearchQueryBuilder()
// .withQuery(
// queryStringQuery(wd)
// )
// .build();
// return esTemplate.queryForList(sQuery, Post.class);
BoolQueryBuilder boolQueryBuilder = boolQuery()
.should(matchQuery("postTitle", wd))
.should(matchQuery("postContent", wd));
logger.debug("查询:{}", boolQueryBuilder.toString());
Page<Post> posts = postRepository.search(boolQueryBuilder, DEFAULT_PAGE);
return posts.getContent();
}
public long maxPostId() {
BoolQueryBuilder boolQueryBuilder = boolQuery().must(matchAllQuery());
FieldSortBuilder sortBuilder = SortBuilders.fieldSort("id").order(SortOrder.DESC);
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.withSort(sortBuilder)
.withPageable(PageRequest.of(0, 1))
.build();
Page<Post> page = postRepository.search(searchQuery);
List<Post> posts = page.getContent();
if (posts.isEmpty()) {
return 0L;
}
Post post = posts.get(0);
return post.getId();
}
}
最终文章内容的模糊搜索,也可以很快出现结果。