@@ -1,6 +1,6 @@ | |||||
# Spring Boot Demo | # Spring Boot Demo | ||||
spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过 AOP 记录 web 请求日志)、统一异常处理( json 级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、JPA(ORM 框架)、mybatis(ORM 框架)、redis-cache(缓存)、swagger(API 接口管理测试)、ureport2(中国式报表)、打包成 war 文件,后续会集成activemq,email,shiro,websocket,quartz,netty等模块。 | |||||
spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过 AOP 记录 web 请求日志)、统一异常处理( json 级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、JPA(ORM 框架)、mybatis(ORM 框架)、redis-cache(缓存)、swagger(API 接口管理测试)、ureport2(中国式报表)、打包成 war 文件、集成 ElasticSearch(采用原生操作 ES 的方式),后续会集成activemq,email,shiro,websocket,quartz,netty等模块。 | |||||
依赖的 Spring Boot 版本: | 依赖的 Spring Boot 版本: | ||||
@@ -58,8 +58,11 @@ spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actu | |||||
<module>../spring-boot-demo-orm-mybatis</module> | <module>../spring-boot-demo-orm-mybatis</module> | ||||
<module>../spring-boot-demo-cache-redis</module> | <module>../spring-boot-demo-cache-redis</module> | ||||
<module>../spring-boot-demo-swagger</module> | <module>../spring-boot-demo-swagger</module> | ||||
<module>../spring-boot-demo-rabc-shiro-mybatis</module> | |||||
<module>../spring-boot-demo-ureport2</module> | <module>../spring-boot-demo-ureport2</module> | ||||
<module>../spring-boot-demo-war</module> | <module>../spring-boot-demo-war</module> | ||||
<module>../spring-boot-demo-util</module> | |||||
<module>../spring-boot-demo-elasticsearch</module> | |||||
</modules> | </modules> | ||||
<parent> | <parent> | ||||
@@ -122,7 +125,7 @@ spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actu | |||||
<dependency> | <dependency> | ||||
<groupId>com.google.guava</groupId> | <groupId>com.google.guava</groupId> | ||||
<artifactId>guava</artifactId> | <artifactId>guava</artifactId> | ||||
<version>20.0</version> | |||||
<version>23.0</version> | |||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
<groupId>org.projectlombok</groupId> | <groupId>org.projectlombok</groupId> | ||||
@@ -161,6 +164,7 @@ spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actu | |||||
| [spring-boot-demo-swagger](./spring-boot-demo-swagger) | spring-boot 集成 [spring-boot-starter-swagger](https://github.com/SpringForAll/spring-boot-starter-swagger) (由大佬[翟永超](http://blog.didispace.com/)开源)用于统一管理、测试 API 接口 | | | [spring-boot-demo-swagger](./spring-boot-demo-swagger) | spring-boot 集成 [spring-boot-starter-swagger](https://github.com/SpringForAll/spring-boot-starter-swagger) (由大佬[翟永超](http://blog.didispace.com/)开源)用于统一管理、测试 API 接口 | | ||||
| [spring-boot-demo-ureport2](./spring-boot-demo-ureport2) | spring-boot 集成 [ureport2](https://github.com/youseries/ureport) 实现自定义报表(ureport2可以轻松实现复杂的中国式报表,功能十分强大) | | | [spring-boot-demo-ureport2](./spring-boot-demo-ureport2) | spring-boot 集成 [ureport2](https://github.com/youseries/ureport) 实现自定义报表(ureport2可以轻松实现复杂的中国式报表,功能十分强大) | | ||||
| [spring-boot-demo-war](./spring-boot-demo-war) | spring-boot 打成 war 包的配置 | | | [spring-boot-demo-war](./spring-boot-demo-war) | spring-boot 打成 war 包的配置 | | ||||
| [spring-boot-demo-elasticsearch](./spring-boot-demo-elasticsearch) | spring-boot 集成 ElasticSearch(采用原生操作 ES 的方式) | | |||||
# 官方提供的 starter 介绍 | # 官方提供的 starter 介绍 | ||||
@@ -39,6 +39,7 @@ | |||||
- [ ] spring-boot-demo-async(Spring boot 实现异步调用) | - [ ] spring-boot-demo-async(Spring boot 实现异步调用) | ||||
- [ ] spring-boot-demo-dubbo(集成 dubbo) | - [ ] spring-boot-demo-dubbo(集成 dubbo) | ||||
- [x] ~~spring-boot-demo-war(打包成war包)~~ | - [x] ~~spring-boot-demo-war(打包成war包)~~ | ||||
- [x] ~~spring-boot-demo-elasticsearch(集成 ElasticSearch,使用原生操作 ES 的方式)~~ | |||||
### 备注 | ### 备注 | ||||
@@ -0,0 +1,24 @@ | |||||
target/ | |||||
!.mvn/wrapper/maven-wrapper.jar | |||||
### STS ### | |||||
.apt_generated | |||||
.classpath | |||||
.factorypath | |||||
.project | |||||
.settings | |||||
.springBeans | |||||
### IntelliJ IDEA ### | |||||
.idea | |||||
*.iws | |||||
*.iml | |||||
*.ipr | |||||
### NetBeans ### | |||||
nbproject/private/ | |||||
build/ | |||||
nbbuild/ | |||||
dist/ | |||||
nbdist/ | |||||
.nb-gradle/ |
@@ -0,0 +1,280 @@ | |||||
# spring-boot-demo-elasticsearch | |||||
依赖 [spring-boot-demo-parent](../spring-boot-demo-parent) | |||||
ElasticSearch 的 demo 我这里没有使用 spring-data-elasticsearch,我使用的是原生的方式 | |||||
操作 ElasticSearch 由很多种方式: | |||||
1. ES 官方提供的原生方式,**本例子使用这种方式**,这种方式的好处是高度自定义,并且可以使用最新的 ES 版本,缺点就是所有操作都得自己写。 | |||||
2. 使用 Spring 官方提供的 spring-data-elasticsearch,这里给出地址 https://projects.spring.io/spring-data-elasticsearch/ ,采用的方式类似 JPA,并且为 SpringBoot 提供了一个 `spring-boot-starter-data-elasticsearch`,优点是操作 ES 的方式采用了 JPA 的方式,都已经封装好了,缺点是版本得随着官方慢慢迭代,不能使用 ES 的最新特性。 | |||||
### pom.xml | |||||
```xml | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>spring-boot-demo-elasticsearch</artifactId> | |||||
<version>0.0.1-SNAPSHOT</version> | |||||
<packaging>jar</packaging> | |||||
<name>spring-boot-demo-elasticsearch</name> | |||||
<description>Demo project for Spring Boot</description> | |||||
<parent> | |||||
<groupId>com.xkcoding</groupId> | |||||
<artifactId>spring-boot-demo-parent</artifactId> | |||||
<version>0.0.1-SNAPSHOT</version> | |||||
<relativePath>../spring-boot-demo-parent/pom.xml</relativePath> | |||||
</parent> | |||||
<properties> | |||||
<!--默认 Spring-Boot 依赖的 ES 版本是 2.X 版本,这里采用最新版--> | |||||
<elasticsearch.version>6.1.1</elasticsearch.version> | |||||
</properties> | |||||
<dependencies> | |||||
<!-- ES --> | |||||
<dependency> | |||||
<groupId>org.elasticsearch.client</groupId> | |||||
<artifactId>transport</artifactId> | |||||
<version>${elasticsearch.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.apache.logging.log4j</groupId> | |||||
<artifactId>log4j-core</artifactId> | |||||
<version>2.7</version> | |||||
</dependency> | |||||
</dependencies> | |||||
<build> | |||||
<finalName>spring-boot-demo-elasticsearch</finalName> | |||||
</build> | |||||
</project> | |||||
``` | |||||
### application.yml | |||||
```yaml | |||||
server: | |||||
port: 8080 | |||||
context-path: /demo | |||||
elasticsearch: | |||||
host: 127.0.0.1 | |||||
port: 9300 | |||||
cluster: | |||||
name: xkcoding | |||||
``` | |||||
ElasticSearchConfig.java | |||||
```java | |||||
/** | |||||
* <p> | |||||
* ES 的配置类 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.springbootdemoelasticsearch.config | |||||
* @description: ES 的配置类 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018/1/18 下午4:41 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: 0.0.1 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@Configuration | |||||
public class ElasticSearchConfig { | |||||
@Value("${elasticsearch.host}") | |||||
private String host; | |||||
@Value("${elasticsearch.port}") | |||||
private int port; | |||||
@Value("${elasticsearch.cluster.name}") | |||||
private String clusterName; | |||||
@Bean | |||||
public TransportClient esClient() throws UnknownHostException { | |||||
Settings settings = Settings.builder().put("cluster.name", this.clusterName).put("client.transport.sniff", true).build(); | |||||
TransportAddress master = new TransportAddress(InetAddress.getByName(host), port); | |||||
TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(master); | |||||
return client; | |||||
} | |||||
} | |||||
``` | |||||
PersonController.java | |||||
```java | |||||
/** | |||||
* <p> | |||||
* Person Controller | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.springbootdemoelasticsearch.web.controller | |||||
* @description: Person Controller | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018/1/18 下午5:06 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: 0.0.1 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@RestController | |||||
@Slf4j | |||||
public class PersonController { | |||||
public static final String INDEX = "people"; | |||||
public static final String TYPE = "person"; | |||||
@Autowired | |||||
private TransportClient esClient; | |||||
/** | |||||
* 插入一条数据到 ES 中,id 由 ES 生成 | |||||
* | |||||
* @param name 名称 | |||||
* @param country 国籍 | |||||
* @param age 年龄 | |||||
* @param birthday 生日 | |||||
* @return 插入数据的主键 | |||||
*/ | |||||
@PostMapping("/person") | |||||
public ApiResponse add(@RequestParam String name, @RequestParam String country, @RequestParam Integer age, @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) { | |||||
try { | |||||
XContentBuilder content = XContentFactory.jsonBuilder().startObject().field("name", name).field("country", country).field("age", age).field("birthday", birthday.getTime()).endObject(); | |||||
IndexResponse response = esClient.prepareIndex(INDEX, TYPE).setSource(content).get(); | |||||
return ApiResponse.ofSuccess(response.getId()); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
return ApiResponse.ofStatus(Status.INTERNAL_SERVER_ERROR); | |||||
} | |||||
} | |||||
/** | |||||
* 根据 id 删除 ES 的一条记录 | |||||
* | |||||
* @param id ES 中的 id | |||||
* @return DELETED 代表删除 | |||||
*/ | |||||
@DeleteMapping("/person/{id}") | |||||
public ApiResponse delete(@PathVariable String id) { | |||||
DeleteResponse response = esClient.prepareDelete(INDEX, TYPE, id).get(); | |||||
return ApiResponse.ofSuccess(response.getResult()); | |||||
} | |||||
/** | |||||
* 根据主键,修改传递字段对应的值 | |||||
* | |||||
* @param id ES 中的 id | |||||
* @param name 姓名 | |||||
* @param country 国籍 | |||||
* @param age 年龄 | |||||
* @param birthday 生日 | |||||
* @return UPDATED 代表文档修改成功 | |||||
*/ | |||||
@PutMapping("/person/{id}") | |||||
public ApiResponse update(@PathVariable String id, @RequestParam(value = "name", required = false) String name, @RequestParam(value = "country", required = false) String country, @RequestParam(value = "age", required = false) Integer age, @RequestParam(value = "birthday", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) { | |||||
UpdateRequest request = new UpdateRequest(INDEX, TYPE, id); | |||||
try { | |||||
XContentBuilder builder = XContentFactory.jsonBuilder().startObject(); | |||||
if (!Strings.isNullOrEmpty(name)) { | |||||
builder.field("name", name); | |||||
} | |||||
if (!Strings.isNullOrEmpty(country)) { | |||||
builder.field("country", country); | |||||
} | |||||
if (age != null && age > 0) { | |||||
builder.field("age", age); | |||||
} | |||||
if (birthday != null) { | |||||
builder.field("birthday", birthday.getTime()); | |||||
} | |||||
builder.endObject(); | |||||
request.doc(builder); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
return ApiResponse.ofStatus(Status.INTERNAL_SERVER_ERROR); | |||||
} | |||||
try { | |||||
UpdateResponse response = esClient.update(request).get(); | |||||
return ApiResponse.ofSuccess(response); | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
return ApiResponse.ofStatus(Status.INTERNAL_SERVER_ERROR); | |||||
} | |||||
} | |||||
/** | |||||
* 简单查询 根据 id 查 ES 中的文档内容 | |||||
* | |||||
* @param id ES 中存储的 id | |||||
* @return 对应 id 的文档内容 | |||||
*/ | |||||
@GetMapping("/person/{id}") | |||||
public ApiResponse get(@PathVariable String id) { | |||||
GetResponse response = esClient.prepareGet(INDEX, TYPE, id).get(); | |||||
if (!response.isExists() || response.isSourceEmpty()) { | |||||
return ApiResponse.ofStatus(Status.NOT_FOUND); | |||||
} | |||||
return ApiResponse.ofSuccess(response.getSource()); | |||||
} | |||||
/** | |||||
* 复合查询,根据传进来的条件,查询具体内容 | |||||
* | |||||
* @param name 根据姓名匹配 | |||||
* @param country 根据国籍匹配 | |||||
* @param gtAge 大于年龄 | |||||
* @param ltAge 小于年龄 | |||||
* @return 满足条件的文档内容 | |||||
*/ | |||||
@PostMapping("/person/query") | |||||
public ApiResponse query(@RequestParam(value = "name", required = false) String name, | |||||
@RequestParam(value = "country", required = false) String country, | |||||
@RequestParam(value = "gt_age", defaultValue = "0") int gtAge, | |||||
@RequestParam(value = "lt_age", required = false) Integer ltAge) { | |||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); | |||||
if (!Strings.isNullOrEmpty(name)) { | |||||
boolQueryBuilder.must(QueryBuilders.matchQuery("name", name)); | |||||
} | |||||
if (!Strings.isNullOrEmpty(country)) { | |||||
boolQueryBuilder.must(QueryBuilders.matchQuery("country", country)); | |||||
} | |||||
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").from(gtAge); | |||||
if (ltAge != null && ltAge > 0) { | |||||
rangeQueryBuilder.to(ltAge); | |||||
} | |||||
boolQueryBuilder.filter(rangeQueryBuilder); | |||||
SearchRequestBuilder searchRequestBuilder = esClient.prepareSearch(INDEX) | |||||
.setTypes(TYPE) | |||||
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH) | |||||
.setQuery(boolQueryBuilder) | |||||
.setFrom(0) | |||||
.setSize(20); | |||||
log.info("【query】:{}", searchRequestBuilder); | |||||
SearchResponse searchResponse = searchRequestBuilder.get(); | |||||
List<Map<String, Object>> result = Lists.newArrayList(); | |||||
searchResponse.getHits().forEach(hit -> { | |||||
result.add(hit.getSourceAsMap()); | |||||
}); | |||||
return ApiResponse.ofSuccess(result); | |||||
} | |||||
} | |||||
``` | |||||
@@ -0,0 +1,44 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>spring-boot-demo-elasticsearch</artifactId> | |||||
<version>0.0.1-SNAPSHOT</version> | |||||
<packaging>jar</packaging> | |||||
<name>spring-boot-demo-elasticsearch</name> | |||||
<description>Demo project for Spring Boot</description> | |||||
<parent> | |||||
<groupId>com.xkcoding</groupId> | |||||
<artifactId>spring-boot-demo-parent</artifactId> | |||||
<version>0.0.1-SNAPSHOT</version> | |||||
<relativePath>../spring-boot-demo-parent/pom.xml</relativePath> | |||||
</parent> | |||||
<properties> | |||||
<!--默认 Spring-Boot 依赖的 ES 版本是 2.X 版本,这里采用最新版--> | |||||
<elasticsearch.version>6.1.1</elasticsearch.version> | |||||
</properties> | |||||
<dependencies> | |||||
<!-- ES --> | |||||
<dependency> | |||||
<groupId>org.elasticsearch.client</groupId> | |||||
<artifactId>transport</artifactId> | |||||
<version>${elasticsearch.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.apache.logging.log4j</groupId> | |||||
<artifactId>log4j-core</artifactId> | |||||
<version>2.7</version> | |||||
</dependency> | |||||
</dependencies> | |||||
<build> | |||||
<finalName>spring-boot-demo-elasticsearch</finalName> | |||||
</build> | |||||
</project> |
@@ -0,0 +1,26 @@ | |||||
package com.xkcoding.springbootdemoelasticsearch; | |||||
import org.springframework.boot.SpringApplication; | |||||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
/** | |||||
* <p> | |||||
* 应用启动类 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.springbootdemoelasticsearch.web.controller | |||||
* @description: 应用启动类 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018/1/18 下午5:06 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: 0.0.1 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@SpringBootApplication | |||||
public class SpringBootDemoElasticsearchApplication { | |||||
public static void main(String[] args) { | |||||
SpringApplication.run(SpringBootDemoElasticsearchApplication.class, args); | |||||
} | |||||
} |
@@ -0,0 +1,46 @@ | |||||
package com.xkcoding.springbootdemoelasticsearch.config; | |||||
import org.elasticsearch.client.transport.TransportClient; | |||||
import org.elasticsearch.common.settings.Settings; | |||||
import org.elasticsearch.common.transport.TransportAddress; | |||||
import org.elasticsearch.transport.client.PreBuiltTransportClient; | |||||
import org.springframework.beans.factory.annotation.Value; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import java.net.InetAddress; | |||||
import java.net.UnknownHostException; | |||||
/** | |||||
* <p> | |||||
* ES 的配置类 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.springbootdemoelasticsearch.config | |||||
* @description: ES 的配置类 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018/1/18 下午4:41 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: 0.0.1 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@Configuration | |||||
public class ElasticSearchConfig { | |||||
@Value("${elasticsearch.host}") | |||||
private String host; | |||||
@Value("${elasticsearch.port}") | |||||
private int port; | |||||
@Value("${elasticsearch.cluster.name}") | |||||
private String clusterName; | |||||
@Bean | |||||
public TransportClient esClient() throws UnknownHostException { | |||||
Settings settings = Settings.builder().put("cluster.name", this.clusterName).put("client.transport.sniff", true).build(); | |||||
TransportAddress master = new TransportAddress(InetAddress.getByName(host), port); | |||||
TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(master); | |||||
return client; | |||||
} | |||||
} |
@@ -0,0 +1,48 @@ | |||||
package com.xkcoding.springbootdemoelasticsearch.web.base; | |||||
import lombok.Data; | |||||
/** | |||||
* <p> | |||||
* 统一接口返回类型 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.springbootdemoelasticsearch | |||||
* @description: 统一接口返回类型 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018/1/18 下午5:34 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: 0.0.1 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@Data | |||||
public class ApiResponse { | |||||
private int code; | |||||
private String message; | |||||
private Object data; | |||||
private boolean more; | |||||
public ApiResponse(int code, String message, Object data) { | |||||
this.code = code; | |||||
this.message = message; | |||||
this.data = data; | |||||
} | |||||
public ApiResponse() { | |||||
this.code = Status.SUCCESS.getCode(); | |||||
this.message = Status.SUCCESS.getMsg(); | |||||
} | |||||
public static ApiResponse ofMessage(int code, String message) { | |||||
return new ApiResponse(code, message, null); | |||||
} | |||||
public static ApiResponse ofSuccess(Object data) { | |||||
return new ApiResponse(Status.SUCCESS.getCode(), Status.SUCCESS.getMsg(), data); | |||||
} | |||||
public static ApiResponse ofStatus(Status status) { | |||||
return new ApiResponse(status.getCode(), status.getMsg(), null); | |||||
} | |||||
} |
@@ -0,0 +1,29 @@ | |||||
package com.xkcoding.springbootdemoelasticsearch.web.base; | |||||
import lombok.Getter; | |||||
/** | |||||
* <p> | |||||
* 通用状态码 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.springbootdemoelasticsearch.web.base | |||||
* @description: 通用状态码 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018/1/18 下午5:35 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: 0.0.1 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@Getter | |||||
public enum Status { | |||||
SUCCESS(200, "OK"), BAD_REQUEST(400, "Bad Request"), NOT_FOUND(404, "Not Found"), INTERNAL_SERVER_ERROR(500, "Unknown Internal Error"); | |||||
private int code; | |||||
private String msg; | |||||
Status(int code, String msg) { | |||||
this.code = code; | |||||
this.msg = msg; | |||||
} | |||||
} |
@@ -0,0 +1,194 @@ | |||||
package com.xkcoding.springbootdemoelasticsearch.web.controller; | |||||
import com.google.common.base.Strings; | |||||
import com.google.common.collect.Lists; | |||||
import com.xkcoding.springbootdemoelasticsearch.web.base.ApiResponse; | |||||
import com.xkcoding.springbootdemoelasticsearch.web.base.Status; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.elasticsearch.action.delete.DeleteResponse; | |||||
import org.elasticsearch.action.get.GetResponse; | |||||
import org.elasticsearch.action.index.IndexResponse; | |||||
import org.elasticsearch.action.search.SearchRequestBuilder; | |||||
import org.elasticsearch.action.search.SearchResponse; | |||||
import org.elasticsearch.action.search.SearchType; | |||||
import org.elasticsearch.action.update.UpdateRequest; | |||||
import org.elasticsearch.action.update.UpdateResponse; | |||||
import org.elasticsearch.client.transport.TransportClient; | |||||
import org.elasticsearch.common.xcontent.XContentBuilder; | |||||
import org.elasticsearch.common.xcontent.XContentFactory; | |||||
import org.elasticsearch.index.query.BoolQueryBuilder; | |||||
import org.elasticsearch.index.query.QueryBuilders; | |||||
import org.elasticsearch.index.query.RangeQueryBuilder; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.format.annotation.DateTimeFormat; | |||||
import org.springframework.web.bind.annotation.*; | |||||
import java.io.IOException; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | |||||
* <p> | |||||
* Person Controller | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.springbootdemoelasticsearch.web.controller | |||||
* @description: Person Controller | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018/1/18 下午5:06 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: 0.0.1 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@RestController | |||||
@Slf4j | |||||
public class PersonController { | |||||
public static final String INDEX = "people"; | |||||
public static final String TYPE = "person"; | |||||
@Autowired | |||||
private TransportClient esClient; | |||||
/** | |||||
* 插入一条数据到 ES 中,id 由 ES 生成 | |||||
* | |||||
* @param name 名称 | |||||
* @param country 国籍 | |||||
* @param age 年龄 | |||||
* @param birthday 生日 | |||||
* @return 插入数据的主键 | |||||
*/ | |||||
@PostMapping("/person") | |||||
public ApiResponse add(@RequestParam String name, @RequestParam String country, @RequestParam Integer age, @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) { | |||||
try { | |||||
XContentBuilder content = XContentFactory.jsonBuilder().startObject().field("name", name).field("country", country).field("age", age).field("birthday", birthday.getTime()).endObject(); | |||||
IndexResponse response = esClient.prepareIndex(INDEX, TYPE).setSource(content).get(); | |||||
return ApiResponse.ofSuccess(response.getId()); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
return ApiResponse.ofStatus(Status.INTERNAL_SERVER_ERROR); | |||||
} | |||||
} | |||||
/** | |||||
* 根据 id 删除 ES 的一条记录 | |||||
* | |||||
* @param id ES 中的 id | |||||
* @return DELETED 代表删除 | |||||
*/ | |||||
@DeleteMapping("/person/{id}") | |||||
public ApiResponse delete(@PathVariable String id) { | |||||
DeleteResponse response = esClient.prepareDelete(INDEX, TYPE, id).get(); | |||||
return ApiResponse.ofSuccess(response.getResult()); | |||||
} | |||||
/** | |||||
* 根据主键,修改传递字段对应的值 | |||||
* | |||||
* @param id ES 中的 id | |||||
* @param name 姓名 | |||||
* @param country 国籍 | |||||
* @param age 年龄 | |||||
* @param birthday 生日 | |||||
* @return UPDATED 代表文档修改成功 | |||||
*/ | |||||
@PutMapping("/person/{id}") | |||||
public ApiResponse update(@PathVariable String id, @RequestParam(value = "name", required = false) String name, @RequestParam(value = "country", required = false) String country, @RequestParam(value = "age", required = false) Integer age, @RequestParam(value = "birthday", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) { | |||||
UpdateRequest request = new UpdateRequest(INDEX, TYPE, id); | |||||
try { | |||||
XContentBuilder builder = XContentFactory.jsonBuilder().startObject(); | |||||
if (!Strings.isNullOrEmpty(name)) { | |||||
builder.field("name", name); | |||||
} | |||||
if (!Strings.isNullOrEmpty(country)) { | |||||
builder.field("country", country); | |||||
} | |||||
if (age != null && age > 0) { | |||||
builder.field("age", age); | |||||
} | |||||
if (birthday != null) { | |||||
builder.field("birthday", birthday.getTime()); | |||||
} | |||||
builder.endObject(); | |||||
request.doc(builder); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
return ApiResponse.ofStatus(Status.INTERNAL_SERVER_ERROR); | |||||
} | |||||
try { | |||||
UpdateResponse response = esClient.update(request).get(); | |||||
return ApiResponse.ofSuccess(response); | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
return ApiResponse.ofStatus(Status.INTERNAL_SERVER_ERROR); | |||||
} | |||||
} | |||||
/** | |||||
* 简单查询 根据 id 查 ES 中的文档内容 | |||||
* | |||||
* @param id ES 中存储的 id | |||||
* @return 对应 id 的文档内容 | |||||
*/ | |||||
@GetMapping("/person/{id}") | |||||
public ApiResponse get(@PathVariable String id) { | |||||
GetResponse response = esClient.prepareGet(INDEX, TYPE, id).get(); | |||||
if (!response.isExists() || response.isSourceEmpty()) { | |||||
return ApiResponse.ofStatus(Status.NOT_FOUND); | |||||
} | |||||
return ApiResponse.ofSuccess(response.getSource()); | |||||
} | |||||
/** | |||||
* 复合查询,根据传进来的条件,查询具体内容 | |||||
* | |||||
* @param name 根据姓名匹配 | |||||
* @param country 根据国籍匹配 | |||||
* @param gtAge 大于年龄 | |||||
* @param ltAge 小于年龄 | |||||
* @return 满足条件的文档内容 | |||||
*/ | |||||
@PostMapping("/person/query") | |||||
public ApiResponse query(@RequestParam(value = "name", required = false) String name, | |||||
@RequestParam(value = "country", required = false) String country, | |||||
@RequestParam(value = "gt_age", defaultValue = "0") int gtAge, | |||||
@RequestParam(value = "lt_age", required = false) Integer ltAge) { | |||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); | |||||
if (!Strings.isNullOrEmpty(name)) { | |||||
boolQueryBuilder.must(QueryBuilders.matchQuery("name", name)); | |||||
} | |||||
if (!Strings.isNullOrEmpty(country)) { | |||||
boolQueryBuilder.must(QueryBuilders.matchQuery("country", country)); | |||||
} | |||||
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").from(gtAge); | |||||
if (ltAge != null && ltAge > 0) { | |||||
rangeQueryBuilder.to(ltAge); | |||||
} | |||||
boolQueryBuilder.filter(rangeQueryBuilder); | |||||
SearchRequestBuilder searchRequestBuilder = esClient.prepareSearch(INDEX) | |||||
.setTypes(TYPE) | |||||
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH) | |||||
.setQuery(boolQueryBuilder) | |||||
.setFrom(0) | |||||
.setSize(20); | |||||
log.info("【query】:{}", searchRequestBuilder); | |||||
SearchResponse searchResponse = searchRequestBuilder.get(); | |||||
List<Map<String, Object>> result = Lists.newArrayList(); | |||||
searchResponse.getHits().forEach(hit -> { | |||||
result.add(hit.getSourceAsMap()); | |||||
}); | |||||
return ApiResponse.ofSuccess(result); | |||||
} | |||||
} |
@@ -0,0 +1,8 @@ | |||||
server: | |||||
port: 8080 | |||||
context-path: /demo | |||||
elasticsearch: | |||||
host: 127.0.0.1 | |||||
port: 9300 | |||||
cluster: | |||||
name: xkcoding |
@@ -0,0 +1,16 @@ | |||||
package com.xkcoding.springbootdemoelasticsearch; | |||||
import org.junit.Test; | |||||
import org.junit.runner.RunWith; | |||||
import org.springframework.boot.test.context.SpringBootTest; | |||||
import org.springframework.test.context.junit4.SpringRunner; | |||||
@RunWith(SpringRunner.class) | |||||
@SpringBootTest | |||||
public class SpringBootDemoElasticsearchApplicationTests { | |||||
@Test | |||||
public void contextLoads() { | |||||
} | |||||
} |
@@ -29,6 +29,7 @@ | |||||
<module>../spring-boot-demo-ureport2</module> | <module>../spring-boot-demo-ureport2</module> | ||||
<module>../spring-boot-demo-war</module> | <module>../spring-boot-demo-war</module> | ||||
<module>../spring-boot-demo-util</module> | <module>../spring-boot-demo-util</module> | ||||
<module>../spring-boot-demo-elasticsearch</module> | |||||
</modules> | </modules> | ||||
<parent> | <parent> | ||||
@@ -91,7 +92,7 @@ | |||||
<dependency> | <dependency> | ||||
<groupId>com.google.guava</groupId> | <groupId>com.google.guava</groupId> | ||||
<artifactId>guava</artifactId> | <artifactId>guava</artifactId> | ||||
<version>20.0</version> | |||||
<version>23.0</version> | |||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
<groupId>org.projectlombok</groupId> | <groupId>org.projectlombok</groupId> | ||||