Move templates out of the Search API, into lang-mustache module

This commit moves template support out of the Search API to its own dedicated Search Template API in the lang-mustache module. It provides a new SearchTemplateAction that can be used to render templates before it gets delegated to the usual Search API. The current REST endpoint are identical, but the Render Search Template endpoint now uses the same Search Template API with a new "simulate" option. When this option is enabled, the Search Template API only renders template and returns immediatly, without executing the search.

Closes #17906
This commit is contained in:
Tanguy Leroux 2016-06-07 17:45:05 +02:00
parent 4be94cdc95
commit 04da1bda0d
61 changed files with 2337 additions and 809 deletions

View File

@ -66,8 +66,6 @@ import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.cluster.stats.TransportClusterStatsAction;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksAction;
import org.elasticsearch.action.admin.cluster.tasks.TransportPendingClusterTasksAction;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateAction;
import org.elasticsearch.action.admin.cluster.validate.template.TransportRenderSearchTemplateAction;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.TransportIndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.exists.AliasesExistAction;
@ -340,7 +338,6 @@ public class ActionModule extends AbstractModule {
registerAction(ExplainAction.INSTANCE, TransportExplainAction.class);
registerAction(ClearScrollAction.INSTANCE, TransportClearScrollAction.class);
registerAction(RecoveryAction.INSTANCE, TransportRecoveryAction.class);
registerAction(RenderSearchTemplateAction.INSTANCE, TransportRenderSearchTemplateAction.class);
//Indexed scripts
registerAction(PutStoredScriptAction.INSTANCE, TransportPutStoredScriptAction.class);

View File

@ -1,69 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.cluster.validate.template;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.script.Template;
import java.io.IOException;
public class RenderSearchTemplateRequest extends ActionRequest<RenderSearchTemplateRequest> {
private Template template;
public void template(Template template) {
this.template = template;
}
public Template template() {
return template;
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException exception = null;
if (template == null) {
exception = new ActionRequestValidationException();
exception.addValidationError("template must not be null");
}
return exception;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
boolean hasTemplate = template!= null;
out.writeBoolean(hasTemplate);
if (hasTemplate) {
template.writeTo(out);
}
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
if (in.readBoolean()) {
template = new Template(in);
}
}
}

View File

@ -1,75 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.cluster.validate.template;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import java.util.Collections;
public class TransportRenderSearchTemplateAction extends HandledTransportAction<RenderSearchTemplateRequest, RenderSearchTemplateResponse> {
private final ScriptService scriptService;
private final ClusterService clusterService;
@Inject
public TransportRenderSearchTemplateAction(ScriptService scriptService, Settings settings, ThreadPool threadPool,
TransportService transportService, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
ClusterService clusterService) {
super(settings, RenderSearchTemplateAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver, RenderSearchTemplateRequest::new);
this.scriptService = scriptService;
this.clusterService = clusterService;
}
@Override
protected void doExecute(final RenderSearchTemplateRequest request, final ActionListener<RenderSearchTemplateResponse> listener) {
threadPool.generic().execute(new AbstractRunnable() {
@Override
public void onFailure(Throwable t) {
listener.onFailure(t);
}
@Override
protected void doRun() throws Exception {
ExecutableScript executable = scriptService.executable(request.template(), ScriptContext.Standard.SEARCH,
Collections.emptyMap(), clusterService.state());
BytesReference processedTemplate = (BytesReference) executable.run();
RenderSearchTemplateResponse response = new RenderSearchTemplateResponse();
response.source(processedTemplate);
listener.onResponse(response);
}
});
}
}

View File

@ -29,7 +29,6 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
@ -71,8 +70,6 @@ public class SearchRequest extends ActionRequest<SearchRequest> implements Indic
private IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
private Template template;
public SearchRequest() {
}
@ -222,21 +219,6 @@ public class SearchRequest extends ActionRequest<SearchRequest> implements Indic
return source;
}
/**
* The stored template
*/
public void template(Template template) {
this.template = template;
}
/**
* The stored template
*/
public Template template() {
return template;
}
/**
* The tye of search to execute.
*/
@ -326,7 +308,6 @@ public class SearchRequest extends ActionRequest<SearchRequest> implements Indic
indicesOptions = IndicesOptions.readIndicesOptions(in);
requestCache = in.readOptionalBoolean();
template = in.readOptionalWriteable(Template::new);
}
@Override
@ -357,6 +338,5 @@ public class SearchRequest extends ActionRequest<SearchRequest> implements Indic
out.writeStringArray(types);
indicesOptions.writeIndicesOptions(out);
out.writeOptionalBoolean(requestCache);
out.writeOptionalWriteable(template);
}
}

View File

@ -26,7 +26,6 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
@ -496,14 +495,6 @@ public class SearchRequestBuilder extends ActionRequestBuilder<SearchRequest, Se
return this;
}
/**
* template stuff
*/
public SearchRequestBuilder setTemplate(Template template) {
request.template(template);
return this;
}
/**
* Sets if this request should use the request cache or not, assuming that it can (for
* example, if "now" is used, it will never be cached). By default (not set, or null,

View File

@ -99,9 +99,6 @@ import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptRespo
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksRequest;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksRequestBuilder;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksResponse;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateRequest;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateRequestBuilder;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateResponse;
import org.elasticsearch.action.ingest.DeletePipelineRequest;
import org.elasticsearch.action.ingest.DeletePipelineRequestBuilder;
import org.elasticsearch.action.ingest.GetPipelineRequest;
@ -536,28 +533,6 @@ public interface ClusterAdminClient extends ElasticsearchClient {
*/
SnapshotsStatusRequestBuilder prepareSnapshotStatus();
/**
* Return the rendered search request for a given search template.
*
* @param request The request
* @return The result future
*/
ActionFuture<RenderSearchTemplateResponse> renderSearchTemplate(RenderSearchTemplateRequest request);
/**
* Return the rendered search request for a given search template.
*
* @param request The request
* @param listener A listener to be notified of the result
*/
void renderSearchTemplate(RenderSearchTemplateRequest request, ActionListener<RenderSearchTemplateResponse> listener);
/**
* Return the rendered search request for a given search template.
*/
RenderSearchTemplateRequestBuilder prepareRenderSearchTemplate();
/**
* Stores an ingest pipeline
*/

View File

