1. Elasticsearch入门
1.1 术语解释1.2 Elasticsearch配置1.3 安装中文分词插件1.4 安装Postman1.5 使用命令行 *** 作Elasticsearch1.6 使用Postman访问Elasticsearch 2. Spring整合Elasticsearch
2.1 引入依赖2.2 Elasticsearch相关配置2.3 数据层2.4 测试 3. 开发社区搜索功能
3.1 搜索服务3.2 发布事件
3.2.1 发布帖子时,将帖子异步的提交到Elasticsearch服务器3.2.2 增加评论时,将帖子异步的提交到Elasticsearch服务器3.2.3 在消费组件中增加一个方法,消费帖子发布事件 3.3 显示结果
1. Elasticsearch入门Elasticsearch简介
一个分布式的、Restful风格的搜索引擎支持对各种类型的数据的检索搜索速度快、可以提供实时的搜索服务便于扩展、每秒可以处理PB级海量数据 Elasticsearch术语
索引、类型、文档、字段集群、节点、分片、副本
1.1 术语解释
索引:相当于MySQL数据库中的database(数据库)类型:相当于MySQL数据库中的table(表)文档:相当于MySQL数据库中的table的一行数据,数据结构为JSON字段:相当于MySQL数据库中的table的一列
Elasticsearch 6.0 以后开始逐步废除类型的概念,索引的含义中也包括了类型
集群:分布式部署,提高性能节点:集群中的每一台服务器分片:对一个索引的进一步划分存储,提高并发处理能力副本:对分片的备份,提高可用性
Elasticsearch连接:官网
1.2 Elasticsearch配置为了与使用的SpringBoot版本相兼容,Elasticsearch的版本选择为6.4.3
文件目录结构为:
修改config目录下elasticsearch.yml文件
2. 配置环境变量
- github 上搜索
解压到指定的目录下
ik目录下的目录结构
在config目录下IkAnalyzer.cfg.xml中可以自己配置新词
其内容为:
Postman相关连接:官网
按部就班安装即可。
- 启动Elasticsearch
双击bin目录下elasticsearch.bat
常用命令介绍
查看节点健康状态
curl -X GET "localhost:9200/_cat/health?v"
查看节点具体信息
curl -X GET "localhost:9200/_cat/nodes?v"
查看索引相关信息
curl -X GET "localhost:9200/_cat/indices?v"
查看状态显示yellow是因为没有分片(备份)。删除索引
curl -X DELETE "localhost:9200/test" //test就是索引名字
再次查询索引相关信息,没有了test
创建索引
curl -X PUT "localhost:9200/test" //test就是索引名字
再次查询索引相关信息,又有了test
1.6 使用Postman访问Elasticsearch
查索引
删除索引
再次查询索引相关信息,没有了test
新建索引
再次查询索引相关信息,又有了test
提交数据
//test:索引 _doc:固定格式 1:id号 然后在请求body中写数据 PUT localhost:9200/test/_doc/1
查数据
改数据
和添加数据 *** 作相同,底层会先删除再添加删除数据
搜索功能演示
添加几组数据
搜索演示:
搜索时,也可以先分词后搜索,并不一定完全匹配
引入依赖
spring-boot-starter-data-elasticsearch 配置Elasticsearch
cluster-name、cluster-nodes Spring Data Elasticsearch
ElasticsearchTemplateElasticsearchRepository
2.1 引入依赖
2.2 Elasticsearch相关配置org.springframework.boot spring-boot-starter-data-elasticsearch
- Spring整合Elasticsearch,在application.properties文件中配置即可
# elasticsearch # ElasticsearchProperties # 在安装目录config/elasticserch.yml中找到 spring.data.elasticsearch.cluster-name=my-application # 9200http访问的端口,9300tcp端口 spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
- 解决Netty冲突问题
问题原因:Redis底层使用了Netty,Elasticsearch也用了Netty,当被注册两次就会报错
解决思路:Elasticsearch中注册Netty前会判断有无一个参数,如果有就不注册
具体解决方法:
给帖子DiscussPost类加上相关注解
package com.ateam.community.entity; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.util.Date; // indexName:索引名,type:固定为_doc,shards:分片,replis:备份 @document(indexName = "discusspost", type = "_doc", shards = 6, replicas = 3) public class DiscussPost { @Id private int id; @Field(type = FieldType.Integer) private int userId; //analyzer:存储时的解析器, //searchAnalyzer:存储时的解析器, 拆分尽可能少的,但满足你意图的分词器 @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 discussType; @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; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getDiscussType() { return discussType; } public void setDiscussType(int discussType) { this.discussType = discussType; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public int getCommentCount() { return commentCount; } public void setCommentCount(int commentCount) { this.commentCount = commentCount; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } @Override public String toString() { return "DiscussPost{" + "id=" + id + ", userId=" + userId + ", title='" + title + ''' + ", content='" + content + ''' + ", discussType=" + discussType + ", status=" + status + ", createTime=" + createTime + ", commentCount=" + commentCount + ", score=" + score + '}'; } }2.3 数据层
在dao下新建一个elasticsearch包,并创建DiscussPostRepository接口
@Repository // spring 提供 public interface DiscussPostRepository extends ElasticsearchRepository2.4 测试{ }
在test包下,新建一个类来测试es
package com.ateam.community; @RunWith(SpringRunner.class) @SpringBootTest @ContextConfiguration(classes = CommunityApplication.class)//配置类 public class ElasticsearchTests { @Resource private DiscussPostMapper discussPostMapper; @Autowired private DiscussPostRepository discussPostRepository; @Autowired private ElasticsearchTemplate elasticsearchTemplate; @Test public void testInert(){ discussPostRepository.save(discussPostMapper.selectDiscussPostById(241)); discussPostRepository.save(discussPostMapper.selectDiscussPostById(242)); discussPostRepository.save(discussPostMapper.selectDiscussPostById(243)); } @Test public void testInsetList(){ discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(101,0,100,0)); discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(102,0,100,0)); discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(103,0,100,0)); discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(111,0,100,0)); discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(131,0,100,0)); discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(132,0,100,0)); discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(133,0,100,0)); discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(134,0,100,0)); } @Test public void testUpdate(){ DiscussPost post = discussPostMapper.selectDiscussPostById(231); post.setContent("我是新人,使劲灌水!!!!"); discussPostRepository.save(post); } @Test public void testDelete(){ discussPostRepository.deleteById(231); } @Test public void testSearchByRepository(){ SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬","title","content")) .withSort(SortBuilders.fieldSort("discussType").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(); //elasticsearchTemplate.queryForPage(searchQuery,class,SearchResultMapper) // 底层获取到了高亮显示的值,但是没有返回 Page3. 开发社区搜索功能page = discussPostRepository.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); } } @Test public void testSearchByTemplate(){ SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬","title","content")) .withSort(SortBuilders.fieldSort("discussType").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 page = elasticsearchTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() { @Override public AggregatedPage mapResults(SearchResponse searchResponse, Class aClass, Pageable pageable) { SearchHits hits = searchResponse.getHits(); if (hits.totalHits <= 0) { return null; } ArrayList 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 discussType = hit.getSourceAsMap().get("discussType").toString(); post.setDiscussType(Integer.valueOf(discussType)); 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)); String score = hit.getSourceAsMap().get("score").toString(); post.setScore(Double.valueOf(score)); // 处理高亮显示的结果 HighlightField titleField = hit.getHighlightFields().get("title"); if (titleField != null) { // titleField.getFragments() 返回的是一个数组 post.setTitle(titleField.getFragments()[0].toString()); } HighlightField contentField = hit.getHighlightFields().get("content"); if (contentField != null) { // titleField.getFragments() 返回的是一个数组 post.setContent(contentField.getFragments()[0].toString()); } list.add(post); } return new AggregatedPageImpl(list, pageable, hits.getTotalHits(),searchResponse.getAggregations(), searchResponse.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); } } }
搜索服务
将帖子保存至Elasticsearch服务器从Elasticsearch服务器删除帖子从Elasticsearch服务器搜索帖子 发布事件
发布帖子时,将帖子异步的提交到Elasticsearch服务器增加评论时,将帖子异步的提交到Elasticsearch服务器在消费组件中增加一个方法,消费帖子发布事件 显示结果
在控制器中处理搜索请求,在HTML上显示搜索结果
3.1 搜索服务
在service包下,新建一个类ElasticsearchService
@Service public class ElasticsearchService { @Autowired private DiscussPostRepository discussPostRepository; @Autowired private ElasticsearchTemplate elasticsearchTemplate; // 添加、修改 public void saveDiscussPost(DiscussPost post) { discussPostRepository.save(post); } // 删除 public void deleteDiscussPost(int id) { discussPostRepository.deleteById(id); } // 查询 public Page3.2 发布事件 3.2.1 发布帖子时,将帖子异步的提交到Elasticsearch服务器searchDiscussPost(String keyword, int current, int limit) { SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery(keyword,"title","content")) .withSort(SortBuilders.fieldSort("discussType").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("").postTags(""), new HighlightBuilder.Field("content").preTags("").postTags("") ).build(); return elasticsearchTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() { @Override public AggregatedPage mapResults(SearchResponse searchResponse, Class aClass, Pageable pageable) { SearchHits hits = searchResponse.getHits(); if (hits.totalHits <= 0) { return null; } ArrayList 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 discussType = hit.getSourceAsMap().get("discussType").toString(); post.setDiscussType(Integer.valueOf(discussType)); 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)); String score = hit.getSourceAsMap().get("score").toString(); post.setScore(Double.valueOf(score)); // 处理高亮显示的结果 HighlightField titleField = hit.getHighlightFields().get("title"); if (titleField != null) { // titleField.getFragments() 返回的是一个数组 post.setTitle(titleField.getFragments()[0].toString()); } HighlightField contentField = hit.getHighlightFields().get("content"); if (contentField != null) { // titleField.getFragments() 返回的是一个数组 post.setContent(contentField.getFragments()[0].toString()); } list.add(post); } return new AggregatedPageImpl(list, pageable, hits.getTotalHits(),searchResponse.getAggregations(), searchResponse.getScrollId(),hits.getMaxScore()); } }); } }
修改DiscussPostController类中的addDiscussPost方法
修改CommentController类中的addComment方法
在event包下,EventConsumer类中新增一个消费帖子发布事件的方法
// 消费发帖事件 @KafkaListener(topics = {TOPIC_PUBLISH}) public void handlePublishMessage(ConsumerRecord record) { if (record == null || record.value() == null) { logger.error("消息的内容为空!"); return; } // 利用fastjson将json字符串转化为Event对象 Event event = JSONObject.parseObject(record.value().toString(), Event.class); if (event == null) { logger.error("消息格式错误!"); return; } // 查询这个帖子 DiscussPost post = discussPostService.findDiscussPostById(event.getEntityId()); // 将这个帖子存储到es服务器中 elasticsearchService.saveDiscussPost(post); }3.3 显示结果
- 在controller包下新建一个searchController类
package com.ateam.community.controller; @Controller public class SearchController implements CommunityConstant { @Autowired private ElasticsearchService elasticsearchService; @Autowired private UserService userService; @Autowired private LikeService likeService; // search?keyword=xxx @RequestMapping(value = "/search", method = RequestMethod.GET) public String search(String keyword, Page page, Model model){ // 搜索帖子 org.springframework.data.domain.PagesearchResult = elasticsearchService.searchDiscussPost(keyword,page.getCurrent() - 1, page.getLimit()); // 聚合数据 List
- 修改index.html页面中搜索框
处理search.html页面
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)