diff --git a/docs/reference/search/search-template.asciidoc b/docs/reference/search/search-template.asciidoc index 9f836ddd663b6..4d9aeca6ce2bd 100644 --- a/docs/reference/search/search-template.asciidoc +++ b/docs/reference/search/search-template.asciidoc @@ -288,3 +288,24 @@ GET /_search/template } ------------------------------------------ <1> Name of the the query template stored in the .scripts index. +[added 1.5.0] +===== Render template + +You can get the rendered version of a template by calling +[source,js] +GET /_render/template +------------------------------------------ +GET /_render/template +{ + "template": { + <1> + }, + "params": { + <2> + } +} +------------------------------------------ +<1> The template as defined by the _search/template enpoint +<2> The params for this template + +This will return a rendered version of this template. \ No newline at end of file diff --git a/rest-api-spec/api/render_template.json b/rest-api-spec/api/render_template.json new file mode 100644 index 0000000000000..e28685e36dea9 --- /dev/null +++ b/rest-api-spec/api/render_template.json @@ -0,0 +1,20 @@ +{ + "render_template": { + "documentation": "http://www.elasticsearch.org/guide/en/elasticsearch/reference/master/search-template.html", + "methods": ["GET", "POST"], + "url": { + "path": "/_render/template", + "paths": [ "/_render/template" ], + "parts": { + "id": { + "type" : "string", + "description" : "Template ID", + "required" : true + } + } + }, + "body": { + "description": "The search definition template and its params" + } + } +} \ No newline at end of file diff --git a/rest-api-spec/test/template/30_render.yaml b/rest-api-spec/test/template/30_render.yaml new file mode 100644 index 0000000000000..3cdc439f3ff8e --- /dev/null +++ b/rest-api-spec/test/template/30_render.yaml @@ -0,0 +1,18 @@ +--- +"Render Template": + + - do: + render_template: + body: { "template": { "query": { "{{foo}}": {}}, "size": "{{my_size}}" }, "params": { "foo": "match_all", "my_size": 10} } + - match: { "template": { "query": { "match_all": {} }, "size": "10"} } + + - do: + put_template: + id: "1" + body: { "template": { "query": { "{{foo}}": {}}, "size": "{{my_size}}" } } + - match: { _id: "1" } + + - do: + render_template: + body: { "template": { "id" : 1 }, "params": { "foo": "match_all", "my_size": 10} } + - match: { "template": { "query": { "match_all": {} }, "size": "10"} } diff --git a/src/main/java/org/elasticsearch/rest/action/RestActionModule.java b/src/main/java/org/elasticsearch/rest/action/RestActionModule.java index 1eb9016f40f73..73299ee58ec3b 100644 --- a/src/main/java/org/elasticsearch/rest/action/RestActionModule.java +++ b/src/main/java/org/elasticsearch/rest/action/RestActionModule.java @@ -106,6 +106,7 @@ import org.elasticsearch.rest.action.template.RestDeleteSearchTemplateAction; import org.elasticsearch.rest.action.template.RestGetSearchTemplateAction; import org.elasticsearch.rest.action.template.RestPutSearchTemplateAction; +import org.elasticsearch.rest.action.template.RestRenderSearchTemplate; import org.elasticsearch.rest.action.termvector.RestMultiTermVectorsAction; import org.elasticsearch.rest.action.termvector.RestTermVectorAction; import org.elasticsearch.rest.action.update.RestUpdateAction; @@ -226,6 +227,7 @@ protected void configure() { bind(RestGetSearchTemplateAction.class).asEagerSingleton(); bind(RestPutSearchTemplateAction.class).asEagerSingleton(); bind(RestDeleteSearchTemplateAction.class).asEagerSingleton(); + bind(RestRenderSearchTemplate.class).asEagerSingleton(); // Scripts API bind(RestGetIndexedScriptAction.class).asEagerSingleton(); diff --git a/src/main/java/org/elasticsearch/search/SearchService.java b/src/main/java/org/elasticsearch/search/SearchService.java index 03cbb7995d471..027198ccc7eda 100644 --- a/src/main/java/org/elasticsearch/search/SearchService.java +++ b/src/main/java/org/elasticsearch/search/SearchService.java @@ -597,7 +597,6 @@ private void cleanContext(SearchContext context) { } private void parseTemplate(ShardSearchRequest request) { - final ExecutableScript executable; if (hasLength(request.templateName())) { executable = this.scriptService.executable("mustache", request.templateName(), request.templateType(), request.templateParams()); @@ -605,46 +604,51 @@ private void parseTemplate(ShardSearchRequest request) { if (!hasLength(request.templateSource())) { return; } - XContentParser parser = null; - TemplateQueryParser.TemplateContext templateContext = null; + TemplateQueryParser.TemplateContext templateContext = getTemplateContext(request.templateSource()); + executable = this.scriptService.executable("mustache", templateContext.template(), templateContext.scriptType(), templateContext.params()); + } - try { - parser = XContentFactory.xContent(request.templateSource()).createParser(request.templateSource()); - templateContext = TemplateQueryParser.parse(parser, "params", "template"); + BytesReference processedQuery = (BytesReference) executable.run(); + request.source(processedQuery); + } - if (templateContext.scriptType().equals(ScriptService.ScriptType.INLINE)) { - //Try to double parse for nested template id/file - parser = null; - try { - byte[] templateBytes = templateContext.template().getBytes(Charsets.UTF_8); - parser = XContentFactory.xContent(templateBytes).createParser(templateBytes); - } catch (ElasticsearchParseException epe) { - //This was an non-nested template, the parse failure was due to this, it is safe to assume this refers to a file - //for backwards compatibility and keep going - templateContext = new TemplateQueryParser.TemplateContext(ScriptService.ScriptType.FILE, templateContext.template(), templateContext.params()); - } - if (parser != null) { - TemplateQueryParser.TemplateContext innerContext = TemplateQueryParser.parse(parser, "params"); - if (hasLength(innerContext.template()) && !innerContext.scriptType().equals(ScriptService.ScriptType.INLINE)) { - //An inner template referring to a filename or id - templateContext = new TemplateQueryParser.TemplateContext(innerContext.scriptType(), innerContext.template(), templateContext.params()); - } + public static TemplateQueryParser.TemplateContext getTemplateContext(BytesReference templateSource) { + XContentParser parser = null; + TemplateQueryParser.TemplateContext templateContext = null; + + try { + parser = XContentFactory.xContent(templateSource).createParser(templateSource); + templateContext = TemplateQueryParser.parse(parser, "params", "template"); + + if (templateContext.scriptType().equals(ScriptService.ScriptType.INLINE)) { + //Try to double parse for nested template id/file + parser = null; + try { + byte[] templateBytes = templateContext.template().getBytes(Charsets.UTF_8); + parser = XContentFactory.xContent(templateBytes).createParser(templateBytes); + } catch (ElasticsearchParseException epe) { + //This was an non-nested template, the parse failure was due to this, it is safe to assume this refers to a file + //for backwards compatibility and keep going + templateContext = new TemplateQueryParser.TemplateContext(ScriptService.ScriptType.FILE, templateContext.template(), templateContext.params()); + } + if (parser != null) { + TemplateQueryParser.TemplateContext innerContext = TemplateQueryParser.parse(parser, "params"); + if (hasLength(innerContext.template()) && !innerContext.scriptType().equals(ScriptService.ScriptType.INLINE)) { + //An inner template referring to a filename or id + templateContext = new TemplateQueryParser.TemplateContext(innerContext.scriptType(), innerContext.template(), templateContext.params()); } } - } catch (IOException e) { - throw new ElasticsearchParseException("Failed to parse template", e); - } finally { - Releasables.closeWhileHandlingException(parser); - } - - if (templateContext == null || !hasLength(templateContext.template())) { - throw new ElasticsearchParseException("Template must have [template] field configured"); } - executable = this.scriptService.executable("mustache", templateContext.template(), templateContext.scriptType(), templateContext.params()); + } catch (IOException e) { + throw new ElasticsearchParseException("Failed to parse template", e); + } finally { + Releasables.closeWhileHandlingException(parser); } - BytesReference processedQuery = (BytesReference) executable.run(); - request.source(processedQuery); + if (templateContext == null || !hasLength(templateContext.template())) { + throw new ElasticsearchParseException("Template must have [template] field configured"); + } + return templateContext; } private void parseSource(SearchContext context, BytesReference source) throws SearchParseException {