ElasticSearch客户端使用

  • SpringCloud集成
  • 环境说明
  • ES客户端说明
  • POM依赖
  • HighLevelClient与LowLevelClient
  • 官方参考文档
  • 差异对比
  • 客户端初始化
  • 基本增删改查
  • Search
  • Get
  • Delete
  • Update
  • Index
  • Bulk
  • 关于异步方法的一些说明


SpringCloud集成

环境说明

JDK: 1.8
SpringBoot版本:2.1.5.RELEASE
ElasticSearch版本:6.3.0

ES客户端说明

ElasticSearch默认有两个端口,9200和9300,分别对应rest(http)和netty(tcp)两种连接方式,与之对应的客户端分别是transport client和rest client,根据官方的计划,transport客户端将会在7.x逐步废弃,在8.x不再使用。

在以前老的版本里面,很多项目都是使用transport的方式去集成ES的,也在此基础上衍生出了一些第三方的包,比较常用的就是elasticsearch-sql,这个包提供将sql转换为dsl进行查询的能力,这为老项目集成ES和不熟悉ES语法的同学提供了很大的便利,也因此很多项目会使用transport的方式,然而transport已经逐渐被官方抛弃了,早晚是需要使用rest的方式的,而且ES在6.3之后已经考虑到这种开发的成本,可以支持直接查询SQL(作者的项目因为是两三年前集成的,当时也就到6.3.0,很遗憾没有搭上这趟车,也使用了elasticsearch-sql来转换sql)。

除此之外,如果ES需要做权限控制的话,transport还不支持,必须要使用x-pack-transport,但好巧不巧x-pack-transport在maven中央仓库中只有一部分的版本可以集成,官方的maven库又连不上,实在有点尴尬。 因此,建议尽量使用官方的客户端进行集成,本文也是基于HighLevelClient进行说明。

POM依赖

POM依赖不复杂,但是ES的版本号与客户端的版本号有强关联关系,最好一一对应,尽管高版本的客户端提供了更多的API,但是否能完全兼容,官方也没说,也没找到有什么文章有提及到。

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>${elasticsearch.version}</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>${elasticsearch.version}</version>
</dependency>

HighLevelClient与LowLevelClient

官方参考文档

官方文档里面有使用说明,不过只是比较简单的介绍,建议先了解一下
Java High Level REST ClientJava Low Level REST Client

差异对比

HighLevelClient和LowLevelClient都是官方推荐客户端,前者是在后者的基础上做了一层封装,提供了一些便于使用的API,本质并无差异,但高度的封装也使得HighLevelClient缺少了一些灵活性,下面会有具体例子说明。

客户端初始化

客户端通过注入Bean方式进行初始化,HighLevelClient内置连接池,不需要另外设置连接池,使用的时候直接通过@Autowired注入即可,不需要另外初始化。

@Bean
public RestHighLevelClient restHighLevelHttpClient() {
	//新建host
	HttpHost host = new HttpHost("ip", 9200, "http");
	
	//如ES设置了帐号密码,则需要提供登录令牌
	final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
	credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username,password));
	
	//创建client实例,host可以有多个,对应集群的情况
	RestClientBuilder builder = RestClient.builder(host);
	
	//设置帐号密码、同时使用线程数、同一域名同时使用线程数
	builder.setHttpClientConfigCallback(config -> config.setDefaultCredentialsProvider(credentialsProvider).setMaxConnTotal(hosts.size()*500).setMaxConnPerRoute(500));
	
	//最大重试
	builder.setMaxRetryTimeoutMillis(timeout*5);

	//设置获取连接超时、请求超时、读超时
	builder.setRequestConfigCallback(config -> config.setConnectTimeout(timeout/10).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout));
       RestHighLevelClient client = new RestHighLevelClient(builder);

	return client;
}

基本增删改查

这里提供了Search、Update、Delete、Index、Bulk几种常规操作的具体使用。

Search

根据条件查询

public static List<Map<String, Object>> search() {
	List<Map<String, Object>> result = new ArrayList<>();
	SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
	//设置查询条件,queryBuilder就是构造DSL,有很多教程细说DSL的语法
	QueryBuilder queryBuilder = new BoolQueryBuilder();
	searchSourceBuilder.query(queryBuilder);
	//分页大小
	searchSourceBuilder.size(0);
	//开始页
	searchSourceBuilder.from(0);
	//查询指定的字段
	String[] includeFields = new String[]{};
	//排除指定字段
	String[] excludesFields = new String[]{};
	//设置需要查询哪些字段,排除哪些字段,不设置即全部字段返回
	searchSourceBuilder.fetchSource(includeFields,excludesFields);
	
	//构造request请求
	SearchRequest searchRequest = new SearchRequest();
	//指定查询索引,可以同时查询多个索引
	String[] indices = new String[]{"index1","index2"};
	searchRequest.indices(indices);
	//查询模式
	searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
	//设置前面设定好的查询设置
	searchRequest.source(searchSourceBuilder);
	try {
		SearchResponse response = restHighLevelClient.search(searchRequest);
		if (response==null || response.getHits()==null) {
			return result;
		}
		//组装查询数据
		for (SearchHit hit : response.getHits()) {
			result.add(hit.getSourceAsMap());
		}
	} catch (Exception e) {
		logger.error(e.getMessage(),e);
	}
	return result;
}

