在springboot中集成Elasticsearch,以我开发的博客系统项目为例,这是一篇文章内容。
准备
本次Elasticsearch使用的是单机版,版本为6.7.8,并且装好中文分词器。
Springboot集成Elasticsearch
- 首先在pom文件中引入Elasticsearch依赖。
<!--elasticsearch-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
- 创建一个对象,与数据库存储结构字段对应,使用@Data是导入了lombok,
@Document(indexName = “article_document”, type = “docs”, shards = 1, replicas = 0),这个注解会在elasticsearch里面创建一个article_document的索引。@Id 注解为elasticsearch的id字段。
类型(type)
每个文档都有与之对应的类型(type)定义。这允许用户在一个索引中存储多种文档类型,并为不同文档提供类型提供不同的映射。
分片(shards)#
代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。5.X默认不能通过配置文件定义分片
副本(replicas)#
代表索引副本,es可以设置多个索引的副本,副本的作用一是提高系统的容错性,当个某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。
@Data
@Document(indexName = "article_document", type = "docs", shards = 1, replicas = 0)
public class ArticleDocument implements Serializable {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title; //标题
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String summary;// 概述
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String content; // 内容
@Field(type = FieldType.Boolean)
private Boolean published;//是否已发布
@Field(type = FieldType.Integer)
private Integer status;//审核状态
public interface Table {
String ID = "id";
String TITLE = "title";
String SUMMARY = "summary";
String CONTENT = "content";
String PUBLISHED = "published";
String STATUS = "status";
}
}
- 写一个接口ArticleDocumentRepository继承ElasticsearchRepository,ElasticsearchRepository包含了基本的增删改查的能力,并在接口类上加上@Repository注解,注入到spring 容器中.
@Repository
public interface ArticleDocumentRepository extends ElasticsearchCrudRepository<ArticleDocument, Long> {
List<ArticleDocument> findDistinctByTitleLikeOrSummaryLikeOrContentLike(String title, String summary, String content);
}
- ArticleDocumentServiceImpl类自动注入ArticleDocumentRepository的接口,去操作Elasticsearch。
@Autowired
private ArticleDocumentRepository articleDocumentRepository;
- 接着就对Elasticsearch进行增删改查的操作。
保存
我是用了mybatis-plus,可以根据自己修改,在保存文章的时候,调用这个方法,Article是我的文章对象。
public void saveToElasticSearch(Article article) {
ArticleDocument articleDocument = new ArticleDocument();
BeanUtils.copyProperties(article, articleDocument);
if (articleDocument.getPublished() == null) {
QueryWrapper<Article> wrapper = new QueryWrapper<>();
wrapper.select(Article.Table.PUBLISHED).eq(Article.Table.ID, article.getId());
Article temp = articleMapper.selectOne(wrapper);
articleDocument.setPublished(temp.getPublished());
}
if (articleDocument.getStatus() == null) {
QueryWrapper<Article> wrapper = new QueryWrapper<>();
wrapper.select(Article.Table.STATUS).eq(Article.Table.ID, article.getId());
Article temp = articleMapper.selectOne(wrapper);
articleDocument.setStatus(temp.getStatus());
}
articleDocumentRepository.save(articleDocument);
}
更新
先将原来的索引删除掉
//从ElasticSearch中删除
articleDocumentRepository.deleteById(article.getId());
再保存新的索引
saveToElasticSearch(article);
删除
articleDocumentRepository.deleteById(article.getId());
查询
将查询的结果用List返回。
public List<ArticleDocument> listByKeyword(String keyword) throws IOException {
SearchRequest searchRequest = new SearchRequest(TableConstant.ARTICLE_DOCUMENT);
//匹配查询
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, ArticleDocument.Table.TITLE, ArticleDocument.Table.SUMMARY, ArticleDocument.Table.CONTENT);
TermQueryBuilder termQueryBuilder1 = QueryBuilders.termQuery(ArticleDocument.Table.PUBLISHED, true);
TermQueryBuilder termQueryBuilder2 = QueryBuilders.termQuery(ArticleDocument.Table.STATUS, Constant.AUDIT_PASS);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(multiMatchQueryBuilder).must(termQueryBuilder1).must(termQueryBuilder2);
sourceBuilder.query(boolQueryBuilder);
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field(ArticleDocument.Table.TITLE).field(ArticleDocument.Table.SUMMARY).field(ArticleDocument.Table.CONTENT);
highlightBuilder.preTags(Constant.HIGH_LIGHT_PRE_TAGS);
highlightBuilder.postTags(Constant.HIGH_LIGHT_POST_TAGS);
sourceBuilder.highlighter(highlightBuilder);
//执行搜索
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//解析结果
List<ArticleDocument> articleDocuments = new ArrayList<>();
for (SearchHit hit : searchResponse.getHits().getHits()) {
Map<String, Object> map = hit.getSourceAsMap();//原来的结果
//解析高亮的字段
HighLightUtil.parseField(hit, ArticleDocument.Table.TITLE);
HighLightUtil.parseField(hit, ArticleDocument.Table.SUMMARY);
HighLightUtil.parseField(hit, ArticleDocument.Table.CONTENT);
ArticleDocument articleDocument = new ArticleDocument();
articleDocument.setId(Long.valueOf((Integer) map.get(ArticleDocument.Table.ID)));
articleDocument.setTitle((String) map.get(ArticleDocument.Table.TITLE));
articleDocument.setSummary((String) map.get(ArticleDocument.Table.SUMMARY));
articleDocument.setContent((String) map.get(ArticleDocument.Table.CONTENT));
articleDocuments.add(articleDocument);
}
return articleDocuments;
}
elasticsearch有一个高亮配置HighLightUtil,可以使关键词高亮显示。
import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import java.util.Map;
public class HighLightUtil {
public static void parseField(SearchHit hit, String field) {
Map<String, Object> map = hit.getSourceAsMap();//原来的结果
HighlightField title = hit.getHighlightFields().get(field);
//解析高亮的字段
if (title != null) {
Text[] fragments = title.fragments();
StringBuilder newTitle = new StringBuilder();
for (Text fragment : fragments) {
newTitle.append(fragment);
}
//高亮文本替换掉原来的内容
map.put(field, newTitle.toString());
}
}
我们可以看到,这里我使用的是谷歌商城的ElasticSearch Heap这个插件,非常方便。
有什么问题可以再评论区评论,我会尽快回复。
参考文章:https://blog.csdn.net/forezp/article/details/106839262