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 INDEX = "indices/index/shard/index";
|
||||||
|
|
||||||
public static final String COUNT = "indices/count";
|
public static final String COUNT = "indices/count";
|
||||||
|
|
||||||
|
public static final String VALIDATE = "indices/validate";
|
||||||
|
|
||||||
public static final String DELETE = "indices/index/shard/delete";
|
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.SearchRequest;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
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.bulk.BulkRequestBuilder;
|
||||||
import org.elasticsearch.client.action.count.CountRequestBuilder;
|
import org.elasticsearch.client.action.count.CountRequestBuilder;
|
||||||
import org.elasticsearch.client.action.delete.DeleteRequestBuilder;
|
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.percolate.PercolateRequestBuilder;
|
||||||
import org.elasticsearch.client.action.search.SearchRequestBuilder;
|
import org.elasticsearch.client.action.search.SearchRequestBuilder;
|
||||||
import org.elasticsearch.client.action.search.SearchScrollRequestBuilder;
|
import org.elasticsearch.client.action.search.SearchScrollRequestBuilder;
|
||||||
|
import org.elasticsearch.client.action.validate.ValidateRequestBuilder;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -274,6 +277,29 @@ public interface Client {
|
||||||
*/
|
*/
|
||||||
CountRequestBuilder prepareCount(String... indices);
|
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.
|
* 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.PercolateResponse;
|
||||||
import org.elasticsearch.action.percolate.TransportPercolateAction;
|
import org.elasticsearch.action.percolate.TransportPercolateAction;
|
||||||
import org.elasticsearch.action.search.*;
|
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.AdminClient;
|
||||||
|
import org.elasticsearch.client.action.validate.ValidateRequestBuilder;
|
||||||
import org.elasticsearch.client.internal.InternalClient;
|
import org.elasticsearch.client.internal.InternalClient;
|
||||||
import org.elasticsearch.client.support.AbstractClient;
|
import org.elasticsearch.client.support.AbstractClient;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
|
@ -73,6 +77,8 @@ public class NodeClient extends AbstractClient implements InternalClient {
|
||||||
|
|
||||||
private final TransportCountAction countAction;
|
private final TransportCountAction countAction;
|
||||||
|
|
||||||
|
private final TransportValidateAction validateAction;
|
||||||
|
|
||||||
private final TransportSearchAction searchAction;
|
private final TransportSearchAction searchAction;
|
||||||
|
|
||||||
private final TransportSearchScrollAction searchScrollAction;
|
private final TransportSearchScrollAction searchScrollAction;
|
||||||
|
@ -85,7 +91,7 @@ public class NodeClient extends AbstractClient implements InternalClient {
|
||||||
public NodeClient(Settings settings, ThreadPool threadPool, NodeAdminClient admin,
|
public NodeClient(Settings settings, ThreadPool threadPool, NodeAdminClient admin,
|
||||||
TransportIndexAction indexAction, TransportDeleteAction deleteAction, TransportBulkAction bulkAction,
|
TransportIndexAction indexAction, TransportDeleteAction deleteAction, TransportBulkAction bulkAction,
|
||||||
TransportDeleteByQueryAction deleteByQueryAction, TransportGetAction getAction, TransportMultiGetAction multiGetAction, TransportCountAction countAction,
|
TransportDeleteByQueryAction deleteByQueryAction, TransportGetAction getAction, TransportMultiGetAction multiGetAction, TransportCountAction countAction,
|
||||||
TransportSearchAction searchAction, TransportSearchScrollAction searchScrollAction,
|
TransportSearchAction searchAction, TransportValidateAction validateAction, TransportSearchScrollAction searchScrollAction,
|
||||||
TransportMoreLikeThisAction moreLikeThisAction, TransportPercolateAction percolateAction) {
|
TransportMoreLikeThisAction moreLikeThisAction, TransportPercolateAction percolateAction) {
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
this.admin = admin;
|
this.admin = admin;
|
||||||
|
@ -96,6 +102,7 @@ public class NodeClient extends AbstractClient implements InternalClient {
|
||||||
this.getAction = getAction;
|
this.getAction = getAction;
|
||||||
this.multiGetAction = multiGetAction;
|
this.multiGetAction = multiGetAction;
|
||||||
this.countAction = countAction;
|
this.countAction = countAction;
|
||||||
|
this.validateAction = validateAction;
|
||||||
this.searchAction = searchAction;
|
this.searchAction = searchAction;
|
||||||
this.searchScrollAction = searchScrollAction;
|
this.searchScrollAction = searchScrollAction;
|
||||||
this.moreLikeThisAction = moreLikeThisAction;
|
this.moreLikeThisAction = moreLikeThisAction;
|
||||||
|
@ -187,6 +194,16 @@ public class NodeClient extends AbstractClient implements InternalClient {
|
||||||
countAction.execute(request, listener);
|
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
|
@Override
|
||||||
public ActionFuture<SearchResponse> search(SearchRequest request) {
|
public ActionFuture<SearchResponse> search(SearchRequest request) {
|
||||||
return searchAction.execute(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.percolate.PercolateRequestBuilder;
|
||||||
import org.elasticsearch.client.action.search.SearchRequestBuilder;
|
import org.elasticsearch.client.action.search.SearchRequestBuilder;
|
||||||
import org.elasticsearch.client.action.search.SearchScrollRequestBuilder;
|
import org.elasticsearch.client.action.search.SearchScrollRequestBuilder;
|
||||||
|
import org.elasticsearch.client.action.validate.ValidateRequestBuilder;
|
||||||
import org.elasticsearch.client.internal.InternalClient;
|
import org.elasticsearch.client.internal.InternalClient;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
|
|
||||||
|
@ -103,6 +104,11 @@ public abstract class AbstractClient implements InternalClient {
|
||||||
return new CountRequestBuilder(this).setIndices(indices);
|
return new CountRequestBuilder(this).setIndices(indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidateRequestBuilder prepareValidate(String... indices) {
|
||||||
|
return new ValidateRequestBuilder(this).setIndices(indices);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MoreLikeThisRequestBuilder prepareMoreLikeThis(String index, String type, String id) {
|
public MoreLikeThisRequestBuilder prepareMoreLikeThis(String index, String type, String id) {
|
||||||
return new MoreLikeThisRequestBuilder(this, index, type, 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.SearchRequest;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
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.AdminClient;
|
||||||
import org.elasticsearch.client.support.AbstractClient;
|
import org.elasticsearch.client.support.AbstractClient;
|
||||||
import org.elasticsearch.client.transport.action.ClientTransportActionModule;
|
import org.elasticsearch.client.transport.action.ClientTransportActionModule;
|
||||||
|
@ -319,6 +321,16 @@ public class TransportClient extends AbstractClient {
|
||||||
internalClient.count(request, listener);
|
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
|
@Override
|
||||||
public ActionFuture<SearchResponse> search(SearchRequest request) {
|
public ActionFuture<SearchResponse> search(SearchRequest request) {
|
||||||
return internalClient.search(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.SearchRequest;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
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.AdminClient;
|
||||||
import org.elasticsearch.client.internal.InternalClient;
|
import org.elasticsearch.client.internal.InternalClient;
|
||||||
import org.elasticsearch.client.support.AbstractClient;
|
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.percolate.ClientTransportPercolateAction;
|
||||||
import org.elasticsearch.client.transport.action.search.ClientTransportSearchAction;
|
import org.elasticsearch.client.transport.action.search.ClientTransportSearchAction;
|
||||||
import org.elasticsearch.client.transport.action.search.ClientTransportSearchScrollAction;
|
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.cluster.node.DiscoveryNode;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
@ -87,6 +90,8 @@ public class InternalTransportClient extends AbstractClient implements InternalC
|
||||||
|
|
||||||
private final ClientTransportCountAction countAction;
|
private final ClientTransportCountAction countAction;
|
||||||
|
|
||||||
|
private final ClientTransportValidateAction validateAction;
|
||||||
|
|
||||||
private final ClientTransportSearchAction searchAction;
|
private final ClientTransportSearchAction searchAction;
|
||||||
|
|
||||||
private final ClientTransportSearchScrollAction searchScrollAction;
|
private final ClientTransportSearchScrollAction searchScrollAction;
|
||||||
|
@ -99,7 +104,7 @@ public class InternalTransportClient extends AbstractClient implements InternalC
|
||||||
public InternalTransportClient(Settings settings, ThreadPool threadPool,
|
public InternalTransportClient(Settings settings, ThreadPool threadPool,
|
||||||
TransportClientNodesService nodesService, InternalTransportAdminClient adminClient,
|
TransportClientNodesService nodesService, InternalTransportAdminClient adminClient,
|
||||||
ClientTransportIndexAction indexAction, ClientTransportDeleteAction deleteAction, ClientTransportBulkAction bulkAction, ClientTransportGetAction getAction, ClientTransportMultiGetAction multiGetAction,
|
ClientTransportIndexAction indexAction, ClientTransportDeleteAction deleteAction, ClientTransportBulkAction bulkAction, ClientTransportGetAction getAction, ClientTransportMultiGetAction multiGetAction,
|
||||||
ClientTransportDeleteByQueryAction deleteByQueryAction, ClientTransportCountAction countAction,
|
ClientTransportDeleteByQueryAction deleteByQueryAction, ClientTransportCountAction countAction, ClientTransportValidateAction validateAction,
|
||||||
ClientTransportSearchAction searchAction, ClientTransportSearchScrollAction searchScrollAction,
|
ClientTransportSearchAction searchAction, ClientTransportSearchScrollAction searchScrollAction,
|
||||||
ClientTransportMoreLikeThisAction moreLikeThisAction, ClientTransportPercolateAction percolateAction) {
|
ClientTransportMoreLikeThisAction moreLikeThisAction, ClientTransportPercolateAction percolateAction) {
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
|
@ -113,6 +118,7 @@ public class InternalTransportClient extends AbstractClient implements InternalC
|
||||||
this.multiGetAction = multiGetAction;
|
this.multiGetAction = multiGetAction;
|
||||||
this.deleteByQueryAction = deleteByQueryAction;
|
this.deleteByQueryAction = deleteByQueryAction;
|
||||||
this.countAction = countAction;
|
this.countAction = countAction;
|
||||||
|
this.validateAction = validateAction;
|
||||||
this.searchAction = searchAction;
|
this.searchAction = searchAction;
|
||||||
this.searchScrollAction = searchScrollAction;
|
this.searchScrollAction = searchScrollAction;
|
||||||
this.moreLikeThisAction = moreLikeThisAction;
|
this.moreLikeThisAction = moreLikeThisAction;
|
||||||
|
@ -274,6 +280,26 @@ public class InternalTransportClient extends AbstractClient implements InternalC
|
||||||
}, listener);
|
}, 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
|
@Override
|
||||||
public ActionFuture<SearchResponse> search(final SearchRequest request) {
|
public ActionFuture<SearchResponse> search(final SearchRequest request) {
|
||||||
return nodesService.execute(new TransportClientNodesService.NodeCallback<ActionFuture<SearchResponse>>() {
|
return nodesService.execute(new TransportClientNodesService.NodeCallback<ActionFuture<SearchResponse>>() {
|
||||||
|
|
|
@ -105,6 +105,10 @@ public interface IndexShard extends IndexShardComponent {
|
||||||
|
|
||||||
Engine.Searcher searcher();
|
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)
|
* 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.MergeStats;
|
||||||
import org.elasticsearch.index.merge.scheduler.MergeSchedulerProvider;
|
import org.elasticsearch.index.merge.scheduler.MergeSchedulerProvider;
|
||||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||||
|
import org.elasticsearch.index.query.QueryParsingException;
|
||||||
import org.elasticsearch.index.refresh.RefreshStats;
|
import org.elasticsearch.index.refresh.RefreshStats;
|
||||||
import org.elasticsearch.index.search.stats.SearchStats;
|
import org.elasticsearch.index.search.stats.SearchStats;
|
||||||
import org.elasticsearch.index.search.stats.ShardSearchService;
|
import org.elasticsearch.index.search.stats.ShardSearchService;
|
||||||
|
@ -518,6 +519,28 @@ public class InternalIndexShard extends AbstractIndexShardComponent implements I
|
||||||
return engine.searcher();
|
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) {
|
public void close(String reason) {
|
||||||
synchronized (mutex) {
|
synchronized (mutex) {
|
||||||
indexSettingsService.removeListener(applyRefreshSettings);
|
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.percolate.RestPercolateAction;
|
||||||
import org.elasticsearch.rest.action.search.RestSearchAction;
|
import org.elasticsearch.rest.action.search.RestSearchAction;
|
||||||
import org.elasticsearch.rest.action.search.RestSearchScrollAction;
|
import org.elasticsearch.rest.action.search.RestSearchScrollAction;
|
||||||
|
import org.elasticsearch.rest.action.validate.RestValidateAction;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -149,6 +150,8 @@ public class RestActionModule extends AbstractModule {
|
||||||
|
|
||||||
bind(RestSearchAction.class).asEagerSingleton();
|
bind(RestSearchAction.class).asEagerSingleton();
|
||||||
bind(RestSearchScrollAction.class).asEagerSingleton();
|
bind(RestSearchScrollAction.class).asEagerSingleton();
|
||||||
|
|
||||||
|
bind(RestValidateAction.class).asEagerSingleton();
|
||||||
|
|
||||||
bind(RestMoreLikeThisAction.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