Add query validation feature
This commit is contained in:
parent
73b74847aa
commit
be6e18cb36
|
@ -29,6 +29,8 @@ public class TransportActions {
|
|||
public static final String INDEX = "indices/index/shard/index";
|
||||
|
||||
public static final String COUNT = "indices/count";
|
||||
|
||||
public static final String VALIDATE = "indices/validate";
|
||||
|
||||
public static final String DELETE = "indices/index/shard/delete";
|
||||
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.validate;
|
||||
|
||||
import org.elasticsearch.action.support.broadcast.BroadcastShardOperationRequest;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Internal validate request executed directly against a specific index shard.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class ShardValidateRequest extends BroadcastShardOperationRequest {
|
||||
|
||||
private byte[] querySource;
|
||||
private int querySourceOffset;
|
||||
private int querySourceLength;
|
||||
|
||||
private String[] types = Strings.EMPTY_ARRAY;
|
||||
|
||||
@Nullable
|
||||
private String[] filteringAliases;
|
||||
|
||||
ShardValidateRequest() {
|
||||
|
||||
}
|
||||
|
||||
public ShardValidateRequest(String index, int shardId, @Nullable String[] filteringAliases, ValidateRequest request) {
|
||||
super(index, shardId);
|
||||
this.querySource = request.querySource();
|
||||
this.querySourceOffset = request.querySourceOffset();
|
||||
this.querySourceLength = request.querySourceLength();
|
||||
this.types = request.types();
|
||||
this.filteringAliases = filteringAliases;
|
||||
}
|
||||
|
||||
public byte[] querySource() {
|
||||
return querySource;
|
||||
}
|
||||
|
||||
public int querySourceOffset() {
|
||||
return querySourceOffset;
|
||||
}
|
||||
|
||||
public int querySourceLength() {
|
||||
return querySourceLength;
|
||||
}
|
||||
|
||||
public String[] types() {
|
||||
return this.types;
|
||||
}
|
||||
|
||||
public String[] filteringAliases() {
|
||||
return filteringAliases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
querySourceLength = in.readVInt();
|
||||
querySourceOffset = 0;
|
||||
querySource = new byte[querySourceLength];
|
||||
in.readFully(querySource);
|
||||
int typesSize = in.readVInt();
|
||||
if (typesSize > 0) {
|
||||
types = new String[typesSize];
|
||||
for (int i = 0; i < typesSize; i++) {
|
||||
types[i] = in.readUTF();
|
||||
}
|
||||
}
|
||||
int aliasesSize = in.readVInt();
|
||||
if (aliasesSize > 0) {
|
||||
filteringAliases = new String[aliasesSize];
|
||||
for (int i = 0; i < aliasesSize; i++) {
|
||||
filteringAliases[i] = in.readUTF();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeVInt(querySourceLength);
|
||||
out.writeBytes(querySource, querySourceOffset, querySourceLength);
|
||||
out.writeVInt(types.length);
|
||||
for (String type : types) {
|
||||
out.writeUTF(type);
|
||||
}
|
||||
if (filteringAliases != null) {
|
||||
out.writeVInt(filteringAliases.length);
|
||||
for (String alias : filteringAliases) {
|
||||
out.writeUTF(alias);
|
||||
}
|
||||
} else {
|
||||
out.writeVInt(0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.validate;
|
||||
|
||||
import org.elasticsearch.action.support.broadcast.BroadcastShardOperationResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Internal validate response of a shard validate request executed directly against a specific shard.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class ShardValidateResponse extends BroadcastShardOperationResponse {
|
||||
|
||||
private boolean valid;
|
||||
|
||||
ShardValidateResponse() {
|
||||
|
||||
}
|
||||
|
||||
public ShardValidateResponse(String index, int shardId, boolean valid) {
|
||||
super(index, shardId);
|
||||
this.valid = valid;
|
||||
}
|
||||
|
||||
boolean valid() {
|
||||
return this.valid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
valid = in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeBoolean(valid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.validate;
|
||||
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
import org.elasticsearch.action.ShardOperationFailedException;
|
||||
import org.elasticsearch.action.TransportActions;
|
||||
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
|
||||
import org.elasticsearch.action.support.broadcast.BroadcastShardOperationFailedException;
|
||||
import org.elasticsearch.action.support.broadcast.TransportBroadcastOperationAction;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
import org.elasticsearch.cluster.routing.GroupShardsIterator;
|
||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.shard.service.IndexShard;
|
||||
import org.elasticsearch.indices.IndicesService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class TransportValidateAction extends TransportBroadcastOperationAction<ValidateRequest, ValidateResponse, ShardValidateRequest, ShardValidateResponse> {
|
||||
|
||||
private final IndicesService indicesService;
|
||||
|
||||
@Inject
|
||||
public TransportValidateAction(Settings settings, ThreadPool threadPool, ClusterService clusterService, TransportService transportService, IndicesService indicesService) {
|
||||
super(settings, threadPool, clusterService, transportService);
|
||||
this.indicesService = indicesService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.SEARCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String transportAction() {
|
||||
return TransportActions.VALIDATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String transportShardAction() {
|
||||
return "indices/validate/shard";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValidateRequest newRequest() {
|
||||
return new ValidateRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShardValidateRequest newShardRequest() {
|
||||
return new ShardValidateRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShardValidateRequest newShardRequest(ShardRouting shard, ValidateRequest request) {
|
||||
String[] filteringAliases = clusterService.state().metaData().filteringAliases(shard.index(), request.indices());
|
||||
return new ShardValidateRequest(shard.index(), shard.id(), filteringAliases, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShardValidateResponse newShardResponse() {
|
||||
return new ShardValidateResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GroupShardsIterator shards(ValidateRequest request, String[] concreteIndices, ClusterState clusterState) {
|
||||
// Hard-code routing to limit request to a single shard.
|
||||
Map<String, Set<String>> routingMap = clusterState.metaData().resolveSearchRouting("0", request.indices());
|
||||
return clusterService.operationRouting().searchShards(clusterState, request.indices(), concreteIndices, null, routingMap, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkBlock(ValidateRequest request, String[] concreteIndices, ClusterState state) {
|
||||
for (String index : concreteIndices) {
|
||||
state.blocks().indexBlocked(ClusterBlockLevel.READ, index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValidateResponse newResponse(ValidateRequest request, AtomicReferenceArray shardsResponses, ClusterState clusterState) {
|
||||
int successfulShards = 0;
|
||||
int failedShards = 0;
|
||||
boolean valid = true;
|
||||
List<ShardOperationFailedException> shardFailures = null;
|
||||
for (int i = 0; i < shardsResponses.length(); i++) {
|
||||
Object shardResponse = shardsResponses.get(i);
|
||||
if (shardResponse == null) {
|
||||
failedShards++;
|
||||
} else if (shardResponse instanceof BroadcastShardOperationFailedException) {
|
||||
failedShards++;
|
||||
if (shardFailures == null) {
|
||||
shardFailures = newArrayList();
|
||||
}
|
||||
shardFailures.add(new DefaultShardOperationFailedException((BroadcastShardOperationFailedException) shardResponse));
|
||||
} else {
|
||||
valid = valid && ((ShardValidateResponse) shardResponse).valid();
|
||||
successfulShards++;
|
||||
}
|
||||
}
|
||||
return new ValidateResponse(valid, shardsResponses.length(), successfulShards, failedShards, shardFailures);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShardValidateResponse shardOperation(ShardValidateRequest request) throws ElasticSearchException {
|
||||
IndexShard indexShard = indicesService.indexServiceSafe(request.index()).shardSafe(request.shardId());
|
||||
boolean valid = indexShard.validate(request.querySource(), request.querySourceOffset(), request.querySourceLength(),
|
||||
request.filteringAliases(), request.types());
|
||||
return new ShardValidateResponse(request.index(), request.shardId(), valid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.validate;
|
||||
|
||||
import org.apache.lucene.util.UnicodeUtil;
|
||||
import org.elasticsearch.ElasticSearchGenerationException;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.broadcast.BroadcastOperationRequest;
|
||||
import org.elasticsearch.action.support.broadcast.BroadcastOperationThreading;
|
||||
import org.elasticsearch.client.Requests;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Required;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.Unicode;
|
||||
import org.elasticsearch.common.io.BytesStream;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A request to validate a specific query.
|
||||
* <p/>
|
||||
* <p>The request requires the query source to be set either using {@link #query(org.elasticsearch.index.query.QueryBuilder)},
|
||||
* or {@link #query(byte[])}.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ValidateRequest extends BroadcastOperationRequest {
|
||||
|
||||
private static final XContentType contentType = Requests.CONTENT_TYPE;
|
||||
|
||||
private byte[] querySource;
|
||||
private int querySourceOffset;
|
||||
private int querySourceLength;
|
||||
private boolean querySourceUnsafe;
|
||||
|
||||
private String[] types = Strings.EMPTY_ARRAY;
|
||||
|
||||
ValidateRequest() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new validate request against the provided indices. No indices provided means it will
|
||||
* run against all indices.
|
||||
*/
|
||||
public ValidateRequest(String... indices) {
|
||||
super(indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = super.validate();
|
||||
return validationException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the operation threading model.
|
||||
*/
|
||||
@Override
|
||||
public ValidateRequest operationThreading(BroadcastOperationThreading operationThreading) {
|
||||
super.operationThreading(operationThreading);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeStart() {
|
||||
if (querySourceUnsafe) {
|
||||
querySource = Arrays.copyOfRange(querySource, querySourceOffset, querySourceOffset + querySourceLength);
|
||||
querySourceOffset = 0;
|
||||
querySourceUnsafe = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the listener be called on a separate thread if needed.
|
||||
*/
|
||||
@Override
|
||||
public ValidateRequest listenerThreaded(boolean threadedListener) {
|
||||
super.listenerThreaded(threadedListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidateRequest indices(String... indices) {
|
||||
this.indices = indices;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The query source to execute.
|
||||
*/
|
||||
byte[] querySource() {
|
||||
return querySource;
|
||||
}
|
||||
|
||||
int querySourceOffset() {
|
||||
return querySourceOffset;
|
||||
}
|
||||
|
||||
int querySourceLength() {
|
||||
return querySourceLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* The query source to execute.
|
||||
*
|
||||
* @see org.elasticsearch.index.query.QueryBuilders
|
||||
*/
|
||||
@Required
|
||||
public ValidateRequest query(QueryBuilder queryBuilder) {
|
||||
BytesStream bos = queryBuilder.buildAsUnsafeBytes();
|
||||
this.querySource = bos.underlyingBytes();
|
||||
this.querySourceOffset = 0;
|
||||
this.querySourceLength = bos.size();
|
||||
this.querySourceUnsafe = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The query source to execute in the form of a map.
|
||||
*/
|
||||
@Required
|
||||
public ValidateRequest query(Map querySource) {
|
||||
try {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(contentType);
|
||||
builder.map(querySource);
|
||||
return query(builder);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticSearchGenerationException("Failed to generate [" + querySource + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Required
|
||||
public ValidateRequest query(XContentBuilder builder) {
|
||||
try {
|
||||
this.querySource = builder.underlyingBytes();
|
||||
this.querySourceOffset = 0;
|
||||
this.querySourceLength = builder.underlyingBytesLength();
|
||||
this.querySourceUnsafe = false;
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ElasticSearchGenerationException("Failed to generate [" + builder + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The query source to validate. It is preferable to use either {@link #query(byte[])}
|
||||
* or {@link #query(org.elasticsearch.index.query.QueryBuilder)}.
|
||||
*/
|
||||
@Required
|
||||
public ValidateRequest query(String querySource) {
|
||||
UnicodeUtil.UTF8Result result = Unicode.fromStringAsUtf8(querySource);
|
||||
this.querySource = result.result;
|
||||
this.querySourceOffset = 0;
|
||||
this.querySourceLength = result.length;
|
||||
this.querySourceUnsafe = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The query source to validate.
|
||||
*/
|
||||
@Required
|
||||
public ValidateRequest query(byte[] querySource) {
|
||||
return query(querySource, 0, querySource.length, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The query source to validate.
|
||||
*/
|
||||
@Required
|
||||
public ValidateRequest query(byte[] querySource, int offset, int length, boolean unsafe) {
|
||||
this.querySource = querySource;
|
||||
this.querySourceOffset = offset;
|
||||
this.querySourceLength = length;
|
||||
this.querySourceUnsafe = unsafe;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The types of documents the query will run against. Defaults to all types.
|
||||
*/
|
||||
String[] types() {
|
||||
return this.types;
|
||||
}
|
||||
|
||||
/**
|
||||
* The types of documents the query will run against. Defaults to all types.
|
||||
*/
|
||||
public ValidateRequest types(String... types) {
|
||||
this.types = types;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
|
||||
querySourceUnsafe = false;
|
||||
querySourceOffset = 0;
|
||||
querySourceLength = in.readVInt();
|
||||
querySource = new byte[querySourceLength];
|
||||
in.readFully(querySource);
|
||||
|
||||
int typesSize = in.readVInt();
|
||||
if (typesSize > 0) {
|
||||
types = new String[typesSize];
|
||||
for (int i = 0; i < typesSize; i++) {
|
||||
types[i] = in.readUTF();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
|
||||
out.writeVInt(querySourceLength);
|
||||
out.writeBytes(querySource, querySourceOffset, querySourceLength);
|
||||
|
||||
out.writeVInt(types.length);
|
||||
for (String type : types) {
|
||||
out.writeUTF(type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + Arrays.toString(indices) + "]" + Arrays.toString(types) + ", querySource[" + Unicode.fromBytes(querySource) + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.validate;
|
||||
|
||||
import org.elasticsearch.action.ShardOperationFailedException;
|
||||
import org.elasticsearch.action.support.broadcast.BroadcastOperationResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The response of the validate action.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ValidateResponse extends BroadcastOperationResponse {
|
||||
|
||||
private boolean valid;
|
||||
|
||||
ValidateResponse() {
|
||||
|
||||
}
|
||||
|
||||
ValidateResponse(boolean valid, int totalShards, int successfulShards, int failedShards, List<ShardOperationFailedException> shardFailures) {
|
||||
super(totalShards, successfulShards, failedShards, shardFailures);
|
||||
this.valid = valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* A boolean denoting whether the query is valid.
|
||||
*/
|
||||
public boolean valid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* A boolean denoting whether the query is valid.
|
||||
*/
|
||||
public boolean getValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
valid = in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeBoolean(valid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Validate action.
|
||||
*/
|
||||
package org.elasticsearch.action.validate;
|
|
@ -41,6 +41,8 @@ import org.elasticsearch.action.percolate.PercolateResponse;
|
|||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.validate.ValidateRequest;
|
||||
import org.elasticsearch.action.validate.ValidateResponse;
|
||||
import org.elasticsearch.client.action.bulk.BulkRequestBuilder;
|
||||
import org.elasticsearch.client.action.count.CountRequestBuilder;
|
||||
import org.elasticsearch.client.action.delete.DeleteRequestBuilder;
|
||||
|
@ -52,6 +54,7 @@ import org.elasticsearch.client.action.mlt.MoreLikeThisRequestBuilder;
|
|||
import org.elasticsearch.client.action.percolate.PercolateRequestBuilder;
|
||||
import org.elasticsearch.client.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.client.action.search.SearchScrollRequestBuilder;
|
||||
import org.elasticsearch.client.action.validate.ValidateRequestBuilder;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
|
||||
/**
|
||||
|
@ -274,6 +277,29 @@ public interface Client {
|
|||
*/
|
||||
CountRequestBuilder prepareCount(String... indices);
|
||||
|
||||
/**
|
||||
* A count of all the documents matching a specific query.
|
||||
*
|
||||
* @param request The count request
|
||||
* @return The result future
|
||||
* @see Requests#countRequest(String...)
|
||||
*/
|
||||
ActionFuture<ValidateResponse> validate(ValidateRequest request);
|
||||
|
||||
/**
|
||||
* A count of all the documents matching a specific query.
|
||||
*
|
||||
* @param request The count request
|
||||
* @param listener A listener to be notified of the result
|
||||
* @see Requests#countRequest(String...)
|
||||
*/
|
||||
void validate(ValidateRequest request, ActionListener<ValidateResponse> listener);
|
||||
|
||||
/**
|
||||
* A count of all the documents matching a specific query.
|
||||
*/
|
||||
ValidateRequestBuilder prepareValidate(String... indices);
|
||||
|
||||
/**
|
||||
* Search across one or more indices and one or more types with a query.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package org.elasticsearch.client.action.validate;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.broadcast.BroadcastOperationThreading;
|
||||
import org.elasticsearch.action.validate.ValidateRequest;
|
||||
import org.elasticsearch.action.validate.ValidateResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.action.support.BaseRequestBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ValidateRequestBuilder extends BaseRequestBuilder<ValidateRequest, ValidateResponse> {
|
||||
public ValidateRequestBuilder(Client client) {
|
||||
super(client, new ValidateRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indices the query validation will run against.
|
||||
*/
|
||||
public ValidateRequestBuilder setIndices(String... indices) {
|
||||
request.indices(indices);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The types of documents the query will run against. Defaults to all types.
|
||||
*/
|
||||
public ValidateRequestBuilder setTypes(String... types) {
|
||||
request.types(types);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The query source to validate.
|
||||
*
|
||||
* @see org.elasticsearch.index.query.QueryBuilders
|
||||
*/
|
||||
public ValidateRequestBuilder setQuery(QueryBuilder queryBuilder) {
|
||||
request.query(queryBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The query source to validate.
|
||||
*
|
||||
* @see org.elasticsearch.index.query.QueryBuilders
|
||||
*/
|
||||
public ValidateRequestBuilder setQuery(byte[] querySource) {
|
||||
request.query(querySource);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the operation threading model.
|
||||
*/
|
||||
public ValidateRequestBuilder setOperationThreading(BroadcastOperationThreading operationThreading) {
|
||||
request.operationThreading(operationThreading);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the listener be called on a separate thread if needed.
|
||||
*/
|
||||
public ValidateRequestBuilder setListenerThreaded(boolean threadedListener) {
|
||||
request.listenerThreaded(threadedListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(ActionListener<ValidateResponse> listener) {
|
||||
client.validate(request, listener);
|
||||
}
|
||||
}
|
|
@ -43,7 +43,11 @@ import org.elasticsearch.action.percolate.PercolateRequest;
|
|||
import org.elasticsearch.action.percolate.PercolateResponse;
|
||||
import org.elasticsearch.action.percolate.TransportPercolateAction;
|
||||
import org.elasticsearch.action.search.*;
|
||||
import org.elasticsearch.action.validate.TransportValidateAction;
|
||||
import org.elasticsearch.action.validate.ValidateRequest;
|
||||
import org.elasticsearch.action.validate.ValidateResponse;
|
||||
import org.elasticsearch.client.AdminClient;
|
||||
import org.elasticsearch.client.action.validate.ValidateRequestBuilder;
|
||||
import org.elasticsearch.client.internal.InternalClient;
|
||||
import org.elasticsearch.client.support.AbstractClient;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
|
@ -73,6 +77,8 @@ public class NodeClient extends AbstractClient implements InternalClient {
|
|||
|
||||
private final TransportCountAction countAction;
|
||||
|
||||
private final TransportValidateAction validateAction;
|
||||
|
||||
private final TransportSearchAction searchAction;
|
||||
|
||||
private final TransportSearchScrollAction searchScrollAction;
|
||||
|
@ -85,7 +91,7 @@ public class NodeClient extends AbstractClient implements InternalClient {
|
|||
public NodeClient(Settings settings, ThreadPool threadPool, NodeAdminClient admin,
|
||||
TransportIndexAction indexAction, TransportDeleteAction deleteAction, TransportBulkAction bulkAction,
|
||||
TransportDeleteByQueryAction deleteByQueryAction, TransportGetAction getAction, TransportMultiGetAction multiGetAction, TransportCountAction countAction,
|
||||
TransportSearchAction searchAction, TransportSearchScrollAction searchScrollAction,
|
||||
TransportSearchAction searchAction, TransportValidateAction validateAction, TransportSearchScrollAction searchScrollAction,
|
||||
TransportMoreLikeThisAction moreLikeThisAction, TransportPercolateAction percolateAction) {
|
||||
this.threadPool = threadPool;
|
||||
this.admin = admin;
|
||||
|
@ -96,6 +102,7 @@ public class NodeClient extends AbstractClient implements InternalClient {
|
|||
this.getAction = getAction;
|
||||
this.multiGetAction = multiGetAction;
|
||||
this.countAction = countAction;
|
||||
this.validateAction = validateAction;
|
||||
this.searchAction = searchAction;
|
||||
this.searchScrollAction = searchScrollAction;
|
||||
this.moreLikeThisAction = moreLikeThisAction;
|
||||
|
@ -187,6 +194,16 @@ public class NodeClient extends AbstractClient implements InternalClient {
|
|||
countAction.execute(request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<ValidateResponse> validate(ValidateRequest request) {
|
||||
return validateAction.execute(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(ValidateRequest request, ActionListener<ValidateResponse> listener) {
|
||||
validateAction.execute(request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<SearchResponse> search(SearchRequest request) {
|
||||
return searchAction.execute(request);
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.elasticsearch.client.action.mlt.MoreLikeThisRequestBuilder;
|
|||
import org.elasticsearch.client.action.percolate.PercolateRequestBuilder;
|
||||
import org.elasticsearch.client.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.client.action.search.SearchScrollRequestBuilder;
|
||||
import org.elasticsearch.client.action.validate.ValidateRequestBuilder;
|
||||
import org.elasticsearch.client.internal.InternalClient;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
|
||||
|
@ -103,6 +104,11 @@ public abstract class AbstractClient implements InternalClient {
|
|||
return new CountRequestBuilder(this).setIndices(indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidateRequestBuilder prepareValidate(String... indices) {
|
||||
return new ValidateRequestBuilder(this).setIndices(indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MoreLikeThisRequestBuilder prepareMoreLikeThis(String index, String type, String id) {
|
||||
return new MoreLikeThisRequestBuilder(this, index, type, id);
|
||||
|
|
|
@ -43,6 +43,8 @@ import org.elasticsearch.action.percolate.PercolateResponse;
|
|||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.validate.ValidateRequest;
|
||||
import org.elasticsearch.action.validate.ValidateResponse;
|
||||
import org.elasticsearch.client.AdminClient;
|
||||
import org.elasticsearch.client.support.AbstractClient;
|
||||
import org.elasticsearch.client.transport.action.ClientTransportActionModule;
|
||||
|
@ -319,6 +321,16 @@ public class TransportClient extends AbstractClient {
|
|||
internalClient.count(request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<ValidateResponse> validate(ValidateRequest request) {
|
||||
return internalClient.validate(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(ValidateRequest request, ActionListener<ValidateResponse> listener) {
|
||||
internalClient.validate(request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<SearchResponse> search(SearchRequest request) {
|
||||
return internalClient.search(request);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package org.elasticsearch.client.transport.action.validate;
|
||||
|
||||
import org.elasticsearch.action.TransportActions;
|
||||
import org.elasticsearch.action.validate.ValidateRequest;
|
||||
import org.elasticsearch.action.validate.ValidateResponse;
|
||||
import org.elasticsearch.client.transport.action.support.BaseClientTransportAction;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ClientTransportValidateAction extends BaseClientTransportAction<ValidateRequest, ValidateResponse> {
|
||||
|
||||
@Inject
|
||||
public ClientTransportValidateAction(Settings settings, TransportService transportService) {
|
||||
super(settings, transportService, ValidateResponse.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String action() {
|
||||
return TransportActions.VALIDATE;
|
||||
}
|
||||
}
|
|
@ -42,6 +42,8 @@ import org.elasticsearch.action.percolate.PercolateResponse;
|
|||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.validate.ValidateRequest;
|
||||
import org.elasticsearch.action.validate.ValidateResponse;
|
||||
import org.elasticsearch.client.AdminClient;
|
||||
import org.elasticsearch.client.internal.InternalClient;
|
||||
import org.elasticsearch.client.support.AbstractClient;
|
||||
|
@ -57,6 +59,7 @@ import org.elasticsearch.client.transport.action.mlt.ClientTransportMoreLikeThis
|
|||
import org.elasticsearch.client.transport.action.percolate.ClientTransportPercolateAction;
|
||||
import org.elasticsearch.client.transport.action.search.ClientTransportSearchAction;
|
||||
import org.elasticsearch.client.transport.action.search.ClientTransportSearchScrollAction;
|
||||
import org.elasticsearch.client.transport.action.validate.ClientTransportValidateAction;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -87,6 +90,8 @@ public class InternalTransportClient extends AbstractClient implements InternalC
|
|||
|
||||
private final ClientTransportCountAction countAction;
|
||||
|
||||
private final ClientTransportValidateAction validateAction;
|
||||
|
||||
private final ClientTransportSearchAction searchAction;
|
||||
|
||||
private final ClientTransportSearchScrollAction searchScrollAction;
|
||||
|
@ -99,7 +104,7 @@ public class InternalTransportClient extends AbstractClient implements InternalC
|
|||
public InternalTransportClient(Settings settings, ThreadPool threadPool,
|
||||
TransportClientNodesService nodesService, InternalTransportAdminClient adminClient,
|
||||
ClientTransportIndexAction indexAction, ClientTransportDeleteAction deleteAction, ClientTransportBulkAction bulkAction, ClientTransportGetAction getAction, ClientTransportMultiGetAction multiGetAction,
|
||||
ClientTransportDeleteByQueryAction deleteByQueryAction, ClientTransportCountAction countAction,
|
||||
ClientTransportDeleteByQueryAction deleteByQueryAction, ClientTransportCountAction countAction, ClientTransportValidateAction validateAction,
|
||||
ClientTransportSearchAction searchAction, ClientTransportSearchScrollAction searchScrollAction,
|
||||
ClientTransportMoreLikeThisAction moreLikeThisAction, ClientTransportPercolateAction percolateAction) {
|
||||
this.threadPool = threadPool;
|
||||
|
@ -113,6 +118,7 @@ public class InternalTransportClient extends AbstractClient implements InternalC
|
|||
this.multiGetAction = multiGetAction;
|
||||
this.deleteByQueryAction = deleteByQueryAction;
|
||||
this.countAction = countAction;
|
||||
this.validateAction = validateAction;
|
||||
this.searchAction = searchAction;
|
||||
this.searchScrollAction = searchScrollAction;
|
||||
this.moreLikeThisAction = moreLikeThisAction;
|
||||
|
@ -274,6 +280,26 @@ public class InternalTransportClient extends AbstractClient implements InternalC
|
|||
}, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<ValidateResponse> validate(final ValidateRequest request) {
|
||||
return nodesService.execute(new TransportClientNodesService.NodeCallback<ActionFuture<ValidateResponse>>() {
|
||||
@Override
|
||||
public ActionFuture<ValidateResponse> doWithNode(DiscoveryNode node) throws ElasticSearchException {
|
||||
return validateAction.execute(node, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(final ValidateRequest request, final ActionListener<ValidateResponse> listener) {
|
||||
nodesService.execute(new TransportClientNodesService.NodeListenerCallback<ValidateResponse>() {
|
||||
@Override
|
||||
public void doWithNode(DiscoveryNode node, ActionListener<ValidateResponse> listener) throws ElasticSearchException {
|
||||
validateAction.execute(node, request, listener);
|
||||
}
|
||||
}, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<SearchResponse> search(final SearchRequest request) {
|
||||
return nodesService.execute(new TransportClientNodesService.NodeCallback<ActionFuture<SearchResponse>>() {
|
||||
|
|
|
@ -105,6 +105,10 @@ public interface IndexShard extends IndexShardComponent {
|
|||
|
||||
Engine.Searcher searcher();
|
||||
|
||||
boolean validate(byte[] querySource, @Nullable String[] filteringAliases, String... types) throws ElasticSearchException;
|
||||
|
||||
boolean validate(byte[] querySource, int querySourceOffset, int querySourceLength, @Nullable String[] filteringAliases, String... types) throws ElasticSearchException;
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if this shard can ignore a recovery attempt made to it (since the already doing/done it)
|
||||
*/
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.elasticsearch.index.mapper.*;
|
|||
import org.elasticsearch.index.merge.MergeStats;
|
||||
import org.elasticsearch.index.merge.scheduler.MergeSchedulerProvider;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
import org.elasticsearch.index.refresh.RefreshStats;
|
||||
import org.elasticsearch.index.search.stats.SearchStats;
|
||||
import org.elasticsearch.index.search.stats.ShardSearchService;
|
||||
|
@ -518,6 +519,28 @@ public class InternalIndexShard extends AbstractIndexShardComponent implements I
|
|||
return engine.searcher();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(byte[] querySource, @Nullable String[] filteringAliases, String... types) throws ElasticSearchException {
|
||||
return validate(querySource, 0, querySource.length, filteringAliases, types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(byte[] querySource, int querySourceOffset, int querySourceLength, @Nullable String[] filteringAliases, String... types) throws ElasticSearchException {
|
||||
readAllowed();
|
||||
if (querySourceLength == 0) {
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
queryParserService.parse(querySource, querySourceOffset, querySourceLength);
|
||||
} catch (QueryParsingException e) {
|
||||
return false;
|
||||
} catch (AssertionError e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close(String reason) {
|
||||
synchronized (mutex) {
|
||||
indexSettingsService.removeListener(applyRefreshSettings);
|
||||
|
|
|
@ -70,6 +70,7 @@ import org.elasticsearch.rest.action.mlt.RestMoreLikeThisAction;
|
|||
import org.elasticsearch.rest.action.percolate.RestPercolateAction;
|
||||
import org.elasticsearch.rest.action.search.RestSearchAction;
|
||||
import org.elasticsearch.rest.action.search.RestSearchScrollAction;
|
||||
import org.elasticsearch.rest.action.validate.RestValidateAction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -149,6 +150,8 @@ public class RestActionModule extends AbstractModule {
|
|||
|
||||
bind(RestSearchAction.class).asEagerSingleton();
|
||||
bind(RestSearchScrollAction.class).asEagerSingleton();
|
||||
|
||||
bind(RestValidateAction.class).asEagerSingleton();
|
||||
|
||||
bind(RestMoreLikeThisAction.class).asEagerSingleton();
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.rest.action.validate;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.broadcast.BroadcastOperationThreading;
|
||||
import org.elasticsearch.action.validate.ValidateRequest;
|
||||
import org.elasticsearch.action.validate.ValidateResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.rest.*;
|
||||
import org.elasticsearch.rest.action.support.RestActions;
|
||||
import org.elasticsearch.rest.action.support.RestXContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||
import static org.elasticsearch.rest.RestStatus.BAD_REQUEST;
|
||||
import static org.elasticsearch.rest.RestStatus.OK;
|
||||
import static org.elasticsearch.rest.action.support.RestActions.buildBroadcastShardsHeader;
|
||||
import static org.elasticsearch.rest.action.support.RestActions.splitTypes;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class RestValidateAction extends BaseRestHandler {
|
||||
|
||||
@Inject
|
||||
public RestValidateAction(Settings settings, Client client, RestController controller) {
|
||||
super(settings, client);
|
||||
controller.registerHandler(GET, "/_validate", this);
|
||||
controller.registerHandler(POST, "/_validate", this);
|
||||
controller.registerHandler(GET, "/{index}/_validate", this);
|
||||
controller.registerHandler(POST, "/{index}/_validate", this);
|
||||
controller.registerHandler(GET, "/{index}/{type}/_validate", this);
|
||||
controller.registerHandler(POST, "/{index}/{type}/_validate", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(final RestRequest request, final RestChannel channel) {
|
||||
ValidateRequest validateRequest = new ValidateRequest(RestActions.splitIndices(request.param("index")));
|
||||
// we just send back a response, no need to fork a listener
|
||||
validateRequest.listenerThreaded(false);
|
||||
try {
|
||||
BroadcastOperationThreading operationThreading = BroadcastOperationThreading.fromString(request.param("operation_threading"), BroadcastOperationThreading.SINGLE_THREAD);
|
||||
if (operationThreading == BroadcastOperationThreading.NO_THREADS) {
|
||||
// since we don't spawn, don't allow no_threads, but change it to a single thread
|
||||
operationThreading = BroadcastOperationThreading.SINGLE_THREAD;
|
||||
}
|
||||
validateRequest.operationThreading(operationThreading);
|
||||
if (request.hasContent()) {
|
||||
validateRequest.query(request.contentByteArray(), request.contentByteArrayOffset(), request.contentLength(), true);
|
||||
} else {
|
||||
String source = request.param("source");
|
||||
if (source != null) {
|
||||
validateRequest.query(source);
|
||||
} else {
|
||||
byte[] querySource = RestActions.parseQuerySource(request);
|
||||
if (querySource != null) {
|
||||
validateRequest.query(querySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
validateRequest.types(splitTypes(request.param("type")));
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
|
||||
channel.sendResponse(new XContentRestResponse(request, BAD_REQUEST, builder.startObject().field("error", e.getMessage()).endObject()));
|
||||
} catch (IOException e1) {
|
||||
logger.error("Failed to send failure response", e1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
client.validate(validateRequest, new ActionListener<ValidateResponse>() {
|
||||
@Override
|
||||
public void onResponse(ValidateResponse response) {
|
||||
try {
|
||||
XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
|
||||
builder.startObject();
|
||||
builder.field("valid", response.valid());
|
||||
|
||||
buildBroadcastShardsHeader(builder, response);
|
||||
|
||||
builder.endObject();
|
||||
channel.sendResponse(new XContentRestResponse(request, OK, builder));
|
||||
} catch (Exception e) {
|
||||
onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
try {
|
||||
channel.sendResponse(new XContentThrowableRestResponse(request, e));
|
||||
} catch (IOException e1) {
|
||||
logger.error("Failed to send failure response", e1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Licensed to Elastic Search and Shay Banon under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Elastic Search 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.test.integration.validate;
|
||||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.test.integration.AbstractNodesTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SimpleValidateTests extends AbstractNodesTests {
|
||||
|
||||
private Client client;
|
||||
|
||||
@BeforeClass
|
||||
public void createNodes() throws Exception {
|
||||
startNode("node1");
|
||||
startNode("node2");
|
||||
client = getClient();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void closeNodes() {
|
||||
client.close();
|
||||
closeAllNodes();
|
||||
}
|
||||
|
||||
protected Client getClient() {
|
||||
return client("node1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleValidateQuery() throws Exception {
|
||||
client.admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client.admin().indices().prepareCreate("test").setSettings(ImmutableSettings.settingsBuilder().put("index.number_of_shards", 1)).execute().actionGet();
|
||||
client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
|
||||
client.admin().indices().preparePutMapping("test").setType("type1")
|
||||
.setSource(XContentFactory.jsonBuilder().startObject().startObject("type1").startObject("properties")
|
||||
.startObject("foo").field("type", "string").endObject()
|
||||
.startObject("bar").field("type", "integer").endObject()
|
||||
.endObject().endObject().endObject())
|
||||
.execute().actionGet();
|
||||
|
||||
client.admin().indices().prepareRefresh().execute().actionGet();
|
||||
|
||||
assertThat(client.prepareValidate("test").setQuery("foo".getBytes()).execute().actionGet().valid(), equalTo(false));
|
||||
assertThat(client.prepareValidate("test").setQuery(QueryBuilders.queryString("_id:1")).execute().actionGet().valid(), equalTo(true));
|
||||
assertThat(client.prepareValidate("test").setQuery(QueryBuilders.queryString("_i:d:1")).execute().actionGet().valid(), equalTo(false));
|
||||
|
||||
assertThat(client.prepareValidate("test").setQuery(QueryBuilders.queryString("foo:1")).execute().actionGet().valid(), equalTo(true));
|
||||
assertThat(client.prepareValidate("test").setQuery(QueryBuilders.queryString("bar:hey")).execute().actionGet().valid(), equalTo(false));
|
||||
|
||||
assertThat(client.prepareValidate("test").setQuery(QueryBuilders.queryString("nonexistent:hello")).execute().actionGet().valid(), equalTo(true));
|
||||
|
||||
assertThat(client.prepareValidate("test").setQuery(QueryBuilders.queryString("foo:1 AND")).execute().actionGet().valid(), equalTo(false));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue