Elasticsearch简介
Elasticsearch术语
Elasticsearch的下载安装
下载elasticsearch安装包,解压缩。修改config/elasticsearch.yml文件中的cluster.name、path.data、path.logs。下载ik中文分词器(springboot、elasticsearch、ik 版本要对应),在plugins文件夹下创建名为ik的文件夹,将下载的中文分词器解压缩到新建的ik文件夹下。执行 ./bin/elasticsearch启动ES服务。
第一步:引入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
第二步:配置Elasticsearch
# ElasticsearchProperties
spring.data.elasticsearch.cluster-name=amelia # 这个就是es配置文件中配置的cluster.name
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
⚠️ :elasticsearch有两个默认端口,9200是http访问,9300是tcp访问
⚠️:由于redis和es底层都使用了netty,所以会有netty启动冲突的问题,需要在启动类中进行一些配置,详见 CommunityApplication
@PostConstruct
public void init(){
// 解决netty启动冲突的问题
// Netty4Utils.setAvailableProcessors()
System.setProperty("es.set.netty.runtime.available.processors","false");
}
第三步:调用Spring提供的关于ES的API,访问ES服务器(Spring Data Elasticsearch)
ElasticsearchTemplate(类)
ElasticsearchRepository(接口)——这种方案更简单一点,优先选择这个
示例:
// 示例
@Document(indexName = "discusspost",type = "_doc",shards = 6,replicas = 3)
public class DiscussPost {
@Id
private int id;
@Field(type = FieldType.Integer)
private int userId;
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
private String title;
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
private String content;
@Field(type = FieldType.Integer)
private int type;
@Field(type = FieldType.Integer)
private int status;
@Field(type = FieldType.Date)
private Date createTime;
@Field(type = FieldType.Integer)
private int commentCount;
@Field(type = FieldType.Double)
private double score;
}
@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost,Integer> { //DiscussPost是数据的类型,Integer是主键的类型
}
// 示例
// 添加一条数据
discussRepository.save(discussPostMapper.selectDiscussPostById(241));
// 添加一组数据
discussRepository.saveAll(discussPostMapper.selectDiscussPosts(101,0,100,0));
// 修改数据
DiscussPost post = discussPostMapper.selectDiscussPostById(231);
discussRepository.save(post);
// 删除一条数据
discussRepository.deleteById(231);
// 删除全部数据
discussRepository.deleteAll();
// 示例
public void testSearchByRepository(){
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery("互联网寒冬","title","content"))
.withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
.withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.withPageable(PageRequest.of(0,10))
.withHighlightFields(
new HighlightBuilder.Field("title").preTags("").postTags(""),
new HighlightBuilder.Field("content").preTags("").postTags("")
).build();
// elasticTemplate.queryForPage(searchQuery,class,SearchResultMapper)
// 底层获取得到了高亮显示的值,但是没有返回
Page<DiscussPost> page = discussRepository.search(searchQuery);
System.out.println(page.getTotalElements());
System.out.println(page.getTotalPages());
System.out.println(page.getNumber());
System.out.println(page.getSize());
for(DiscussPost post : page){
System.out.println(post);
}
}
// 示例
public void testSearchByTemplate(){
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery("互联网寒冬","title","content"))
.withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
.withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.withPageable(PageRequest.of(0,10))
.withHighlightFields(
new HighlightBuilder.Field("title").preTags("").postTags(""),
new HighlightBuilder.Field("content").preTags("").postTags("")
).build();
Page<DiscussPost> page = elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
SearchHits hits = response.getHits();
if(hits.getTotalHits() <= 0){
return null;
}
List<DiscussPost> list = new ArrayList<>();
for(SearchHit hit:hits){
DiscussPost post = new DiscussPost();
String id = hit.getSourceAsMap().get("id").toString();
post.setId(Integer.valueOf(id));
String userId = hit.getSourceAsMap().get("userId").toString();
post.setUserId(Integer.valueOf(userId));
String title = hit.getSourceAsMap().get("title").toString();
post.setTitle(title);
String content = hit.getSourceAsMap().get("content").toString();
post.setContent(content);
String status = hit.getSourceAsMap().get("status").toString();
post.setStatus(Integer.valueOf(status));
String createTime = hit.getSourceAsMap().get("createTime").toString();
post.setCreateTime(new Date(Long.valueOf(createTime)));
String commentCount = hit.getSourceAsMap().get("commentCount").toString();
post.setCommentCount(Integer.valueOf(commentCount));
// 处理高亮显示的结果
HighlightField titleField = hit.getHighlightFields().get("title");
if(titleField != null){
post.setTitle(titleField.getFragments()[0].toString());
}
HighlightField contentField = hit.getHighlightFields().get("content");
if(contentField != null){
post.setContent(contentField.getFragments()[0].toString());
}
list.add(post);
}
return new AggregatedPageImpl(list,pageable,
hits.getTotalHits(),response.getAggregations(),response.getScrollId(),hits.getMaxScore());
}
});
System.out.println(page.getTotalElements());
System.out.println(page.getTotalPages());
System.out.println(page.getNumber());
System.out.println(page.getSize());
for(DiscussPost post : page){
System.out.println(post);
}
}
搜索服务
采用事件的方式来处理,发布事件
显示结果
实现细节:
首先,要对entity中的实体类discusspost进行一些注解,在数据访问层创建一个DiscussPostRepository的接口,service层创建一个elasticsearch的服务类(ElasticsearchService),在表现层创建一个SearchController类,处理搜索功能。
然后,在EventConsumer中创建一个消费发帖的事件【这个事件,主要就是用于把帖子存到Elasticsearch中】,分别在CommentController的发布评论方法和DiscussPostController的发布帖子方法中添加触发 发帖事件 的代码段。
还有一点需要注意,由于redis和es底层都使用了netty,所以会有netty启动冲突的问题,需要在启动类中进行一些配置,详见 CommunityApplication
