本文通过一个简单的访问普通log并添加Movie类型日志介绍如何使用spring-data-elasticsearch访问elasticsearch数据库。
关于ELK的理论以及使用场景不在本文的讨论访问,如何安装ELK全家桶也不再本文讨论范文。本文主要介绍一个简单的使用场景,就是如何通过spring-data-elasticsearch访问elasticsearch中数据,以及如何添加数据。
本文主要参考官方文档https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/和https://spring.io/projects/spring-data-elasticsearch
前提
完整的代码在这里,欢迎fork,加星。
1, 前提
前提条件:ELK全套已经安装并运行良好。
网上关于如何安装ELK的介绍很多,本文就不再赘述。 笔者采用https://github.com/deviantony/docker-elk提供的docker-compose文件安装。 Kibana访问地址为http://localhost:5601
2, 添加java项目依赖。
主要是添加spring-boot-starter-data-elasticsearch依赖,参考spring boot guide。
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
...
dependencies>
3, 编写domain对象
本文中的有两个对象Movie和Log,都比较简单。 需要注意的是要加上@NoArgsConstructor
和@AllArgsConstructory以及@Document, 其中@NoArgsConstructor自动为我们创建一个无参数的构造函数,@Document表示这个是ElasticSearch中的store会关联。
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = xxx-2018.07.06", type = "logs", shards = 1, replicas = 0)
public class Log {
@Id
private String id;
private String HOSTNAME;
private String level;
private int port;
private String thread_name;
private int level_value;
private String host;
private String logger_name;
private String message;
private String[] tags;
//private int version;
//试图更改updateDate的时间显示格式
//@Field(type = FieldType.Date, format = DateFormat.date_optional_time) ---timestamp is long
//@Field(type = FieldType.Date, format = DateFormat.basic_date_time )
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second )
@JsonProperty(value = "@timestamp")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
@DateTimeFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
private Date updateDate;
}
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "movie-store", type = "movie", shards = 1, replicas = 0)
public class Movie {
@Id
private Long id;
private String name;
private Double rating;
}
4,继承ElasticsearchRepository接口
ElasticsearchRepository就如同spring jpa中的JpaRepository一样, 已经帮我们实现很多save、delete、findAll、 findOne、分页等功能,还能根据方法的名称和查询关系字符串自动组成And、or 、GT、 LT等等。
LogRepository中的List findByLevel(String level);方法会自动按照日志级别帮我们查询Log,就相当于jpa中的select * from log-index where log-index.level =level
MovieRepository中的findByRatingBetween(Double start, Double end);方法就相当于就相当于jpa中的select * from movie-index where movie-index.rating between start, and end
注意这段代码来自官方例子
package com.yq.repository;
import com.yq.domain.Director;
import com.yq.domain.Movie;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* Created by Nasir on 12-09-2015.
*/
@Repository
public interface MovieRepository extends ElasticsearchRepository<Movie, Long> {
List<Movie> findByName(String name);
List<Movie> findByRatingBetween(Double start, Double end);
}
LogRepository代码与MovieRepository类似就不再此展示了,详细可见代码。 欢迎加star和fork。
5, 实现service
一般而言单个repository只是实现该对象自身的CRUD,如果我们要实现业务逻辑,需要在service中完成,本示例比较简单,service只是将repository做了加单包装。
6, 在Controller中访问
完成domain、repository和service就可以访问数据, 我们这里使用controller只是为了展示数据。
第一个controller,LogController会会查询日志,其中ElasticsearchTemplate是另外一种访问elasticsearch数据的方式,以后会单独在介绍。大家主要关注controller调用LogService就行。
第二个controller,MovieResource会展示添加、删除和查询
代码示例
@RestController
public class MovieResource {
private MovieService movieService;
@Autowired
public MovieResource(MovieService movieService) {
this.movieService = movieService;
}
@PostMapping("/movie/add")
@ApiOperation(value = "add", notes="private")
@ApiImplicitParams({
@ApiImplicitParam(name = "newMovie", value = "newMovie", required = true, dataType = "newMovie", paramType = "body")
})
public ResponseEntity<Movie> addMovie(@RequestBody Movie newMovie) {
Movie savedMovie = movieService.addMovie(newMovie);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path(
"/{id}").buildAndExpand(savedMovie.getId()).toUri();
return ResponseEntity.ok(savedMovie).created(location).build();
}
@ApiOperation(value = "add", notes="private")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "id", required = true, dataType = "long", paramType = "path")
})
@DeleteMapping("/movie/{id}/delete")
public ResponseEntity<String> deleteMovie(@PathVariable("id") Long id) {
movieService.deleteMovie(id);
return ResponseEntity.ok("Deleted");
}
@ApiOperation(value = "add", notes="private")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "name", required = true, dataType = "string", paramType = "path")
})
@GetMapping("/movie/get-by-name/{name}")
public ResponseEntity<List<Movie>> findMovieByName(@PathVariable("name") String name) {
List<Movie> fetchedMovie = movieService.getByName(name);
return ResponseEntity.ok(fetchedMovie);
}
}
LogController代码可参看github代码。
7, 配置文件加上elasticsearc相关配置
需要在application.properties 中配置elasticsearch信息。
#Cluster node port configuration
spring.data.elasticsearch.cluster-name=docker-cluster
spring.data.elasticsearch.cluster-nodes=a.b.c.d:9300
spring.data.elasticsearch.repositories.enabled=true
8, 总结
使用spring-data-elasticsearch访问elasticsearch,就是通过domain(加上@Document注解)和ElasticsearchRepository完成elasticsearch的访问和操作。
关于如何使用ElasticsearchTemplate后面的博文会继续讨论。