Jasper Ji

开口不在舌头上

0%

Spring Boot使用Elasticsearch

主要是想把Elasticsearch整合到当前的系统中,用来搜索文章。

环境

Elasticsearch 7.12.0
Logstash 7.12.0
Mysql 8.0

数据同步

把Elasticsearch整合到现有系统意味需要把当前要搜索的数据添加到Elasticsearch里面,这就有个数据同步问题。因为是测试工程使用的是Logstash的全量数据同步。Logstash的新版本已经包含了Jdbc的插件,所以不需要安装。

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
input {
jdbc {
# myslq驱动,可以官网下载,我用的是Maven依赖已经下载的Jar文件
jdbc_driver_library => ""
# Mysql驱动类全类名,注意mysql8.x以上需要加cj
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
# mysql连接url
jdbc_connection_string => ""
# mysql 用户名
jdbc_user => ""
# mysql 密码
jdbc_password => ""
# 设置定时任务,多久执行一次查询,默认一分钟,需要无延迟可使用schedule => "* * * * * *"
schedule => "* * * * *"
# 清空上次的sql_last_value记录
clean_run => true
# 要执行的sql(同步)语句,替换成你自己的sql
statement => "SELECT * FROM t_article"
}
}

output {
elasticsearch {
# es主机和端口
hosts => ["127.0.0.1:9200"]
# 同步数据在ES的索引名称,替换为你自己的。
index => "my-cms"
# es文档的id,表示使用mysql表的id
document_id => "%{id}"
}
}

生产环境的数据同步可以参考阿里云Elasticsearch

参考:
Jdbc input plugin

Spring Boot配置

添加Maven依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

添加Elasticsearch客户端配置类

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
@Configuration
@EnableElasticsearchRepositories(basePackages = "io.pratik.elasticsearch.repositories")
@ComponentScan(basePackages = { "io.pratik.elasticsearch" })
public class ElasticsearchClientConfig extends AbstractElasticsearchConfiguration {
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
// 配置连接信息
final ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo("localhost:9200")
.build();

return RestClients.create(clientConfiguration).rest();
}

/**
* 这个很重要,不添加的话无法使用@Filed注解
*/
@Bean
@Override
public EntityMapper entityMapper() {

ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
elasticsearchMappingContext(), new DefaultConversionService()
);
entityMapper.setConversions(elasticsearchCustomConversions());

return entityMapper;
}
}

测试文章类,必须要加type='_doc',不然无法使用。另外遇到的问题是我数据库的日期用的是Date话,一直提示解析错误,最后使用LocalDateTime后就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
@Document(indexName = "my-cms", type = "_doc")
public class EsArticle {
@Id // org.springframework.data.annotation.Id
private Integer id; // 文章ID

@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title; // 文章标题

@Field(name = "content", type = FieldType.Text, analyzer = "ik_max_word")
private String articleContent; // 文章正文内容

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Field(name = "create_time")
private LocalDateTime createTime; // 文章创建时间
}

创建Repository类。

1
2
3
public interface ElasticArticleRepository extends ElasticsearchRepository<EsArticle, Integer> {

}

生成Controller,使用Postman测试接口是否正常工作。

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
@RestController
@RequestMapping("/article")
public class SearchController {
@Autowired
private ElasticArticleRepository elasticRepository;

@PostMapping(value = "/search")
public List<EsArticle> handleSearchRequest(
@RequestParam Map<String, Object> requestParam) {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
// 构造ES查询条件
String keyword = (String) requestParam.get("keyword");
queryBuilder.should(QueryBuilders.matchPhraseQuery("title", keyword))
.should(QueryBuilders.matchPhraseQuery("content", keyword));

StopWatch watch = new StopWatch();
watch.start(); // 开始计时

Page<EsArticle> articles = (Page<EsArticle>)
elasticRepository.search(queryBuilder); // 检索数据

watch.stop(); // 结束计时
System.out.println(String.format("数据检索耗时:%s ms",
watch.getTotalTimeMillis()));

return articles.getContent();
}
}