社区网站项目6.2 Spring整合ElasticSearch
引入依赖spring-boot-starter-data-elasticsearch 配置Elasticsearch-cluster-name、cluster-nodes Spring Data ElasticsearchElasticsearchTemplateElasticsearchRepository其中ElasticsearchRepository是比较方便的接口,底层依赖于El
引入依赖
spring-boot-starter-data-elasticsearch
配置Elasticsearch
-cluster-name、cluster-nodes
Spring Data Elasticsearch
ElasticsearchTemplate
ElasticsearchRepository
其中ElasticsearchRepository是比较方便的接口,底层依赖于ElasticsearchTemplate;当ElasticsearchRepository不适用时,才去编写调用ElasticsearchTemplate。
去mvn库搜索spring-boot-starter-data-elasticsearch粘贴到pom.xml。在application.properties里添加配置
# ElasticsearchProperties
spring.data.elasticsearch.cluster-name=nowcoder
# 注意:elasticsearch有两个默认端口,其中9200是http形式访问,9300是tcp形式访问,java程序中一般用9300这个端口
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
# 还要注意:elasticsearch底层依赖Netty,而redis底层也依赖Netty,它俩在底层有个小冲突,需要手动处理一下
# 解决办法:在d:\javing\workspace\community/src/main/java/com/nowcoder/community/CommunityApplication提前里加一句
在CommunityApplication里添加
@PostConstruct
public void init(){
//解决netty启动冲突的问题
//see Netty4Utils.setAvailableProcessors(),解决调用NettyRuntime这个类的时候由于之前redis调用了而产生的冲突
System.setProperty("es.set.netty.runtime.available.processors", "false");
}
在entity包下的DiscussPost类里,添加相应的注解
//底层自动把实体和elasticsearch的索引映射起来
@Document(indexName = "discusspost",type = "_doc",shards = 6,replicas = 3) //type(类型)以后会被废弃(这里固定写为_doc就好),shards代表分片数,replicas代表副本数(往往是根据服务器的容量能力来配的,这里先不写太多,随便写一下)
public class DiscussPost {
@Id //和elasticsearch的字段对应起来
private int id;
@Field(type = FieldType.Integer)
private int userId;
// 比如“互联网校招”(尽可能地把这句话拆分成多个词条,与之匹配,增加搜索的范围,而搜索时用ik_smart分词器,缩小范围较精确地满足需求)
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart") //analyzer是存储时的解析器,searchAnalyzer是搜索时的解析器
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;
在dao包下新建一个子包elasticsearch,新建一个接口DiscussPostRepository,
@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost,Integer> {
}
先用postman查一下es里面有多少个索引,输入
localhost:9200/_cat/indices?v
然后新建测试类ElasticSearchTests,
@Autowired
private DiscussPostMapper discussMapper;
@Autowired
private DiscussPostRepository discussRepository;
@Autowired
private ElasticsearchTemplate elasticTemplate;
@Test
public void testInsert(){
discussRepository.save(discussMapper.selectDiscussPostById(241));
discussRepository.save(discussMapper.selectDiscussPostById(242));
discussRepository.save(discussMapper.selectDiscussPostById(243));
}
执行完后再去postman输入
localhost:9200/discusspost/_search
能查到刚才插入的数据。
往ElasticSearchTests里添加
@Test
public void testInsertList(){
discussRepository.saveAll(discussMapper.selectDiscussPosts(101,0,100,0));
discussRepository.saveAll(discussMapper.selectDiscussPosts(102,0,100,0));
discussRepository.saveAll(discussMapper.selectDiscussPosts(103,0,100,0));
discussRepository.saveAll(discussMapper.selectDiscussPosts(111,0,100,0));
discussRepository.saveAll(discussMapper.selectDiscussPosts(112,0,100,0));
discussRepository.saveAll(discussMapper.selectDiscussPosts(131,0,100,0));
discussRepository.saveAll(discussMapper.selectDiscussPosts(132,0,100,0));
discussRepository.saveAll(discussMapper.selectDiscussPosts(133,0,100,0));
discussRepository.saveAll(discussMapper.selectDiscussPosts(134,0,100,0));
}
执行完后再去postman输入
localhost:9200/discusspost/_search
能查到刚才添加的多条数据。
测试更改操作,在postman里选择GET方式,输入
localhost:9200/discusspost/doc_/231
可以看到id为231的帖子。往ElasticSearchTests里添加
@Test
public void testUpdate(){
DiscussPost post = discussMapper.selectDiscussPostById(231);
post.setContent("我是新人,使劲灌水");
discussRepository.save(post);
}
执行完再在postman里查一下,就能看到数据已经被修改了。
删除数据可以用
@Test
public void testDelete(){
discussRepository.deleteById(231);
// discussRepository.deleteAll();
}
接着测试最重要的搜索功能
@Test
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("<em>").postTags("</em>"),
new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
).build();
// Repository底层是调用了elasticTemplate.queryForPage(searchQuery,class,SearchResultMapper)
//底层获取到了高亮显示的值,但它没有返回,如果要整合起来,需要改写、并调用SearchResultMapper(太麻烦了,不如直接用elasticTemplate)
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);
}
}
改用elasticTemplate来查询
@Test
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("<em>").postTags("</em>"),
new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
).build();
Page<DiscussPost> page = elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() { //DiscussPost.class作为实体类传进去,而把SearchResultMapper类匿名实现
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, 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());
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
return null;
}
});
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);
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)