Search Templates: Adds API endpoint to render search templates as a response
Closes #6821
This commit is contained in:
parent
bbaf4710cb
commit
d9ab3cba77
|
@ -1,7 +1,6 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding//src/main/java=UTF-8
|
||||
encoding//src/main/resources=UTF-8
|
||||
encoding//src/test/java=UTF-8
|
||||
encoding//src/test/resources=UTF-8
|
||||
encoding/<project>=UTF-8
|
||||
encoding/rest-api-spec=UTF-8
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.action;
|
|||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
|
||||
import org.elasticsearch.action.admin.cluster.health.TransportClusterHealthAction;
|
||||
import org.elasticsearch.action.admin.cluster.node.hotthreads.NodesHotThreadsAction;
|
||||
|
@ -117,6 +118,8 @@ import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeAction;
|
|||
import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeSettingsAction;
|
||||
import org.elasticsearch.action.admin.indices.validate.query.TransportValidateQueryAction;
|
||||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryAction;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateAction;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.TransportRenderSearchTemplateAction;
|
||||
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerAction;
|
||||
import org.elasticsearch.action.admin.indices.warmer.delete.TransportDeleteWarmerAction;
|
||||
import org.elasticsearch.action.admin.indices.warmer.get.GetWarmersAction;
|
||||
|
@ -302,6 +305,7 @@ 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(PutIndexedScriptAction.INSTANCE, TransportPutIndexedScriptAction.class);
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.indices.validate.template;
|
||||
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class RenderSearchTemplateAction extends Action<RenderSearchTemplateRequest, RenderSearchTemplateResponse, RenderSearchTemplateRequestBuilder> {
|
||||
|
||||
public static final RenderSearchTemplateAction INSTANCE = new RenderSearchTemplateAction();
|
||||
public static final String NAME = "indices:admin/render/template/search";
|
||||
|
||||
public RenderSearchTemplateAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderSearchTemplateRequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new RenderSearchTemplateRequestBuilder(client, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderSearchTemplateResponse newResponse() {
|
||||
return new RenderSearchTemplateResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.indices.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 = Template.readTemplate(in);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.indices.validate.template;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestBuilder;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.script.Template;
|
||||
|
||||
public class RenderSearchTemplateRequestBuilder extends ActionRequestBuilder<RenderSearchTemplateRequest, RenderSearchTemplateResponse, RenderSearchTemplateRequestBuilder> {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.indices.validate.template;
|
||||
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
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.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RenderSearchTemplateResponse extends ActionResponse implements ToXContent {
|
||||
|
||||
private BytesReference source;
|
||||
|
||||
public BytesReference source() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void source(BytesReference source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
boolean hasSource = source != null;
|
||||
out.writeBoolean(hasSource);
|
||||
if (hasSource) {
|
||||
out.writeBytesReference(source);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
if (in.readBoolean()) {
|
||||
source = in.readBytesReference();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.rawField("template_output", source);
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.indices.validate.template;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.HandledTransportAction;
|
||||
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;
|
||||
|
||||
public class TransportRenderSearchTemplateAction extends HandledTransportAction<RenderSearchTemplateRequest, RenderSearchTemplateResponse> {
|
||||
|
||||
private final ScriptService scriptService;
|
||||
|
||||
@Inject
|
||||
protected TransportRenderSearchTemplateAction(ScriptService scriptService, Settings settings, ThreadPool threadPool,
|
||||
TransportService transportService, ActionFilters actionFilters) {
|
||||
super(settings, RenderSearchTemplateAction.NAME, threadPool, transportService, actionFilters, RenderSearchTemplateRequest.class);
|
||||
this.scriptService = scriptService;
|
||||
}
|
||||
|
||||
@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);
|
||||
BytesReference processedTemplate = (BytesReference) executable.run();
|
||||
RenderSearchTemplateResponse response = new RenderSearchTemplateResponse();
|
||||
response.source(processedTemplate);
|
||||
listener.onResponse(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,9 @@ package org.elasticsearch.client;
|
|||
|
||||
import org.elasticsearch.action.ActionFuture;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateRequest;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateResponse;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.bulk.BulkRequestBuilder;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
|
|
|
@ -102,6 +102,9 @@ import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeResponse;
|
|||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
|
||||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateRequest;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateResponse;
|
||||
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerRequest;
|
||||
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerResponse;
|
||||
|
@ -717,6 +720,27 @@ public interface IndicesAdminClient extends ElasticsearchClient {
|
|||
*/
|
||||
ValidateQueryRequestBuilder prepareValidateQuery(String... indices);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Puts an index search warmer to be applies when applicable.
|
||||
*/
|
||||
|
|
|
@ -204,6 +204,10 @@ import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryAction
|
|||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
|
||||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateAction;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateRequest;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateResponse;
|
||||
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerAction;
|
||||
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerRequest;
|
||||
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerRequestBuilder;
|
||||
|
@ -1594,6 +1598,21 @@ public abstract class AbstractClient extends AbstractComponent implements Client
|
|||
return new ValidateQueryRequestBuilder(this, ValidateQueryAction.INSTANCE).setIndices(indices);
|
||||
}
|
||||
|
||||
@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 ActionFuture<PutWarmerResponse> putWarmer(PutWarmerRequest request) {
|
||||
return execute(PutWarmerAction.INSTANCE, request);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.rest.action;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.multibindings.Multibinder;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
|
@ -76,6 +77,7 @@ 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.admin.indices.warmer.delete.RestDeleteWarmerAction;
|
||||
import org.elasticsearch.rest.action.admin.indices.warmer.get.RestGetWarmerAction;
|
||||
import org.elasticsearch.rest.action.admin.indices.warmer.put.RestPutWarmerAction;
|
||||
|
@ -207,6 +209,7 @@ public class RestActionModule extends AbstractModule {
|
|||
bind(RestSearchScrollAction.class).asEagerSingleton();
|
||||
bind(RestClearScrollAction.class).asEagerSingleton();
|
||||
bind(RestMultiSearchAction.class).asEagerSingleton();
|
||||
bind(RestRenderSearchTemplateAction.class).asEagerSingleton();
|
||||
|
||||
bind(RestValidateQueryAction.class).asEagerSingleton();
|
||||
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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.indices.validate.template.RenderSearchTemplateRequest;
|
||||
import org.elasticsearch.action.admin.indices.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 org.elasticsearch.script.mustache.MustacheScriptEngineService;
|
||||
|
||||
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, controller, 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);
|
||||
XContentParser parser = XContentFactory.xContent(source).createParser(source);
|
||||
String templateId = request.param("id");
|
||||
final Template template;
|
||||
if (templateId == null) {
|
||||
template = Template.parse(parser);
|
||||
} else {
|
||||
Map<String, Object> params = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token != XContentParser.Token.START_OBJECT) {
|
||||
throw new ElasticsearchParseException("request body must start with [" + XContentParser.Token.START_OBJECT + "] but found [" + token + "]");
|
||||
}
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (ScriptField.PARAMS.match(currentFieldName)) {
|
||||
if (token == XContentParser.Token.START_OBJECT) {
|
||||
params = parser.map();
|
||||
} else {
|
||||
throw new ElasticsearchParseException("Expected [" + XContentParser.Token.START_OBJECT + "] for [params] but found [" + token + "]");
|
||||
}
|
||||
} else {
|
||||
throw new ElasticsearchParseException("Unknown field [" + currentFieldName + "] of type [" + token + "]");
|
||||
}
|
||||
}
|
||||
template = new Template(templateId, ScriptType.INDEXED, MustacheScriptEngineService.NAME, null, params);
|
||||
}
|
||||
renderSearchTemplateRequest = new RenderSearchTemplateRequest();
|
||||
renderSearchTemplateRequest.template(template);
|
||||
client.admin().indices().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);
|
||||
}});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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.validate;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateResponse;
|
||||
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.script.ScriptService.ScriptType;
|
||||
import org.elasticsearch.script.Template;
|
||||
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
|
||||
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
@ElasticsearchIntegrationTest.SuiteScopeTest
|
||||
public class RenderSearchTemplateTests extends ElasticsearchIntegrationTest {
|
||||
|
||||
private static final String TEMPLATE_CONTENTS = "{\"size\":\"{{size}}\",\"query\":{\"match\":{\"foo\":\"{{value}}\"}},\"aggs\":{\"objects\":{\"terms\":{\"field\":\"{{value}}\",\"size\":\"{{size}}\"}}}}";
|
||||
|
||||
@Override
|
||||
protected void setupSuiteScopeCluster() throws Exception {
|
||||
client().preparePutIndexedScript(MustacheScriptEngineService.NAME, "index_template_1", "{ \"template\": " + TEMPLATE_CONTENTS + " }").get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings nodeSettings(int nodeOrdinal) {
|
||||
//Set path so ScriptService will pick up the test scripts
|
||||
return settingsBuilder().put(super.nodeSettings(nodeOrdinal))
|
||||
.put("path.conf", this.getDataPath("config")).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inlineTemplate() {
|
||||
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().indices().prepareRenderSearchTemplate().template(template).get();
|
||||
assertThat(response, notNullValue());
|
||||
BytesReference source = response.source();
|
||||
assertThat(source, notNullValue());
|
||||
Map<String, Object> sourceAsMap = XContentHelper.convertToMap(source, false).v2();
|
||||
assertThat(sourceAsMap, notNullValue());
|
||||
String expected = TEMPLATE_CONTENTS.replace("{{value}}", "bar").replace("{{size}}", "20");
|
||||
Map<String, Object> expectedMap = XContentHelper.convertToMap(new BytesArray(expected), false).v2();
|
||||
assertThat(sourceAsMap, equalTo(expectedMap));
|
||||
|
||||
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().indices().prepareRenderSearchTemplate().template(template).get();
|
||||
assertThat(response, notNullValue());
|
||||
source = response.source();
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexedTemplate() {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("value", "bar");
|
||||
params.put("size", 20);
|
||||
Template template = new Template("index_template_1", ScriptType.INDEXED, MustacheScriptEngineService.NAME, XContentType.JSON, params);
|
||||
RenderSearchTemplateResponse response = client().admin().indices().prepareRenderSearchTemplate().template(template).get();
|
||||
assertThat(response, notNullValue());
|
||||
BytesReference source = response.source();
|
||||
assertThat(source, notNullValue());
|
||||
Map<String, Object> sourceAsMap = XContentHelper.convertToMap(source, false).v2();
|
||||
assertThat(sourceAsMap, notNullValue());
|
||||
String expected = TEMPLATE_CONTENTS.replace("{{value}}", "bar").replace("{{size}}", "20");
|
||||
Map<String, Object> expectedMap = XContentHelper.convertToMap(new BytesArray(expected), false).v2();
|
||||
assertThat(sourceAsMap, equalTo(expectedMap));
|
||||
|
||||
params = new HashMap<>();
|
||||
params.put("value", "baz");
|
||||
params.put("size", 100);
|
||||
template = new Template("index_template_1", ScriptType.INDEXED, MustacheScriptEngineService.NAME, XContentType.JSON, params);
|
||||
response = client().admin().indices().prepareRenderSearchTemplate().template(template).get();
|
||||
assertThat(response, notNullValue());
|
||||
source = response.source();
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fileTemplate() {
|
||||
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().indices().prepareRenderSearchTemplate().template(template).get();
|
||||
assertThat(response, notNullValue());
|
||||
BytesReference source = response.source();
|
||||
assertThat(source, notNullValue());
|
||||
Map<String, Object> sourceAsMap = XContentHelper.convertToMap(source, false).v2();
|
||||
assertThat(sourceAsMap, notNullValue());
|
||||
String expected = TEMPLATE_CONTENTS.replace("{{value}}", "bar").replace("{{size}}", "20");
|
||||
Map<String, Object> expectedMap = XContentHelper.convertToMap(new BytesArray(expected), false).v2();
|
||||
assertThat(sourceAsMap, equalTo(expectedMap));
|
||||
|
||||
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().indices().prepareRenderSearchTemplate().template(template).get();
|
||||
assertThat(response, notNullValue());
|
||||
source = response.source();
|
||||
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));
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"size":"{{size}}","query":{"match":{"foo":"{{value}}"}},"aggs":{"objects":{"terms":{"field":"{{value}}","size":"{{size}}"}}}}
|
|
@ -298,3 +298,74 @@ GET /_search/template
|
|||
}
|
||||
------------------------------------------
|
||||
<1> Name of the the query template stored in the `.scripts` index.
|
||||
|
||||
[float]
|
||||
==== Validating templates
|
||||
|
||||
A template can be rendered in a response with given parameters using
|
||||
|
||||
[source,js]
|
||||
------------------------------------------
|
||||
GET /_render/template
|
||||
{
|
||||
"inline": {
|
||||
"query": {
|
||||
"terms": {
|
||||
"status": [
|
||||
"{{#status}}",
|
||||
"{{.}}",
|
||||
"{{/status}}"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"status": [ "pending", "published" ]
|
||||
}
|
||||
}
|
||||
------------------------------------------
|
||||
|
||||
This call will return the rendered template:
|
||||
|
||||
[source,js]
|
||||
------------------------------------------
|
||||
{
|
||||
"template_output": {
|
||||
"query": {
|
||||
"terms": {
|
||||
"status": [ <1>
|
||||
"pending",
|
||||
"published"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
------------------------------------------
|
||||
<1> `status` array has been populated with values from the `params` object.
|
||||
|
||||
File and indexed templates can also be rendered by replacing `inline` with
|
||||
`file` or `id` respectively. For example, to render a file template
|
||||
|
||||
[source,js]
|
||||
------------------------------------------
|
||||
GET /_render/template
|
||||
{
|
||||
"file": "my_template",
|
||||
"params": {
|
||||
"status": [ "pending", "published" ]
|
||||
}
|
||||
}
|
||||
------------------------------------------
|
||||
|
||||
Pre-registered templates can also be rendered using
|
||||
|
||||
[source,js]
|
||||
------------------------------------------
|
||||
GET /_render/template/<template_name>
|
||||
{
|
||||
"params": {
|
||||
"...
|
||||
}
|
||||
}
|
||||
------------------------------------------
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"render_search_template": {
|
||||
"documentation": "http://www.elasticsearch.org/guide/en/elasticsearch/reference/master/search-template.html",
|
||||
"methods": ["GET", "POST"],
|
||||
"url": {
|
||||
"path": "/_render/template",
|
||||
"paths": [ "/_render/template", "/_render/template/{id}" ],
|
||||
"parts": {
|
||||
"id": {
|
||||
"type" : "string",
|
||||
"description" : "The id of the stored search template"
|
||||
}
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"description": "The search definition template and its params"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
"Indexed Template validate tests":
|
||||
|
||||
- do:
|
||||
put_template:
|
||||
id: "1"
|
||||
body: { "template": { "query": { "match": { "text": "{{my_value}}" } }, "aggs": { "my_terms": { "terms": { "field": "{{my_field}}" } } } } }
|
||||
- match: { _id: "1" }
|
||||
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
body: { "id": "1", "params": { "my_value": "foo", "my_field": "field1" } }
|
||||
|
||||
- match: { template_output.query.match.text: "foo" }
|
||||
- match: { template_output.aggs.my_terms.terms.field: "field1" }
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
body: { "id": "1", "params": { "my_value": "bar", "my_field": "my_other_field" } }
|
||||
|
||||
- match: { template_output.query.match.text: "bar" }
|
||||
- match: { template_output.aggs.my_terms.terms.field: "my_other_field" }
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
id: "1"
|
||||
body: { "params": { "my_value": "bar", "my_field": "field1" } }
|
||||
|
||||
- match: { template_output.query.match.text: "bar" }
|
||||
- match: { template_output.aggs.my_terms.terms.field: "field1" }
|
||||
|
||||
---
|
||||
"Inline Template validate tests":
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
body: { "inline": { "query": { "match": { "text": "{{my_value}}" } }, "aggs": { "my_terms": { "terms": { "field": "{{my_field}}" } } } }, "params": { "my_value": "foo", "my_field": "field1" } }
|
||||
|
||||
- match: { template_output.query.match.text: "foo" }
|
||||
- match: { template_output.aggs.my_terms.terms.field: "field1" }
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
body: { "inline": { "query": { "match": { "text": "{{my_value}}" } }, "aggs": { "my_terms": { "terms": { "field": "{{my_field}}" } } } }, "params": { "my_value": "bar", "my_field": "my_other_field" } }
|
||||
|
||||
- match: { template_output.query.match.text: "bar" }
|
||||
- match: { template_output.aggs.my_terms.terms.field: "my_other_field" }
|
||||
|
||||
- do:
|
||||
catch: /Improperly.closed.variable.in.query-template/
|
||||
render_search_template:
|
||||
body: { "inline": { "query": { "match": { "text": "{{{my_value}}" } }, "aggs": { "my_terms": { "terms": { "field": "{{my_field}}" } } } }, "params": { "my_value": "bar", "my_field": "field1" } }
|
||||
---
|
||||
"Escaped Indexed Template validate tests":
|
||||
|
||||
- do:
|
||||
put_template:
|
||||
id: "1"
|
||||
body: { "template": "{ \"query\": { \"match\": { \"text\": \"{{my_value}}\" } }, \"size\": {{my_size}} }" }
|
||||
- match: { _id: "1" }
|
||||
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
body: { "id": "1", "params": { "my_value": "foo", "my_size": 20 } }
|
||||
|
||||
- match: { template_output.query.match.text: "foo" }
|
||||
- match: { template_output.size: 20 }
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
body: { "id": "1", "params": { "my_value": "bar", "my_size": 100 } }
|
||||
|
||||
- match: { template_output.query.match.text: "bar" }
|
||||
- match: { template_output.size: 100 }
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
id: "1"
|
||||
body: { "params": { "my_value": "bar", "my_size": 100 } }
|
||||
|
||||
- match: { template_output.query.match.text: "bar" }
|
||||
- match: { template_output.size: 100 }
|
||||
|
||||
---
|
||||
"Escaped Inline Template validate tests":
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
body: { "inline": "{ \"query\": { \"match\": { \"text\": \"{{my_value}}\" } }, \"size\": {{my_size}} }", "params": { "my_value": "foo", "my_size": 20 } }
|
||||
|
||||
- match: { template_output.query.match.text: "foo" }
|
||||
- match: { template_output.size: 20 }
|
||||
|
||||
- do:
|
||||
render_search_template:
|
||||
body: { "inline": "{ \"query\": { \"match\": { \"text\": \"{{my_value}}\" } }, \"size\": {{my_size}} }", "params": { "my_value": "bar", "my_size": 100 } }
|
||||
|
||||
- match: { template_output.query.match.text: "bar" }
|
||||
- match: { template_output.size: 100 }
|
||||
|
||||
- do:
|
||||
catch: /Improperly.closed.variable.in.query-template/
|
||||
render_search_template:
|
||||
body: { "inline": "{ \"query\": { \"match\": { \"text\": \"{{{my_value}}\" } }, \"size\": {{my_size}} }", "params": { "my_value": "bar", "my_size": 100 } }
|
Loading…
Reference in New Issue