文章目录
- 概述
- 官方JAVA API文档
- 工程
- pom.xml
- es配置文件
- Es配置类
- 控制层
- 简单查询
- 新增数据
- 删除数据
- 更新数据
- 复合查询
- 其他
- 新建索引
- 删除索引
- 判断index中某个type是否存在
- spring-data-elasticsearch 操作ES
- 代码
概述
前面几篇,学习了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
代码语言:javascript复制<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.9.RELEASEversion>
<relativePath />
parent>
<groupId>masterSpringMvcgroupId>
<artifactId>springBootElasticSearchartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>springBootElasticSearchname>
<description>masterSpringMvc project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>transportartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
es配置文件
代码语言:javascript复制# 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配置类
代码语言:javascript复制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获取数据的方法来测试下班
代码语言:javascript复制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方法
代码语言:javascript复制@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 中
代码语言:javascript复制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中更改如下
代码语言:javascript复制public ResponseEntity<String> add(@RequestBody NovelDTO novel) {
}
删除数据
代码语言:javascript复制@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
代码语言:javascript复制/**
* 根据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插件查看
复合查询
代码语言:javascript复制 /**
* 综合查询
* @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数据:
代码语言:javascript复制{
"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的操作也尝试写下吧
工具类
代码语言:javascript复制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;
}
}
新建索引
代码语言:javascript复制@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查看下
删除索引
代码语言:javascript复制 @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是否存在
代码语言:javascript复制 @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