Skip to content

Commit 43b46d7

Browse files
sohaibiftikharnik9000
authored andcommitted
REST high-level client: add validate query API (#31077)
Adds the validate query API to the high level rest client.
1 parent e81ee30 commit 43b46d7

File tree

13 files changed

+618
-20
lines changed

13 files changed

+618
-20
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
5959
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
6060
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
61+
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
62+
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
6163
import org.elasticsearch.rest.RestStatus;
6264

6365
import java.io.IOException;
@@ -1091,6 +1093,36 @@ public void putTemplateAsync(PutIndexTemplateRequest putIndexTemplateRequest, Re
10911093
PutIndexTemplateResponse::fromXContent, listener, emptySet());
10921094
}
10931095

1096+
/**
1097+
* Validate a potentially expensive query without executing it.
1098+
* <p>
1099+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-validate.html"> Validate Query API
1100+
* on elastic.co</a>
1101+
* @param validateQueryRequest the request
1102+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1103+
* @return the response
1104+
* @throws IOException in case there is a problem sending the request or parsing back the response
1105+
*/
1106+
public ValidateQueryResponse validateQuery(ValidateQueryRequest validateQueryRequest, RequestOptions options) throws IOException {
1107+
return restHighLevelClient.performRequestAndParseEntity(validateQueryRequest, RequestConverters::validateQuery, options,
1108+
ValidateQueryResponse::fromXContent, emptySet());
1109+
}
1110+
1111+
/**
1112+
* Asynchronously validate a potentially expensive query without executing it.
1113+
* <p>
1114+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-validate.html"> Validate Query API
1115+
* on elastic.co</a>
1116+
* @param validateQueryRequest the request
1117+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1118+
* @param listener the listener to be notified upon request completion
1119+
*/
1120+
public void validateQueryAsync(ValidateQueryRequest validateQueryRequest, RequestOptions options,
1121+
ActionListener<ValidateQueryResponse> listener) {
1122+
restHighLevelClient.performRequestAsyncAndParseEntity(validateQueryRequest, RequestConverters::validateQuery, options,
1123+
ValidateQueryResponse::fromXContent, listener, emptySet());
1124+
}
1125+
10941126
/**
10951127
* Gets index templates using the Index Templates API
10961128
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API

client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
5959
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
6060
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
61+
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
6162
import org.elasticsearch.action.bulk.BulkRequest;
6263
import org.elasticsearch.action.delete.DeleteRequest;
6364
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
@@ -877,6 +878,20 @@ static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) t
877878
return request;
878879
}
879880

881+
static Request validateQuery(ValidateQueryRequest validateQueryRequest) throws IOException {
882+
String[] indices = validateQueryRequest.indices() == null ? Strings.EMPTY_ARRAY : validateQueryRequest.indices();
883+
String[] types = validateQueryRequest.types() == null || indices.length <= 0 ? Strings.EMPTY_ARRAY : validateQueryRequest.types();
884+
String endpoint = endpoint(indices, types, "_validate/query");
885+
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
886+
Params params = new Params(request);
887+
params.withIndicesOptions(validateQueryRequest.indicesOptions());
888+
params.putParam("explain", Boolean.toString(validateQueryRequest.explain()));
889+
params.putParam("all_shards", Boolean.toString(validateQueryRequest.allShards()));
890+
params.putParam("rewrite", Boolean.toString(validateQueryRequest.rewrite()));
891+
request.setEntity(createEntity(validateQueryRequest, REQUEST_BODY_CONTENT_TYPE));
892+
return request;
893+
}
894+
880895
static Request getAlias(GetAliasesRequest getAliasesRequest) {
881896
String[] indices = getAliasesRequest.indices() == null ? Strings.EMPTY_ARRAY : getAliasesRequest.indices();
882897
String[] aliases = getAliasesRequest.aliases() == null ? Strings.EMPTY_ARRAY : getAliasesRequest.aliases();

client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.client;
2121

2222
import org.apache.http.client.methods.HttpGet;
23+
import org.apache.http.client.methods.HttpPost;
2324
import org.apache.http.client.methods.HttpPut;
2425
import org.elasticsearch.ElasticsearchException;
2526
import org.elasticsearch.ElasticsearchStatusException;
@@ -64,6 +65,8 @@
6465
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
6566
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
6667
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
68+
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
69+
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
6770
import org.elasticsearch.action.index.IndexRequest;
6871
import org.elasticsearch.action.support.IndicesOptions;
6972
import org.elasticsearch.action.support.WriteRequest;
@@ -81,6 +84,8 @@
8184
import org.elasticsearch.common.xcontent.json.JsonXContent;
8285
import org.elasticsearch.common.xcontent.support.XContentMapValues;
8386
import org.elasticsearch.index.IndexSettings;
87+
import org.elasticsearch.index.query.QueryBuilder;
88+
import org.elasticsearch.index.query.QueryBuilders;
8489
import org.elasticsearch.rest.RestStatus;
8590

8691
import java.io.IOException;
@@ -1197,6 +1202,40 @@ public void testPutTemplateBadRequests() throws Exception {
11971202
assertThat(unknownSettingError.getDetailedMessage(), containsString("unknown setting [index.this-setting-does-not-exist]"));
11981203
}
11991204

1205+
public void testValidateQuery() throws IOException{
1206+
String index = "some_index";
1207+
createIndex(index, Settings.EMPTY);
1208+
QueryBuilder builder = QueryBuilders
1209+
.boolQuery()
1210+
.must(QueryBuilders.queryStringQuery("*:*"))
1211+
.filter(QueryBuilders.termQuery("user", "kimchy"));
1212+
ValidateQueryRequest request = new ValidateQueryRequest(index).query(builder);
1213+
request.explain(randomBoolean());
1214+
ValidateQueryResponse response = execute(request, highLevelClient().indices()::validateQuery,
1215+
highLevelClient().indices()::validateQueryAsync);
1216+
assertTrue(response.isValid());
1217+
}
1218+
1219+
public void testInvalidValidateQuery() throws IOException{
1220+
String index = "shakespeare";
1221+
1222+
createIndex(index, Settings.EMPTY);
1223+
Request postDoc = new Request(HttpPost.METHOD_NAME, "/" + index + "/1");
1224+
postDoc.setJsonEntity(
1225+
"{\"type\":\"act\",\"line_id\":1,\"play_name\":\"Henry IV\", \"speech_number\":\"\"," +
1226+
"\"line_number\":\"\",\"speaker\":\"\",\"text_entry\":\"ACT I\"}");
1227+
assertOK(client().performRequest(postDoc));
1228+
1229+
QueryBuilder builder = QueryBuilders
1230+
.queryStringQuery("line_id:foo")
1231+
.lenient(false);
1232+
ValidateQueryRequest request = new ValidateQueryRequest(index).query(builder);
1233+
request.explain(true);
1234+
ValidateQueryResponse response = execute(request, highLevelClient().indices()::validateQuery,
1235+
highLevelClient().indices()::validateQueryAsync);
1236+
assertFalse(response.isValid());
1237+
}
1238+
12001239
public void testGetIndexTemplate() throws Exception {
12011240
RestHighLevelClient client = highLevelClient();
12021241

client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
6161
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
6262
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
63+
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
6364
import org.elasticsearch.action.bulk.BulkRequest;
6465
import org.elasticsearch.action.bulk.BulkShardRequest;
6566
import org.elasticsearch.action.delete.DeleteRequest;
@@ -1928,6 +1929,40 @@ public void testPutTemplateRequest() throws Exception {
19281929
assertToXContentBody(putTemplateRequest, request.getEntity());
19291930
}
19301931

1932+
public void testValidateQuery() throws Exception {
1933+
String[] indices = randomBoolean() ? null : randomIndicesNames(0, 5);
1934+
String[] types = randomBoolean() ? generateRandomStringArray(5, 5, false, false) : null;
1935+
ValidateQueryRequest validateQueryRequest;
1936+
if (randomBoolean()) {
1937+
validateQueryRequest = new ValidateQueryRequest(indices);
1938+
} else {
1939+
validateQueryRequest = new ValidateQueryRequest();
1940+
validateQueryRequest.indices(indices);
1941+
}
1942+
validateQueryRequest.types(types);
1943+
Map<String, String> expectedParams = new HashMap<>();
1944+
setRandomIndicesOptions(validateQueryRequest::indicesOptions, validateQueryRequest::indicesOptions, expectedParams);
1945+
validateQueryRequest.explain(randomBoolean());
1946+
validateQueryRequest.rewrite(randomBoolean());
1947+
validateQueryRequest.allShards(randomBoolean());
1948+
expectedParams.put("explain", Boolean.toString(validateQueryRequest.explain()));
1949+
expectedParams.put("rewrite", Boolean.toString(validateQueryRequest.rewrite()));
1950+
expectedParams.put("all_shards", Boolean.toString(validateQueryRequest.allShards()));
1951+
Request request = RequestConverters.validateQuery(validateQueryRequest);
1952+
StringJoiner endpoint = new StringJoiner("/", "/", "");
1953+
if (indices != null && indices.length > 0) {
1954+
endpoint.add(String.join(",", indices));
1955+
if (types != null && types.length > 0) {
1956+
endpoint.add(String.join(",", types));
1957+
}
1958+
}
1959+
endpoint.add("_validate/query");
1960+
assertThat(request.getEndpoint(), equalTo(endpoint.toString()));
1961+
assertThat(request.getParameters(), equalTo(expectedParams));
1962+
assertToXContentBody(validateQueryRequest, request.getEntity());
1963+
assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME));
1964+
}
1965+
19311966
public void testGetTemplateRequest() throws Exception {
19321967
Map<String, String> encodes = new HashMap<>();
19331968
encodes.put("log", "log");

client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
6363
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
6464
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
65+
import org.elasticsearch.action.admin.indices.validate.query.QueryExplanation;
66+
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
67+
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
6568
import org.elasticsearch.action.support.ActiveShardCount;
6669
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
6770
import org.elasticsearch.action.support.IndicesOptions;
@@ -81,6 +84,7 @@
8184
import org.elasticsearch.common.xcontent.XContentBuilder;
8285
import org.elasticsearch.common.xcontent.XContentFactory;
8386
import org.elasticsearch.common.xcontent.XContentType;
87+
import org.elasticsearch.index.query.QueryBuilder;
8488
import org.elasticsearch.index.query.QueryBuilders;
8589
import org.elasticsearch.rest.RestStatus;
8690

@@ -2126,4 +2130,83 @@ public void onFailure(Exception e) {
21262130

21272131
assertTrue(latch.await(30L, TimeUnit.SECONDS));
21282132
}
2133+
2134+
public void testValidateQuery() throws IOException, InterruptedException {
2135+
RestHighLevelClient client = highLevelClient();
2136+
2137+
String index = "some_index";
2138+
createIndex(index, Settings.EMPTY);
2139+
2140+
// tag::validate-query-request
2141+
ValidateQueryRequest request = new ValidateQueryRequest(index); // <1>
2142+
// end::validate-query-request
2143+
2144+
// tag::validate-query-request-query
2145+
QueryBuilder builder = QueryBuilders
2146+
.boolQuery() // <1>
2147+
.must(QueryBuilders.queryStringQuery("*:*"))
2148+
.filter(QueryBuilders.termQuery("user", "kimchy"));
2149+
request.query(builder); // <2>
2150+
// end::validate-query-request-query
2151+
2152+
// tag::validate-query-request-explain
2153+
request.explain(true); // <1>
2154+
// end::validate-query-request-explain
2155+
2156+
// tag::validate-query-request-allShards
2157+
request.allShards(true); // <1>
2158+
// end::validate-query-request-allShards
2159+
2160+
// tag::validate-query-request-rewrite
2161+
request.rewrite(true); // <1>
2162+
// end::validate-query-request-rewrite
2163+
2164+
// tag::validate-query-execute
2165+
ValidateQueryResponse response = client.indices().validateQuery(request, RequestOptions.DEFAULT); // <1>
2166+
// end::validate-query-execute
2167+
2168+
// tag::validate-query-response
2169+
boolean isValid = response.isValid(); // <1>
2170+
int totalShards = response.getTotalShards(); // <2>
2171+
int successfulShards = response.getSuccessfulShards(); // <3>
2172+
int failedShards = response.getFailedShards(); // <4>
2173+
if (failedShards > 0) {
2174+
for(DefaultShardOperationFailedException failure: response.getShardFailures()) { // <5>
2175+
String failedIndex = failure.index(); // <6>
2176+
int shardId = failure.shardId(); // <7>
2177+
String reason = failure.reason(); // <8>
2178+
}
2179+
}
2180+
for(QueryExplanation explanation: response.getQueryExplanation()) { // <9>
2181+
String explanationIndex = explanation.getIndex(); // <10>
2182+
int shardId = explanation.getShard(); // <11>
2183+
String explanationString = explanation.getExplanation(); // <12>
2184+
}
2185+
// end::validate-query-response
2186+
2187+
// tag::validate-query-execute-listener
2188+
ActionListener<ValidateQueryResponse> listener =
2189+
new ActionListener<ValidateQueryResponse>() {
2190+
@Override
2191+
public void onResponse(ValidateQueryResponse validateQueryResponse) {
2192+
// <1>
2193+
}
2194+
2195+
@Override
2196+
public void onFailure(Exception e) {
2197+
// <2>
2198+
}
2199+
};
2200+
// end::validate-query-execute-listener
2201+
2202+
// Replace the empty listener by a blocking listener in test
2203+
final CountDownLatch latch = new CountDownLatch(1);
2204+
listener = new LatchedActionListener<>(listener, latch);
2205+
2206+
// tag::validate-query-execute-async
2207+
client.indices().validateQueryAsync(request, RequestOptions.DEFAULT, listener); // <1>
2208+
// end::validate-query-execute-async
2209+
2210+
assertTrue(latch.await(30L, TimeUnit.SECONDS));
2211+
}
21292212
}

0 commit comments

Comments
 (0)