elasticsearch high level客户端实现

本文详细介绍使用RestHighLevelClient与Elasticsearch进行高级交互的方法,包括创建索引、批量保存文档、更新设置、查询及高亮显示等功能,为开发者提供全面的Elasticsearch操作指南。
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;

import kd.bos.exception.BosErrorCode;
import kd.bos.exception.KDException;
import kd.bos.fulltext.common.ConstantKeys;

/**
 * Rest High Level 方式与  Elastic Search 交互
 * 
 * Default port: 9200
 * 
 * index/indice: 相当于mysql中的data base
 * type: 相当于mysql中的表
 * document: 相当于mysql中的行
 * field: 相当于mysql中的字段
 * 
 * 若与服务端版本不一致可能出现版本问题
 * @author rd_jianbin_lai
 */
public class HighLevelAccess {
	
	private HighLevelAccess(){
		
	}
	
	private static final String RETRY_ON_CONFLICT_KEY = "fulltext.retryonconflict";
	
	public static RestHighLevelClient createRestHighLevelClient(String ip, int port, String username, String password) {
		//use username and password for authentication
		if(username != null && password != null) {
			final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
			credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
			return new RestHighLevelClient(RestClient.builder(new HttpHost(ip, port))
					.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
						public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
							httpClientBuilder.disableAuthCaching();
							return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
						}
					}));
		}
		//without authentication
		return new RestHighLevelClient(RestClient.builder(new HttpHost(ip, port, "http")));
	}

public static boolean isIndexExist(RestHighLevelClient client, String index) throws IOException {
		GetIndexRequest req = new GetIndexRequest(index);
		return client.indices().exists(req, RequestOptions.DEFAULT);
	}

	public static void createIndex(RestHighLevelClient client, String index, Map<String, Integer> settings)
			throws IOException {
		CreateIndexRequest req = new CreateIndexRequest(index);
		client.indices().create(req.settings(settings), RequestOptions.DEFAULT);
	}

	public static void createIndexMapping(RestHighLevelClient client, String index, String type,String fieldsMapping) throws IOException {
		PutMappingRequest req = new PutMappingRequest(index);
		req.type(type);
		req.source(fieldsMapping, XContentType.JSON);
		AcknowledgedResponse res = client.indices().putMapping(req, RequestOptions.DEFAULT);
		if (!res.isAcknowledged()) {
			throw new KDException(BosErrorCode.fulltextException, "Failed to create index:" + index  + res.toString());
		}
	}
	
	public static void updateIndexSettings(RestHighLevelClient client, String[] indexs,
			Map<String, Integer> indexSettings) throws IOException {
		String shardsNum = "number_of_shards";
		if (indexSettings.containsKey(shardsNum)) {
			indexSettings.remove(shardsNum);
		}
		UpdateSettingsRequest req = new UpdateSettingsRequest(indexs);
		req.settings(indexSettings);
		AcknowledgedResponse res = client.indices().putSettings(req, RequestOptions.DEFAULT);
		if (!res.isAcknowledged()) {
			throw new KDException(BosErrorCode.fulltextException,  "failed to update indexs settings:" + res.toString());
		}
	}

public static List<BulkItemResponse> batchSave(RestHighLevelClient client, String index, String type,
			List<Map<String, Object>> listMap) throws IOException {
		BulkRequest req = new BulkRequest();
		req.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
		for (Map<String, Object> map : listMap) {
			Object id = map.get(ConstantKeys.ESID_FIELD);
			map.remove(ConstantKeys.ESID_FIELD, id);
			IndexRequest indexRequest = new IndexRequest(index, type, String.valueOf(id)).source(map);
			UpdateRequest updateRequest = new UpdateRequest(index, type, String.valueOf(id))
					.upsert(indexRequest);
			updateRequest.doc(indexRequest);
			updateRequest.retryOnConflict(Integer.parseInt(System.getProperty(RETRY_ON_CONFLICT_KEY, "5")));
			req.add(updateRequest);
		}
		BulkResponse res = client.bulk(req, RequestOptions.DEFAULT);
		List<BulkItemResponse> failureRes = new ArrayList<>();
		if (res.hasFailures()) {
			for (BulkItemResponse resp : res.getItems()) {
				if (resp.isFailed()) {
					failureRes.add(resp);
				}
			}
		}
		return failureRes;
	}

