Skip to content

Commit

Permalink
Refactoring to allow for non quoted templates.
Browse files Browse the repository at this point in the history
As per suggestion in elastic#4879 modified the implementation to allow for templates
that are not quoted but legitimate json. Leaving the option to completely quote
in - needed anyway for support for referencing templates stored in the config
directory.

Relates to elastic#4879
  • Loading branch information
Isabel Drost-Fromm committed Feb 14, 2014
1 parent e1806a9 commit 21903db
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 22 deletions.
71 changes: 71 additions & 0 deletions docs/reference/query-dsl/queries/template-query.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
[[query-dsl-template-query]]
=== Template Query

A query that accepts a query template and a map of key/value pairs to fill in
template parameters.

[source,js]
------------------------------------------
GET _search
{
"query": {
"template": {
"query": {"match_{{template}}": {}},
"params" : {
"template" : "all"
}
}
}
}
------------------------------------------


Alternatively escaping the template works as well:

[source,js]
------------------------------------------
GET _search
{
"query": {
"template": {
"query": "{\"match_{{template}}\": {}}\"",
"params" : {
"template" : "all"
}
}
}
}
------------------------------------------

You register a template by storing it in the conf/scripts directory of
elasticsearch. In order to execute the stored template reference it in the query parameters:


[source,js]
------------------------------------------
GET _search
{
"query": {
"template": {
"query": "storedTemplate",
"params" : {
"template" : "all"
}
}
}
}
------------------------------------------


Templating is based on Mustache. Substitution of tokens works as follows:


