Skip to content

Commit

Permalink
Search Templates: Add render endpoint for rendering templates
Browse files Browse the repository at this point in the history
This commit adds a render endpoint to allow rendering of templates.
````
POST /_render/template
'{
  "template":
  {
    "query":
      {
        "{{foo}}": {}
      },
    "size": "{{my_{{}}size}}"
   },
   "params": { "foo": "match_all", "my_size": 10}
}'
````
Will render
````
{
  "template" :
  {
    "query" : {
      "match_all" : {}
    },
    "size" : 10
  }
}
````

Closes elastic#6821
  • Loading branch information
GaelTadh committed Sep 25, 2014
1 parent 07ca08d commit 7a7acb0
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 34 deletions.
21 changes: 21 additions & 0 deletions docs/reference/search/search-template.asciidoc
Expand Up @@ -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.
20 changes: 20 additions & 0 deletions 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"
}
}
}
18 changes: 18 additions & 0 deletions 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"} }
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
72 changes: 38 additions & 34 deletions src/main/java/org/elasticsearch/search/SearchService.java
Expand Up @@ -597,54 +597,58 @@ 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());
} else {
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 {
Expand Down

0 comments on commit 7a7acb0

Please sign in to comment.