Elasticsearch:一个分布式的、Restful 风格的搜索引擎;支持对各种类型的数据的索引;搜索速度快,可以提供实时的搜索服务;便于水平扩展,每秒可以处理 PB 级海量数据
目录
1.Spring 整合 Elasticsearch
1.1 实体类与 ES 建立关系
1.2 实现接口
1.3 测试类
1.Spring 整合 Elasticsearch
- 引入依赖:spring-boot-starter-data-elasticsearch
- 配置 Elasticsearch:cluster-name、cluster-notes
- Spring Data Elaticsearch:ElaticsearchRestTemplate、ElaticsearchRepository
依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
配置:
# ElasticsearchProperties
spring.data.elasticsearch.cluster-name=xiaowen
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
解决冲突:Elasticsearch 底层是基于 Netty;Redis 底层也是基于 Netty,两者启动 natty 的时候由冲突
在 Application 类(核心入口配置类,最先被加载的)添加:
- 添加 @PostConstruct 注解:管理 Bean 的初始化方法,由注解所修饰的方法会在构造器调用完成之后被执行
- 添加方法:解决 netty 启动冲突问题(Netty4Utils.setAvailableProcessors())
package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;import javax.annotation.PostConstruct;@SpringBootApplication
public class DemoApplication {//添加 @PostConstruct 注解:管理 Bean 的初始化方法,由注解所修饰的方法会在构造器调用完成之后被执行@PostConstructpublic void init() {// 解决netty启动冲突问题// see Netty4Utils.setAvailableProcessors()System.setProperty("es.set.netty.runtime.available.processors", "false");}public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}
1.1 实体类与 ES 建立关系
编写与需求相关的代码:把数据库中存的帖子再存到 ES 服务器中,在 ES 服务器中搜索帖子
帖子表存到 ES 中的索引,每个字段对应的类型、怎么搜索都是要进行配置,但是配置不需要写 xml 文件,通过注解实现,注解写在实体类中(针对帖子操作,写在 discussPost 类中)
- 在此类中添加注解 @Document(indexName = " ", type = " ", shards = , replicas = ):Spring 整合 ES,底层访问 ES 会自动将实体数据和 ES 服务器的索引映射:indexName:索引;type:类型;shards:分片;replicas:副本(ElasticSearch7.x已经不推荐使用type了,并且将在8.0彻底移除)
- 主键通常会加 @Id、普通字段添加 @Field(type = FiledType. )
- 重点是 title 和 context:搜索帖子主要搜索标题和内容(类型为 type = FieldType.Text),存储的时候尽可能多的拆分关键词,增加搜索范围(使用 analyzer = "ik_max_word");搜索的时候,需要尽可能满足需求,不需要拆分太多次(使用searchAnalyzer = "ik_smart")
/*** 与 ES 中的索引对应*///Spring 整合 ES,底层访问 ES 会自动将实体数据和 ES 服务器的索引映射
@Document(indexName = "discusspost")
public class DiscussPost {@Idprivate int id;@Field(type = FieldType.Integer)private int userId;//搜索帖子主要搜索标题和内容//存储的时候尽可能多的拆分关键词,增加搜索范围(使用 analyzer = "ik_max_word")// 搜索的时候,需要尽可能满足需求,不需要拆分太多次(使用searchAnalyzer = "ik_smart")@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;//分数
}
上述这个实体类就和es之间的数据建立了联系。
1.2 实现接口
在dao下新建elasticsearch,实现 DiscussPostRepository 接口,继承时声明泛型,主键类型
- 添加注解 @Repository:这个接口是数据访问层代码,ES可以看作特殊的数据库(@Mapper 是 MyBatis 专用注解)
package com.example.demo.dao.elasticsearch;import com.example.demo.entity.DiscussPost;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {}
1.3 测试类
package com.example.demo;import com.example.demo.dao.DiscussPostMapper;
import com.example.demo.dao.elasticsearch.DiscussPostRepository;
import com.example.demo.entity.DiscussPost;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = DemoApplication.class)
public class ElasticsearchTests {@Autowiredprivate DiscussPostMapper discussMapper;@Autowiredprivate DiscussPostRepository discussRepository;@Autowiredprivate ElasticsearchRestTemplate elasticsearchRestTemplate;//查询@Testpublic void testInsert() {discussRepository.save(discussMapper.selectDiscussPostById(241));discussRepository.save(discussMapper.selectDiscussPostById(242));discussRepository.save(discussMapper.selectDiscussPostById(243));}//插入多条数据@Testpublic void testInsertList() {discussRepository.saveAll(discussMapper.selectDiscussPosts(101, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(102, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(103, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(111, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(112, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(131, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(132, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(133, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(134, 0, 100));}//修改@Testpublic void testUpdate() {DiscussPost post = discussMapper.selectDiscussPostById(231);post.setContent("我是新人,使劲灌水.");discussRepository.save(post);}//删除@Testpublic void testDelete() {// discussRepository.deleteById(231);discussRepository.deleteAll();}//搜索@Testpublic void testSearchByRepository() {NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")).withSorts(SortBuilders.fieldSort("type").order(SortOrder.DESC),SortBuilders.fieldSort("score").order(SortOrder.DESC),SortBuilders.fieldSort("createTime").order(SortOrder.DESC)).withPageable(PageRequest.of(0, 10)).withHighlightFields( //高亮显示new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")).build();// elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)// 底层获取得到了高亮显示的值, 但是没有返回.Iterable<DiscussPost> page = discussRepository.findAll();for (DiscussPost post : page) {System.out.println(post);}}// 查询数据并将关键字高亮显示@Testpublic void testSearchByTemplate() {// withQuery()用于构造搜索条件NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")).withSorts(SortBuilders.fieldSort("type").order(SortOrder.DESC),SortBuilders.fieldSort("score").order(SortOrder.DESC),SortBuilders.fieldSort("createTime").order(SortOrder.DESC)).withPageable(PageRequest.of(0, 10)).withHighlightFields(new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")).build();//查询SearchHits<DiscussPost> search = elasticsearchRestTemplate.search(searchQuery, DiscussPost.class);long count = elasticsearchRestTemplate.count(searchQuery, DiscussPost.class);System.out.println("共查询到: "+ count +"条数据");//得到查询返回的内容List<SearchHit<DiscussPost>> searchHits = search.getSearchHits();// 设置一个最后需要返回的实体类集合List<DiscussPost> discussPosts = new ArrayList<>();// 遍历返回的内容,进行处理for (SearchHit<DiscussPost> hit : searchHits) {// 获取高亮的内容Map<String, List<String>> highlightFields = hit.getHighlightFields();// 将高亮的内容添加到content中(匹配到的如果是多段,就将第一段高亮显示)// 没有匹配到关键字就显示原来的title和contenthit.getContent().setTitle(highlightFields.get("title")==null ? hit.getContent().getTitle():highlightFields.get("title").get(0));hit.getContent().setContent(highlightFields.get("content")==null ? hit.getContent().getContent():highlightFields.get("content").get(0));// 放到实体类中discussPosts.add(hit.getContent());}for (DiscussPost post : discussPosts) {System.out.println(post);}}
}