public static long updateByQuery(RestHighLevelClient client, String index, String type,
			Map<String, Object> filterData, Map<String, Object> updateData) throws IOException {
		UpdateByQueryRequest req = new UpdateByQueryRequest(index);
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
		queryBuilder.must(QueryBuilders.typeQuery(type));
		for (Entry<String, Object> entry : filterData.entrySet()) {
			queryBuilder.must(QueryBuilders.termQuery(entry.getKey(), entry.getValue()));
		}
		req.setQuery(queryBuilder);

		int upFlag = 0;
		int upSize = updateData.size();
		StringBuilder sb = new StringBuilder();
		for (Map.Entry<String, Object> entry : updateData.entrySet()) {
			upFlag++;
			String key = entry.getKey();
			sb.append("ctx._source." + key + " = params." + key);
			if (upFlag < upSize) {
				sb.append(";");
			}
		}
		req.setScript(new Script(ScriptType.INLINE, "painless", sb.toString(), updateData)).setRefresh(true)
				.indices(index);
		BulkByScrollResponse res = client.updateByQuery(req, RequestOptions.DEFAULT);
		return res.getUpdated();

	}

	public static long deleteByQuery(RestHighLevelClient client, String index, String type,
			Map<String, Object> filterData) throws IOException {
		DeleteByQueryRequest req = new DeleteByQueryRequest(index);
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
		queryBuilder.must(QueryBuilders.typeQuery(type));
		for (Entry<String, Object> entry : filterData.entrySet()) {
			queryBuilder.must(QueryBuilders.termQuery(entry.getKey(), entry.getValue()));
		}
		req.setQuery(queryBuilder).setRefresh(true).indices(index);
		BulkByScrollResponse res = client.deleteByQuery(req, RequestOptions.DEFAULT);
		return res.getDeleted();
	}

public static void refreshIndex(RestHighLevelClient client, String index) throws IOException {
		RefreshRequest req = new RefreshRequest(index);
		RefreshResponse refreshResponse = client.indices().refresh(req, RequestOptions.DEFAULT);
		if (refreshResponse.getFailedShards() > 0) {
			throw new KDException(BosErrorCode.fulltextException, "refresh failed:" + refreshResponse.toString());
		}
	}
	
	//for test
	public static List<Map<String, Object>> queryAll(RestHighLevelClient client, String indexName, String typeName)
			throws IOException {
		SearchRequest req = new SearchRequest(indexName);
		SearchSourceBuilder builder = new SearchSourceBuilder();
		req.types(typeName);
		builder.query(QueryBuilders.matchAllQuery());
		req.source(builder);
		SearchResponse res = client.search(req, RequestOptions.DEFAULT);
		SearchHit[] hits = res.getHits().getHits();

		List<Map<String, Object>> result = new ArrayList<>();
		for (SearchHit hit : hits) {
			result.add(hit.getSourceAsMap());
		}
		return result;

	}

public static Map<String, List<Map<String, Object>>> query(RestHighLevelClient client, String index, String[] types,
			String[] selectFields, String mustOrShould, QueryBuilder[] queryBuilders,
			Map<String, SortOrder> sortFieldMap, boolean score, int start, int size) throws IOException {
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
		BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
		mustOrShould(mustOrShould, queryBuilders, filterQueryBuilder);

		isNeedScore(score, queryBuilder, filterQueryBuilder);
		SearchRequest req = new SearchRequest(index);
		req.types(types);
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.query(queryBuilder).from(start).size(size).fetchSource(selectFields, null);

		sort(sortFieldMap, sourceBuilder);
		req.source(sourceBuilder);
		SearchResponse searchResponse = client.search(req, RequestOptions.DEFAULT);
		SearchHit[] hits = searchResponse.getHits().getHits();
		Map<String, List<Map<String, Object>>> resultMap = new HashMap<>();
		for (String type : types) {
			resultMap.put(type, new ArrayList<>());
		}
		for (SearchHit hit : hits) {
			String type = hit.getType();
			resultMap.get(type).add(hit.getSourceAsMap());
		}
		return resultMap;
	}

	private static void sort(Map<String, SortOrder> sortFieldMap, SearchSourceBuilder builder) {
		if (sortFieldMap != null && !sortFieldMap.isEmpty()) {
			for (Entry<String, SortOrder> entry : sortFieldMap.entrySet()) {
				builder.sort(entry.getKey(), entry.getValue());
			}
		}
	}

	private static void isNeedScore(boolean score, BoolQueryBuilder queryBuilder, BoolQueryBuilder filterQb) {
		if (score) {
			queryBuilder.must(filterQb);
		} else {
			queryBuilder.filter(filterQb);
		}
	}

