es简介
- 分布式的、Restful风格的搜索引擎。
- 支持对各种类型的数据的检索
- 搜索速度快,可以提供实时的搜索服务
- 便于水平扩展,每秒可以处理PB级海量数据
基本术语
- 索引、类型、文档、字段 —对应 数据库 表 行 列
- 集群、节点、分片、副本
在es6.x以后,类型 将被废弃 索引直接与关系型数据库的表对应
项目使用的是springboot2.1.5,所以es需要使用6.4.3
安装es中文分词插件
SpringBoot整合Elasticsearch
引入依赖
spring-boot-starter-data-elasticsearch
配置Elasticsearch
cluster-name、cluster-nodes
配置完成之后需要注意一个小问题,如果项目中还有redis的话,在项目启动时会有冲突,因为redis与es底层都依赖于netty,解决方法:
@SpringBootApplication
public class CommunityApplication {
// @PostConstruct管理bean的初始化方法,在构造器调用完以后被执行
@PostConstruct
public void init(){
//解决netty启动冲突问题
//see Netty4Util
System.setProperty("es.set.netty.runtime.available.processors","false");
}
public static void main(String[] args) {
SpringApplication.run(CommunityApplication.class, args);
}
}
Spring Dataa Elasticsearch
ElasticsearchTemplate
ElasticsearchRepository
项目中使用ElasticsearchRepository,首先需要将实体类映射至es,在实体类中加上注解,实例:
ok,简单将我们数据库字段映射之后,进行简单测试
- 单条数据插入
- 多条数据插入
- 单条数据删除
- 多条数据删除
- 使用Repository方式进行查询
使用此方式查询高亮显示内容无法插入到原数据中,所以选择Template来执行查询(因为高亮内容是单独的,所以需要手动替换)
- 使用Template方式进行查询并且添加关键字高亮显示
为了完成高亮显示功能,需要使用queryForPage方法,在SearchResultMapper中整合数据,具体看mapResults的操作
将数据依次封装,返回结果。
es应用
在项目中进行文章的检索功能
-
搜索服务
将帖子保存至Es服务器中
从Es服务器删除帖子
从Es服务器中搜索帖子 -
发布事件
发布帖子时,将帖子异步的提交到Es服务器
增加评论时,将帖子异步提交到Es服务器
在消费组件中增加一个方法,消费帖子发布事件。 -
显示结果
在控制器处理搜索请求,在HTML上显示搜索结果
编写Service,使用Repository加Template查询
/**
* @author :LY
* @date :Created in 2021/3/9 11:31
* @modified By:
*/
@Service
public class ElasticsearchService {
@Autowired
private DiscussPostRepository discussRepository;
@Autowired
private ElasticsearchTemplate elasticTemplate;
public void saveDiscussPost(DiscussPost discussPost){
discussRepository.save(discussPost);
}
public void deleteDiscussPost(int id){
discussRepository.deleteById(id);
}
public Page<DiscussPost> searchDiscussPost(String keyword, int current,int limit){
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(keyword,"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(current,limit))
.withHighlightFields(
new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
).build();
return elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
//首先通过searchResponse得到这次搜索得到的数据
SearchHits hits = searchResponse.getHits();
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(),searchResponse.getAggregations(),
searchResponse.getScrollId(),hits.getMaxScore());
}
});
}
}
controller编写
/**
* @author :LY
* @date :Created in 2021/3/9 15:21
* @modified By:
*/
@Controller
public class SearchController {
@Autowired
private ElasticsearchService elasticsearchService;
@Autowired
private UserSerice userSerice;
@Autowired
private LikeService likeService;
@GetMapping("/search")
public String search(String keyword, Page page, Model model){
//搜索
org.springframework.data.domain.Page<DiscussPost> searchDiscussPost = elasticsearchService.searchDiscussPost(keyword, page.getCurrent() - 1, page.getLimit());
//聚合数据
List<Map<String,Object>> discussPosts = new ArrayList<>();
if(searchDiscussPost != null){
searchDiscussPost.forEach(discussPost -> {
Map<String,Object> map = new HashMap<>();
//帖子
map.put("post",discussPost);
//作者
map.put("user",userSerice.findUserById(discussPost.getUserId()));
//点赞数量
map.put("likeCount",likeService.findEntityLikeCount(CommunityConstant.ENTITY_TYPE_POST.getValue(), discussPost.getId()));
discussPosts.add(map);
});
}
model.addAttribute("discussPosts",discussPosts);
model.addAttribute("keyword",keyword);
//分页信息
page.setPath("/search?keyword="+keyword);
page.setRows(searchDiscussPost == null?0:(int) searchDiscussPost.getTotalElements());
return "/site/search";
}
}
高亮效果