@ -129,10 +129,6 @@ import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksAction;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksRequest;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksRequestBuilder;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksResponse;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateAction;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateRequest;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateRequestBuilder;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateResponse;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
@ -1073,21 +1069,6 @@ public abstract class AbstractClient extends AbstractComponent implements Client
return new SnapshotsStatusRequestBuilder(this, SnapshotsStatusAction.INSTANCE);
}
@Override
public ActionFuture<RenderSearchTemplateResponse> renderSearchTemplate(final RenderSearchTemplateRequest request) {
return execute(RenderSearchTemplateAction.INSTANCE, request);
}
@Override
public void renderSearchTemplate(final RenderSearchTemplateRequest request, final ActionListener<RenderSearchTemplateResponse> listener) {
execute(RenderSearchTemplateAction.INSTANCE, request, listener);
}
@Override
public RenderSearchTemplateRequestBuilder prepareRenderSearchTemplate() {
return new RenderSearchTemplateRequestBuilder(this, RenderSearchTemplateAction.INSTANCE);
}
@Override
public void putPipeline(PutPipelineRequest request, ActionListener<WritePipelineResponse> listener) {
execute(PutPipelineAction.INSTANCE, request, listener);

View File

@ -66,11 +66,8 @@ import org.elasticsearch.rest.action.admin.cluster.snapshots.restore.RestRestore
import org.elasticsearch.rest.action.admin.cluster.snapshots.status.RestSnapshotsStatusAction;
import org.elasticsearch.rest.action.admin.cluster.state.RestClusterStateAction;
import org.elasticsearch.rest.action.admin.cluster.stats.RestClusterStatsAction;
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestDeleteSearchTemplateAction;
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestDeleteStoredScriptAction;
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestGetSearchTemplateAction;
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestGetStoredScriptAction;
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestPutSearchTemplateAction;
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestPutStoredScriptAction;
import org.elasticsearch.rest.action.admin.cluster.tasks.RestPendingClusterTasksAction;
import org.elasticsearch.rest.action.admin.indices.RestRolloverIndexAction;
@ -108,7 +105,6 @@ import org.elasticsearch.rest.action.admin.indices.template.head.RestHeadIndexTe
import org.elasticsearch.rest.action.admin.indices.template.put.RestPutIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.upgrade.RestUpgradeAction;
import org.elasticsearch.rest.action.admin.indices.validate.query.RestValidateQueryAction;
import org.elasticsearch.rest.action.admin.indices.validate.template.RestRenderSearchTemplateAction;
import org.elasticsearch.rest.action.bulk.RestBulkAction;
import org.elasticsearch.rest.action.cat.AbstractCatAction;
import org.elasticsearch.rest.action.cat.RestAliasAction;
@ -256,7 +252,6 @@ public class NetworkModule extends AbstractModule {
RestSearchScrollAction.class,
RestClearScrollAction.class,
RestMultiSearchAction.class,
RestRenderSearchTemplateAction.class,
RestValidateQueryAction.class,
@ -264,11 +259,6 @@ public class NetworkModule extends AbstractModule {
RestRecoveryAction.class,
// Templates API
RestGetSearchTemplateAction.class,
RestPutSearchTemplateAction.class,
RestDeleteSearchTemplateAction.class,
// Scripts API
RestGetStoredScriptAction.class,
RestPutStoredScriptAction.class,
@ -359,6 +349,10 @@ public class NetworkModule extends AbstractModule {
}
}
public boolean isTransportClient() {
return transportClient;
}
/** Adds a transport service implementation that can be selected by setting {@link #TRANSPORT_SERVICE_TYPE_KEY}. */
public void registerTransportService(String name, Class<? extends TransportService> clazz) {
transportServiceTypes.registerExtension(name, clazz);

View File

@ -426,6 +426,7 @@ public final class ObjectParser<Value, Context extends ParseFieldMatcherSupplier
OBJECT(START_OBJECT),
OBJECT_ARRAY(START_OBJECT, START_ARRAY),
OBJECT_OR_BOOLEAN(START_OBJECT, VALUE_BOOLEAN),
OBJECT_OR_STRING(START_OBJECT, VALUE_STRING),
VALUE(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING);
private final EnumSet<XContentParser.Token> tokens;

View File

@ -1048,10 +1048,6 @@ public class IndicesService extends AbstractLifecycleComponent<IndicesService>
* Can the shard request be cached at all?
*/
public boolean canCache(ShardSearchRequest request, SearchContext context) {
if (request.template() != null) {
return false;
}
// for now, only enable it for requests with no hits
if (context.size() != 0) {
return false;

View File

@ -43,21 +43,6 @@ public class RestPutStoredScriptAction extends BaseRestHandler {
if (registerDefaultHandlers) {
controller.registerHandler(POST, "/_scripts/{lang}/{id}", this);
controller.registerHandler(PUT, "/_scripts/{lang}/{id}", this);
controller.registerHandler(PUT, "/_scripts/{lang}/{id}/_create", new CreateHandler(settings, controller, client));
controller.registerHandler(POST, "/_scripts/{lang}/{id}/_create", new CreateHandler(settings, controller, client));
}
}
final class CreateHandler extends BaseRestHandler {
protected CreateHandler(Settings settings, RestController controller, Client client) {
super(settings, client);
}
@Override
public void handleRequest(RestRequest request, RestChannel channel, final Client client) {
request.params().put("op_type", "create");
RestPutStoredScriptAction.this.handleRequest(request, channel, client);
}
}
@ -71,5 +56,4 @@ public class RestPutStoredScriptAction extends BaseRestHandler {
putRequest.script(request.content());
client.admin().cluster().putStoredScript(putRequest, new AcknowledgedRestListener<>(channel));
}
}

View File

@ -1,109 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.admin.indices.validate.template;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateRequest;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.action.support.RestActions;
import org.elasticsearch.rest.action.support.RestBuilderListener;
import org.elasticsearch.script.Script.ScriptField;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import java.util.Map;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.rest.RestStatus.OK;
public class RestRenderSearchTemplateAction extends BaseRestHandler {
@Inject
public RestRenderSearchTemplateAction(Settings settings, RestController controller, Client client) {
super(settings, client);
controller.registerHandler(GET, "/_render/template", this);
controller.registerHandler(POST, "/_render/template", this);
controller.registerHandler(GET, "/_render/template/{id}", this);
controller.registerHandler(POST, "/_render/template/{id}", this);
}
@Override
protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
RenderSearchTemplateRequest renderSearchTemplateRequest;
BytesReference source = RestActions.getRestContent(request);
try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
String templateId = request.param("id");
final Template template;
if (templateId == null) {
template = Template.parse(parser, parseFieldMatcher);
} else {
Map<String, Object> params = null;
String currentFieldName = null;
XContentParser.Token token = parser.nextToken();
if (token != XContentParser.Token.START_OBJECT) {
throw new ElasticsearchParseException("failed to parse request. request body must be an object but found [{}] instead",
token);
}
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (parseFieldMatcher.match(currentFieldName, ScriptField.PARAMS)) {
if (token == XContentParser.Token.START_OBJECT) {
params = parser.map();
} else {
throw new ElasticsearchParseException(
"failed to parse request. field [{}] is expected to be an object, but found [{}] instead",
currentFieldName, token);
}
} else {
throw new ElasticsearchParseException("failed to parse request. unknown field [{}] of type [{}]", currentFieldName,
token);
}
}
template = new Template(templateId, ScriptType.STORED, Template.DEFAULT_LANG, null, params);
}
renderSearchTemplateRequest = new RenderSearchTemplateRequest();
renderSearchTemplateRequest.template(template);
}
client.admin().cluster().renderSearchTemplate(renderSearchTemplateRequest, new RestBuilderListener<RenderSearchTemplateResponse>(channel) {
@Override
public RestResponse buildResponse(RenderSearchTemplateResponse response, XContentBuilder builder) throws Exception {
builder.prettyPrint();
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
return new BytesRestResponse(OK, builder);
}});
}
}

View File

@ -19,19 +19,11 @@
package org.elasticsearch.rest.action.search;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.lenientNodeBooleanValue;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringArrayValue;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringValue;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import java.util.Map;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.MultiSearchRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
@ -41,7 +33,6 @@ import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.TemplateQueryBuilder;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestChannel;
@ -49,11 +40,20 @@ import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.support.RestActions;
import org.elasticsearch.rest.action.support.RestToXContentListener;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import java.io.IOException;
import java.util.Map;
import java.util.function.BiConsumer;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.lenientNodeBooleanValue;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringArrayValue;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringValue;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
/**
*/
public class RestMultiSearchAction extends BaseRestHandler {
@ -77,48 +77,55 @@ public class RestMultiSearchAction extends BaseRestHandler {
controller.registerHandler(GET, "/{index}/{type}/_msearch", this);
controller.registerHandler(POST, "/{index}/{type}/_msearch", this);
controller.registerHandler(GET, "/_msearch/template", this);
controller.registerHandler(POST, "/_msearch/template", this);
controller.registerHandler(GET, "/{index}/_msearch/template", this);
controller.registerHandler(POST, "/{index}/_msearch/template", this);
controller.registerHandler(GET, "/{index}/{type}/_msearch/template", this);
controller.registerHandler(POST, "/{index}/{type}/_msearch/template", this);
this.allowExplicitIndex = MULTI_ALLOW_EXPLICIT_INDEX.get(settings);
this.indicesQueriesRegistry = indicesQueriesRegistry;
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) throws Exception {
MultiSearchRequest multiSearchRequest = new MultiSearchRequest();
if (request.hasParam("max_concurrent_searches")) {
multiSearchRequest.maxConcurrentSearchRequests(request.paramAsInt("max_concurrent_searches", 0));
}
String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
String[] types = Strings.splitStringByCommaToArray(request.param("type"));
String path = request.path();
boolean isTemplateRequest = isTemplateRequest(path);
IndicesOptions indicesOptions = IndicesOptions.fromRequest(request, multiSearchRequest.indicesOptions());
parseRequest(multiSearchRequest, RestActions.getRestContent(request), isTemplateRequest, indices, types,
request.param("search_type"), request.param("routing"), indicesOptions, allowExplicitIndex, indicesQueriesRegistry,
parseFieldMatcher, aggParsers, suggesters);
MultiSearchRequest multiSearchRequest = parseRequest(request, allowExplicitIndex, indicesQueriesRegistry, parseFieldMatcher,
aggParsers, suggesters);
client.multiSearch(multiSearchRequest, new RestToXContentListener<>(channel));
}
private boolean isTemplateRequest(String path) {
return (path != null && path.endsWith("/template"));
/**
* Parses a {@link RestRequest} body and returns a {@link MultiSearchRequest}
*/
public static MultiSearchRequest parseRequest(RestRequest restRequest, boolean allowExplicitIndex,
IndicesQueriesRegistry queriesRegistry, ParseFieldMatcher parseFieldMatcher,
AggregatorParsers aggParsers, Suggesters suggesters) throws IOException {
MultiSearchRequest multiRequest = new MultiSearchRequest();
if (restRequest.hasParam("max_concurrent_searches")) {
multiRequest.maxConcurrentSearchRequests(restRequest.paramAsInt("max_concurrent_searches", 0));
}
parseMultiLineRequest(restRequest, multiRequest.indicesOptions(), allowExplicitIndex, (searchRequest, bytes) -> {
try (XContentParser requestParser = XContentFactory.xContent(bytes).createParser(bytes)) {
final QueryParseContext queryParseContext = new QueryParseContext(queriesRegistry, requestParser, parseFieldMatcher);
searchRequest.source(SearchSourceBuilder.fromXContent(queryParseContext, aggParsers, suggesters));
multiRequest.add(searchRequest);
} catch (IOException e) {
throw new ElasticsearchParseException("Exception when parsing search request", e);
}
});
return multiRequest;
}
public static MultiSearchRequest parseRequest(MultiSearchRequest msr, BytesReference data, boolean isTemplateRequest,
@Nullable String[] indices,
@Nullable String[] types,
@Nullable String searchType,
@Nullable String routing,
IndicesOptions indicesOptions,
boolean allowExplicitIndex, IndicesQueriesRegistry indicesQueriesRegistry,
ParseFieldMatcher parseFieldMatcher, AggregatorParsers aggParsers,
Suggesters suggesters) throws Exception {
/**
* Parses a multi-line {@link RestRequest} body, instanciating a {@link SearchRequest} for each line and applying the given consumer.
*/
public static void parseMultiLineRequest(RestRequest request, IndicesOptions indicesOptions, boolean allowExplicitIndex,
BiConsumer<SearchRequest, BytesReference> consumer) throws IOException {
String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
String[] types = Strings.splitStringByCommaToArray(request.param("type"));
String searchType = request.param("search_type");
String routing = request.param("routing");
final BytesReference data = RestActions.getRestContent(request);
XContent xContent = XContentFactory.xContent(data);
int from = 0;
int length = data.length();
@ -147,7 +154,9 @@ public class RestMultiSearchAction extends BaseRestHandler {
if (routing != null) {
searchRequest.routing(routing);
}
searchRequest.searchType(searchType);
if (searchType != null) {
searchRequest.searchType(searchType);
}
IndicesOptions defaultOptions = IndicesOptions.strictExpandOpenAndForbidClosed();
@ -187,26 +196,10 @@ public class RestMultiSearchAction extends BaseRestHandler {
if (nextMarker == -1) {
break;
}
final BytesReference slice = data.slice(from, nextMarker - from);
if (isTemplateRequest) {
try (XContentParser parser = XContentFactory.xContent(slice).createParser(slice)) {
final QueryParseContext queryParseContext = new QueryParseContext(indicesQueriesRegistry, parser, parseFieldMatcher);
Template template = TemplateQueryBuilder.parse(parser, queryParseContext.getParseFieldMatcher(), "params", "template");
searchRequest.template(template);
}
} else {
try (XContentParser requestParser = XContentFactory.xContent(slice).createParser(slice)) {
final QueryParseContext queryParseContext = new QueryParseContext(indicesQueriesRegistry, requestParser,
parseFieldMatcher);
searchRequest.source(SearchSourceBuilder.fromXContent(queryParseContext, aggParsers, suggesters));
}
}
consumer.accept(searchRequest, data.slice(from, nextMarker - from));
// move pointers
from = nextMarker + 1;
msr.add(searchRequest);
}
return msr;
}
private static int findNextMarker(byte marker, int from, BytesReference data, int length) {

View File

@ -33,7 +33,6 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.TemplateQueryBuilder;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestChannel;
@ -41,7 +40,6 @@ import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.support.RestActions;
import org.elasticsearch.rest.action.support.RestStatusToXContentListener;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
@ -82,18 +80,13 @@ public class RestSearchAction extends BaseRestHandler {
controller.registerHandler(POST, "/{index}/_search", this);
controller.registerHandler(GET, "/{index}/{type}/_search", this);
controller.registerHandler(POST, "/{index}/{type}/_search", this);
controller.registerHandler(GET, "/_search/template", this);
controller.registerHandler(POST, "/_search/template", this);
controller.registerHandler(GET, "/{index}/_search/template", this);
controller.registerHandler(POST, "/{index}/_search/template", this);
controller.registerHandler(GET, "/{index}/{type}/_search/template", this);
controller.registerHandler(POST, "/{index}/{type}/_search/template", this);
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) throws IOException {
SearchRequest searchRequest = new SearchRequest();
parseSearchRequest(searchRequest, queryRegistry, request, parseFieldMatcher, aggParsers, suggesters, null);
BytesReference restContent = RestActions.hasBodyContent(request) ? RestActions.getRestContent(request) : null;
parseSearchRequest(searchRequest, queryRegistry, request, parseFieldMatcher, aggParsers, suggesters, restContent);
client.search(searchRequest, new RestStatusToXContentListener<>(channel));
}
@ -114,23 +107,10 @@ public class RestSearchAction extends BaseRestHandler {
searchRequest.source(new SearchSourceBuilder());
}
searchRequest.indices(Strings.splitStringByCommaToArray(request.param("index")));
// get the content, and put it in the body
// add content/source as template if template flag is set
boolean isTemplateRequest = request.path().endsWith("/template");
if (restContent == null) {
if (RestActions.hasBodyContent(request)) {
restContent = RestActions.getRestContent(request);
}
}
if (restContent != null) {
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry, parser, parseFieldMatcher);
if (isTemplateRequest) {
Template template = TemplateQueryBuilder.parse(parser, context.getParseFieldMatcher(), "params", "template");
searchRequest.template(template);
} else {
searchRequest.source().parseXContent(context, aggParsers, suggesters);
}
searchRequest.source().parseXContent(context, aggParsers, suggesters);
}
}

View File

@ -39,7 +39,6 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.Lucene;
@ -58,20 +57,17 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.InnerHitBuilder;
import org.elasticsearch.index.shard.IndexEventListener;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.shard.SearchOperationListener;
import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.aggregations.AggregationInitializationException;
import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.aggregations.SearchContextAggregations;
import org.elasticsearch.search.aggregations.support.AggregationContext;
import org.elasticsearch.search.builder.SearchSourceBuilder;
@ -103,7 +99,6 @@ import org.elasticsearch.search.rescore.RescoreBuilder;
import org.elasticsearch.search.searchafter.SearchAfterBuilder;
import org.elasticsearch.search.sort.SortAndFormats;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.threadpool.ThreadPool;
import com.carrotsearch.hppc.ObjectFloatHashMap;
@ -153,16 +148,11 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> imp
private final Map<String, SearchParseElement> elementParsers;
private final ParseFieldMatcher parseFieldMatcher;
private final AggregatorParsers aggParsers;
private final Suggesters suggesters;
@Inject
public SearchService(Settings settings, ClusterSettings clusterSettings, ClusterService clusterService, IndicesService indicesService,
ThreadPool threadPool, ScriptService scriptService, BigArrays bigArrays,
FetchPhase fetchPhase, AggregatorParsers aggParsers, Suggesters suggesters) {
ThreadPool threadPool, ScriptService scriptService, BigArrays bigArrays, FetchPhase fetchPhase) {
super(settings);
this.aggParsers = aggParsers;
this.suggesters = suggesters;
this.parseFieldMatcher = new ParseFieldMatcher(settings);
this.threadPool = threadPool;
this.clusterService = clusterService;
@ -556,16 +546,6 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> imp
context.scrollContext(new ScrollContext());
context.scrollContext().scroll = request.scroll();
}
if (request.template() != null) {
ExecutableScript executable = this.scriptService.executable(request.template(), ScriptContext.Standard.SEARCH,
Collections.emptyMap(), context.getQueryShardContext().getClusterState());
BytesReference run = (BytesReference) executable.run();
try (XContentParser parser = XContentFactory.xContent(run).createParser(run)) {
QueryParseContext queryParseContext = new QueryParseContext(indicesService.getIndicesQueryRegistry(), parser,
parseFieldMatcher);
parseSource(context, SearchSourceBuilder.fromXContent(queryParseContext, aggParsers, suggesters));
}
}
parseSource(context, request.source());
// if the from and size are still not set, default them

View File

@ -29,7 +29,6 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
@ -66,7 +65,6 @@ public class ShardSearchLocalRequest implements ShardSearchRequest {
private String[] types = Strings.EMPTY_ARRAY;
private String[] filteringAliases;
private SearchSourceBuilder source;
private Template template;
private Boolean requestCache;
private long nowInMillis;
@ -79,7 +77,6 @@ public class ShardSearchLocalRequest implements ShardSearchRequest {
String[] filteringAliases, long nowInMillis) {
this(shardRouting.shardId(), numberOfShards, searchRequest.searchType(),
searchRequest.source(), searchRequest.types(), searchRequest.requestCache());
this.template = searchRequest.template();
this.scroll = searchRequest.scroll();
this.filteringAliases = filteringAliases;
this.nowInMillis = nowInMillis;
@ -145,10 +142,6 @@ public class ShardSearchLocalRequest implements ShardSearchRequest {
public long nowInMillis() {
return nowInMillis;
}
@Override
public Template template() {
return template;
}
@Override
public Boolean requestCache() {
@ -183,7 +176,6 @@ public class ShardSearchLocalRequest implements ShardSearchRequest {
types = in.readStringArray();
filteringAliases = in.readStringArray();
nowInMillis = in.readVLong();
template = in.readOptionalWriteable(Template::new);
requestCache = in.readOptionalBoolean();
}
@ -211,8 +203,6 @@ public class ShardSearchLocalRequest implements ShardSearchRequest {
if (!asKey) {
out.writeVLong(nowInMillis);
}
out.writeOptionalWriteable(template);
out.writeOptionalBoolean(requestCache);
}

View File

@ -23,7 +23,6 @@ import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
@ -52,8 +51,6 @@ public interface ShardSearchRequest {
long nowInMillis();
Template template();
Boolean requestCache();
Scroll scroll();

View File

@ -30,7 +30,6 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.transport.TransportRequest;
@ -113,10 +112,6 @@ public class ShardSearchTransportRequest extends TransportRequest implements Sha
public long nowInMillis() {
return shardSearchLocalRequest.nowInMillis();
}
@Override
public Template template() {
return shardSearchLocalRequest.template();
}
@Override
public Boolean requestCache() {

View File

@ -21,6 +21,7 @@ package org.elasticsearch.action.search;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -28,21 +29,21 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryParser;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.search.RestMultiSearchAction;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.StreamsUtils;
import org.elasticsearch.test.rest.FakeRestRequest;
import java.io.IOException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
public class MultiSearchRequestTests extends ESTestCase {
public void testSimpleAdd() throws Exception {
byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch1.json");
MultiSearchRequest request = RestMultiSearchAction.parseRequest(new MultiSearchRequest(), new BytesArray(data), false, null, null,
null, null, IndicesOptions.strictExpandOpenAndForbidClosed(), true, registry(), ParseFieldMatcher.EMPTY, null, null);
MultiSearchRequest request = parseMultiSearchRequest("/org/elasticsearch/action/search/simple-msearch1.json");
assertThat(request.requests().size(), equalTo(8));
assertThat(request.requests().get(0).indices()[0], equalTo("test"));
assertThat(request.requests().get(0).indicesOptions(), equalTo(IndicesOptions.fromOptions(true, true, true, true, IndicesOptions.strictExpandOpenAndForbidClosed())));
@ -56,37 +57,33 @@ public class MultiSearchRequestTests extends ESTestCase {
assertThat(request.requests().get(3).indicesOptions(), equalTo(IndicesOptions.fromOptions(true, true, true, true, IndicesOptions.strictExpandOpenAndForbidClosed())));
assertThat(request.requests().get(4).indices()[0], equalTo("test"));
assertThat(request.requests().get(4).indicesOptions(), equalTo(IndicesOptions.fromOptions(true, false, false, true, IndicesOptions.strictExpandOpenAndForbidClosed())));
assertThat(request.requests().get(5).indices(), nullValue());
assertThat(request.requests().get(5).indices(), is(Strings.EMPTY_ARRAY));
assertThat(request.requests().get(5).types().length, equalTo(0));
assertThat(request.requests().get(6).indices(), nullValue());
assertThat(request.requests().get(6).indices(), is(Strings.EMPTY_ARRAY));
assertThat(request.requests().get(6).types().length, equalTo(0));
assertThat(request.requests().get(6).searchType(), equalTo(SearchType.DFS_QUERY_THEN_FETCH));
assertThat(request.requests().get(7).indices(), nullValue());
assertThat(request.requests().get(7).indices(), is(Strings.EMPTY_ARRAY));
assertThat(request.requests().get(7).types().length, equalTo(0));
}
public void testSimpleAdd2() throws Exception {
byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch2.json");
MultiSearchRequest request = RestMultiSearchAction.parseRequest(new MultiSearchRequest(), new BytesArray(data), false, null, null,
null, null, IndicesOptions.strictExpandOpenAndForbidClosed(), true, registry(), ParseFieldMatcher.EMPTY, null, null);
MultiSearchRequest request = parseMultiSearchRequest("/org/elasticsearch/action/search/simple-msearch2.json");
assertThat(request.requests().size(), equalTo(5));
assertThat(request.requests().get(0).indices()[0], equalTo("test"));
assertThat(request.requests().get(0).types().length, equalTo(0));
assertThat(request.requests().get(1).indices()[0], equalTo("test"));
assertThat(request.requests().get(1).types()[0], equalTo("type1"));
assertThat(request.requests().get(2).indices(), nullValue());
assertThat(request.requests().get(2).indices(), is(Strings.EMPTY_ARRAY));
assertThat(request.requests().get(2).types().length, equalTo(0));
assertThat(request.requests().get(3).indices(), nullValue());
assertThat(request.requests().get(3).indices(), is(Strings.EMPTY_ARRAY));
assertThat(request.requests().get(3).types().length, equalTo(0));
assertThat(request.requests().get(3).searchType(), equalTo(SearchType.DFS_QUERY_THEN_FETCH));
assertThat(request.requests().get(4).indices(), nullValue());
assertThat(request.requests().get(4).indices(), is(Strings.EMPTY_ARRAY));
assertThat(request.requests().get(4).types().length, equalTo(0));
}
public void testSimpleAdd3() throws Exception {
byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch3.json");
MultiSearchRequest request = RestMultiSearchAction.parseRequest(new MultiSearchRequest(), new BytesArray(data), false, null, null,
null, null, IndicesOptions.strictExpandOpenAndForbidClosed(), true, registry(), ParseFieldMatcher.EMPTY, null, null);
MultiSearchRequest request = parseMultiSearchRequest("/org/elasticsearch/action/search/simple-msearch3.json");
assertThat(request.requests().size(), equalTo(4));
assertThat(request.requests().get(0).indices()[0], equalTo("test0"));
assertThat(request.requests().get(0).indices()[1], equalTo("test1"));
@ -97,15 +94,13 @@ public class MultiSearchRequestTests extends ESTestCase {
assertThat(request.requests().get(2).indices()[1], equalTo("test1"));
assertThat(request.requests().get(2).types()[0], equalTo("type2"));
assertThat(request.requests().get(2).types()[1], equalTo("type1"));
assertThat(request.requests().get(3).indices(), nullValue());
assertThat(request.requests().get(3).indices(), is(Strings.EMPTY_ARRAY));
assertThat(request.requests().get(3).types().length, equalTo(0));
assertThat(request.requests().get(3).searchType(), equalTo(SearchType.DFS_QUERY_THEN_FETCH));
}
public void testSimpleAdd4() throws Exception {
byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch4.json");
MultiSearchRequest request = RestMultiSearchAction.parseRequest(new MultiSearchRequest(), new BytesArray(data), false, null, null,
null, null, IndicesOptions.strictExpandOpenAndForbidClosed(), true, registry(), ParseFieldMatcher.EMPTY, null, null);
MultiSearchRequest request = parseMultiSearchRequest("/org/elasticsearch/action/search/simple-msearch4.json");
assertThat(request.requests().size(), equalTo(3));
assertThat(request.requests().get(0).indices()[0], equalTo("test0"));
assertThat(request.requests().get(0).indices()[1], equalTo("test1"));
@ -123,39 +118,6 @@ public class MultiSearchRequestTests extends ESTestCase {
assertThat(request.requests().get(2).routing(), equalTo("123"));
}
public void testSimpleAdd5() throws Exception {
byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch5.json");
MultiSearchRequest request = RestMultiSearchAction.parseRequest(new MultiSearchRequest(), new BytesArray(data), true, null, null,
null, null, IndicesOptions.strictExpandOpenAndForbidClosed(), true, registry(), ParseFieldMatcher.EMPTY, null, null);
assertThat(request.requests().size(), equalTo(3));
assertThat(request.requests().get(0).indices()[0], equalTo("test0"));
assertThat(request.requests().get(0).indices()[1], equalTo("test1"));
assertThat(request.requests().get(0).requestCache(), equalTo(true));
assertThat(request.requests().get(0).preference(), nullValue());
assertThat(request.requests().get(1).indices()[0], equalTo("test2"));
assertThat(request.requests().get(1).indices()[1], equalTo("test3"));
assertThat(request.requests().get(1).types()[0], equalTo("type1"));
assertThat(request.requests().get(1).requestCache(), nullValue());
assertThat(request.requests().get(1).preference(), equalTo("_local"));
assertThat(request.requests().get(2).indices()[0], equalTo("test4"));
assertThat(request.requests().get(2).indices()[1], equalTo("test1"));
assertThat(request.requests().get(2).types()[0], equalTo("type2"));
assertThat(request.requests().get(2).types()[1], equalTo("type1"));
assertThat(request.requests().get(2).routing(), equalTo("123"));
assertNotNull(request.requests().get(0).template());
assertNotNull(request.requests().get(1).template());
assertNotNull(request.requests().get(2).template());
assertEquals(ScriptService.ScriptType.INLINE, request.requests().get(0).template().getType());
assertEquals(ScriptService.ScriptType.INLINE, request.requests().get(1).template().getType());
assertEquals(ScriptService.ScriptType.INLINE, request.requests().get(2).template().getType());
assertEquals("{\"query\":{\"match_{{template}}\":{}}}", request.requests().get(0).template().getScript());
assertEquals("{\"query\":{\"match_{{template}}\":{}}}", request.requests().get(1).template().getScript());
assertEquals("{\"query\":{\"match_{{template}}\":{}}}", request.requests().get(2).template().getScript());
assertEquals(1, request.requests().get(0).template().getParams().size());
assertEquals(1, request.requests().get(1).template().getParams().size());
assertEquals(1, request.requests().get(2).template().getParams().size());
}
public void testResponseErrorToXContent() throws IOException {
MultiSearchResponse response = new MultiSearchResponse(new MultiSearchResponse.Item[]{new MultiSearchResponse.Item(null, new IllegalStateException("foobar")), new MultiSearchResponse.Item(null, new IllegalStateException("baaaaaazzzz"))});
XContentBuilder builder = XContentFactory.jsonBuilder();
@ -171,6 +133,12 @@ public class MultiSearchRequestTests extends ESTestCase {
request.maxConcurrentSearchRequests(randomIntBetween(Integer.MIN_VALUE, 0)));
}
private MultiSearchRequest parseMultiSearchRequest(String sample) throws IOException {
byte[] data = StreamsUtils.copyToBytesFromClasspath(sample);
RestRequest restRequest = new FakeRestRequest.Builder().withContent(new BytesArray(data)).build();
return RestMultiSearchAction.parseRequest(restRequest, true, registry(), ParseFieldMatcher.EMPTY, null, null);
}
private IndicesQueriesRegistry registry() {
IndicesQueriesRegistry registry = new IndicesQueriesRegistry();
QueryParser<MatchAllQueryBuilder> parser = MatchAllQueryBuilder::fromXContent;

View File

@ -115,11 +115,13 @@ public class NetworkModuleTests extends ModuleTestCase {
NetworkModule module = new NetworkModule(new NetworkService(settings), settings, false, new NamedWriteableRegistry());
module.registerTransportService("custom", FakeTransportService.class);
assertBinding(module, TransportService.class, FakeTransportService.class);
assertFalse(module.isTransportClient());
// check it works with transport only as well
module = new NetworkModule(new NetworkService(settings), settings, true, new NamedWriteableRegistry());
module.registerTransportService("custom", FakeTransportService.class);
assertBinding(module, TransportService.class, FakeTransportService.class);
assertTrue(module.isTransportClient());
}
public void testRegisterTransport() {
@ -127,11 +129,13 @@ public class NetworkModuleTests extends ModuleTestCase {
NetworkModule module = new NetworkModule(new NetworkService(settings), settings, false, new NamedWriteableRegistry());
module.registerTransport("custom", FakeTransport.class);
assertBinding(module, Transport.class, FakeTransport.class);
assertFalse(module.isTransportClient());
// check it works with transport only as well
module = new NetworkModule(new NetworkService(settings), settings, true, new NamedWriteableRegistry());
module.registerTransport("custom", FakeTransport.class);
assertBinding(module, Transport.class, FakeTransport.class);
assertTrue(module.isTransportClient());
}
public void testRegisterHttpTransport() {
@ -139,9 +143,11 @@ public class NetworkModuleTests extends ModuleTestCase {
NetworkModule module = new NetworkModule(new NetworkService(settings), settings, false, new NamedWriteableRegistry());
module.registerHttpTransport("custom", FakeHttpTransport.class);
assertBinding(module, HttpServerTransport.class, FakeHttpTransport.class);
assertFalse(module.isTransportClient());
// check registration not allowed for transport only
module = new NetworkModule(new NetworkService(settings), settings, true, new NamedWriteableRegistry());
assertTrue(module.isTransportClient());
try {
module.registerHttpTransport("custom", FakeHttpTransport.class);
fail();
@ -154,6 +160,7 @@ public class NetworkModuleTests extends ModuleTestCase {
settings = Settings.builder().put(NetworkModule.HTTP_ENABLED.getKey(), false).build();
module = new NetworkModule(new NetworkService(settings), settings, false, new NamedWriteableRegistry());
assertNotBound(module, HttpServerTransport.class);
assertFalse(module.isTransportClient());
}
public void testRegisterRestHandler() {
@ -186,6 +193,7 @@ public class NetworkModuleTests extends ModuleTestCase {
NamedWriteableRegistry registry = new NamedWriteableRegistry();
Settings settings = Settings.EMPTY;
NetworkModule module = new NetworkModule(new NetworkService(settings), settings, false, registry);
assertFalse(module.isTransportClient());
// Builtin reader comes back
assertNotNull(registry.getReader(Task.Status.class, ReplicationTask.Status.NAME));

View File

@ -29,7 +29,6 @@ import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.internal.SearchContext;
@ -93,11 +92,6 @@ public class SearchSlowLogTests extends ESSingleNodeTestCase {
return 0;
}
@Override
public Template template() {
return null;
}
@Override
public Boolean requestCache() {
return null;

View File

@ -18,7 +18,6 @@
*/
package org.elasticsearch.script;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateResponse;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.Plugin;
@ -55,11 +54,6 @@ public class StoredScriptsIT extends ESIntegTestCase {
assertNotNull(script);
assertEquals("1", script);
RenderSearchTemplateResponse response = client().admin().cluster().prepareRenderSearchTemplate()
.template(new Template("/" + LANG + "/foobar", ScriptService.ScriptType.STORED, LANG, null, null))
.get();
assertEquals("1", response.source().toUtf8());
assertAcked(client().admin().cluster().prepareDeleteStoredScript()
.setId("foobar")
.setScriptLang(LANG));

View File

@ -1,6 +0,0 @@
{"index":["test0", "test1"], "request_cache": true}
{"template": {"query" : {"match_{{template}}" :{}}}, "params": {"template": "all" } } }
{"index" : "test2,test3", "type" : "type1", "preference": "_local"}
{"template": {"query" : {"match_{{template}}" :{}}}, "params": {"template": "all" } } }
{"index" : ["test4", "test1"], "type" : [ "type2", "type1" ], "routing": "123"}
{"template": {"query" : {"match_{{template}}" :{}}}, "params": {"template": "all" } } }

View File

@ -265,13 +265,14 @@ The `setTemplateSource(String)` and `setTemplateSource(BytesReference)` methods
==== SearchRequest
All `template` methods have been removed in favor of a single `template(Template)` method.
All `source` methods have been removed in favor of a single `source(SearchSourceBuilder)` method. This means that all search requests can now be validated
at call time which results in much clearer errors.
All `extraSource` methods have been removed.
All `template` methods have been removed in favor of a new Search Template API. A new `SearchTemplateRequest` now accepts a template and
a `SearchRequest` and must be executed using the new `SearchTemplateAction` action.
==== SearchResponse
Sort values for `string` fields are now return as `java.lang.String` objects rather than `org.elasticsearch.common.text.Text`.
@ -310,3 +311,11 @@ Removed the `getMemoryAvailable` method from `OsStats`, which could be previousl
`setRefresh(boolean)` has been removed in favor of `setRefreshPolicy(RefreshPolicy)` because there
are now three options (NONE, IMMEDIATE, and WAIT_FOR). `setRefresh(IMMEDIATE)` has the same behavior
as `setRefresh(true)` used to have. See `setRefreshPolicy`'s javadoc for more.
=== Render Search Template Java API has been removed
The Render Search Template Java API including `RenderSearchTemplateAction`, `RenderSearchTemplateRequest` and
`RenderSearchTemplateResponse` has been removed in favor of a new `simulate` option in the Search Template Java API.
This Search Template API is now included in the `lang-mustache` module and the `simulate` flag must be set on the
`SearchTemplateRequest` object.

View File

@ -62,3 +62,15 @@ renamed `filter`/`token_filter`/`char_filter`.
The `DELETE /_query` endpoint provided by the Delete-By-Query plugin has been
removed and replaced by the <<docs-delete-by-query,Delete By Query API>>.
==== Create stored script endpoint removed
The `PUT /_scripts/{lang}/{id}/_create` endpoint that previously allowed to create
indexed scripts has been removed. Indexed scripts have been replaced
by <<modules-scripting-stored-scripts,stored scripts>>.
==== Create stored template endpoint removed
The `PUT /_search/template/{id}/_create` endpoint that previously allowed to create
indexed template has been removed. Indexed templates have been replaced
by <<pre-registered-templates, Pre-registered templates>>.

View File

@ -3,7 +3,7 @@
==== Indexed scripts and templates
Indexed scrips and templates have been replaced by <<modules-scripting-stored-scripts,stored scripts>>
Indexed scripts and templates have been replaced by <<modules-scripting-stored-scripts,stored scripts>>
which stores the scripts and templates in the cluster state instead of a dedicate `.scripts` index.
For the size of stored scripts there is a soft limit of 65535 bytes. If scripts exceed that size then

View File

@ -369,3 +369,52 @@ GET /_render/template/<template_name>
}
}
------------------------------------------
[[multi-search-template]]
== Multi Search Template
The multi search template API allows to execute several search template
requests within the same API using the `_msearch/template` endpoint.
The format of the request is similar to the <<search-multi-search, Multi
Search API>> format:
[source,js]
--------------------------------------------------
header\n
body\n
header\n
body\n
--------------------------------------------------
The header part supports the same `index`, `types`, `search_type`,
`preference`, and `routing` options as the usual Multi Search API.
The body includes a search template body request and supports inline,
stored and file templates. Here is an example:
[source,js]
--------------------------------------------------
$ cat requests
{"index": "test"}
{"inline": {"query": {"match": {"user" : "{{username}}" }}}, "params": {"username": "john"}} <1>
{"index": "_all", "types": "accounts"}
{"inline": {"query": {"{{query_type}}": {"name": "{{name}}" }}}, "params": {"query_type": "match_phrase_prefix", "name": "Smith"}}
{"index": "_all"}
{"id": "template_1", "params": {"query_string": "search for these words" }} <2>
{"types": "users"}
{"file": "template_2", "params": {"field_name": "fullname", "field_value": "john smith" }} <3>
$ curl -XGET localhost:9200/_msearch/template --data-binary "@requests"; echo
--------------------------------------------------
<1> Inline search template request
<2> Search template request based on a stored template
<3> Search template request based on a file template
The response returns a `responses` array, which includes the search template
response for each search template request matching its order in the original
multi search template request. If there was a complete failure for that specific
search template request, an object with `error` message will be returned in place
of the actual search response.

View File

@ -30,5 +30,9 @@ integTest {
cluster {
setting 'script.inline', 'true'
setting 'script.stored', 'true'
File template = new File('src/test/resources/org/elasticsearch/messy/tests/config/scripts/template_1.mustache')
extraConfigFile 'scripts/template_1.mustache', template
}
}

View File

@ -17,28 +17,28 @@
* under the License.
*/
package org.elasticsearch.action.admin.cluster.validate.template;
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.Action;
import org.elasticsearch.client.ElasticsearchClient;
public class RenderSearchTemplateAction extends Action<RenderSearchTemplateRequest, RenderSearchTemplateResponse, RenderSearchTemplateRequestBuilder> {
public class MultiSearchTemplateAction
extends Action<MultiSearchTemplateRequest, MultiSearchTemplateResponse, MultiSearchTemplateRequestBuilder> {
public static final RenderSearchTemplateAction INSTANCE = new RenderSearchTemplateAction();
public static final String NAME = "cluster:admin/render/template/search";
public static final MultiSearchTemplateAction INSTANCE = new MultiSearchTemplateAction();
public static final String NAME = "indices:data/read/msearch/template";
public RenderSearchTemplateAction() {
private MultiSearchTemplateAction() {
super(NAME);
}
@Override
public RenderSearchTemplateRequestBuilder newRequestBuilder(ElasticsearchClient client) {
return new RenderSearchTemplateRequestBuilder(client, this);
public MultiSearchTemplateResponse newResponse() {
return new MultiSearchTemplateResponse();
}
@Override
public RenderSearchTemplateResponse newResponse() {
return new RenderSearchTemplateResponse();
public MultiSearchTemplateRequestBuilder newRequestBuilder(ElasticsearchClient client) {
return new MultiSearchTemplateRequestBuilder(client, this);
}
}

View File

@ -0,0 +1,107 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.CompositeIndicesRequest;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.elasticsearch.action.ValidateActions.addValidationError;
public class MultiSearchTemplateRequest extends ActionRequest<MultiSearchTemplateRequest> implements CompositeIndicesRequest {
private List<SearchTemplateRequest> requests = new ArrayList<>();
private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpenAndForbidClosed();
/**
* Add a search template request to execute. Note, the order is important, the search response will be returned in the
* same order as the search requests.
*/
public MultiSearchTemplateRequest add(SearchTemplateRequestBuilder request) {
requests.add(request.request());
return this;
}
/**
* Add a search template request to execute. Note, the order is important, the search response will be returned in the
* same order as the search requests.
*/
public MultiSearchTemplateRequest add(SearchTemplateRequest request) {
requests.add(request);
return this;
}
public List<SearchTemplateRequest> requests() {
return this.requests;
}
@Override
public List<? extends IndicesRequest> subRequests() {
return requests;
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (requests.isEmpty()) {
validationException = addValidationError("no requests added", validationException);
}
for (SearchTemplateRequest request : requests) {
ActionRequestValidationException ex = request.validate();
if (ex != null) {
if (validationException == null) {
validationException = new ActionRequestValidationException();
}
validationException.addValidationErrors(ex.validationErrors());
}
}
return validationException;
}
public IndicesOptions indicesOptions() {
return indicesOptions;
}
public MultiSearchTemplateRequest indicesOptions(IndicesOptions indicesOptions) {
this.indicesOptions = indicesOptions;
return this;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
requests = in.readStreamableList(SearchTemplateRequest::new);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeStreamableList(requests);
}
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.ElasticsearchClient;
public class MultiSearchTemplateRequestBuilder
extends ActionRequestBuilder<MultiSearchTemplateRequest, MultiSearchTemplateResponse, MultiSearchTemplateRequestBuilder> {
protected MultiSearchTemplateRequestBuilder(ElasticsearchClient client, MultiSearchTemplateAction action) {
super(client, action, new MultiSearchTemplateRequest());
}
public MultiSearchTemplateRequestBuilder(ElasticsearchClient client) {
this(client, MultiSearchTemplateAction.INSTANCE);
}
public MultiSearchTemplateRequestBuilder add(SearchTemplateRequest request) {
if (request.getRequest().indicesOptions() == IndicesOptions.strictExpandOpenAndForbidClosed()
&& request().indicesOptions() != IndicesOptions.strictExpandOpenAndForbidClosed()) {
request.getRequest().indicesOptions(request().indicesOptions());
}
super.request.add(request);
return this;
}
public MultiSearchTemplateRequestBuilder add(SearchTemplateRequestBuilder request) {
if (request.request().getRequest().indicesOptions() == IndicesOptions.strictExpandOpenAndForbidClosed()
&& request().indicesOptions() != IndicesOptions.strictExpandOpenAndForbidClosed()) {
request.request().getRequest().indicesOptions(request().indicesOptions());
}
super.request.add(request);
return this;
}
public MultiSearchTemplateRequestBuilder setIndicesOptions(IndicesOptions indicesOptions) {
request().indicesOptions(indicesOptions);
return this;
}
}

View File

@ -0,0 +1,181 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
public class MultiSearchTemplateResponse extends ActionResponse implements Iterable<MultiSearchTemplateResponse.Item>, ToXContent {
/**
* A search template response item, holding the actual search template response, or an error message if it failed.
*/
public static class Item implements Streamable {
private SearchTemplateResponse response;
private Throwable throwable;
Item() {
}
public Item(SearchTemplateResponse response, Throwable throwable) {
this.response = response;
this.throwable = throwable;
}
/**
* Is it a failed search?
*/
public boolean isFailure() {
return throwable != null;
}
/**
* The actual failure message, null if its not a failure.
*/
@Nullable
public String getFailureMessage() {
return throwable == null ? null : throwable.getMessage();
}
/**
* The actual search response, null if its a failure.
*/
@Nullable
public SearchTemplateResponse getResponse() {
return this.response;
}
public static Item readItem(StreamInput in) throws IOException {
Item item = new Item();
item.readFrom(in);
return item;
}
@Override
public void readFrom(StreamInput in) throws IOException {
if (in.readBoolean()) {
this.response = new SearchTemplateResponse();
response.readFrom(in);
} else {
throwable = in.readThrowable();
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
if (response != null) {
out.writeBoolean(true);
response.writeTo(out);
} else {
out.writeBoolean(false);
out.writeThrowable(throwable);
}
}
public Throwable getFailure() {
return throwable;
}
}
private Item[] items;
MultiSearchTemplateResponse() {
}
public MultiSearchTemplateResponse(Item[] items) {
this.items = items;
}
@Override
public Iterator<Item> iterator() {
return Arrays.stream(items).iterator();
}
/**
* The list of responses, the order is the same as the one provided in the request.
*/
public Item[] getResponses() {
return this.items;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
items = new Item[in.readVInt()];
for (int i = 0; i < items.length; i++) {
items[i] = Item.readItem(in);
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeVInt(items.length);
for (Item item : items) {
item.writeTo(out);
}
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startArray(Fields.RESPONSES);
for (Item item : items) {
builder.startObject();
if (item.isFailure()) {
ElasticsearchException.renderThrowable(builder, params, item.getFailure());
} else {
item.getResponse().toXContent(builder, params);
}
builder.endObject();
}
builder.endArray();
return builder;
}
static final class Fields {
static final String RESPONSES = "responses";
static final String ERROR = "error";
static final String ROOT_CAUSE = "root_cause";
}
@Override
public String toString() {
try {
XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint();
builder.startObject();
toXContent(builder, EMPTY_PARAMS);
builder.endObject();
return builder.string();
} catch (IOException e) {
return "{ \"error\" : \"" + e.getMessage() + "\"}";
}
}
}

View File

@ -17,26 +17,28 @@
* under the License.
*/
package org.elasticsearch.action.admin.cluster.validate.template;
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.script.Template;
public class RenderSearchTemplateRequestBuilder extends ActionRequestBuilder<RenderSearchTemplateRequest, RenderSearchTemplateResponse, RenderSearchTemplateRequestBuilder> {
public class SearchTemplateAction extends Action<SearchTemplateRequest, SearchTemplateResponse, SearchTemplateRequestBuilder> {
public RenderSearchTemplateRequestBuilder(ElasticsearchClient client,
RenderSearchTemplateAction action) {
super(client, action, new RenderSearchTemplateRequest());
}
public RenderSearchTemplateRequestBuilder template(Template template) {
request.template(template);
return this;
}
public Template template() {
return request.template();
public static final SearchTemplateAction INSTANCE = new SearchTemplateAction();
public static final String NAME = "indices:data/read/search/template";
private SearchTemplateAction() {
super(NAME);
}
@Override
public SearchTemplateRequestBuilder newRequestBuilder(ElasticsearchClient client) {
return new SearchTemplateRequestBuilder(client, this);
}
@Override
public SearchTemplateResponse newResponse() {
return new SearchTemplateResponse();
}
}

View File

@ -0,0 +1,156 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.script.ScriptService;
import java.io.IOException;
import java.util.Map;
import static org.elasticsearch.action.ValidateActions.addValidationError;
/**
* A request to execute a search based on a search template.
*/
public class SearchTemplateRequest extends ActionRequest<SearchTemplateRequest> implements IndicesRequest {
private SearchRequest request;
private boolean simulate = false;
private ScriptService.ScriptType scriptType;
private String script;
private Map<String, Object> scriptParams;
public SearchTemplateRequest() {
}
public SearchTemplateRequest(SearchRequest searchRequest) {
this.request = searchRequest;
}
public void setRequest(SearchRequest request) {
this.request = request;
}
public SearchRequest getRequest() {
return request;
}
public boolean isSimulate() {
return simulate;
}
public void setSimulate(boolean simulate) {
this.simulate = simulate;
}
public ScriptService.ScriptType getScriptType() {
return scriptType;
}
public void setScriptType(ScriptService.ScriptType scriptType) {
this.scriptType = scriptType;
}
public String getScript() {
return script;
}
public void setScript(String script) {
this.script = script;
}
public Map<String, Object> getScriptParams() {
return scriptParams;
}
public void setScriptParams(Map<String, Object> scriptParams) {
this.scriptParams = scriptParams;
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (script == null || script.isEmpty()) {
validationException = addValidationError("template is missing", validationException);
}
if (scriptType == null) {
validationException = addValidationError("template's script type is missing", validationException);
}
if (simulate == false) {
if (request == null) {
validationException = addValidationError("search request is missing", validationException);
} else {
ActionRequestValidationException ex = request.validate();
if (ex != null) {
if (validationException == null) {
validationException = new ActionRequestValidationException();
}
validationException.addValidationErrors(ex.validationErrors());
}
}
}
return validationException;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
request = in.readOptionalStreamable(SearchRequest::new);
simulate = in.readBoolean();
scriptType = ScriptService.ScriptType.readFrom(in);
script = in.readOptionalString();
if (in.readBoolean()) {
scriptParams = in.readMap();
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeOptionalStreamable(request);
out.writeBoolean(simulate);
ScriptService.ScriptType.writeTo(scriptType, out);
out.writeOptionalString(script);
boolean hasParams = scriptParams != null;
out.writeBoolean(hasParams);
if (hasParams) {
out.writeMap(scriptParams);
}
}
@Override
public String[] indices() {
return request != null ? request.indices() : Strings.EMPTY_ARRAY;
}
@Override
public IndicesOptions indicesOptions() {
return request != null ? request.indicesOptions() : SearchRequest.DEFAULT_INDICES_OPTIONS;
}
}

View File

@ -0,0 +1,64 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.script.ScriptService;
import java.util.Map;
public class SearchTemplateRequestBuilder
extends ActionRequestBuilder<SearchTemplateRequest, SearchTemplateResponse, SearchTemplateRequestBuilder> {
SearchTemplateRequestBuilder(ElasticsearchClient client, SearchTemplateAction action) {
super(client, action, new SearchTemplateRequest());
}
public SearchTemplateRequestBuilder(ElasticsearchClient client) {
this(client, SearchTemplateAction.INSTANCE);
}
public SearchTemplateRequestBuilder setRequest(SearchRequest searchRequest) {
request.setRequest(searchRequest);
return this;
}
public SearchTemplateRequestBuilder setSimulate(boolean simulate) {
request.setSimulate(simulate);
return this;
}
public SearchTemplateRequestBuilder setScriptType(ScriptService.ScriptType scriptType) {
request.setScriptType(scriptType);
return this;
}
public SearchTemplateRequestBuilder setScript(String script) {
request.setScript(script);
return this;
}
public SearchTemplateRequestBuilder setScriptParams(Map<String, Object> scriptParams) {
request.setScriptParams(scriptParams);
return this;
}
}

View File

@ -17,52 +17,80 @@
* under the License.
*/
package org.elasticsearch.action.admin.cluster.validate.template;
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.StatusToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
public class RenderSearchTemplateResponse extends ActionResponse implements ToXContent {
public class SearchTemplateResponse extends ActionResponse implements StatusToXContent {
/** Contains the source of the rendered template **/
private BytesReference source;
public BytesReference source() {
/** Contains the search response, if any **/
private SearchResponse response;
SearchTemplateResponse() {
}
public BytesReference getSource() {
return source;
}
public void source(BytesReference source) {
public void setSource(BytesReference source) {
this.source = source;
}
public SearchResponse getResponse() {
return response;
}
public void setResponse(SearchResponse searchResponse) {
this.response = searchResponse;
}
public boolean hasResponse() {
return response != null;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
boolean hasSource = source != null;
out.writeBoolean(hasSource);
if (hasSource) {
out.writeBytesReference(source);
}
out.writeOptionalBytesReference(source);
out.writeOptionalStreamable(response);
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
if (in.readBoolean()) {
source = in.readBytesReference();
}
source = in.readOptionalBytesReference();
response = in.readOptionalStreamable(SearchResponse::new);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.rawField("template_output", source);
builder.endObject();
if (hasResponse()) {
response.toXContent(builder, params);
} else {
builder.rawField("template_output", source);
}
return builder;
}
@Override
public RestStatus status() {
if (hasResponse()) {
return response.status();
} else {
return RestStatus.OK;
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.AtomicArray;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import java.util.concurrent.atomic.AtomicInteger;
public class TransportMultiSearchTemplateAction extends HandledTransportAction<MultiSearchTemplateRequest, MultiSearchTemplateResponse> {
private final TransportSearchTemplateAction searchTemplateAction;
@Inject
public TransportMultiSearchTemplateAction(Settings settings, ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, IndexNameExpressionResolver resolver,
TransportSearchTemplateAction searchTemplateAction) {
super(settings, MultiSearchTemplateAction.NAME, threadPool, transportService, actionFilters, resolver,
MultiSearchTemplateRequest::new);
this.searchTemplateAction = searchTemplateAction;
}
@Override
protected void doExecute(MultiSearchTemplateRequest request, ActionListener<MultiSearchTemplateResponse> listener) {
final AtomicArray<MultiSearchTemplateResponse.Item> responses = new AtomicArray<>(request.requests().size());
final AtomicInteger counter = new AtomicInteger(responses.length());
for (int i = 0; i < responses.length(); i++) {
final int index = i;
searchTemplateAction.execute(request.requests().get(i), new ActionListener<SearchTemplateResponse>() {
@Override
public void onResponse(SearchTemplateResponse searchTemplateResponse) {
responses.set(index, new MultiSearchTemplateResponse.Item(searchTemplateResponse, null));
if (counter.decrementAndGet() == 0) {
finishHim();
}
}
@Override
public void onFailure(Throwable e) {
responses.set(index, new MultiSearchTemplateResponse.Item(null, e));
if (counter.decrementAndGet() == 0) {
finishHim();
}
}
private void finishHim() {
MultiSearchTemplateResponse.Item[] items = responses.toArray(new MultiSearchTemplateResponse.Item[responses.length()]);
listener.onResponse(new MultiSearchTemplateResponse(items));
}
});
}
}
}

View File

@ -0,0 +1,122 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.TransportSearchAction;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import java.io.IOException;
import static java.util.Collections.emptyMap;
import static org.elasticsearch.script.ScriptContext.Standard.SEARCH;
public class TransportSearchTemplateAction extends HandledTransportAction<SearchTemplateRequest, SearchTemplateResponse> {
private static final String TEMPLATE_LANG = MustacheScriptEngineService.NAME;
private final ClusterService clusterService;
private final ScriptService scriptService;
private final TransportSearchAction searchAction;
private final IndicesQueriesRegistry queryRegistry;
private final AggregatorParsers aggsParsers;
private final Suggesters suggesters;
@Inject
public TransportSearchTemplateAction(Settings settings, ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, IndexNameExpressionResolver resolver,
ClusterService clusterService, ScriptService scriptService,
TransportSearchAction searchAction, IndicesQueriesRegistry indicesQueryRegistry,
AggregatorParsers aggregatorParsers, Suggesters suggesters) {
super(settings, SearchTemplateAction.NAME, threadPool, transportService, actionFilters, resolver, SearchTemplateRequest::new);
this.clusterService = clusterService;
this.scriptService = scriptService;
this.searchAction = searchAction;
this.queryRegistry = indicesQueryRegistry;
this.aggsParsers = aggregatorParsers;
this.suggesters = suggesters;
}
@Override
protected void doExecute(SearchTemplateRequest request, ActionListener<SearchTemplateResponse> listener) {
final SearchTemplateResponse response = new SearchTemplateResponse();
try {
Script script = new Script(request.getScript(), request.getScriptType(), TEMPLATE_LANG, request.getScriptParams());
ExecutableScript executable = scriptService.executable(script, SEARCH, emptyMap(), clusterService.state());
BytesReference source = (BytesReference) executable.run();
response.setSource(source);
if (request.isSimulate()) {
listener.onResponse(response);
return;
}
// Executes the search
SearchRequest searchRequest = request.getRequest();
try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
SearchSourceBuilder builder = SearchSourceBuilder.searchSource();
builder.parseXContent(new QueryParseContext(queryRegistry, parser, parseFieldMatcher), aggsParsers, suggesters);
searchRequest.source(builder);
searchAction.execute(searchRequest, new ActionListener<SearchResponse>() {
@Override
public void onResponse(SearchResponse searchResponse) {
try {
response.setResponse(searchResponse);
listener.onResponse(response);
} catch (Throwable t) {
listener.onFailure(t);
}
}
@Override
public void onFailure(Throwable t) {
listener.onFailure(t);
}
});
}
} catch (Throwable t) {
listener.onFailure(t);
}
}
}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.admin.cluster.storedscripts;
package org.elasticsearch.rest.action.search.template;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.admin.cluster.storedscripts;
package org.elasticsearch.rest.action.search.template;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
@ -28,11 +28,10 @@ import org.elasticsearch.script.Template;
import static org.elasticsearch.rest.RestRequest.Method.GET;
/**
*
*/
public class RestGetSearchTemplateAction extends RestGetStoredScriptAction {
private static final String TEMPLATE = "template";
@Inject
public RestGetSearchTemplateAction(Settings settings, RestController controller, Client client) {
super(settings, controller, false, client);
@ -48,6 +47,4 @@ public class RestGetSearchTemplateAction extends RestGetStoredScriptAction {
protected String getScriptFieldName() {
return TEMPLATE;
}
private static final String TEMPLATE = "template";
}

View File

@ -0,0 +1,92 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.search.template;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.template.MultiSearchTemplateAction;
import org.elasticsearch.action.search.template.MultiSearchTemplateRequest;
import org.elasticsearch.action.search.template.SearchTemplateRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.search.RestMultiSearchAction;
import org.elasticsearch.rest.action.support.RestActions;
import org.elasticsearch.rest.action.support.RestToXContentListener;
import java.io.IOException;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
public class RestMultiSearchTemplateAction extends BaseRestHandler {
private final boolean allowExplicitIndex;
@Inject
public RestMultiSearchTemplateAction(Settings settings, RestController controller, Client client) {
super(settings, client);
this.allowExplicitIndex = MULTI_ALLOW_EXPLICIT_INDEX.get(settings);
controller.registerHandler(GET, "/_msearch/template", this);
controller.registerHandler(POST, "/_msearch/template", this);
controller.registerHandler(GET, "/{index}/_msearch/template", this);
controller.registerHandler(POST, "/{index}/_msearch/template", this);
controller.registerHandler(GET, "/{index}/{type}/_msearch/template", this);
controller.registerHandler(POST, "/{index}/{type}/_msearch/template", this);
}
@Override
protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
if (RestActions.hasBodyContent(request) == false) {
throw new ElasticsearchException("request body is required");
}
MultiSearchTemplateRequest multiRequest = parseRequest(request, allowExplicitIndex);
client.execute(MultiSearchTemplateAction.INSTANCE, multiRequest, new RestToXContentListener<>(channel));
}
/**
* Parses a {@link RestRequest} body and returns a {@link MultiSearchTemplateRequest}
*/
public static MultiSearchTemplateRequest parseRequest(RestRequest restRequest, boolean allowExplicitIndex) throws IOException {
MultiSearchTemplateRequest multiRequest = new MultiSearchTemplateRequest();
RestMultiSearchAction.parseMultiLineRequest(restRequest, multiRequest.indicesOptions(), allowExplicitIndex,
(searchRequest, bytes) -> {
try {
SearchTemplateRequest searchTemplateRequest = RestSearchTemplateAction.parse(bytes);
if (searchTemplateRequest.getScript() != null) {
searchTemplateRequest.setRequest(searchRequest);
multiRequest.add(searchTemplateRequest);
} else {
throw new IllegalArgumentException("Malformed search template");
}
} catch (IOException e) {
throw new ElasticsearchParseException("Exception when parsing search template request", e);
}
});
return multiRequest;
}
}

View File

@ -16,47 +16,26 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.admin.cluster.storedscripts;
package org.elasticsearch.rest.action.search.template;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestPutStoredScriptAction;
import org.elasticsearch.script.Template;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.rest.RestRequest.Method.PUT;
/**
*
*/
public class RestPutSearchTemplateAction extends RestPutStoredScriptAction {
@Inject
public RestPutSearchTemplateAction(Settings settings, RestController controller, Client client) {
super(settings, controller, false, client);
//controller.registerHandler(GET, "/template", this);
controller.registerHandler(POST, "/_search/template/{id}", this);
controller.registerHandler(PUT, "/_search/template/{id}", this);
controller.registerHandler(PUT, "/_search/template/{id}/_create", new CreateHandler(settings, controller, client));
controller.registerHandler(POST, "/_search/template/{id}/_create", new CreateHandler(settings, controller, client));
}
final class CreateHandler extends BaseRestHandler {
protected CreateHandler(Settings settings, RestController controller, Client client) {
super(settings, client);
}
@Override
public void handleRequest(RestRequest request, RestChannel channel, final Client client) {
request.params().put("op_type", "create");
RestPutSearchTemplateAction.this.handleRequest(request, channel, client);
}
}
@Override

View File

@ -0,0 +1,63 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.search.template;
import org.elasticsearch.action.search.template.SearchTemplateAction;
import org.elasticsearch.action.search.template.SearchTemplateRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.support.RestActions;
import org.elasticsearch.rest.action.support.RestToXContentListener;
import org.elasticsearch.script.ScriptService;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
public class RestRenderSearchTemplateAction extends BaseRestHandler {
@Inject
public RestRenderSearchTemplateAction(Settings settings, RestController controller, Client client) {
super(settings, client);
controller.registerHandler(GET, "/_render/template", this);
controller.registerHandler(POST, "/_render/template", this);
controller.registerHandler(GET, "/_render/template/{id}", this);
controller.registerHandler(POST, "/_render/template/{id}", this);
}
@Override
protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
// Creates the render template request
SearchTemplateRequest renderRequest = RestSearchTemplateAction.parse(RestActions.getRestContent(request));
renderRequest.setSimulate(true);
String id = request.param("id");
if (id != null) {
renderRequest.setScriptType(ScriptService.ScriptType.STORED);
renderRequest.setScript(id);
}
client.execute(SearchTemplateAction.INSTANCE, renderRequest, new RestToXContentListener<>(channel));
}
}

View File

@ -0,0 +1,128 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.search.template;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.template.SearchTemplateAction;
import org.elasticsearch.action.search.template.SearchTemplateRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParseFieldMatcherSupplier;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.rest.action.support.RestActions;
import org.elasticsearch.rest.action.support.RestStatusToXContentListener;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.suggest.Suggesters;
import java.io.IOException;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
public class RestSearchTemplateAction extends BaseRestHandler {
private static ObjectParser<SearchTemplateRequest, ParseFieldMatcherSupplier> PARSER;
static {
PARSER = new ObjectParser<>("search_template");
PARSER.declareField((parser, request, s) ->
request.setScriptParams(parser.map())
, new ParseField("params"), ObjectParser.ValueType.OBJECT);
PARSER.declareString((request, s) -> {
request.setScriptType(ScriptService.ScriptType.FILE);
request.setScript(s);
}, new ParseField("file"));
PARSER.declareString((request, s) -> {
request.setScriptType(ScriptService.ScriptType.STORED);
request.setScript(s);
}, new ParseField("id"));
PARSER.declareField((parser, request, value) -> {
request.setScriptType(ScriptService.ScriptType.INLINE);
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
try (XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType())) {
request.setScript(builder.copyCurrentStructure(parser).bytes().toUtf8());
} catch (IOException e) {
throw new ParsingException(parser.getTokenLocation(), "Could not parse inline template", e);
}
} else {
request.setScript(parser.text());
}
}, new ParseField("inline", "template"), ObjectParser.ValueType.OBJECT_OR_STRING);
}
private final IndicesQueriesRegistry queryRegistry;
private final AggregatorParsers aggParsers;
private final Suggesters suggesters;
@Inject
public RestSearchTemplateAction(Settings settings, RestController controller, Client client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggregatorParsers, Suggesters suggesters) {
super(settings, client);
this.queryRegistry = queryRegistry;
this.aggParsers = aggregatorParsers;
this.suggesters = suggesters;
controller.registerHandler(GET, "/_search/template", this);
controller.registerHandler(POST, "/_search/template", this);
controller.registerHandler(GET, "/{index}/_search/template", this);
controller.registerHandler(POST, "/{index}/_search/template", this);
controller.registerHandler(GET, "/{index}/{type}/_search/template", this);
controller.registerHandler(POST, "/{index}/{type}/_search/template", this);
}
@Override
protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
if (RestActions.hasBodyContent(request) == false) {
throw new ElasticsearchException("request body is required");
}
// Creates the search request with all required params
SearchRequest searchRequest = new SearchRequest();
RestSearchAction.parseSearchRequest(searchRequest, queryRegistry, request, parseFieldMatcher, aggParsers, suggesters, null);
// Creates the search template request
SearchTemplateRequest searchTemplateRequest = parse(RestActions.getRestContent(request));
searchTemplateRequest.setRequest(searchRequest);
client.execute(SearchTemplateAction.INSTANCE, searchTemplateRequest, new RestStatusToXContentListener<>(channel));
}
public static SearchTemplateRequest parse(BytesReference bytes) throws IOException {
try (XContentParser parser = XContentHelper.createParser(bytes)) {
return PARSER.parse(parser, new SearchTemplateRequest(), () -> ParseFieldMatcher.STRICT);
}
}
}

View File

@ -19,9 +19,21 @@
package org.elasticsearch.script.mustache;
import org.elasticsearch.action.ActionModule;
import org.elasticsearch.action.search.template.MultiSearchTemplateAction;
import org.elasticsearch.action.search.template.SearchTemplateAction;
import org.elasticsearch.action.search.template.TransportMultiSearchTemplateAction;
import org.elasticsearch.action.search.template.TransportSearchTemplateAction;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.rest.action.search.template.RestDeleteSearchTemplateAction;
import org.elasticsearch.rest.action.search.template.RestGetSearchTemplateAction;
import org.elasticsearch.rest.action.search.template.RestMultiSearchTemplateAction;
import org.elasticsearch.rest.action.search.template.RestPutSearchTemplateAction;
import org.elasticsearch.rest.action.search.template.RestRenderSearchTemplateAction;
import org.elasticsearch.rest.action.search.template.RestSearchTemplateAction;
import org.elasticsearch.script.ScriptEngineRegistry;
import org.elasticsearch.script.ScriptEngineService;
import org.elasticsearch.script.ScriptModule;
@ -32,4 +44,20 @@ public class MustachePlugin extends Plugin implements ScriptPlugin {
public ScriptEngineService getScriptEngineService(Settings settings) {
return new MustacheScriptEngineService(settings);
}
public void onModule(ActionModule module) {
module.registerAction(SearchTemplateAction.INSTANCE, TransportSearchTemplateAction.class);
module.registerAction(MultiSearchTemplateAction.INSTANCE, TransportMultiSearchTemplateAction.class);
}
public void onModule(NetworkModule module) {
if (module.isTransportClient() == false) {
module.registerRestHandler(RestSearchTemplateAction.class);
module.registerRestHandler(RestMultiSearchTemplateAction.class);
module.registerRestHandler(RestGetSearchTemplateAction.class);
module.registerRestHandler(RestPutSearchTemplateAction.class);
module.registerRestHandler(RestDeleteSearchTemplateAction.class);
module.registerRestHandler(RestRenderSearchTemplateAction.class);
}
}
}

View File

@ -0,0 +1,179 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.test.ESIntegTestCase;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.core.Is.is;
public class MultiSearchTemplateIT extends ESIntegTestCase {
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Collections.singleton(MustachePlugin.class);
}
@Override
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
return nodePlugins();
}
public void testBasic() throws Exception {
createIndex("msearch");
final int numDocs = randomIntBetween(10, 100);
IndexRequestBuilder[] indexRequestBuilders = new IndexRequestBuilder[numDocs];
for (int i = 0; i < numDocs; i++) {
indexRequestBuilders[i] = client().prepareIndex("msearch", "test", String.valueOf(i))
.setSource("odd", (i % 2 == 0), "group", (i % 3));
}
indexRandom(true, indexRequestBuilders);
final String template = jsonBuilder().startObject()
.startObject("query")
.startObject("{{query_type}}")
.field("{{field_name}}", "{{field_value}}")
.endObject()
.endObject()
.endObject().string();
MultiSearchTemplateRequest multiRequest = new MultiSearchTemplateRequest();
// Search #1
SearchTemplateRequest search1 = new SearchTemplateRequest();
search1.setRequest(new SearchRequest("msearch"));
search1.setScriptType(ScriptService.ScriptType.INLINE);
search1.setScript(template);
Map<String, Object> params1 = new HashMap<>();
params1.put("query_type", "match");
params1.put("field_name", "odd");
params1.put("field_value", true);
search1.setScriptParams(params1);
multiRequest.add(search1);
// Search #2 (Simulate is true)
SearchTemplateRequest search2 = new SearchTemplateRequest();
search2.setRequest(new SearchRequest("msearch"));
search2.setScriptType(ScriptService.ScriptType.INLINE);
search2.setScript(template);
search2.setSimulate(true);
Map<String, Object> params2 = new HashMap<>();
params2.put("query_type", "match_phrase_prefix");
params2.put("field_name", "message");
params2.put("field_value", "quick brown f");
search2.setScriptParams(params2);
multiRequest.add(search2);
// Search #3
SearchTemplateRequest search3 = new SearchTemplateRequest();
search3.setRequest(new SearchRequest("msearch"));
search3.setScriptType(ScriptService.ScriptType.INLINE);
search3.setScript(template);
search3.setSimulate(false);
Map<String, Object> params3 = new HashMap<>();
params3.put("query_type", "term");
params3.put("field_name", "odd");
params3.put("field_value", "false");
search3.setScriptParams(params3);
multiRequest.add(search3);
// Search #4 (Fail because of unknown index)
SearchTemplateRequest search4 = new SearchTemplateRequest();
search4.setRequest(new SearchRequest("unknown"));
search4.setScriptType(ScriptService.ScriptType.INLINE);
search4.setScript(template);
Map<String, Object> params4 = new HashMap<>();
params4.put("query_type", "match");
params4.put("field_name", "group");
params4.put("field_value", "test");
search4.setScriptParams(params4);
multiRequest.add(search4);
// Search #5 (Simulate is true)
SearchTemplateRequest search5 = new SearchTemplateRequest();
search5.setRequest(new SearchRequest("msearch"));
search5.setScriptType(ScriptService.ScriptType.INLINE);
search5.setScript("{{! ignore me }}{\"query\":{\"terms\":{\"group\":[{{#groups}}{{.}},{{/groups}}]}}}");
search5.setSimulate(true);
Map<String, Object> params5 = new HashMap<>();
params5.put("groups", Arrays.asList(1, 2, 3));
search5.setScriptParams(params5);
multiRequest.add(search5);
MultiSearchTemplateResponse response = client().execute(MultiSearchTemplateAction.INSTANCE, multiRequest).get();
assertThat(response.getResponses(), arrayWithSize(5));
MultiSearchTemplateResponse.Item response1 = response.getResponses()[0];
assertThat(response1.isFailure(), is(false));
SearchTemplateResponse searchTemplateResponse1 = response1.getResponse();
assertThat(searchTemplateResponse1.hasResponse(), is(true));
assertHitCount(searchTemplateResponse1.getResponse(), (numDocs / 2) + (numDocs % 2));
assertThat(searchTemplateResponse1.getSource().toUtf8(),
equalTo("{\"query\":{\"match\":{\"odd\":\"true\"}}}"));
MultiSearchTemplateResponse.Item response2 = response.getResponses()[1];
assertThat(response2.isFailure(), is(false));
SearchTemplateResponse searchTemplateResponse2 = response2.getResponse();
assertThat(searchTemplateResponse2.hasResponse(), is(false));
assertThat(searchTemplateResponse2.getSource().toUtf8(),
equalTo("{\"query\":{\"match_phrase_prefix\":{\"message\":\"quick brown f\"}}}"));
MultiSearchTemplateResponse.Item response3 = response.getResponses()[2];
assertThat(response3.isFailure(), is(false));
SearchTemplateResponse searchTemplateResponse3 = response3.getResponse();
assertThat(searchTemplateResponse3.hasResponse(), is(true));
assertHitCount(searchTemplateResponse3.getResponse(), (numDocs / 2));
assertThat(searchTemplateResponse3.getSource().toUtf8(),
equalTo("{\"query\":{\"term\":{\"odd\":\"false\"}}}"));
MultiSearchTemplateResponse.Item response4 = response.getResponses()[3];
assertThat(response4.isFailure(), is(true));
assertThat(response4.getFailure(), instanceOf(IndexNotFoundException.class));
assertThat(response4.getFailure().getMessage(), equalTo("no such index"));
MultiSearchTemplateResponse.Item response5 = response.getResponses()[4];
assertThat(response5.isFailure(), is(false));
SearchTemplateResponse searchTemplateResponse5 = response5.getResponse();
assertThat(searchTemplateResponse5.hasResponse(), is(false));
assertThat(searchTemplateResponse5.getSource().toUtf8(),
equalTo("{\"query\":{\"terms\":{\"group\":[1,2,3,]}}}"));
}
}

View File

@ -0,0 +1,72 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.search.template.RestMultiSearchTemplateAction;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.StreamsUtils;
import org.elasticsearch.test.rest.FakeRestRequest;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
public class MultiSearchTemplateRequestTests extends ESTestCase {
public void testParseRequest() throws Exception {
byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/template/simple-msearch-template.json");
RestRequest restRequest = new FakeRestRequest.Builder().withContent(new BytesArray(data)).build();
MultiSearchTemplateRequest request = RestMultiSearchTemplateAction.parseRequest(restRequest, true);
assertThat(request.requests().size(), equalTo(3));
assertThat(request.requests().get(0).getRequest().indices()[0], equalTo("test0"));
assertThat(request.requests().get(0).getRequest().indices()[1], equalTo("test1"));
assertThat(request.requests().get(0).indices(), arrayContaining("test0", "test1"));
assertThat(request.requests().get(0).getRequest().requestCache(), equalTo(true));
assertThat(request.requests().get(0).getRequest().preference(), nullValue());
assertThat(request.requests().get(1).indices()[0], equalTo("test2"));
assertThat(request.requests().get(1).indices()[1], equalTo("test3"));
assertThat(request.requests().get(1).getRequest().types()[0], equalTo("type1"));
assertThat(request.requests().get(1).getRequest().requestCache(), nullValue());
assertThat(request.requests().get(1).getRequest().preference(), equalTo("_local"));
assertThat(request.requests().get(2).indices()[0], equalTo("test4"));
assertThat(request.requests().get(2).indices()[1], equalTo("test1"));
assertThat(request.requests().get(2).getRequest().types()[0], equalTo("type2"));
assertThat(request.requests().get(2).getRequest().types()[1], equalTo("type1"));
assertThat(request.requests().get(2).getRequest().routing(), equalTo("123"));
assertNotNull(request.requests().get(0).getScript());
assertNotNull(request.requests().get(1).getScript());
assertNotNull(request.requests().get(2).getScript());
assertEquals(ScriptService.ScriptType.INLINE, request.requests().get(0).getScriptType());
assertEquals(ScriptService.ScriptType.INLINE, request.requests().get(1).getScriptType());
assertEquals(ScriptService.ScriptType.INLINE, request.requests().get(2).getScriptType());
assertEquals("{\"query\":{\"match_{{template}}\":{}}}", request.requests().get(0).getScript());
assertEquals("{\"query\":{\"match_{{template}}\":{}}}", request.requests().get(1).getScript());
assertEquals("{\"query\":{\"match_{{template}}\":{}}}", request.requests().get(2).getScript());
assertEquals(1, request.requests().get(0).getScriptParams().size());
assertEquals(1, request.requests().get(1).getScriptParams().size());
assertEquals(1, request.requests().get(2).getScriptParams().size());
}
}

View File

@ -0,0 +1,155 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.search.template;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.rest.action.search.template.RestSearchTemplateAction;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
import java.util.List;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.nullValue;
public class SearchTemplateRequestTests extends ESTestCase {
public void testParseInlineTemplate() throws Exception {
String source = "{" +
" 'inline' : {\n" +
" 'query': {\n" +
" 'terms': {\n" +
" 'status': [\n" +
" '{{#status}}',\n" +
" '{{.}}',\n" +
" '{{/status}}'\n" +
" ]\n" +
" }\n" +
" }\n" +
" }" +
"}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(newBytesReference(source));
assertThat(request.getScript(), equalTo("{\"query\":{\"terms\":{\"status\":[\"{{#status}}\",\"{{.}}\",\"{{/status}}\"]}}}"));
assertThat(request.getScriptType(), equalTo(ScriptService.ScriptType.INLINE));
assertThat(request.getScriptParams(), nullValue());
}
public void testParseInlineTemplateWithParams() throws Exception {
String source = "{" +
" 'inline' : {" +
" 'query': { 'match' : { '{{my_field}}' : '{{my_value}}' } }," +
" 'size' : '{{my_size}}'" +
" }," +
" 'params' : {" +
" 'my_field' : 'foo'," +
" 'my_value' : 'bar'," +
" 'my_size' : 5" +
" }" +
"}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(newBytesReference(source));
assertThat(request.getScript(), equalTo("{\"query\":{\"match\":{\"{{my_field}}\":\"{{my_value}}\"}},\"size\":\"{{my_size}}\"}"));
assertThat(request.getScriptType(), equalTo(ScriptService.ScriptType.INLINE));
assertThat(request.getScriptParams().size(), equalTo(3));
assertThat(request.getScriptParams(), hasEntry("my_field", "foo"));
assertThat(request.getScriptParams(), hasEntry("my_value", "bar"));
assertThat(request.getScriptParams(), hasEntry("my_size", 5));
}
public void testParseInlineTemplateAsString() throws Exception {
String source = "{'inline' : '{\\\"query\\\":{\\\"bool\\\":{\\\"must\\\":{\\\"match\\\":{\\\"foo\\\":\\\"{{text}}\\\"}}}}}'}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(newBytesReference(source));
assertThat(request.getScript(), equalTo("{\"query\":{\"bool\":{\"must\":{\"match\":{\"foo\":\"{{text}}\"}}}}}"));
assertThat(request.getScriptType(), equalTo(ScriptService.ScriptType.INLINE));
assertThat(request.getScriptParams(), nullValue());
}
@SuppressWarnings("unchecked")
public void testParseInlineTemplateAsStringWithParams() throws Exception {
String source = "{'inline' : '{\\\"query\\\":{\\\"match\\\":{\\\"{{field}}\\\":\\\"{{value}}\\\"}}}', " +
"'params': {'status': ['pending', 'published']}}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(newBytesReference(source));
assertThat(request.getScript(), equalTo("{\"query\":{\"match\":{\"{{field}}\":\"{{value}}\"}}}"));
assertThat(request.getScriptType(), equalTo(ScriptService.ScriptType.INLINE));
assertThat(request.getScriptParams().size(), equalTo(1));
assertThat(request.getScriptParams(), hasKey("status"));
assertThat((List<String>) request.getScriptParams().get("status"), hasItems("pending", "published"));
}
public void testParseFileTemplate() throws Exception {
String source = "{'file' : 'fileTemplate'}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(newBytesReference(source));
assertThat(request.getScript(), equalTo("fileTemplate"));
assertThat(request.getScriptType(), equalTo(ScriptService.ScriptType.FILE));
assertThat(request.getScriptParams(), nullValue());
}
public void testParseFileTemplateWithParams() throws Exception {
String source = "{'file' : 'template_foo', 'params' : {'foo': 'bar', 'size': 500}}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(newBytesReference(source));
assertThat(request.getScript(), equalTo("template_foo"));
assertThat(request.getScriptType(), equalTo(ScriptService.ScriptType.FILE));
assertThat(request.getScriptParams().size(), equalTo(2));
assertThat(request.getScriptParams(), hasEntry("foo", "bar"));
assertThat(request.getScriptParams(), hasEntry("size", 500));
}
public void testParseStoredTemplate() throws Exception {
String source = "{'id' : 'storedTemplate'}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(newBytesReference(source));
assertThat(request.getScript(), equalTo("storedTemplate"));
assertThat(request.getScriptType(), equalTo(ScriptService.ScriptType.STORED));
assertThat(request.getScriptParams(), nullValue());
}
public void testParseStoredTemplateWithParams() throws Exception {
String source = "{'id' : 'another_template', 'params' : {'bar': 'foo'}}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(newBytesReference(source));
assertThat(request.getScript(), equalTo("another_template"));
assertThat(request.getScriptType(), equalTo(ScriptService.ScriptType.STORED));
assertThat(request.getScriptParams().size(), equalTo(1));
assertThat(request.getScriptParams(), hasEntry("bar", "foo"));
}
public void testParseWrongTemplate() {
// Unclosed template id
expectThrows(ParsingException.class, () -> RestSearchTemplateAction.parse(newBytesReference("{'id' : 'another_temp }")));
}
/**
* Creates a {@link BytesReference} with the given string while replacing single quote to double quotes.
*/
private static BytesReference newBytesReference(String s) {
assertNotNull(s);
return new BytesArray(s.replace("'", "\""));
}
}

View File

@ -19,16 +19,15 @@
package org.elasticsearch.messy.tests;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateResponse;
import org.elasticsearch.action.search.template.SearchTemplateRequestBuilder;
import org.elasticsearch.action.search.template.SearchTemplateResponse;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.test.ESIntegTestCase;
@ -40,6 +39,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
@ -53,6 +53,11 @@ public class RenderSearchTemplateTests extends ESIntegTestCase {
return Collections.singleton(MustachePlugin.class);
}
@Override
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
return nodePlugins();
}
@Override
protected void setupSuiteScopeCluster() throws Exception {
ElasticsearchAssertions.assertAcked(client().admin().cluster().preparePutStoredScript()
@ -75,14 +80,14 @@ public class RenderSearchTemplateTests extends ESIntegTestCase {
.put(Environment.PATH_CONF_SETTING.getKey(), configDir).build();
}
public void testInlineTemplate() {
public void testInlineTemplate() throws ExecutionException, InterruptedException {
Map<String, Object> params = new HashMap<>();
params.put("value", "bar");
params.put("size", 20);
Template template = new Template(TEMPLATE_CONTENTS, ScriptType.INLINE, MustacheScriptEngineService.NAME, XContentType.JSON, params);
RenderSearchTemplateResponse response = client().admin().cluster().prepareRenderSearchTemplate().template(template).get();
SearchTemplateResponse response = prepareRenderSearchTemplate(TEMPLATE_CONTENTS, ScriptType.INLINE, params).get();
assertThat(response, notNullValue());
BytesReference source = response.source();
assertFalse(response.hasResponse());
BytesReference source = response.getSource();
assertThat(source, notNullValue());
Map<String, Object> sourceAsMap = XContentHelper.convertToMap(source, false).v2();
assertThat(sourceAsMap, notNullValue());
@ -93,10 +98,10 @@ public class RenderSearchTemplateTests extends ESIntegTestCase {
params = new HashMap<>();
params.put("value", "baz");
params.put("size", 100);
template = new Template(TEMPLATE_CONTENTS, ScriptType.INLINE, MustacheScriptEngineService.NAME, XContentType.JSON, params);
response = client().admin().cluster().prepareRenderSearchTemplate().template(template).get();
response = prepareRenderSearchTemplate(TEMPLATE_CONTENTS, ScriptType.INLINE, params).get();
assertThat(response, notNullValue());
source = response.source();
assertFalse(response.hasResponse());
source = response.getSource();
assertThat(source, notNullValue());
sourceAsMap = XContentHelper.convertToMap(source, false).v2();
expected = TEMPLATE_CONTENTS.replace("{{value}}", "baz").replace("{{size}}", "100");
@ -104,14 +109,14 @@ public class RenderSearchTemplateTests extends ESIntegTestCase {
assertThat(sourceAsMap, equalTo(expectedMap));
}
public void testIndexedTemplate() {
public void testIndexedTemplate() throws ExecutionException, InterruptedException {
Map<String, Object> params = new HashMap<>();
params.put("value", "bar");
params.put("size", 20);
Template template = new Template("index_template_1", ScriptType.STORED, MustacheScriptEngineService.NAME, XContentType.JSON, params);
RenderSearchTemplateResponse response = client().admin().cluster().prepareRenderSearchTemplate().template(template).get();
SearchTemplateResponse response = prepareRenderSearchTemplate("index_template_1", ScriptType.STORED, params).get();
assertThat(response, notNullValue());
BytesReference source = response.source();
assertFalse(response.hasResponse());
BytesReference source = response.getSource();
assertThat(source, notNullValue());
Map<String, Object> sourceAsMap = XContentHelper.convertToMap(source, false).v2();
assertThat(sourceAsMap, notNullValue());
@ -122,10 +127,9 @@ public class RenderSearchTemplateTests extends ESIntegTestCase {
params = new HashMap<>();
params.put("value", "baz");
params.put("size", 100);
template = new Template("index_template_1", ScriptType.STORED, MustacheScriptEngineService.NAME, XContentType.JSON, params);
response = client().admin().cluster().prepareRenderSearchTemplate().template(template).get();
response = prepareRenderSearchTemplate("index_template_1", ScriptType.STORED, params).get();
assertThat(response, notNullValue());
source = response.source();
source = response.getSource();
assertThat(source, notNullValue());
sourceAsMap = XContentHelper.convertToMap(source, false).v2();
expected = TEMPLATE_CONTENTS.replace("{{value}}", "baz").replace("{{size}}", "100");
@ -133,14 +137,14 @@ public class RenderSearchTemplateTests extends ESIntegTestCase {
assertThat(sourceAsMap, equalTo(expectedMap));
}
public void testFileTemplate() {
public void testFileTemplate() throws ExecutionException, InterruptedException {
Map<String, Object> params = new HashMap<>();
params.put("value", "bar");
params.put("size", 20);
Template template = new Template("file_template_1", ScriptType.FILE, MustacheScriptEngineService.NAME, XContentType.JSON, params);
RenderSearchTemplateResponse response = client().admin().cluster().prepareRenderSearchTemplate().template(template).get();
SearchTemplateResponse response = prepareRenderSearchTemplate("file_template_1", ScriptType.FILE, params).get();
assertThat(response, notNullValue());
BytesReference source = response.source();
assertFalse(response.hasResponse());
BytesReference source = response.getSource();
assertThat(source, notNullValue());
Map<String, Object> sourceAsMap = XContentHelper.convertToMap(source, false).v2();
assertThat(sourceAsMap, notNullValue());
@ -151,14 +155,17 @@ public class RenderSearchTemplateTests extends ESIntegTestCase {
params = new HashMap<>();
params.put("value", "baz");
params.put("size", 100);
template = new Template("file_template_1", ScriptType.FILE, MustacheScriptEngineService.NAME, XContentType.JSON, params);
response = client().admin().cluster().prepareRenderSearchTemplate().template(template).get();
response = prepareRenderSearchTemplate("file_template_1", ScriptType.FILE, params).get();
assertThat(response, notNullValue());
source = response.source();
source = response.getSource();
assertThat(source, notNullValue());
sourceAsMap = XContentHelper.convertToMap(source, false).v2();
expected = TEMPLATE_CONTENTS.replace("{{value}}", "baz").replace("{{size}}", "100");
expectedMap = XContentHelper.convertToMap(new BytesArray(expected), false).v2();
assertThat(sourceAsMap, equalTo(expectedMap));
}
private SearchTemplateRequestBuilder prepareRenderSearchTemplate(String script, ScriptType type, Map<String, Object> params) {
return new SearchTemplateRequestBuilder(client()).setScript(script).setScriptType(type).setScriptParams(params).setSimulate(true);
}
}

View File

@ -18,20 +18,16 @@
*/
package org.elasticsearch.messy.tests;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.template.SearchTemplateAction;
import org.elasticsearch.action.search.template.SearchTemplateRequest;
import org.elasticsearch.action.search.template.SearchTemplateRequestBuilder;
import org.elasticsearch.action.search.template.SearchTemplateResponse;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory;
@ -40,6 +36,7 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TemplateQueryBuilder;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.action.search.template.RestSearchTemplateAction;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
@ -49,9 +46,16 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ESIntegTestCase;
import org.junit.Before;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFailures;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.hamcrest.Matchers.containsString;
@ -69,6 +73,11 @@ public class TemplateQueryTests extends ESIntegTestCase {
return Collections.singleton(MustachePlugin.class);
}
@Override
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
return nodePlugins();
}
@Before
public void setup() throws IOException {
createIndex("test");
@ -142,11 +151,11 @@ public class TemplateQueryTests extends ESIntegTestCase {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("_all");
String query = "{ \"template\" : { \"query\": {\"match_{{template}}\": {} } }, \"params\" : { \"template\":\"all\" } }";
searchRequest.template(parseTemplate(query));
SearchResponse searchResponse = client().search(searchRequest).get();
assertHitCount(searchResponse, 2);
String query = "{ \"inline\" : { \"query\": {\"match_{{template}}\": {} } }, \"params\" : { \"template\":\"all\" } }";
SearchTemplateRequest request = RestSearchTemplateAction.parse(new BytesArray(query));
request.setRequest(searchRequest);
SearchTemplateResponse response = client().execute(SearchTemplateAction.INSTANCE, request).get();
assertHitCount(response.getResponse(), 2);
}
private Template parseTemplate(String template) throws IOException {
@ -155,23 +164,28 @@ public class TemplateQueryTests extends ESIntegTestCase {
}
}
// Releates to #6318
// Relates to #6318
public void testSearchRequestFail() throws Exception {
String query = "{ \"query\": {\"match_all\": {}}, \"size\" : \"{{my_size}}\" }";
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("_all");
try {
String query = "{ \"template\" : { \"query\": {\"match_all\": {}}, \"size\" : \"{{my_size}}\" } }";
searchRequest.template(parseTemplate(query));
client().search(searchRequest).get();
fail("expected exception");
} catch (Exception ex) {
// expected - no params
}
String query = "{ \"template\" : { \"query\": {\"match_all\": {}}, \"size\" : \"{{my_size}}\" }, \"params\" : { \"my_size\": 1 } }";
searchRequest.template(parseTemplate(query));
SearchResponse searchResponse = client().search(searchRequest).get();
assertThat(searchResponse.getHits().hits().length, equalTo(1));
expectThrows(Exception.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(searchRequest)
.setScript(query)
.setScriptType(ScriptType.INLINE)
.setScriptParams(randomBoolean() ? null : Collections.emptyMap())
.get());
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(searchRequest)
.setScript(query)
.setScriptType(ScriptType.INLINE)
.setScriptParams(Collections.singletonMap("my_size", 1))
.get();
assertThat(searchResponse.getResponse().getHits().hits().length, equalTo(1));
}
public void testThatParametersCanBeSet() throws Exception {
@ -187,18 +201,24 @@ public class TemplateQueryTests extends ESIntegTestCase {
templateParams.put("myField", "theField");
templateParams.put("myValue", "foo");
SearchResponse searchResponse = client().prepareSearch("test").setTypes("type")
.setTemplate(new Template("full-query-template", ScriptType.FILE, MustacheScriptEngineService.NAME, null, templateParams))
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("full-query-template")
.setScriptType(ScriptType.FILE)
.setScriptParams(templateParams)
.get();
assertHitCount(searchResponse, 4);
assertHitCount(searchResponse.getResponse(), 4);
// size kicks in here...
assertThat(searchResponse.getHits().getHits().length, is(2));
assertThat(searchResponse.getResponse().getHits().getHits().length, is(2));
templateParams.put("myField", "otherField");
searchResponse = client().prepareSearch("test").setTypes("type")
.setTemplate(new Template("full-query-template", ScriptType.FILE, MustacheScriptEngineService.NAME, null, templateParams))
searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("full-query-template")
.setScriptType(ScriptType.FILE)
.setScriptParams(templateParams)
.get();
assertHitCount(searchResponse, 1);
assertHitCount(searchResponse.getResponse(), 1);
}
public void testSearchTemplateQueryFromFile() throws Exception {
@ -206,9 +226,10 @@ public class TemplateQueryTests extends ESIntegTestCase {
searchRequest.indices("_all");
String query = "{" + " \"file\": \"full-query-template\"," + " \"params\":{" + " \"mySize\": 2,"
+ " \"myField\": \"text\"," + " \"myValue\": \"value1\"" + " }" + "}";
searchRequest.template(parseTemplate(query));
SearchResponse searchResponse = client().search(searchRequest).get();
assertThat(searchResponse.getHits().hits().length, equalTo(1));
SearchTemplateRequest request = RestSearchTemplateAction.parse(new BytesArray(query));
request.setRequest(searchRequest);
SearchTemplateResponse searchResponse = client().execute(SearchTemplateAction.INSTANCE, request).get();
assertThat(searchResponse.getResponse().getHits().hits().length, equalTo(1));
}
/**
@ -217,11 +238,12 @@ public class TemplateQueryTests extends ESIntegTestCase {
public void testTemplateQueryAsEscapedString() throws Exception {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("_all");
String query = "{" + " \"template\" : \"{ \\\"size\\\": \\\"{{size}}\\\", \\\"query\\\":{\\\"match_all\\\":{}}}\","
String query = "{" + " \"inline\" : \"{ \\\"size\\\": \\\"{{size}}\\\", \\\"query\\\":{\\\"match_all\\\":{}}}\","
+ " \"params\":{" + " \"size\": 1" + " }" + "}";
searchRequest.template(parseTemplate(query));
SearchResponse searchResponse = client().search(searchRequest).get();
assertThat(searchResponse.getHits().hits().length, equalTo(1));
SearchTemplateRequest request = RestSearchTemplateAction.parse(new BytesArray(query));
request.setRequest(searchRequest);
SearchTemplateResponse searchResponse = client().execute(SearchTemplateAction.INSTANCE, request).get();
assertThat(searchResponse.getResponse().getHits().hits().length, equalTo(1));
}
/**
@ -232,11 +254,12 @@ public class TemplateQueryTests extends ESIntegTestCase {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("_all");
String templateString = "{"
+ " \"template\" : \"{ {{#use_size}} \\\"size\\\": \\\"{{size}}\\\", {{/use_size}} \\\"query\\\":{\\\"match_all\\\":{}}}\","
+ " \"inline\" : \"{ {{#use_size}} \\\"size\\\": \\\"{{size}}\\\", {{/use_size}} \\\"query\\\":{\\\"match_all\\\":{}}}\","
+ " \"params\":{" + " \"size\": 1," + " \"use_size\": true" + " }" + "}";
searchRequest.template(parseTemplate(templateString));
SearchResponse searchResponse = client().search(searchRequest).get();
assertThat(searchResponse.getHits().hits().length, equalTo(1));
SearchTemplateRequest request = RestSearchTemplateAction.parse(new BytesArray(templateString));
request.setRequest(searchRequest);
SearchTemplateResponse searchResponse = client().execute(SearchTemplateAction.INSTANCE, request).get();
assertThat(searchResponse.getResponse().getHits().hits().length, equalTo(1));
}
/**
@ -249,9 +272,10 @@ public class TemplateQueryTests extends ESIntegTestCase {
String templateString = "{"
+ " \"inline\" : \"{ \\\"query\\\":{\\\"match_all\\\":{}} {{#use_size}}, \\\"size\\\": \\\"{{size}}\\\" {{/use_size}} }\","
+ " \"params\":{" + " \"size\": 1," + " \"use_size\": true" + " }" + "}";
searchRequest.template(parseTemplate(templateString));
SearchResponse searchResponse = client().search(searchRequest).get();
assertThat(searchResponse.getHits().hits().length, equalTo(1));
SearchTemplateRequest request = RestSearchTemplateAction.parse(new BytesArray(templateString));
request.setRequest(searchRequest);
SearchTemplateResponse searchResponse = client().execute(SearchTemplateAction.INSTANCE, request).get();
assertThat(searchResponse.getResponse().getHits().hits().length, equalTo(1));
}
public void testIndexedTemplateClient() throws Exception {
@ -296,10 +320,11 @@ public class TemplateQueryTests extends ESIntegTestCase {
Map<String, Object> templateParams = new HashMap<>();
templateParams.put("fieldParam", "foo");
SearchResponse searchResponse = client().prepareSearch("test").setTypes("type")
.setTemplate(new Template("testTemplate", ScriptType.STORED, MustacheScriptEngineService.NAME, null, templateParams))
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("testTemplate").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
.get();
assertHitCount(searchResponse, 4);
assertHitCount(searchResponse.getResponse(), 4);
assertAcked(client().admin().cluster()
.prepareDeleteStoredScript(MustacheScriptEngineService.NAME, "testTemplate"));
@ -308,16 +333,11 @@ public class TemplateQueryTests extends ESIntegTestCase {
.prepareGetStoredScript(MustacheScriptEngineService.NAME, "testTemplate").get();
assertNull(getResponse.getStoredScript());
try {
client().prepareSearch("test")
.setTypes("type")
.setTemplate(
new Template("/template_index/mustache/1000", ScriptType.STORED, MustacheScriptEngineService.NAME, null,
templateParams)).get();
fail("Expected SearchPhaseExecutionException");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString(), containsString("Illegal index script format"));
}
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("/template_index/mustache/1000").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
.get());
assertThat(e.getMessage(), containsString("Illegal index script format [/template_index/mustache/1000] should be /lang/id"));
}
public void testIndexedTemplate() throws Exception {
@ -357,8 +377,6 @@ public class TemplateQueryTests extends ESIntegTestCase {
"}"))
);
List<IndexRequestBuilder> builders = new ArrayList<>();
builders.add(client().prepareIndex("test", "type", "1").setSource("{\"theField\":\"foo\"}"));
builders.add(client().prepareIndex("test", "type", "2").setSource("{\"theField\":\"foo 2\"}"));
@ -370,50 +388,34 @@ public class TemplateQueryTests extends ESIntegTestCase {
Map<String, Object> templateParams = new HashMap<>();
templateParams.put("fieldParam", "foo");
SearchResponse searchResponse = client()
.prepareSearch("test")
.setTypes("type")
.setTemplate(
new Template("/mustache/1a", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
templateParams)).get();
assertHitCount(searchResponse, 4);
try {
client().prepareSearch("test")
.setTypes("type")
.setTemplate(
new Template("/template_index/mustache/1000", ScriptService.ScriptType.STORED,
MustacheScriptEngineService.NAME, null, templateParams)).get();
fail("shouldn't get here");
} catch (SearchPhaseExecutionException spee) {
//all good
}
try {
searchResponse = client()
.prepareSearch("test")
.setTypes("type")
.setTemplate(
new Template("/myindex/mustache/1", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
templateParams)).get();
assertFailures(searchResponse);
} catch (SearchPhaseExecutionException spee) {
//all good
}
searchResponse = client().prepareSearch("test").setTypes("type")
.setTemplate(new Template("1a", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null, templateParams))
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest().indices("test").types("type"))
.setScript("/mustache/1a")
.setScriptType(ScriptType.STORED)
.setScriptParams(templateParams)
.get();
assertHitCount(searchResponse, 4);
assertHitCount(searchResponse.getResponse(), 4);
expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest().indices("test").types("type"))
.setScript("/template_index/mustache/1000")
.setScriptType(ScriptType.STORED)
.setScriptParams(templateParams)
.get());
expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest().indices("test").types("type"))
.setScript("/myindex/mustache/1")
.setScriptType(ScriptType.STORED)
.setScriptParams(templateParams)
.get());
templateParams.put("fieldParam", "bar");
searchResponse = client()
.prepareSearch("test")
.setTypes("type")
.setTemplate(
new Template("/mustache/2", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
templateParams)).get();
assertHitCount(searchResponse, 1);
searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("/mustache/2").setScriptType(ScriptService.ScriptType.STORED).setScriptParams(templateParams)
.get();
assertHitCount(searchResponse.getResponse(), 1);
Map<String, Object> vars = new HashMap<>();
vars.put("fieldParam", "bar");
@ -459,16 +461,11 @@ public class TemplateQueryTests extends ESIntegTestCase {
Map<String, Object> templateParams = new HashMap<>();
templateParams.put("P_Keyword1", "dev");
try {
client().prepareSearch("testindex")
.setTypes("test")
.setTemplate(
new Template("git01", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
templateParams)).get();
fail("Broken test template is parsing w/o error.");
} catch (SearchPhaseExecutionException e) {
// the above is expected to fail
}
ParsingException e = expectThrows(ParsingException.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("testindex").types("test"))
.setScript("git01").setScriptType(ScriptService.ScriptType.STORED).setScriptParams(templateParams)
.get());
assertThat(e.getMessage(), containsString("[match] query does not support type ooophrase_prefix"));
assertAcked(client().admin().cluster().preparePutStoredScript()
.setScriptLang(MustacheScriptEngineService.NAME)
@ -476,13 +473,11 @@ public class TemplateQueryTests extends ESIntegTestCase {
.setSource(new BytesArray("{\"query\": {\"match\": {\"searchtext\": {\"query\": \"{{P_Keyword1}}\"," +
"\"type\": \"phrase_prefix\"}}}}")));
SearchResponse searchResponse = client()
.prepareSearch("testindex")
.setTypes("test")
.setTemplate(
new Template("git01", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null, templateParams))
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("testindex").types("test"))
.setScript("git01").setScriptType(ScriptService.ScriptType.STORED).setScriptParams(templateParams)
.get();
assertHitCount(searchResponse, 1);
assertHitCount(searchResponse.getResponse(), 1);
}
}
@ -508,13 +503,11 @@ public class TemplateQueryTests extends ESIntegTestCase {
String[] fieldParams = {"foo","bar"};
arrayTemplateParams.put("fieldParam", fieldParams);
SearchResponse searchResponse = client()
.prepareSearch("test")
.setTypes("type")
.setTemplate(
new Template("/mustache/4", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
arrayTemplateParams)).get();
assertHitCount(searchResponse, 5);
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("/mustache/4").setScriptType(ScriptService.ScriptType.STORED).setScriptParams(arrayTemplateParams)
.get();
assertHitCount(searchResponse.getResponse(), 5);
}
}

View File

@ -0,0 +1,6 @@
{"index":["test0", "test1"], "request_cache": true}
{"inline": {"query" : {"match_{{template}}" :{}}}, "params": {"template": "all" } }
{"index" : "test2,test3", "type" : "type1", "preference": "_local"}
{"inline": {"query" : {"match_{{template}}" :{}}}, "params": {"template": "all" } }
{"index" : ["test4", "test1"], "type" : [ "type2", "type1" ], "routing": "123"}
{"inline": {"query" : {"match_{{template}}" :{}}}, "params": {"template": "all" } }

View File

@ -0,0 +1,11 @@
{
"query": {
"match": {
"{{field}}": {
"query" : "{{value}}",
"operator" : "{{operator}}{{^operator}}or{{/operator}}"
}
}
},
"size": {{size}}
}

View File

@ -18,12 +18,21 @@
- do:
search_template:
body: { "template" : { "query": { "term": { "text": { "value": "{{template}}" } } } }, "params": { "template": "value1" } }
body: { "inline" : { "query": { "term": { "text": { "value": "{{template}}" } } } }, "params": { "template": "value1" } }
- match: { hits.total: 1 }
- do:
search_template:
body: { "template" : { "query": { "match_{{template}}": {} } }, "params" : { "template" : "all" } }
body: { "inline" : { "query": { "match_{{template}}": {} } }, "params" : { "template" : "all" } }
- match: { hits.total: 2 }
---
"Missing template search request":
- do:
catch: missing
search_template:
body: { "id" : "unknown", "params": { "template": "value1" } }

View File

@ -1,5 +1,5 @@
---
"Basic multi-search":
"Basic multi-search with template query":
- do:
index:
index: test_1
@ -24,26 +24,6 @@
- do:
indices.refresh: {}
- do:
msearch:
body:
- index: test_1
- query:
match_all: {}
- index: test_2
- query:
match_all: {}
- search_type: query_then_fetch
index: test_1
- query:
match: {foo: bar}
- match: { responses.0.hits.total: 3 }
- match: { responses.1.error.root_cause.0.type: index_not_found_exception }
- match: { responses.1.error.root_cause.0.reason: "/no.such.index/" }
- match: { responses.1.error.root_cause.0.index: test_2 }
- match: { responses.2.hits.total: 1 }
- do:
msearch:
body:

View File

@ -0,0 +1,126 @@
---
setup:
- do:
index:
index: index_1
type: test
id: 1
body: { foo: bar }
- do:
index:
index: index_1
type: test
id: 2
body: { foo: baz }
- do:
index:
index: index_1
type: test
id: 3
body: { foo: foo }
- do:
index:
index: index_2
type: test
id: 1
body: { foo: foo }
- do:
indices.refresh: {}
---
"Basic multi-search template":
- do:
template.msearch:
body:
- index: index_*
- inline: '{"query": {"match": {"foo": "{{value}}"} } }'
params:
value: "foo"
- index: index_*
- inline: '{"query": {"match": {"{{field}}": "{{value}}"} } }'
params:
field: "foo"
value: "bar"
- index: _all
- inline: '{"query": {"{{query_type}}": {{query_content}} } }'
params:
query_type: "match_all"
query_content: "{}"
- index: _all
- inline:
query:
match: {foo: "{{text}}"}
size: 0
params:
text: "baz"
- match: { responses.0.hits.total: 2 }
- match: { responses.1.hits.total: 1 }
- match: { responses.2.hits.total: 4 }
- match: { responses.3.hits.total: 1 }
- length: { responses.3.hits.hits: 0 }
---
"Multi-search template with errors":
- do:
template.msearch:
body:
# Search 0 is OK
- index: index_*
- inline: '{"query": {"match": {"foo": "{{value}}"} } }'
params:
value: "foo"
# Search 1 has an unclosed JSON template
- index: index_*
- inline: '{"query": {"match": {'
params:
field: "foo"
value: "bar"
# Search 2 is OK
- index: _all
- inline:
query:
match: {foo: "{{text}}"}
params:
text: "baz"
# Search 3 has an unknown query type
- index: index_*
- inline: '{"query": {"{{query_type}}": {} }' # Unknown query type
params:
query_type: "unknown"
- match: { responses.0.hits.total: 2 }
- match: { responses.1.error.root_cause.0.type: json_parse_exception }
- match: { responses.1.error.root_cause.0.reason: "/Unexpected.end.of.input/" }
- match: { responses.2.hits.total: 1 }
- match: { responses.3.error.root_cause.0.type: parsing_exception }
- match: { responses.3.error.root_cause.0.reason: "/no.\\[query\\].registered.for.\\[unknown\\]/" }
---
"Multi-search template with invalid request":
- do:
catch: /(.)*action_request_validation_exception(.)*template.is.missing(.)*/
template.msearch:
body:
# Search 0 is OK
- index: index_*
- inline: '{"query": {"match": {"foo": "{{value}}"} } }'
params:
value: "foo"
# Search 1 has not template
- index: _all
- inline: ""
# Search 2 is OK
- index: index_*
- inline: '{"query": {"match": {"foo": "{{value}}"} } }'
params:
value: "bar"

View File

@ -0,0 +1,66 @@
---
setup:
- do:
index:
index: index_1
type: test
id: 1
body: { foo: bar }
- do:
index:
index: index_1
type: test
id: 2
body: { foo: baz }
- do:
index:
index: index_1
type: test
id: 3
body: { foo: foo }
- do:
index:
index: index_2
type: test
id: 1
body: { foo: foo }
- do:
indices.refresh: {}
---
"Basic multi-search using stored template":
- do:
put_template:
id: stored_template_1
body: {"template": {"query": {"match": {"{{field}}": "{{value}}" }}}}
- match: { acknowledged: true }
- do:
template.msearch:
body:
- index: index_*
- id: stored_template_1
params:
field: "foo"
value: "foo"
- index: _all
- id: stored_template_1
params:
field: "foo"
value: "bar"
- index: index_2
- id: stored_template_1
params:
field: "foo"
value: "foo"
- match: { responses.0.hits.total: 2 }
- match: { responses.1.hits.total: 1 }
- match: { responses.2.hits.total: 1 }

View File

@ -0,0 +1,72 @@
---
setup:
- do:
index:
index: index_1
type: test
id: 1
body: { foo: bar }
- do:
index:
index: index_1
type: test
id: 2
body: { foo: baz }
- do:
index:
index: index_1
type: test
id: 3
body: { foo: foo }
- do:
index:
index: index_2
type: test
id: 1
body: { foo: foo }
- do:
indices.refresh: {}
---
"Basic multi-search using file template":
- do:
render_search_template:
body: { "file": "template_1", "params": { "field": "foo", "value": "bar", "size": 20 } }
- match: { template_output.query.match.foo.query: "bar" }
- match: { template_output.query.match.foo.operator: "or" }
- match: { template_output.size: 20 }
- do:
template.msearch:
body:
- index: index_*
- file: template_1
params:
field: "foo"
value: "foo"
size: 10
- index: _all
- file: template_1
params:
field: "foo"
value: "bar"
operator: "and"
size: 50
- index: index_2
- file: template_1
params:
field: "foo"
value: "foo"
size: 0
- match: { responses.0.hits.total: 2 }
- match: { responses.1.hits.total: 1 }
- match: { responses.2.hits.total: 1 }

View File

@ -0,0 +1,32 @@
{
"template.msearch": {
"documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html",
"methods": ["GET", "POST"],
"url": {
"path": "/_msearch/template",
"paths": ["/_msearch/template", "/{index}/_msearch/template", "/{index}/{type}/_msearch/template"],
"parts": {
"index": {
"type" : "list",
"description" : "A comma-separated list of index names to use as default"
},
"type": {
"type" : "list",
"description" : "A comma-separated list of document types to use as default"
}
},
"params": {
"search_type": {
"type" : "enum",
"options" : ["query_then_fetch", "query_and_fetch", "dfs_query_then_fetch", "dfs_query_and_fetch"],
"description" : "Search operation type"
}
}
},
"body": {
"description": "The request definitions (metadata-search request definition pairs), separated by newlines",
"required" : true,
"serialize" : "bulk"
}
}
}

View File

@ -0,0 +1,56 @@
{
"template.search": {
"documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html",
"methods": ["GET", "POST"],
"url": {
"path": "/_search/template",
"paths": ["/_search/template", "/{index}/_search/template", "/{index}/{type}/_search/template"],
"parts": {
"index": {
"type" : "list",
"description" : "A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices"
},
"type": {
"type" : "list",
"description" : "A comma-separated list of document types to search; leave empty to perform the operation on all types"
}
},
"params" : {
"ignore_unavailable": {
"type" : "boolean",
"description" : "Whether specified concrete indices should be ignored when unavailable (missing or closed)"
},
"allow_no_indices": {
"type" : "boolean",
"description" : "Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)"
},
"expand_wildcards": {
"type" : "enum",
"options" : ["open","closed","none","all"],
"default" : "open",
"description" : "Whether to expand wildcard expression to concrete indices that are open, closed or both."
},
"preference": {
"type" : "string",
"description" : "Specify the node or shard the operation should be performed on (default: random)"
},
"routing": {
"type" : "list",
"description" : "A comma-separated list of specific routing values"
},
"scroll": {
"type" : "duration",
"description" : "Specify how long a consistent view of the index should be maintained for scrolled search"
},
"search_type": {
"type" : "enum",
"options" : ["query_then_fetch", "query_and_fetch", "dfs_query_then_fetch", "dfs_query_and_fetch"],
"description" : "Search operation type"
}
}
},
"body": {
"description": "The search definition template and its params"
}
}
}

View File

@ -0,0 +1,64 @@
---
setup:
- do:
index:
index: index_1
type: test
id: 1
body: { foo: bar }
- do:
index:
index: index_1
type: test
id: 2
body: { foo: baz }
- do:
index:
index: index_1
type: test
id: 3
body: { foo: foo }
- do:
index:
index: index_2
type: test
id: 1
body: { foo: foo }
- do:
indices.refresh: {}
---
"Basic multi-search":
- do:
msearch:
body:
- index: index_*
- query:
match: {foo: foo}
- index: index_2
- query:
match_all: {}
- index: index_1
- query:
match: {foo: foo}
- index: index_3
- query:
match_all: {}
- type: test
- query:
match_all: {}
- match: { responses.0.hits.total: 2 }
- match: { responses.1.hits.total: 1 }
- match: { responses.2.hits.total: 1 }
- match: { responses.3.error.root_cause.0.type: index_not_found_exception }
- match: { responses.3.error.root_cause.0.reason: "/no.such.index/" }
- match: { responses.3.error.root_cause.0.index: index_3 }
- match: { responses.4.hits.total: 4 }

View File

@ -28,8 +28,10 @@ import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.dfs.DfsPhase;
import org.elasticsearch.search.fetch.FetchPhase;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.query.QueryPhase;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.threadpool.ThreadPool;
@ -74,9 +76,8 @@ public class MockSearchService extends SearchService {
@Inject
public MockSearchService(Settings settings, ClusterSettings clusterSettings, ClusterService clusterService,
IndicesService indicesService, ThreadPool threadPool, ScriptService scriptService,
BigArrays bigArrays, FetchPhase fetchPhase, AggregatorParsers aggParsers, Suggesters suggesters) {
super(settings, clusterSettings, clusterService, indicesService, threadPool, scriptService, bigArrays,
fetchPhase, aggParsers, suggesters);
BigArrays bigArrays, FetchPhase fetchPhase) {
super(settings, clusterSettings, clusterService, indicesService, threadPool, scriptService, bigArrays, fetchPhase);
}
@Override