Validate query api: move query parsing on the coordinating node
Similarly to what we did with the search api, we can now also move query parsing on the coordinating node for the validate query api. Given that the explain api is a single shard operation (compared to search which is instead a broadcast operation), this doesn't change a lot in how the api works internally. The main benefit is that we can simplify the java api by requiring a structured query object to be provided rather than a bytes array that will get parsed on the data node. Previously if you specified a QueryBuilder it would be serialized in json format and would get reparsed on the data node, while now it doesn't go through parsing anymore (as expected), given that after the query-refactoring we are able to properly stream queries natively. Note that the WrapperQueryBuilder can be used from the java api to provide a query as a string, in that case the actual parsing of the inner query will happen on the data node. Relates to #10217 Closes #14384
This commit is contained in:
parent
ebec4bdaf6
commit
b56bbf62dd
|
@ -22,9 +22,9 @@ package org.elasticsearch.action.admin.indices.validate.query;
|
||||||
import org.elasticsearch.action.support.broadcast.BroadcastShardRequest;
|
import org.elasticsearch.action.support.broadcast.BroadcastShardRequest;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -34,7 +34,7 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class ShardValidateQueryRequest extends BroadcastShardRequest {
|
public class ShardValidateQueryRequest extends BroadcastShardRequest {
|
||||||
|
|
||||||
private BytesReference source;
|
private QueryBuilder<?> query;
|
||||||
private String[] types = Strings.EMPTY_ARRAY;
|
private String[] types = Strings.EMPTY_ARRAY;
|
||||||
private boolean explain;
|
private boolean explain;
|
||||||
private boolean rewrite;
|
private boolean rewrite;
|
||||||
|
@ -49,7 +49,7 @@ public class ShardValidateQueryRequest extends BroadcastShardRequest {
|
||||||
|
|
||||||
ShardValidateQueryRequest(ShardId shardId, @Nullable String[] filteringAliases, ValidateQueryRequest request) {
|
ShardValidateQueryRequest(ShardId shardId, @Nullable String[] filteringAliases, ValidateQueryRequest request) {
|
||||||
super(shardId, request);
|
super(shardId, request);
|
||||||
this.source = request.source();
|
this.query = request.query();
|
||||||
this.types = request.types();
|
this.types = request.types();
|
||||||
this.explain = request.explain();
|
this.explain = request.explain();
|
||||||
this.rewrite = request.rewrite();
|
this.rewrite = request.rewrite();
|
||||||
|
@ -57,8 +57,8 @@ public class ShardValidateQueryRequest extends BroadcastShardRequest {
|
||||||
this.nowInMillis = request.nowInMillis;
|
this.nowInMillis = request.nowInMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BytesReference source() {
|
public QueryBuilder<?> query() {
|
||||||
return source;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] types() {
|
public String[] types() {
|
||||||
|
@ -84,7 +84,7 @@ public class ShardValidateQueryRequest extends BroadcastShardRequest {
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
source = in.readBytesReference();
|
query = in.readQuery();
|
||||||
|
|
||||||
int typesSize = in.readVInt();
|
int typesSize = in.readVInt();
|
||||||
if (typesSize > 0) {
|
if (typesSize > 0) {
|
||||||
|
@ -109,7 +109,7 @@ public class ShardValidateQueryRequest extends BroadcastShardRequest {
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
super.writeTo(out);
|
super.writeTo(out);
|
||||||
out.writeBytesReference(source);
|
out.writeQuery(query);
|
||||||
|
|
||||||
out.writeVInt(types.length);
|
out.writeVInt(types.length);
|
||||||
for (String type : types) {
|
for (String type : types) {
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
import org.elasticsearch.cluster.routing.GroupShardsIterator;
|
import org.elasticsearch.cluster.routing.GroupShardsIterator;
|
||||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
|
import org.elasticsearch.common.ParsingException;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.BigArrays;
|
import org.elasticsearch.common.util.BigArrays;
|
||||||
|
@ -43,7 +44,6 @@ import org.elasticsearch.index.IndexService;
|
||||||
import org.elasticsearch.index.engine.Engine;
|
import org.elasticsearch.index.engine.Engine;
|
||||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||||
import org.elasticsearch.index.query.QueryShardException;
|
import org.elasticsearch.index.query.QueryShardException;
|
||||||
import org.elasticsearch.common.ParsingException;
|
|
||||||
import org.elasticsearch.index.shard.IndexShard;
|
import org.elasticsearch.index.shard.IndexShard;
|
||||||
import org.elasticsearch.indices.IndicesService;
|
import org.elasticsearch.indices.IndicesService;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
|
@ -178,9 +178,7 @@ public class TransportValidateQueryAction extends TransportBroadcastAction<Valid
|
||||||
);
|
);
|
||||||
SearchContext.setCurrent(searchContext);
|
SearchContext.setCurrent(searchContext);
|
||||||
try {
|
try {
|
||||||
if (request.source() != null && request.source().length() > 0) {
|
searchContext.parsedQuery(queryParserService.toQuery(request.query()));
|
||||||
searchContext.parsedQuery(queryParserService.parseTopLevelQuery(request.source()));
|
|
||||||
}
|
|
||||||
searchContext.preProcess();
|
searchContext.preProcess();
|
||||||
|
|
||||||
valid = true;
|
valid = true;
|
||||||
|
|
|
@ -19,34 +19,27 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.admin.indices.validate.query;
|
package org.elasticsearch.action.admin.indices.validate.query;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchGenerationException;
|
|
||||||
import org.elasticsearch.action.ActionRequestValidationException;
|
import org.elasticsearch.action.ActionRequestValidationException;
|
||||||
|
import org.elasticsearch.action.ValidateActions;
|
||||||
import org.elasticsearch.action.support.IndicesOptions;
|
import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.action.support.QuerySourceBuilder;
|
|
||||||
import org.elasticsearch.action.support.broadcast.BroadcastRequest;
|
import org.elasticsearch.action.support.broadcast.BroadcastRequest;
|
||||||
import org.elasticsearch.client.Requests;
|
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request to validate a specific query.
|
* A request to validate a specific query.
|
||||||
* <p>
|
* <p>
|
||||||
* The request requires the query source to be set either using {@link #source(QuerySourceBuilder)},
|
* The request requires the query to be set using {@link #query(QueryBuilder)}
|
||||||
* or {@link #source(byte[])}.
|
|
||||||
*/
|
*/
|
||||||
public class ValidateQueryRequest extends BroadcastRequest<ValidateQueryRequest> {
|
public class ValidateQueryRequest extends BroadcastRequest<ValidateQueryRequest> {
|
||||||
|
|
||||||
private BytesReference source;
|
private QueryBuilder<?> query = new MatchAllQueryBuilder();
|
||||||
|
|
||||||
private boolean explain;
|
private boolean explain;
|
||||||
private boolean rewrite;
|
private boolean rewrite;
|
||||||
|
@ -71,67 +64,21 @@ public class ValidateQueryRequest extends BroadcastRequest<ValidateQueryRequest>
|
||||||
@Override
|
@Override
|
||||||
public ActionRequestValidationException validate() {
|
public ActionRequestValidationException validate() {
|
||||||
ActionRequestValidationException validationException = super.validate();
|
ActionRequestValidationException validationException = super.validate();
|
||||||
|
if (query == null) {
|
||||||
|
validationException = ValidateActions.addValidationError("query cannot be null", validationException);
|
||||||
|
}
|
||||||
return validationException;
|
return validationException;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The source to execute.
|
* The query to validate.
|
||||||
*/
|
*/
|
||||||
public BytesReference source() {
|
public QueryBuilder<?> query() {
|
||||||
return source;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidateQueryRequest source(QuerySourceBuilder sourceBuilder) {
|
public ValidateQueryRequest query(QueryBuilder<?> query) {
|
||||||
this.source = sourceBuilder.buildAsBytes(Requests.CONTENT_TYPE);
|
this.query = query;
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source to execute in the form of a map.
|
|
||||||
*/
|
|
||||||
public ValidateQueryRequest source(Map source) {
|
|
||||||
try {
|
|
||||||
XContentBuilder builder = XContentFactory.contentBuilder(Requests.CONTENT_TYPE);
|
|
||||||
builder.map(source);
|
|
||||||
return source(builder);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ElasticsearchGenerationException("Failed to generate [" + source + "]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValidateQueryRequest source(XContentBuilder builder) {
|
|
||||||
this.source = builder.bytes();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The query source to validate. It is preferable to use either {@link #source(byte[])}
|
|
||||||
* or {@link #source(QuerySourceBuilder)}.
|
|
||||||
*/
|
|
||||||
public ValidateQueryRequest source(String source) {
|
|
||||||
this.source = new BytesArray(source);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source to validate.
|
|
||||||
*/
|
|
||||||
public ValidateQueryRequest source(byte[] source) {
|
|
||||||
return source(source, 0, source.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source to validate.
|
|
||||||
*/
|
|
||||||
public ValidateQueryRequest source(byte[] source, int offset, int length) {
|
|
||||||
return source(new BytesArray(source, offset, length));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source to validate.
|
|
||||||
*/
|
|
||||||
public ValidateQueryRequest source(BytesReference source) {
|
|
||||||
this.source = source;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,9 +128,7 @@ public class ValidateQueryRequest extends BroadcastRequest<ValidateQueryRequest>
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
|
query = in.readQuery();
|
||||||
source = in.readBytesReference();
|
|
||||||
|
|
||||||
int typesSize = in.readVInt();
|
int typesSize = in.readVInt();
|
||||||
if (typesSize > 0) {
|
if (typesSize > 0) {
|
||||||
types = new String[typesSize];
|
types = new String[typesSize];
|
||||||
|
@ -191,7 +136,6 @@ public class ValidateQueryRequest extends BroadcastRequest<ValidateQueryRequest>
|
||||||
types[i] = in.readString();
|
types[i] = in.readString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
explain = in.readBoolean();
|
explain = in.readBoolean();
|
||||||
rewrite = in.readBoolean();
|
rewrite = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
@ -199,27 +143,18 @@ public class ValidateQueryRequest extends BroadcastRequest<ValidateQueryRequest>
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
super.writeTo(out);
|
super.writeTo(out);
|
||||||
|
out.writeQuery(query);
|
||||||
out.writeBytesReference(source);
|
|
||||||
|
|
||||||
out.writeVInt(types.length);
|
out.writeVInt(types.length);
|
||||||
for (String type : types) {
|
for (String type : types) {
|
||||||
out.writeString(type);
|
out.writeString(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
out.writeBoolean(explain);
|
out.writeBoolean(explain);
|
||||||
out.writeBoolean(rewrite);
|
out.writeBoolean(rewrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String sSource = "_na_";
|
return "[" + Arrays.toString(indices) + "]" + Arrays.toString(types) + ", query[" + query + "], explain:" + explain +
|
||||||
try {
|
|
||||||
sSource = XContentHelper.convertToJson(source, false);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return "[" + Arrays.toString(indices) + "]" + Arrays.toString(types) + ", source[" + sSource + "], explain:" + explain +
|
|
||||||
", rewrite:" + rewrite;
|
", rewrite:" + rewrite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,8 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.admin.indices.validate.query;
|
package org.elasticsearch.action.admin.indices.validate.query;
|
||||||
|
|
||||||
import org.elasticsearch.action.support.QuerySourceBuilder;
|
|
||||||
import org.elasticsearch.action.support.broadcast.BroadcastOperationRequestBuilder;
|
import org.elasticsearch.action.support.broadcast.BroadcastOperationRequestBuilder;
|
||||||
import org.elasticsearch.client.ElasticsearchClient;
|
import org.elasticsearch.client.ElasticsearchClient;
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,8 +28,6 @@ import org.elasticsearch.index.query.QueryBuilder;
|
||||||
*/
|
*/
|
||||||
public class ValidateQueryRequestBuilder extends BroadcastOperationRequestBuilder<ValidateQueryRequest, ValidateQueryResponse, ValidateQueryRequestBuilder> {
|
public class ValidateQueryRequestBuilder extends BroadcastOperationRequestBuilder<ValidateQueryRequest, ValidateQueryResponse, ValidateQueryRequestBuilder> {
|
||||||
|
|
||||||
private QuerySourceBuilder sourceBuilder;
|
|
||||||
|
|
||||||
public ValidateQueryRequestBuilder(ElasticsearchClient client, ValidateQueryAction action) {
|
public ValidateQueryRequestBuilder(ElasticsearchClient client, ValidateQueryAction action) {
|
||||||
super(client, action, new ValidateQueryRequest());
|
super(client, action, new ValidateQueryRequest());
|
||||||
}
|
}
|
||||||
|
@ -45,32 +41,12 @@ public class ValidateQueryRequestBuilder extends BroadcastOperationRequestBuilde
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The query source to validate.
|
* The query to validate.
|
||||||
*
|
*
|
||||||
* @see org.elasticsearch.index.query.QueryBuilders
|
* @see org.elasticsearch.index.query.QueryBuilders
|
||||||
*/
|
*/
|
||||||
public ValidateQueryRequestBuilder setQuery(QueryBuilder queryBuilder) {
|
public ValidateQueryRequestBuilder setQuery(QueryBuilder queryBuilder) {
|
||||||
sourceBuilder().setQuery(queryBuilder);
|
request.query(queryBuilder);
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source to validate.
|
|
||||||
*
|
|
||||||
* @see org.elasticsearch.index.query.QueryBuilders
|
|
||||||
*/
|
|
||||||
public ValidateQueryRequestBuilder setSource(BytesReference source) {
|
|
||||||
request().source(source);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source to validate.
|
|
||||||
*
|
|
||||||
* @see org.elasticsearch.index.query.QueryBuilders
|
|
||||||
*/
|
|
||||||
public ValidateQueryRequestBuilder setSource(byte[] source) {
|
|
||||||
request.source(source);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,19 +67,4 @@ public class ValidateQueryRequestBuilder extends BroadcastOperationRequestBuilde
|
||||||
request.rewrite(rewrite);
|
request.rewrite(rewrite);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ValidateQueryRequest beforeExecute(ValidateQueryRequest request) {
|
|
||||||
if (sourceBuilder != null) {
|
|
||||||
request.source(sourceBuilder);
|
|
||||||
}
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
private QuerySourceBuilder sourceBuilder() {
|
|
||||||
if (sourceBuilder == null) {
|
|
||||||
sourceBuilder = new QuerySourceBuilder();
|
|
||||||
}
|
|
||||||
return sourceBuilder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.action.support;
|
|
||||||
|
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class QuerySourceBuilder extends ToXContentToBytes {
|
|
||||||
|
|
||||||
private QueryBuilder queryBuilder;
|
|
||||||
|
|
||||||
private BytesReference queryBinary;
|
|
||||||
|
|
||||||
public QuerySourceBuilder setQuery(QueryBuilder query) {
|
|
||||||
this.queryBuilder = query;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public QuerySourceBuilder setQuery(BytesReference queryBinary) {
|
|
||||||
this.queryBinary = queryBinary;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
|
||||||
builder.startObject();
|
|
||||||
innerToXContent(builder, params);
|
|
||||||
builder.endObject();
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void innerToXContent(XContentBuilder builder, Params params) throws IOException {
|
|
||||||
if (queryBuilder != null) {
|
|
||||||
builder.field("query");
|
|
||||||
queryBuilder.toXContent(builder, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryBinary != null) {
|
|
||||||
if (XContentFactory.xContentType(queryBinary) == builder.contentType()) {
|
|
||||||
builder.rawField("query", queryBinary);
|
|
||||||
} else {
|
|
||||||
builder.field("query_binary", queryBinary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -209,30 +209,6 @@ public class IndexQueryParserService extends AbstractIndexComponent {
|
||||||
return indexSettings.getIndexVersionCreated();
|
return indexSettings.getIndexVersionCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Selectively parses a query from a top level query or query_binary json field from the specified source.
|
|
||||||
*/
|
|
||||||
public ParsedQuery parseTopLevelQuery(BytesReference source) {
|
|
||||||
XContentParser parser = null;
|
|
||||||
try {
|
|
||||||
parser = XContentFactory.xContent(source).createParser(source);
|
|
||||||
QueryShardContext queryShardContext = cache.get();
|
|
||||||
queryShardContext.reset(parser);
|
|
||||||
queryShardContext.parseFieldMatcher(parseFieldMatcher);
|
|
||||||
try {
|
|
||||||
QueryBuilder<?> queryBuilder = queryShardContext.parseContext().parseTopLevelQueryBuilder();
|
|
||||||
Query query = toQuery(queryBuilder, queryShardContext);
|
|
||||||
return new ParsedQuery(query, queryShardContext.copyNamedQueries());
|
|
||||||
} finally {
|
|
||||||
queryShardContext.reset(null);
|
|
||||||
}
|
|
||||||
} catch (ParsingException | QueryShardException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new ParsingException(parser == null ? null : parser.getTokenLocation(), "Failed to parse", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ParsedQuery innerParse(QueryShardContext context, XContentParser parser) throws IOException, QueryShardException {
|
private ParsedQuery innerParse(QueryShardContext context, XContentParser parser) throws IOException, QueryShardException {
|
||||||
context.reset(parser);
|
context.reset(parser);
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
package org.elasticsearch.index.query;
|
package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
@ -32,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,7 +108,8 @@ public class WrapperQueryBuilder extends AbstractQueryBuilder<WrapperQueryBuilde
|
||||||
try (XContentParser qSourceParser = XContentFactory.xContent(source).createParser(source)) {
|
try (XContentParser qSourceParser = XContentFactory.xContent(source).createParser(source)) {
|
||||||
final QueryShardContext contextCopy = new QueryShardContext(context.indexQueryParserService());
|
final QueryShardContext contextCopy = new QueryShardContext(context.indexQueryParserService());
|
||||||
contextCopy.reset(qSourceParser);
|
contextCopy.reset(qSourceParser);
|
||||||
QueryBuilder result = contextCopy.parseContext().parseInnerQueryBuilder();
|
contextCopy.parseFieldMatcher(context.indexQueryParserService().parseFieldMatcher());
|
||||||
|
QueryBuilder<?> result = contextCopy.parseContext().parseInnerQueryBuilder();
|
||||||
context.combineNamedQueries(contextCopy);
|
context.combineNamedQueries(contextCopy);
|
||||||
return result.toQuery(context);
|
return result.toQuery(context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,22 +23,21 @@ import org.elasticsearch.action.admin.indices.validate.query.QueryExplanation;
|
||||||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
|
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
|
||||||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
|
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
|
||||||
import org.elasticsearch.action.support.IndicesOptions;
|
import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.action.support.QuerySourceBuilder;
|
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
|
import org.elasticsearch.common.ParsingException;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilderString;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.rest.BaseRestHandler;
|
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
||||||
import org.elasticsearch.rest.BytesRestResponse;
|
import org.elasticsearch.rest.*;
|
||||||
import org.elasticsearch.rest.RestChannel;
|
|
||||||
import org.elasticsearch.rest.RestController;
|
|
||||||
import org.elasticsearch.rest.RestRequest;
|
|
||||||
import org.elasticsearch.rest.RestResponse;
|
|
||||||
import org.elasticsearch.rest.action.support.RestActions;
|
import org.elasticsearch.rest.action.support.RestActions;
|
||||||
import org.elasticsearch.rest.action.support.RestBuilderListener;
|
import org.elasticsearch.rest.action.support.RestBuilderListener;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||||
import static org.elasticsearch.rest.RestStatus.OK;
|
import static org.elasticsearch.rest.RestStatus.OK;
|
||||||
|
@ -49,8 +48,10 @@ import static org.elasticsearch.rest.action.support.RestActions.buildBroadcastSh
|
||||||
*/
|
*/
|
||||||
public class RestValidateQueryAction extends BaseRestHandler {
|
public class RestValidateQueryAction extends BaseRestHandler {
|
||||||
|
|
||||||
|
private final IndicesQueriesRegistry indicesQueriesRegistry;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public RestValidateQueryAction(Settings settings, RestController controller, Client client) {
|
public RestValidateQueryAction(Settings settings, RestController controller, Client client, IndicesQueriesRegistry indicesQueriesRegistry) {
|
||||||
super(settings, controller, client);
|
super(settings, controller, client);
|
||||||
controller.registerHandler(GET, "/_validate/query", this);
|
controller.registerHandler(GET, "/_validate/query", this);
|
||||||
controller.registerHandler(POST, "/_validate/query", this);
|
controller.registerHandler(POST, "/_validate/query", this);
|
||||||
|
@ -58,55 +59,52 @@ public class RestValidateQueryAction extends BaseRestHandler {
|
||||||
controller.registerHandler(POST, "/{index}/_validate/query", this);
|
controller.registerHandler(POST, "/{index}/_validate/query", this);
|
||||||
controller.registerHandler(GET, "/{index}/{type}/_validate/query", this);
|
controller.registerHandler(GET, "/{index}/{type}/_validate/query", this);
|
||||||
controller.registerHandler(POST, "/{index}/{type}/_validate/query", this);
|
controller.registerHandler(POST, "/{index}/{type}/_validate/query", this);
|
||||||
|
this.indicesQueriesRegistry = indicesQueriesRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
|
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) throws Exception {
|
||||||
ValidateQueryRequest validateQueryRequest = new ValidateQueryRequest(Strings.splitStringByCommaToArray(request.param("index")));
|
ValidateQueryRequest validateQueryRequest = new ValidateQueryRequest(Strings.splitStringByCommaToArray(request.param("index")));
|
||||||
validateQueryRequest.indicesOptions(IndicesOptions.fromRequest(request, validateQueryRequest.indicesOptions()));
|
validateQueryRequest.indicesOptions(IndicesOptions.fromRequest(request, validateQueryRequest.indicesOptions()));
|
||||||
|
validateQueryRequest.explain(request.paramAsBoolean("explain", false));
|
||||||
if (RestActions.hasBodyContent(request)) {
|
if (RestActions.hasBodyContent(request)) {
|
||||||
validateQueryRequest.source(RestActions.getRestContent(request));
|
try {
|
||||||
|
validateQueryRequest.query(RestActions.getQueryContent(RestActions.getRestContent(request), indicesQueriesRegistry, parseFieldMatcher));
|
||||||
|
} catch(ParsingException e) {
|
||||||
|
channel.sendResponse(buildErrorResponse(channel.newBuilder(), e.getDetailedMessage(), validateQueryRequest.explain()));
|
||||||
|
return;
|
||||||
|
} catch(Exception e) {
|
||||||
|
channel.sendResponse(buildErrorResponse(channel.newBuilder(), e.getMessage(), validateQueryRequest.explain()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
QueryBuilder<?> queryBuilder = RestActions.urlParamsToQueryBuilder(request);
|
QueryBuilder<?> queryBuilder = RestActions.urlParamsToQueryBuilder(request);
|
||||||
if (queryBuilder != null) {
|
if (queryBuilder != null) {
|
||||||
QuerySourceBuilder querySourceBuilder = new QuerySourceBuilder();
|
validateQueryRequest.query(queryBuilder);
|
||||||
querySourceBuilder.setQuery(queryBuilder);
|
|
||||||
validateQueryRequest.source(querySourceBuilder);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateQueryRequest.types(Strings.splitStringByCommaToArray(request.param("type")));
|
validateQueryRequest.types(Strings.splitStringByCommaToArray(request.param("type")));
|
||||||
if (request.paramAsBoolean("explain", false)) {
|
validateQueryRequest.rewrite(request.paramAsBoolean("rewrite", false));
|
||||||
validateQueryRequest.explain(true);
|
|
||||||
} else {
|
|
||||||
validateQueryRequest.explain(false);
|
|
||||||
}
|
|
||||||
if (request.paramAsBoolean("rewrite", false)) {
|
|
||||||
validateQueryRequest.rewrite(true);
|
|
||||||
} else {
|
|
||||||
validateQueryRequest.rewrite(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
client.admin().indices().validateQuery(validateQueryRequest, new RestBuilderListener<ValidateQueryResponse>(channel) {
|
client.admin().indices().validateQuery(validateQueryRequest, new RestBuilderListener<ValidateQueryResponse>(channel) {
|
||||||
@Override
|
@Override
|
||||||
public RestResponse buildResponse(ValidateQueryResponse response, XContentBuilder builder) throws Exception {
|
public RestResponse buildResponse(ValidateQueryResponse response, XContentBuilder builder) throws Exception {
|
||||||
builder.startObject();
|
builder.startObject();
|
||||||
builder.field("valid", response.isValid());
|
builder.field(VALID_FIELD, response.isValid());
|
||||||
|
|
||||||
buildBroadcastShardsHeader(builder, request, response);
|
buildBroadcastShardsHeader(builder, request, response);
|
||||||
|
|
||||||
if (response.getQueryExplanation() != null && !response.getQueryExplanation().isEmpty()) {
|
if (response.getQueryExplanation() != null && !response.getQueryExplanation().isEmpty()) {
|
||||||
builder.startArray("explanations");
|
builder.startArray(EXPLANATIONS_FIELD);
|
||||||
for (QueryExplanation explanation : response.getQueryExplanation()) {
|
for (QueryExplanation explanation : response.getQueryExplanation()) {
|
||||||
builder.startObject();
|
builder.startObject();
|
||||||
if (explanation.getIndex() != null) {
|
if (explanation.getIndex() != null) {
|
||||||
builder.field("index", explanation.getIndex(), XContentBuilder.FieldCaseConversion.NONE);
|
builder.field(INDEX_FIELD, explanation.getIndex(), XContentBuilder.FieldCaseConversion.NONE);
|
||||||
}
|
}
|
||||||
builder.field("valid", explanation.isValid());
|
builder.field(VALID_FIELD, explanation.isValid());
|
||||||
if (explanation.getError() != null) {
|
if (explanation.getError() != null) {
|
||||||
builder.field("error", explanation.getError());
|
builder.field(ERROR_FIELD, explanation.getError());
|
||||||
}
|
}
|
||||||
if (explanation.getExplanation() != null) {
|
if (explanation.getExplanation() != null) {
|
||||||
builder.field("explanation", explanation.getExplanation());
|
builder.field(EXPLANATION_FIELD, explanation.getExplanation());
|
||||||
}
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
}
|
}
|
||||||
|
@ -117,4 +115,20 @@ public class RestValidateQueryAction extends BaseRestHandler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static BytesRestResponse buildErrorResponse(XContentBuilder builder, String error, boolean explain) throws IOException {
|
||||||
|
builder.startObject();
|
||||||
|
builder.field(VALID_FIELD, false);
|
||||||
|
if (explain) {
|
||||||
|
builder.field(ERROR_FIELD, error);
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
|
return new BytesRestResponse(OK, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final XContentBuilderString INDEX_FIELD = new XContentBuilderString("index");
|
||||||
|
private static final XContentBuilderString VALID_FIELD = new XContentBuilderString("valid");
|
||||||
|
private static final XContentBuilderString EXPLANATIONS_FIELD = new XContentBuilderString("explanations");
|
||||||
|
private static final XContentBuilderString ERROR_FIELD = new XContentBuilderString("error");
|
||||||
|
private static final XContentBuilderString EXPLANATION_FIELD = new XContentBuilderString("explanation");
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class SimpleValidateQueryIT extends ESIntegTestCase {
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
assertThat(client().admin().indices().prepareValidateQuery("test").setSource("foo".getBytes(StandardCharsets.UTF_8)).execute().actionGet().isValid(), equalTo(false));
|
assertThat(client().admin().indices().prepareValidateQuery("test").setQuery(QueryBuilders.wrapperQuery("foo".getBytes(StandardCharsets.UTF_8))).execute().actionGet().isValid(), equalTo(false));
|
||||||
assertThat(client().admin().indices().prepareValidateQuery("test").setQuery(QueryBuilders.queryStringQuery("_id:1")).execute().actionGet().isValid(), equalTo(true));
|
assertThat(client().admin().indices().prepareValidateQuery("test").setQuery(QueryBuilders.queryStringQuery("_id:1")).execute().actionGet().isValid(), equalTo(true));
|
||||||
assertThat(client().admin().indices().prepareValidateQuery("test").setQuery(QueryBuilders.queryStringQuery("_i:d:1")).execute().actionGet().isValid(), equalTo(false));
|
assertThat(client().admin().indices().prepareValidateQuery("test").setQuery(QueryBuilders.queryStringQuery("_i:d:1")).execute().actionGet().isValid(), equalTo(false));
|
||||||
|
|
||||||
|
@ -94,12 +94,12 @@ public class SimpleValidateQueryIT extends ESIntegTestCase {
|
||||||
|
|
||||||
for (Client client : internalCluster()) {
|
for (Client client : internalCluster()) {
|
||||||
ValidateQueryResponse response = client.admin().indices().prepareValidateQuery("test")
|
ValidateQueryResponse response = client.admin().indices().prepareValidateQuery("test")
|
||||||
.setSource("foo".getBytes(StandardCharsets.UTF_8))
|
.setQuery(QueryBuilders.wrapperQuery("foo".getBytes(StandardCharsets.UTF_8)))
|
||||||
.setExplain(true)
|
.setExplain(true)
|
||||||
.execute().actionGet();
|
.execute().actionGet();
|
||||||
assertThat(response.isValid(), equalTo(false));
|
assertThat(response.isValid(), equalTo(false));
|
||||||
assertThat(response.getQueryExplanation().size(), equalTo(1));
|
assertThat(response.getQueryExplanation().size(), equalTo(1));
|
||||||
assertThat(response.getQueryExplanation().get(0).getError(), containsString("Failed to parse"));
|
assertThat(response.getQueryExplanation().get(0).getError(), containsString("Failed to derive xcontent"));
|
||||||
assertThat(response.getQueryExplanation().get(0).getExplanation(), nullValue());
|
assertThat(response.getQueryExplanation().get(0).getExplanation(), nullValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,7 @@ public class SimpleValidateQueryIT extends ESIntegTestCase {
|
||||||
ensureGreen();
|
ensureGreen();
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
assertThat(client().admin().indices().prepareValidateQuery("test").setSource(new BytesArray("{\"foo\": \"bar\", \"query\": {\"term\" : { \"user\" : \"kimchy\" }}}")).get().isValid(), equalTo(false));
|
assertThat(client().admin().indices().prepareValidateQuery("test").setQuery(QueryBuilders.wrapperQuery(new BytesArray("{\"foo\": \"bar\", \"query\": {\"term\" : { \"user\" : \"kimchy\" }}}"))).get().isValid(), equalTo(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIrrelevantPropertiesAfterQuery() throws IOException {
|
public void testIrrelevantPropertiesAfterQuery() throws IOException {
|
||||||
|
@ -268,7 +268,7 @@ public class SimpleValidateQueryIT extends ESIntegTestCase {
|
||||||
ensureGreen();
|
ensureGreen();
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
assertThat(client().admin().indices().prepareValidateQuery("test").setSource(new BytesArray("{\"query\": {\"term\" : { \"user\" : \"kimchy\" }}, \"foo\": \"bar\"}")).get().isValid(), equalTo(false));
|
assertThat(client().admin().indices().prepareValidateQuery("test").setQuery(QueryBuilders.wrapperQuery(new BytesArray("{\"query\": {\"term\" : { \"user\" : \"kimchy\" }}, \"foo\": \"bar\"}"))).get().isValid(), equalTo(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertExplanation(QueryBuilder queryBuilder, Matcher<String> matcher, boolean withRewrite) {
|
private static void assertExplanation(QueryBuilder queryBuilder, Matcher<String> matcher, boolean withRewrite) {
|
||||||
|
|
|
@ -25,6 +25,17 @@ setup:
|
||||||
invalid_query: {}
|
invalid_query: {}
|
||||||
|
|
||||||
- is_false: valid
|
- is_false: valid
|
||||||
|
- is_false: error
|
||||||
|
|
||||||
|
- do:
|
||||||
|
indices.validate_query:
|
||||||
|
explain: true
|
||||||
|
body:
|
||||||
|
query:
|
||||||
|
invalid_query: {}
|
||||||
|
|
||||||
|
- is_false: valid
|
||||||
|
- match: {error: 'org.elasticsearch.common.ParsingException: No query registered for [invalid_query]'}
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
indices.validate_query:
|
indices.validate_query:
|
||||||
|
@ -43,3 +54,13 @@ setup:
|
||||||
match_all: {}
|
match_all: {}
|
||||||
|
|
||||||
- is_false: valid
|
- is_false: valid
|
||||||
|
- is_false: error
|
||||||
|
|
||||||
|
- do:
|
||||||
|
indices.validate_query:
|
||||||
|
explain: true
|
||||||
|
body:
|
||||||
|
match_all: {}
|
||||||
|
|
||||||
|
- is_false: valid
|
||||||
|
- match: {error: 'org.elasticsearch.common.ParsingException: request does not support [match_all]'}
|
||||||
|
|
Loading…
Reference in New Issue