private static void mustOrShould(String mustOrShould, QueryBuilder[] queryBuilders, BoolQueryBuilder filterQb) {
		String must = "must";
		String should = "should";
		if ((!must.equals(mustOrShould)) && (!should.equals(mustOrShould))) {
			throw new KDException(BosErrorCode.fulltextException, "the relation ("+mustOrShould+") does not support.");
		}
		if (must.equals(mustOrShould)) {
			for (QueryBuilder qb : queryBuilders) {
				filterQb.must(qb);
			}
		} else {
			for (QueryBuilder qb : queryBuilders) {
				filterQb.should(qb);
			}
		}

	}

	public static Map<String, List<Map<String, Object>>> queryHighLight(RestHighLevelClient client, String index,
			String[] types, String[] selectFields, String mustOrShould, QueryBuilder[] queryBuilders,
			Map<String, SortOrder> sortFieldMap, boolean score, String preTag, String postTag, int start, int size)
			throws IOException {
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
		BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
		mustOrShould(mustOrShould, queryBuilders, filterQueryBuilder);

		isNeedScore(score, queryBuilder, filterQueryBuilder);
		SearchRequest req = new SearchRequest(index);
		req.types(types);
		SearchSourceBuilder ssb = new SearchSourceBuilder();
		ssb.query(queryBuilder).from(start).size(size).fetchSource(selectFields, null);
		sort(sortFieldMap, ssb);
		// 高亮 match生效
		List<String> highLightFields = highLighMatch(selectFields, preTag, postTag, ssb);
		req.source(ssb);
		SearchResponse searchResponse = client.search(req, RequestOptions.DEFAULT);
		return queryResult(types, highLightFields, searchResponse);

	}

private static Map<String, List<Map<String, Object>>> queryResult(String[] types, List<String> highLightFields,
			SearchResponse searchResponse) {
		SearchHit[] hits = searchResponse.getHits().getHits();
		Map<String, List<Map<String, Object>>> resultMap = new HashMap<>();
		for (String type : types) {
			resultMap.put(type, new ArrayList<>());
		}
		for (SearchHit hit : hits) {
			String type = hit.getType();
			Map<String, Object> source = hit.getSourceAsMap();
			Map<String, HighlightField> highlightFields = hit.getHighlightFields();
			for (String field : highLightFields) {
				HighlightField nameField = highlightFields.get(field);
				if (nameField != null) {
					Text[] fragments = nameField.fragments();
					String nameTmp = "";
					for (Text text : fragments) {
						nameTmp += text;
					}
					source.put(field, nameTmp);
				}
			}
			resultMap.get(type).add(source);
		}
		return resultMap;
	}

	private static List<String> highLighMatch(String[] selectFields, String preTag, String postTag,
			SearchSourceBuilder ssb) {
		List<String> highLightFields = new ArrayList<>();
		for (String field : selectFields) {
			if (!(field.equalsIgnoreCase(ConstantKeys.ENTITY_NAME_FIELD + "_String")
					&& field.equalsIgnoreCase(ConstantKeys.ENTITY_PKID_FIELD + "_String"))) {
				highLightFields.add(field);
			}
		}

		HighlightBuilder hb = new HighlightBuilder();
		hb.preTags(preTag).postTags(postTag);
		for (String field : highLightFields) {
			hb.field(field);
		}
		ssb.highlighter(hb);
		return highLightFields;
	}

