SpringBoot 整合 elasticsearch

#写在前面

无聊看了看自己的博客,发现上一次更新居然是一月份的事情,而且还是自己的无厘头小说,看来是就连自己的二十岁生日也想不到有什么可以写的内容。

但是随着最近我的视频网站开发完成,想着写几篇博客记录一下自己学过的东西。

属于是代码小鬼,内行人看个笑话就好。


#过程

其实在四月份,我就完成了视频网站的基础功能的实现,选择走的极简路线,换句话说就是没那么多花里胡哨的东西,再换句话说,就是我不会整那些玩意。

在我准备开始专心优化代码的时候,前端一个不起眼的地方倒是突然吸引了我的注意,一个搜索框。
这时我才突然想起来,我的视频网站还差一个最基础的功能,那就是搜索功能,算是自己打自己的脸了。

那这个功能怎么实现呢,其实很简单,模糊查询就可以了。 说到这里,相信已经有人开始看不下去了。
模糊查询谁不会啊,总不会还有人不会吧? 这么简单就实现了,那你这篇博客写个毛线。

那肯定不会只写那么简单的内容,于是接下来就开始今天的内容
SpringBoot集成Elasticsearch


#关于Elasticsearch

首先,Elaticsearch是什么?
它是一个分布式、RESTful风格的搜索和数据分析引擎。简单来说,就是你可以把你的数据丢到上面,在上面进行搜索和数据的分析。


#如何下载Elasticsearch

https://www.elastic.co/cn/downloads/past-releases

在官网就可以下到你想要的zip包

Elasticsearch

Kibana

分别是Elaticsearch的本体,以及使其可视化的Kibana。这两个都是开包即用的,所以说很方便。

下载解压之后,分别运行其bin目录下的elasticsearch.bat和kibana.bat

接着访问 http://localhost:5601 , 就可以看到Kibana的用户界面了。


#如何集成Elasticsearch

接下来讲如何讲Elaticsearch集成到我的视频网站,也就是springboot项目当中。


#在pom.xml中添加相关依赖

万变不离其宗,第一件事情肯定是为其添加相关依赖。

1
2
3
4
5
<!--Elasticsearch相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

#客户端配置文件 RestClientConfig

接着是其配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* ElasticSearch 客户端配置
*/
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
@Value("${es.host:localhost}")
private String esHost;
@Value("${es.port:9200}")
private Integer esPort;

@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(esHost+":"+esPort)
.build();
return RestClients.create(clientConfiguration).rest();
}
}

记得不要忘了在application.properties上面配置你的Elasticsearch端口,默认为9200

1
2
es.host=localhost
es.port=9200

在domain中添加文档对象 EsVideo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.makiori.server.domain.es;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
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;

/**
* @Author Makiori
* @create 2022/5/6 13:55
*/
@Data
@Document(indexName = "video")
public class EsVideo {

@Id
private String id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;

//@Field(type = FieldType.Text)
private String item;

//@Field(type = FieldType.Integer)
private Integer time;

//@Field(type = FieldType.Text)
private String vod;

// @Field(type = FieldType.Date,format = DateFormat.basic_date_time)
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createdAt;

// @Field(type = FieldType.Date,format = DateFormat.basic_date_time)
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date updatedAt;

//@Field(type = FieldType.Text)
private String cover;

//@Field(type = FieldType.Text)
private String status;


//getting & setting 省略
}

添加EsVideoRepository接口用于操作Elasticsearch

有了类,就得想办法操作Elaticsearch, 我们通过继承ElasticsearchRepository获得操作Elaticsearch的手段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.makiori.server.repository;

import com.makiori.server.domain.es.EsVideo;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**
* @Author Makiori
* @create 2022/5/6 15:15
*/
public interface EsVideoRepository extends ElasticsearchRepository<EsVideo, String> {

//关键词高亮
@Highlight(fields = {
@HighlightField(name = "title")
})

@Query("{\"match\":{\"title\":\"?0\"}}")
SearchHits<EsVideo> find(String keyword);

}

添加EsVideoService

