Elasticsearch:一个分布式的、Restful 风格的搜索引擎;支持对各种类型的数据的索引;搜索速度快,可以提供实时的搜索服务;便于水平扩展,每秒可以处理 PB 级海量数据
目录
1.Spring 整合 Elasticsearch
1.1 实体类与 ES 建立关系
1.2 实现接口
1.3 测试类
依赖:
org.springframework.boot
spring-boot-starter-data-elasticsearch
配置:
# ElasticsearchProperties
spring.data.elasticsearch.cluster-name=xiaowen
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
解决冲突:Elasticsearch 底层是基于 Netty;Redis 底层也是基于 Netty,两者启动 natty 的时候由冲突
在 Application 类(核心入口配置类,最先被加载的)添加:
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 的初始化方法,由注解所修饰的方法会在构造器调用完成之后被执行
@PostConstruct
public 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);
}
}
编写与需求相关的代码:把数据库中存的帖子再存到 ES 服务器中,在 ES 服务器中搜索帖子
帖子表存到 ES 中的索引,每个字段对应的类型、怎么搜索都是要进行配置,但是配置不需要写 xml 文件,通过注解实现,注解写在实体类中(针对帖子操作,写在 discussPost 类中)
/**
* 与 ES 中的索引对应
*/
//Spring 整合 ES,底层访问 ES 会自动将实体数据和 ES 服务器的索引映射
@Document(indexName = "discusspost")
public class DiscussPost {
@Id
private 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之间的数据建立了联系。
在dao下新建elasticsearch,实现 DiscussPostRepository 接口,继承时声明泛型,主键类型
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 {
}
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 {
@Autowired
private DiscussPostMapper discussMapper;
@Autowired
private DiscussPostRepository discussRepository;
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
//查询
@Test
public void testInsert() {
discussRepository.save(discussMapper.selectDiscussPostById(241));
discussRepository.save(discussMapper.selectDiscussPostById(242));
discussRepository.save(discussMapper.selectDiscussPostById(243));
}
//插入多条数据
@Test
public 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));
}
//修改
@Test
public void testUpdate() {
DiscussPost post = discussMapper.selectDiscussPostById(231);
post.setContent("我是新人,使劲灌水.");
discussRepository.save(post);
}
//删除
@Test
public void testDelete() {
// discussRepository.deleteById(231);
discussRepository.deleteAll();
}
//搜索
@Test
public 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("").postTags(""),
new HighlightBuilder.Field("content").preTags("").postTags("")
).build();
// elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)
// 底层获取得到了高亮显示的值, 但是没有返回.
Iterable page = discussRepository.findAll();
for (DiscussPost post : page) {
System.out.println(post);
}
}
// 查询数据并将关键字高亮显示
@Test
public 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("").postTags(""),
new HighlightBuilder.Field("content").preTags("").postTags("")
).build();
//查询
SearchHits search = elasticsearchRestTemplate.search(searchQuery, DiscussPost.class);
long count = elasticsearchRestTemplate.count(searchQuery, DiscussPost.class);
System.out.println("共查询到: "+ count +"条数据");
//得到查询返回的内容
List> searchHits = search.getSearchHits();
// 设置一个最后需要返回的实体类集合
List discussPosts = new ArrayList<>();
// 遍历返回的内容,进行处理
for (SearchHit hit : searchHits) {
// 获取高亮的内容
Map> highlightFields = hit.getHighlightFields();
// 将高亮的内容添加到content中(匹配到的如果是多段,就将第一段高亮显示)
// 没有匹配到关键字就显示原来的title和content
hit.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);
}
}
}