[source,js]
------------------------------------------
"query": "{\"match_{{template}}\": {}}\"",
"params" : {
"template" : "all"
------------------------------------------

3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.8.13</version>
<optional>true</optional>
<!--optional>false</optional-->
</dependency>
<!-- Lucene spatial -->

Expand Down Expand Up @@ -506,6 +506,7 @@
<include>org.joda:joda-convert</include>
<include>io.netty:netty</include>
<include>com.ning:compress-lzf</include>
<include>com.github.spullara.mustache.java:compiler</include>
</includes>
</artifactSet>
<relocations>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
*/
package org.elasticsearch.index.query;

import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Map;

import org.elasticsearch.common.xcontent.XContentBuilder;

/**
* Facilitates creating template query requests.
* */
Expand All @@ -45,8 +45,8 @@ public TemplateQueryBuilder(String template, Map<String, Object> vars) {
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(TemplateQueryParser.NAME);
builder.field(TemplateQueryParser.STRING, template);
builder.field(TemplateQueryParser.VARS, vars);
builder.field(TemplateQueryParser.QUERY, template);
builder.field(TemplateQueryParser.PARAMS, vars);
builder.endObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import org.apache.lucene.search.Query;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;

Expand All @@ -41,9 +43,9 @@ public class TemplateQueryParser implements QueryParser {
/** Name to reference this type of query. */
public static final String NAME = "template";
/** Name of query parameter containing the template string. */
public static final String STRING = "query";
public static final String QUERY = "query";
/** Name of query parameter containing the template parameters. */
public static final String VARS = "params";
public static final String PARAMS = "params";
/** This is what we are registered with for query executions. */
private final ScriptService scriptService;

Expand All @@ -67,7 +69,6 @@ public String[] names() {
@Nullable
public Query parse(QueryParseContext parseContext) throws IOException {
XContentParser parser = parseContext.parser();

String template = "";
Map<String, Object> vars = new HashMap<String, Object>();

Expand All @@ -76,9 +77,17 @@ public Query parse(QueryParseContext parseContext) throws IOException {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (STRING.equals(currentFieldName)) {
template = (String) parser.objectText();
} else if (VARS.equals(currentFieldName)) {
} else if (QUERY.equals(currentFieldName)) {
if (! parser.hasTextCharacters()) {
// when called with un-escaped json string
XContentBuilder builder = XContentBuilder.builder(JsonXContent.jsonXContent);
builder.copyCurrentStructure(parser);
template = builder.string();
} else {
// when called with excaped json string or when called with filename
template = parser.bytes().utf8ToString();
}
} else if (PARAMS.equals(currentFieldName)) {
XContentParser.Token innerToken;
String key = "";
while ((innerToken = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,4 @@ public void testJSONGeneration() throws IOException {
content.close();
assertEquals(content.string(), "{\"template\":{\"query\":\"I am a $template string\",\"params\":{\"template\":\"filled\"}}}");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.elasticsearch.script.ScriptModule;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.threadpool.ThreadPoolModule;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
Expand All @@ -55,16 +56,15 @@
* */
public class TemplateQueryParserTest extends ElasticsearchTestCase {

@Test
public void testParser() throws IOException {
String templateString = "{\"template\": {"
+ "\"query\":\"{\\\"match_{{template}}\\\": {}}\","
+ "\"params\":{\"template\":\"all\"}}" + "}";

private Injector injector;
private QueryParseContext context;

@Before
public void setup() {
Settings settings = ImmutableSettings.Builder.EMPTY_SETTINGS;

Index index = new Index("test");
Injector injector = new ModulesBuilder().add(
injector = new ModulesBuilder().add(
new SettingsModule(settings),
new CacheRecyclerModule(settings),
new CodecModule(settings),
Expand All @@ -89,9 +89,16 @@ protected void configure() {
).createInjector();

IndexQueryParserService queryParserService = injector.getInstance(IndexQueryParserService.class);
context = new QueryParseContext(index, queryParserService);
}

@Test
public void testParser() throws IOException {
String templateString = "{\"template\": {"
+ "\"query\":{\"match_{{template}}\": {}},"
+ "\"params\":{\"template\":\"all\"}}" + "}";

XContentParser templateSourceParser = XContentFactory.xContent(templateString).createParser(templateString);
QueryParseContext context = new QueryParseContext(index, queryParserService);
context.reset(templateSourceParser);

TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
* Full integration test of the template query plugin.
* */
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE)
public class TemplateQueryPluginTest extends ElasticsearchIntegrationTest {
public class TemplateQueryTest extends ElasticsearchIntegrationTest {

@Test
public void testTemplateInBody() {
public void testTemplateInBody() throws IOException {
createIndex("test");
ensureGreen();

Expand Down Expand Up @@ -77,6 +78,61 @@ public void testTemplateInFile() {

}

@Test
public void testRawEscapedTemplate() throws IOException {
createIndex("test");
ensureGreen();

client().prepareIndex("test", "testtype").setId("1")
.setSource("text", "value1").get();
client().prepareIndex("test", "testtype").setId("2")
.setSource("text", "value2").get();
refresh();

String query = "{\"template\": {\"query\": \"{\\\"match_{{template}}\\\": {}}\\\"\",\"params\" : {\"template\" : \"all\"}}}";

SearchResponse sr = client().prepareSearch().setQuery(query)
.execute().actionGet();
assertEquals("Template query didn't return correct number of hits.", 2,
sr.getHits().totalHits());
}

@Test
public void testRawTemplate() throws IOException {
createIndex("test");
ensureGreen();

client().prepareIndex("test", "testtype").setId("1")
.setSource("text", "value1").get();
client().prepareIndex("test", "testtype").setId("2")
.setSource("text", "value2").get();
refresh();

String query = "{\"template\": {\"query\": {\"match_{{template}}\": {}},\"params\" : {\"template\" : \"all\"}}}";
SearchResponse sr = client().prepareSearch().setQuery(query)
.execute().actionGet();
assertEquals("Template query didn't return correct number of hits.", 2,
sr.getHits().totalHits());
}

@Test
public void testRawFSTemplate() throws IOException {
createIndex("test");
ensureGreen();

client().prepareIndex("test", "testtype").setId("1")
.setSource("text", "value1").get();
client().prepareIndex("test", "testtype").setId("2")
.setSource("text", "value2").get();
refresh();

String query = "{\"template\": {\"query\": \"storedTemplate\",\"params\" : {\"template\" : \"all\"}}}";

SearchResponse sr = client().prepareSearch().setQuery(query)
.execute().actionGet();
assertEquals("Template query didn't return correct number of hits.", 2,
sr.getHits().totalHits());
}
@Override
public Settings nodeSettings(int nodeOrdinal) {
String scriptPath = this.getClass()
Expand Down

0 comments on commit 21903db

Please sign in to comment.