public static long getCount(RestHighLevelClient client, String index, String[] types, String mustOrShould,
			QueryBuilder[] queryBuilders) throws IOException {
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

		BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
		mustOrShould(mustOrShould, queryBuilders, filterQueryBuilder);
		queryBuilder.filter(filterQueryBuilder);
		SearchRequest req = new SearchRequest(index);
		req.types(types);
		SearchSourceBuilder builder = new SearchSourceBuilder();
		builder.query(queryBuilder).size(0);
		req.source(builder);
		SearchResponse searchResponse = client.search(req, RequestOptions.DEFAULT);
		return searchResponse.getHits().getTotalHits();
	}

}
<think>好的,我现在需要帮助用户了解如何用ElasticsearchHigh Level REST客户端更新嵌套字段。首先,我得回想一下Elasticsearch中嵌套字段的结构。嵌套类型是为了处理对象数组中的独立对象,每个嵌套对象都是单独的文档,需要用到嵌套查询和更新。 用户提到的是High Level客户端,所以应该用Java API。首先,确定如何构建更新请求。可能需要使用UpdateRequest类,然后准备脚本或者部分文档来进行更新。 接下来,更新嵌套字段通常有两种方法:使用Painless脚本或者使用doc部分的嵌套路径。因为嵌套字段在内部是作为独立文档存储的,所以必须用脚本通过循环查找特定的嵌套对象来更新。例如,假设结构是user,里面有一个嵌套字段addresses,每个address有city和country。用户可能想根据address的id来更新某个city。 需要构造一个脚本,用ctx._source访问源文档,然后遍历嵌套数组,找到匹配的条目进行修改。同时,要确保脚本正确使用参数,比如用params传递id和新的city值。注意嵌套字段的路径是否需要全路径,比如'addresses'还是别的。 另外,还要考虑是否需要指定lang为painless,以及脚本类型为STORED还是INLINE。通常这里会用INLINE,直接写在请求中。 然后,示例代码的结构应该是先创建UpdateRequest实例,指定索引和文档ID。然后构建脚本,添加参数,最后执行更新。同时,可能需要处理版本冲突,或者设置重试策略等,但用户可能暂时不需要这些细节。 有没有可能用户遇到的错误,比如路径不正确或者脚本语法错误?可能需要提醒用户确保嵌套字段的映射正确,否则更新可能不生效。例如,addresses字段必须被定义为nested类型,而不是普通的object类型,否则嵌套更新无法正确执行。 此外,是否需要使用nested查询来先找到对应的嵌套文档?在更新的时候,脚本是否需要处理多级嵌套?比如,如果嵌套字段在更深层次的结构里,脚本路径需要相应调整。 最后,可能需要提供一个完整的代码示例,展示如何构建请求、设置脚本参数,并执行客户端操作。同时,提醒用户关于异常处理和客户端配置的问题,比如确保客户端正确连接到Elasticsearch实例。</think>要使用ElasticsearchHigh Level REST客户端更新嵌套字段(Nested Field),需结合Painless脚本通过指定文档路径和条件实现。以下是分步说明: --- ### **1. 确认嵌套字段映射** 确保目标字段已定义为`nested`类型。例如: ```json PUT my_index { "mappings": { "properties": { "user": { "type": "nested", "properties": { "name": { "type": "text" }, "address": { "type": "keyword" } } } } } } ``` --- ### **2. 使用Painless脚本更新** 通过`UpdateRequest`结合脚本定位嵌套对象并修改值。 #### **示例场景** 假设文档结构如下: ```json { "id": "1", "user": [ { "name": "Alice", "address": "Beijing" }, { "name": "Bob", "address": "Shanghai" } ] } ``` 需要将`user`数组中`name`为`Bob`的`address`改为`Guangzhou`。 --- #### **Java代码实现** ```java import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.reindex.UpdateByQueryRequest; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; // 1. 创建UpdateRequest UpdateRequest request = new UpdateRequest("my_index", "1"); // 2. 编写Painless脚本 String scriptSource = "for (int i = 0; i < ctx._source.user.length; i++) { " + " if (ctx._source.user[i].name == params.targetName) { " + " ctx._source.user[i].address = params.newAddress; " + " break; " + " } " + "}"; Map<String, Object> params = new HashMap<>(); params.put("targetName", "Bob"); params.put("newAddress", "Guangzhou"); Script script = new Script(ScriptType.INLINE, "painless", scriptSource, params); // 3. 将脚本附加到请求 request.script(script); // 4. 执行更新 RestHighLevelClient client = ...; // 初始化客户端 client.update(request, RequestOptions.DEFAULT); ``` --- ### **3. 关键注意事项** 1. **脚本权限** 需确保Elasticsearch配置允许运行内联脚本(默认开启)。 2. **并发控制** 可通过`retryOnConflict`设置重试次数,避免版本冲突: ```java request.retryOnConflict(3); ``` 3. **部分更新** 如果只需更新部分字段,可使用`doc`方法而非脚本,但需指定嵌套路径: ```java UpdateRequest request = new UpdateRequest("my_index", "1") .doc("user", Collections.singletonMap("name", "Bob"), "address", "Guangzhou"); ``` --- ### **4. 验证更新** 通过查询确认修改是否生效: ```java GetRequest getRequest = new GetRequest("my_index", "1"); GetResponse response = client.get(getRequest, RequestOptions.DEFAULT); System.out.println(response.getSourceAsString()); ``` --- ### **常见问题排查** - **错误:`failed to execute script`** 检查字段路径是否正确,确保目标字段是`nested`类型而非普通`object`。 - **未更新数据** 确认脚本中的条件(如`params.targetName`)是否匹配文档中的实际值。 通过上述步骤,即可高效更新Elasticsearch中的嵌套字段。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值