Get

获取单条数据

public static Map<String, Object> get(String id) {
	//创建request请求
	GetRequest getRequest = new GetRequest("index1");
	//设置查询的文档主键
	getRequest.id(id);
	//需要返回的字段
	String[] includeFields = new String[]{};
	//需要排除的字段
	String[] excludeFields = new String[]{};
	if (ArrayUtils.isNotEmpty(includeFields) || ArrayUtils.isNotEmpty(excludeFields)) {
		//设置排除字段、包含字段
		FetchSourceContext fetchSource = new FetchSourceContext(true, includeFields, excludeFields);
		getRequest.fetchSourceContext(fetchSource);
	}
	try {
		GetResponse response = restHighLevelClient.get(getRequest);
		if (response!=null) {
			return response.getSourceAsMap();
		}
	} catch (IOException e) {
		logger.error(e.getMessage(),e);
	}

	return new HashMap<>();
}

Delete

删除某个数据

public static void delete(String id) {
	DeleteRequest request = new DeleteRequest();
	request.index("index1");
	request.id(id);
	try {
		restHighLevelClient.delete(request);
	} catch (Exception e) {
		logger.error(e.getMessage(),e);
	}
}

Update

更新某条数据

public static void update(String id) {
	//创建update请求
	UpdateRequest request = new UpdateRequest();
	//设置索引
	request.index("index1");
	//设置主键
	request.id(id);
	//这是需要更新的数据
	Map<String,String> data = new HashMap<>();
	request.doc(data);
	//设置冲突重试次数,并发的时候有可能存在版本冲突导致update失败
	request.retryOnConflict(5);
	//设置刷新策略,三种策略:IMMEDIATE(提交后立即刷新,实时,延时低,消耗高)、WAIT_UNTIL(提交后等待数据完成再刷新,实时,延时高,消耗低)、NONE(默认策略,不管是否刷新,不实时,延时低,消耗低)
	request.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
	try {
		restHighLevelClient.update(request);
	} catch (IOException e) {
		logger.error(e.getMessage(),e);
	}
}

Index

插入/更新记录

public static void index() {
	//创建index请求
	IndexRequest request = new IndexRequest();
	//设置索引
	request.index("index1");
	//设置主键ID,插入记录的话生成id,更新的话设置已有数据的ID
	request.id(UUID.randomUUID().toString());
	//需要插入/更新的数据
	Map<String,String> data = new HashMap<>();
	request.source(data);
	//刷新策略
	request.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
	try {
		restHighLevelClient.index(request);
	} catch (IOException e) {
		logger.error(e.getMessage(),e);
	}
}

Bulk

批量执行请求

public static void bulk() {
	//创建bulk request
	BulkRequest bulkRequest = new BulkRequest();
	//添加一个update请求
	bulkRequest.add(new UpdateRequest().index("index1").id("123").doc(new HashMap()));
	//添加一个delete请求
	bulkRequest.add(new DeleteRequest().index("index1").id("123"));
	//添加一个index请求
	bulkRequest.add(new IndexRequest().index("index1").id("123").source(new HashMap()));
	try {
		//批量执行
		restHighLevelClient.bulk(bulkRequest);
	} catch (Exception e) {
		logger.error(e.getMessage(),e);
	}
}

关于异步方法的一些说明

HighLevelClient提供了一些异步的方法,例如searchAsync、deleteAsync、updateAsync等,这些异步方法是不会返回结果的,而是通过监听器来监听结果。

public static void updateAsync() {
	UpdateRequest updateRequest = new UpdateRequest();
	ActionListener<UpdateResponse> listener = new ActionListener<UpdateResponse>() {
		@Override
		public void onResponse(UpdateResponse updateResponse) {
			//执行成功时进入这里处理response
			logger.info("update successfully");
		}

		@Override
		public void onFailure(Exception e) {
			//执行失败时进入这里
			logger.error("update fail");
		}
	};
	restHighLevelClient.updateAsync(updateRequest,listener);
}

上面是异步的一个调用示例,但是这里会发现一个使用上不太方便问题,一是没有提供等待的方法,不知道什么时候返回结果,如果在后面的逻辑中需要依赖异步中的结果才能进行,官方没有提供解决的思路,二是onResponse里面没法将response传递到外层,也就是说后面想要获取到response的内容,需要自行做一些处理去进行传递,不知道后面的版本里面是否有提供一些API去解决这些问题。