Elasticsearch安装
1、配置
#集群名
cluster.name: nowcoder
#路径
path.data: D:\JavaWork\data\elasticsearch-6.4.3\data
path.logs: D:\JavaWork\data\elasticsearch-6.4.3\logs
Spring整合Elasticsearch
- 引入依赖
- spring-boot-starter-data-elasticsearch
- 配置Elasticsearch
- cluster-name、cluster-nodes
- Spring Data Elasticsearch
- ElasticsearchTemplate
- ElasticsearchRepository
引入依赖
1、pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
这里删去了版本。因为POM文件是继承了父POM,父POM里面的版本是已经测试过的不会产生冲突的。
2、配置
# ElasticsearchProperties
spring.data.elasticsearch.cluster-name=nowcoder # 集群名称
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300 # 设置节点
3、解决netty冲突问题
@PostConstruct
public void init() {
// 解决netty启动冲突问题
// see Netty4Utils.setAvailableProcessors()
System.setProperty("es.set.netty.runtime.available.processors", "false");
}
该注解表明是在构造器后调用,即初始化方法。
设置参数es.set.netty.runtime.available.processors 是因为,Redis和ElasticsearchProperties 底层都基于netty,同时加载的时候ElasticsearchProperties会有点小dug。
使用
在实体类中添加注解
对于要搜索的实体(这里是帖子),必须要将数据放在Es服务器里才能搜索。因此要配置数据库中的表、字段,与Es中的索引、类型等的对应关系。
Elasticsearch常用注解:https://blog.csdn.net/qq_41345773/article/details/105618712
import java.util.Date;
@Document(indexName = "discusspost", type = "_doc", shards = 6, replicas = 3)
public class DiscussPost {
@Id
private int id;
@Field(type = FieldType.Integer)
private int userId;
// 互联网校招
@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;
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 getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
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 + '\'' +
", type=" + type +
", status=" + status +
", createTime=" + createTime +
", commentCount=" + commentCount +
", score=" + score +
'}';
}
}
1、@id 表示是文档的id,文档可以认为是mysql中表行的概念
2、@Field(type = FieldType.Text, analyzer = “ik_max_word”, searchAnalyzer = “ik_smart”)
type参数是文档中字段的类型,FieldType.Text 会进行分词并建了索引的字符类型;analyzer = “ik_max_word” 指定储存时的解析器,尽可能多的进行分词;searchAnalyzer = “ik_smart” 搜索时使用的解析器。
controller
@Repository // 数据库注解
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> { // DiscussPost要搜索的实体类,Integer声明主键类型
}
增删改
在test包里进行测试
1、增加
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class 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));
}
// 一次插入多条数据
@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));
}
2、修改
@Test
public void testUpdate() {
DiscussPost post = discussMapper.selectDiscussPostById(231);
post.setContent("我是新人,使劲灌水.");
discussRepository.save(post);
}
3、删除
@Test
public void testDelete() {
discussRepository.deleteById(231); // 删除一条数据
discussRepository.deleteAll(); // 删除整个索引
}
总结下来就是,先从数据库中查找数据,再使用ElasticsearchRepository的接口中的方法调用相关API
搜索数据
1、使用Repository,最后返回结果封装到Page里。该方法中 底层获取得到了高亮显示的值, 但是没有返回
@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();
// elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)
// 底层获取得到了高亮显示的值, 但是没有返回.
Page<DiscussPost> page = discussRepository.search(searchQuery); // 返回的结果由Page封装
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);
}
}
2、使用Template 将搜索结果高亮显示
@Test
public void testSearchByTemplate() {
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")) // 多字段搜索
.withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC)) // 按照type(是否加精)、分数、创建时间排序
.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() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
SearchHits hits = response.getHits(); // 获取命中的数据
if (hits.getTotalHits() <= 0) {
return null;
}
// 将命中的数据进行处理,包装成Discusspost 放入list内
List<DiscussPost> list = new ArrayList<>();
for (SearchHit hit : hits) {
// 将命中的数据包装成Discusspost
DiscussPost post = new DiscussPost();
String id = hit.getSourceAsMap().get("id").toString(); // 先获取数据再设置·Discusspost类
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());
}
});
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.queryForPage
1、获取命中的数据
2、将命中的数据进行处理,包装成Discusspost 放入list内
a)高亮显示的结果
HighlightField titleField = hit.getHighlightFields().get(“title”); 得到一个字段
titleField.getFragments()[0] 返回一个数组,取第一段,重新放入实体中
3、最后返回 new AggregatedPageImpl