添加EsVideoService,实现相应操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package com.makiori.server.service.es;

import com.makiori.server.domain.es.EsVideo;
import com.makiori.server.domain.Video;
import com.makiori.server.mapper.my.MyVideoMapper;
import com.makiori.server.repository.EsVideoRepository;
import com.makiori.server.util.CopyUtil;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Iterator;
import java.util.List;


@Service
public class EsVideoService {

@Resource
private EsVideoRepository esVideoRepository;

@Resource
private MyVideoMapper myVideoMapper;




/**
* 导入所有视频到ES
*/
public int importAll() {
List<EsVideo> esVideoList = myVideoMapper.getAllEsVideoList(null);
Iterable<EsVideo> esVideoIterable = esVideoRepository.saveAll(esVideoList);
Iterator<EsVideo> iterator = esVideoIterable.iterator();
int result = 0;
while(iterator.hasNext()) {
result++;
iterator.next();
}
return result;
}

/**
* 查询视频
*/
public SearchHits<EsVideo> searchVideo (String keyword) {

return esVideoRepository.find(keyword);
}


/**
* es新增
*/
public void esInsert(Video video) {
EsVideo esVideo = CopyUtil.copy(video, EsVideo.class);
try {
esVideoRepository.save(esVideo);
} catch (Exception e) {
e.printStackTrace();
}

}

/**
* es修改
*/
public void esUpdate(Video video) {
EsVideo esVideo = CopyUtil.copy(video, EsVideo.class);
// Optional<EsVideo> esVideo1 = esVideoRepository.findById(esVideo.getId());
// EsVideo test = esVideo1.get();
// System.out.println("es修改:" + test);
try{
esVideoRepository.save(esVideo);
} catch (Exception e) {
e.printStackTrace();
}



}

/**
* es删除
*/
public void esDelete(String id) {
try{
esVideoRepository.deleteById(id);
} catch (Exception e) {
e.printStackTrace();
}
}

}


添加EsVideoController定义接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.makiori.search.controller;

import com.makiori.server.domain.es.EsVideo;
import com.makiori.server.dto.ResponseDto;
import com.makiori.server.service.es.EsVideoService;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
* @Author Makiori
* @create 2022/5/7 13:45
*/
@RestController
@RequestMapping("/esvideo")
public class EsVideoController {

@Resource
private EsVideoService esVideoService;



/**
* 导入所有数据库中视频到ES
*/
@PostMapping("/importAll")
public ResponseDto importAllList() {
ResponseDto responseDto = new ResponseDto();
int count = esVideoService.importAll();
responseDto.setContent(count);
return responseDto;
}


/**
* ElasticSearch查询
*/
@PostMapping("/search/{key}")
public ResponseDto search(@PathVariable String key) {

ResponseDto responseDto = new ResponseDto();
//计时器
final StopWatch stopWatch = new StopWatch();
//开始记时
stopWatch.start();

final SearchHits<EsVideo> searchHits = esVideoService.searchVideo(key);

//停止计时
stopWatch.stop();
final double totalTimeSeconds = stopWatch.getTotalTimeSeconds();
responseDto.setContent(searchHits.getSearchHits());
// responseDto.setCode(String.valueOf(totalTimeSeconds));
return responseDto;
}

}

学习思考

由于是第一次使用这玩意,所以第一次集成的时候并没有考虑将其分离出一个模块来,但毕竟是分布式,所以还是得想法设法将其服务分离出来。
其实现在还有一些未解决的问题,比如说如何同步搜索引擎与数据库中的数据呢。
网上看来的一个侵入性比较强的方法,就是在对数据库进行增删改查的同时,对搜索引擎也进行相同的操作。忽略侵入性的问题,如果当搜索引擎崩溃的时候,就会堆积出莫名奇妙的数据。或者像我这样项目删除添加了事务注释之后,会导致无法完成任务而使得整个软件崩溃。那么分布式就变得没有一点意义了。

文章作者: 牧尾伊織
文章链接: http://example.com/2022/05/08/blog/SpringBoot 整合 elasticsearch/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Makiori's blog