概述
前面几篇,学习了ES的基本操作,那我们这里集成到代码中吧。 我们这里没有使用Spring 提供的 spring-boot-starter-data-elasticsearch,使用的是ES原生的API 。
官方JAVA API文档
当前7.0
https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html
5.6
https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.6/index.html
工程
ES服务端的版本 5.6.16 ,工程里es客户端的版本最好和服务端保持一致 。 正好Spring Boot 2.0.9RELEASE版本搭配transport里es是5.6.16版本。
pom.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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.9.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>masterSpringMvc</groupId> <artifactId>springBootElasticSearch</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springBootElasticSearch</name> <description>masterSpringMvc project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
es配置文件
# Elasticsearch elasticsearch.ip=127.0.0.1 #9200端口是RESTFul API来访问ElasticSearch的端口,9300端口是es节点通讯的默认端口,给java程序用的配置9300 elasticsearch.port=9300 elasticsearch.pool=5 # 集群的名字,和elasticsearch.yml中的cluster.name保持一致 elasticsearch.cluster.name=artisan
Es配置类
package com.artisan.config; import java.net.InetAddress; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ESConfig { private static final Logger logger = LoggerFactory.getLogger(ESConfig.class); @Value("${elasticsearch.ip}") private String hostName; @Value("${elasticsearch.port}") private String port; @Value("${elasticsearch.cluster.name}") private String clusterName; @Value("${elasticsearch.pool}") private String poolSize; @Bean public TransportClient transportClient() { logger.info("Elasticsearch begin to init "); TransportClient transportClient = null; try { // 地址信息 InetSocketTransportAddress transportAddress = new InetSocketTransportAddress( InetAddress.getByName(hostName), Integer.valueOf(port)); // 配置信息 Settings esSetting = Settings.builder().put("cluster.name", clusterName) // 集群名字 .put("client.transport.sniff", true)// 增加嗅探机制,找到ES集群 .put("thread_pool.search.size", Integer.parseInt(poolSize))// 线程池个数 .build(); // 配置信息Settings自定义 transportClient = new PreBuiltTransportClient(esSetting); transportClient.addTransportAddresses(transportAddress); } catch (Exception e) { logger.error("TransportClient create error", e); } return transportClient; } }
控制层
简单起见,我们直接在Controller层操作ES吧,仅仅是为了测试下功能。
简单查询
先写个简单的根据id获取数据的方法来测试下班
package com.artisan.controller; import java.util.Map; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.client.transport.TransportClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class ESController { @Autowired private TransportClient client; @GetMapping("/book/novel/{id}") public ResponseEntity<Map<String, Object>> getByIdFromES(@PathVariable String id){ GetResponse response = this.client.prepareGet("book", "novel", id).get(); if (!response.isExists()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } return new ResponseEntity<Map<String, Object>>(response.getSource(),HttpStatus.OK); } }
启动下服务,在浏览器或者postman中访问下吧
我们通过head插件来看下id=11的数据
OK,可以访问到正确的数据。
新增数据
ESController新增add方法
@PostMapping("/book/novel/add") public ResponseEntity<String> add(NovelDTO novel) { try { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .field("title", novel.getTitle()) .field("author", novel.getAuthor()) .field("word_count", novel.getWordCount()) .field("public_date", novel.getPublishDate().getTime()) .endObject(); IndexResponse response = this.client.prepareIndex("book", "novel").setSource(builder).get(); return new ResponseEntity<String>(response.getId(), HttpStatus.OK); } catch (IOException e) { e.printStackTrace(); return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); } }
我们把数据请求参数封装到了NovelDTO 中
package com.artisan.dto; import java.util.Date; import org.springframework.format.annotation.DateTimeFormat; public class NovelDTO { private String title; private String author; private String wordCount; @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") private Date publishDate; // set/get方法 省略 }
启动服务,POST http://localhost:8080/book/novel/add
head插件中查看数据 访问 http://localhost:9100/
新增成功
如果要想用JSON传的话,需要在方法入参前加入 @RequestBody ,postman中更改如下
public ResponseEntity<String> add(@RequestBody NovelDTO novel) { }
删除数据
@DeleteMapping("/book/novel/del") public ResponseEntity<String> delete(String id ){ DeleteResponse response = this.client.prepareDelete("book", "novel", id).get(); return new ResponseEntity<String>(response.getResult().toString(),HttpStatus.OK); }
把刚才新增的数据删掉
通过head插件查看已经没有该记录了,删除OK
更新数据
举个例子,根据id更新title
/** * 根据id 修改title * @param id * @param title * @return */ @PutMapping("/book/novel/update") public ResponseEntity<String> update(@RequestParam(name="id")String id , @RequestParam(name="title") String title){ UpdateRequest updateRequest = new UpdateRequest("book", "novel", id); try { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .field("title",title) .endObject(); updateRequest.doc(builder); UpdateResponse response = this.client.update(updateRequest).get(); return new ResponseEntity<String>(response.getResult().toString(), HttpStatus.OK); } catch (Exception e) { e.printStackTrace(); return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); } }
head插件查看
复合查询
/** * 综合查询 * @param title * @param author * @param gtWordCount * @param ltWordCount * @return */ @PostMapping("/book/novel/query") public ResponseEntity<List<Map<String, Object>>> query( @RequestParam(name="title",required=false)String title , @RequestParam(name="author",required=false)String author , @RequestParam(name="gtWordCount",defaultValue="0") Integer gtWordCount , @RequestParam(name="ltWordCount",required=false) Integer ltWordCount ) { BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); if (title !=null) { boolQueryBuilder.must(QueryBuilders.matchQuery("title", title)); } if (author !=null) { boolQueryBuilder.must(QueryBuilders.matchQuery("author", author)); } RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("word_count") .from(gtWordCount); if (ltWordCount != null && ltWordCount > 0) { rangeQueryBuilder.to(ltWordCount); } // 关联 boolQueryBuilder.filter(rangeQueryBuilder); SearchRequestBuilder builder = this.client.prepareSearch("book") .setTypes("novel") .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(boolQueryBuilder) .setFrom(0) .setSize(10); System.out.println("请求JSON数据:\n" + builder); SearchResponse response = builder.get(); List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); for(SearchHit searchHit : response.getHits()) { list.add(searchHit.getSource()); } return new ResponseEntity<List<Map<String, Object>>>(list, HttpStatus.OK); }
看下控制台输出的
请求JSON数据:
{ "from" : 0, "size" : 10, "query" : { "bool" : { "must" : [ { "match" : { "title" : { "query" : "Elasticsearch", "operator" : "OR", "prefix_length" : 0, "max_expansions" : 50, "fuzzy_transpositions" : true, "lenient" : false, "zero_terms_query" : "NONE", "boost" : 1.0 } } } ], "filter" : [ { "range" : { "word_count" : { "from" : 500, "to" : null, "include_lower" : true, "include_upper" : true, "boost" : 1.0 } } } ], "disable_coord" : false, "adjust_pure_negative" : true, "boost" : 1.0 } } }
使用postman测试下 ,返回了3条数据,符合预期。
其他
上面通过代码实现了数据的CRUD操作,那么我们把多索引和type的操作也尝试写下吧
工具类
package com.artisan.utils; import javax.annotation.PostConstruct; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest; import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse; import org.elasticsearch.client.transport.TransportClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ESUtil { private static final Logger logger = LoggerFactory.getLogger(ESUtil.class); @Autowired private TransportClient transportClient; private static TransportClient client; @PostConstruct public void init() { client = this.transportClient; } /** * 创建索引 * * @param index * @return */ public static boolean createIndex(String index) { if (!isIndexExist(index)) { logger.info("Index is not exits!"); CreateIndexResponse indexresponse = client.admin().indices().prepareCreate(index).execute().actionGet(); logger.info("执行建立成功?" + indexresponse.isAcknowledged()); return indexresponse.isAcknowledged(); } return false; } /** * 删除索引 * * @param index * @return */ public static boolean deleteIndex(String index) { if (!isIndexExist(index)) { logger.info("Index is not exits!"); } DeleteIndexResponse dResponse = client.admin().indices().prepareDelete(index).execute().actionGet(); if (dResponse.isAcknowledged()) { logger.info("delete index " + index + " successfully!"); } else { logger.info("Fail to delete index " + index); } return dResponse.isAcknowledged(); } /** * 判断索引是否存在 * * @param index * @return */ public static boolean isIndexExist(String index) { IndicesExistsResponse inExistsResponse = client.admin().indices().exists(new IndicesExistsRequest(index)) .actionGet(); if (inExistsResponse.isExists()) { logger.info("Index [" + index + "] is exist!"); } else { logger.info("Index [" + index + "] is not exist!"); } return inExistsResponse.isExists(); } /** * 判断index下指定type是否存在 * * @param index * @param type * @return */ public static boolean isTypeExist(String index, String type) { return isIndexExist(index) ? client.admin().indices().prepareTypesExists(index).setTypes(type).execute().actionGet().isExists() : false; } }
新建索引
@SuppressWarnings({ "rawtypes", "unchecked" }) @PostMapping("/index/create") public ResponseEntity create(String index){ if (ESUtil.createIndex(index)) { return new ResponseEntity(Boolean.TRUE,HttpStatus.OK); }else { return new ResponseEntity(HttpStatus.FOUND); } }
测试
head查看下
删除索引
@SuppressWarnings({ "rawtypes", "unchecked" }) @PostMapping("/index/delete") public ResponseEntity deleteIndex(String index){ if (ESUtil.deleteIndex(index)) { return new ResponseEntity(Boolean.TRUE,HttpStatus.OK); }else { return new ResponseEntity(HttpStatus.NOT_FOUND); } }
测试下
head查看,已经删除成功
判断index中某个type是否存在
@SuppressWarnings({ "rawtypes", "unchecked" }) @PostMapping("/index/isTypeExist") public ResponseEntity isTypeExist(String index,String type){ if (ESUtil.isTypeExist(index,type)) { return new ResponseEntity(Boolean.TRUE,HttpStatus.OK); }else { return new ResponseEntity(HttpStatus.NOT_FOUND); } }
测试一下
spring-data-elasticsearch 操作ES
https://docs.spring.io/spring-data/
我们也可以用Spring给我们提供的封装好的 ElasticsearchTemplate 更方便的操作ES,这里我就不演示了,和data jpa操作很像,比较简单。
https://spring.io/projects/spring-data-elasticsearch
https://github.com/spring-projects/spring-data-elasticsearch
如果也是使用spring boot集成的话,就用 spring-boot-starter-data-elasticsearch 这个maven的依赖,带有starter的这种。
代码
Github: https://github.com/yangshangwei/springBootElasticSearch



















