diff --git a/pom.xml b/pom.xml index b83085a4607fd..78ba573cb5aac 100644 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,14 @@ - + + + com.github.spullara.mustache.java + compiler + 0.8.13 + true + + diff --git a/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java new file mode 100644 index 0000000000000..659366ba1aabe --- /dev/null +++ b/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java @@ -0,0 +1,52 @@ +/** + * 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.index.query; + +import java.io.IOException; +import java.util.Map; + +import org.elasticsearch.common.xcontent.XContentBuilder; + +/** + * Facilitates creating template query requests. + * */ +public class TemplateQueryBuilder extends BaseQueryBuilder { + + /** Parameters to fill the template with. */ + private Map vars; + /** Template to fill.*/ + private String template; + + /** + * @param template the template to use for that query. + * @param vars the parameters to fill the template with. + * */ + public TemplateQueryBuilder(String template, Map vars) { + this.template = template; + this.vars = 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.endObject(); + } +} diff --git a/src/main/java/org/elasticsearch/index/query/TemplateQueryParser.java b/src/main/java/org/elasticsearch/index/query/TemplateQueryParser.java new file mode 100644 index 0000000000000..a713d4f536ea3 --- /dev/null +++ b/src/main/java/org/elasticsearch/index/query/TemplateQueryParser.java @@ -0,0 +1,111 @@ +/** + * 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.index.query; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.lucene.search.Query; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.script.ExecutableScript; +import org.elasticsearch.script.ScriptService; + +/** + * In the simplest case, parse template string and variables from the request, compile the template and + * execute the template against the given variables. + * + * TODO support named templates + * */ +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 = "template_string"; + /** Name of query parameter containing the template parameters. */ + public static final String VARS = "template_vars"; + /** This is what we are registered with for query executions. */ + private final ScriptService scriptService; + + /** + * @param scriptService will automatically be wired by Guice + * */ + @Inject + public TemplateQueryParser(ScriptService scriptService) { + this.scriptService = scriptService; + } + + /** + * @return a list of names this query is registered under. + * */ + @Override + public String[] names() { + return new String[] {NAME}; + } + + @Override + @Nullable + public Query parse(QueryParseContext parseContext) throws IOException { + XContentParser parser = parseContext.parser(); + + String template = ""; + Map vars = new HashMap(); + + String currentFieldName = null; + XContentParser.Token token; + 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)) { + XContentParser.Token innerToken; + String key = ""; + while ((innerToken = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + // parsing template parameter map + if (innerToken == XContentParser.Token.FIELD_NAME) { + key = parser.currentName(); + } else { + vars.put(key, parser.objectText()); + key = ""; + } + } + } + } + + ExecutableScript executable = this.scriptService.executable("mustache", template, vars); + String querySource = (String) executable.run(); + + XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource); + try { + final QueryParseContext context = new QueryParseContext(parseContext.index(), parseContext.indexQueryParser); + context.reset(qSourceParser); + Query result = context.parseInnerQuery(); + parser.nextToken(); + return result; + } finally { + qSourceParser.close(); + } + } +} diff --git a/src/main/java/org/elasticsearch/indices/query/IndicesQueriesModule.java b/src/main/java/org/elasticsearch/indices/query/IndicesQueriesModule.java index da7029f6ea3c0..44a9474669af3 100644 --- a/src/main/java/org/elasticsearch/indices/query/IndicesQueriesModule.java +++ b/src/main/java/org/elasticsearch/indices/query/IndicesQueriesModule.java @@ -103,6 +103,7 @@ protected void configure() { qpBinders.addBinding().to(SpanMultiTermQueryParser.class).asEagerSingleton(); qpBinders.addBinding().to(FunctionScoreQueryParser.class).asEagerSingleton(); qpBinders.addBinding().to(SimpleQueryStringParser.class).asEagerSingleton(); + qpBinders.addBinding().to(TemplateQueryParser.class).asEagerSingleton(); if (ShapesAvailability.JTS_AVAILABLE) { qpBinders.addBinding().to(GeoShapeQueryParser.class).asEagerSingleton(); diff --git a/src/main/java/org/elasticsearch/script/ScriptModule.java b/src/main/java/org/elasticsearch/script/ScriptModule.java index bee40a4fb881e..22a2230937673 100644 --- a/src/main/java/org/elasticsearch/script/ScriptModule.java +++ b/src/main/java/org/elasticsearch/script/ScriptModule.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.inject.multibindings.MapBinder; import org.elasticsearch.common.inject.multibindings.Multibinder; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.script.mustache.MustacheScriptEngineService; import org.elasticsearch.script.mvel.MvelScriptEngineService; import java.util.List; @@ -80,6 +81,13 @@ protected void configure() { } catch (Throwable t) { // no MVEL } + + try { + multibinder.addBinding().to(MustacheScriptEngineService.class); + } catch (Throwable t) { + // no Mustache + } + for (Class scriptEngine : scriptEngines) { multibinder.addBinding().to(scriptEngine); } diff --git a/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngineService.java b/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngineService.java new file mode 100644 index 0000000000000..953ffbcca78ae --- /dev/null +++ b/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngineService.java @@ -0,0 +1,157 @@ +/** + * 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.script.mustache; + +import com.fasterxml.jackson.core.io.SegmentedStringWriter; +import com.fasterxml.jackson.core.util.BufferRecycler; +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; +import com.github.mustachejava.MustacheFactory; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.component.AbstractComponent; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.io.FastStringReader; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.script.ExecutableScript; +import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.SearchScript; +import org.elasticsearch.search.lookup.SearchLookup; + +import java.io.StringWriter; +import java.util.Map; + +/** + * Main entry point handling template registration, compilation and + * execution. + * + * Template handling is based on Mustache. Template handling is a two step + * process: First compile the string representing the template, the resulting + * {@link Mustache} object can then be re-used for subsequent executions. + */ +public class MustacheScriptEngineService extends AbstractComponent implements ScriptEngineService { + + /** Factory to generate Mustache objects from. */ + private static final MustacheFactory MFACTORY = new DefaultMustacheFactory(); + + /** + * @param settings automatically wired by Guice. + * */ + @Inject + public MustacheScriptEngineService(Settings settings) { + super(settings); + } + + /** + * Compile a template string to (in this case) a Mustache object than can + * later be re-used for execution to fill in missing parameter values. + * + * @param template + * a string representing the template to compile. + * @return a compiled template object for later execution. + * */ + public Object compile(String template) { + return MFACTORY.compile(new FastStringReader(template), "query-template"); + } + + /** + * Execute a compiled template object (as retrieved from the compile method) + * and fill potential place holders with the variables given. + * + * @param template + * compiled template object. + * @param vars + * map of variables to use during substitution. + * + * @return the processed string with all given variables substitued. + * */ + public Object execute(Object template, Map vars) { + SegmentedStringWriter result = new SegmentedStringWriter(new BufferRecycler()); + ((Mustache) template).execute(result, vars); + return result.getAndClear(); + } + + @Override + public String[] types() { + return new String[] {"mustache"}; + } + + @Override + public String[] extensions() { + return new String[] {"mustache"}; + } + + @Override + public ExecutableScript executable(Object mustache, + @Nullable Map vars) { + return new MustacheExecutableScript((Mustache) mustache, vars); + } + + @Override + public SearchScript search(Object compiledScript, SearchLookup lookup, + @Nullable Map vars) { + throw new UnsupportedOperationException(); + } + + @Override + public Object unwrap(Object value) { + return value; + } + + @Override + public void close() { + // Nothing to do here + } + + /** + * Used at query execution time by script service in order to execute a query template. + * */ + private class MustacheExecutableScript implements ExecutableScript { + /** Compiled template object. */ + private Mustache mustache; + /** Parameters to fill above object with. */ + private Map vars; + + /** + * @param mustache the compiled template object + * @param vars the parameters to fill above object with + **/ + public MustacheExecutableScript(Mustache mustache, + Map vars) { + this.mustache = mustache; + this.vars = vars; + } + + @Override + public void setNextVar(String name, Object value) { + this.vars.put(name, value); + } + + @Override + public Object run() { + StringWriter result = new StringWriter(); + ((Mustache) mustache).execute(result, vars); + return result.toString(); + } + + @Override + public Object unwrap(Object value) { + return value; + } + } +} diff --git a/src/test/java/org/elasticsearch/index/query/TemplateQueryBuilderTest.java b/src/test/java/org/elasticsearch/index/query/TemplateQueryBuilderTest.java new file mode 100644 index 0000000000000..b34883b6c5094 --- /dev/null +++ b/src/test/java/org/elasticsearch/index/query/TemplateQueryBuilderTest.java @@ -0,0 +1,49 @@ +/** + * 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.index.query; + +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.junit.Test; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +/** + * Test building and serialising a template search request. + * */ +public class TemplateQueryBuilderTest { + + @Test + public void testJSONGeneration() throws IOException { + Map vars = new HashMap(); + vars.put("template", "filled"); + TemplateQueryBuilder builder = new TemplateQueryBuilder("I am a $template string", vars); + XContentBuilder content = XContentFactory.jsonBuilder(); + content.startObject(); + builder.doXContent(content, null); + content.endObject(); + content.close(); + assertEquals(content.string(), "{\"template\":{\"template_string\":\"I am a $template string\",\"template_vars\":{\"template\":\"filled\"}}}"); + } + +} diff --git a/src/test/java/org/elasticsearch/index/query/TemplateQueryParserTest.java b/src/test/java/org/elasticsearch/index/query/TemplateQueryParserTest.java new file mode 100644 index 0000000000000..e39888bdcae36 --- /dev/null +++ b/src/test/java/org/elasticsearch/index/query/TemplateQueryParserTest.java @@ -0,0 +1,101 @@ +/** + * 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.index.query; + +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.Query; +import org.elasticsearch.cache.recycler.CacheRecyclerModule; +import org.elasticsearch.cluster.ClusterService; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Injector; +import org.elasticsearch.common.inject.ModulesBuilder; +import org.elasticsearch.common.inject.util.Providers; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.settings.SettingsModule; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.IndexNameModule; +import org.elasticsearch.index.analysis.AnalysisModule; +import org.elasticsearch.index.cache.IndexCacheModule; +import org.elasticsearch.index.codec.CodecModule; +import org.elasticsearch.index.engine.IndexEngineModule; +import org.elasticsearch.index.query.functionscore.FunctionScoreModule; +import org.elasticsearch.index.settings.IndexSettingsModule; +import org.elasticsearch.index.similarity.SimilarityModule; +import org.elasticsearch.indices.fielddata.breaker.CircuitBreakerService; +import org.elasticsearch.indices.fielddata.breaker.DummyCircuitBreakerService; +import org.elasticsearch.indices.query.IndicesQueriesModule; +import org.elasticsearch.script.ScriptModule; +import org.elasticsearch.test.ElasticsearchTestCase; +import org.elasticsearch.threadpool.ThreadPoolModule; +import org.junit.Test; + +import java.io.IOException; + +/** + * Test parsing and executing a template request. + * */ +public class TemplateQueryParserTest extends ElasticsearchTestCase { + + @Test + public void testParser() throws IOException { + String templateString = "{\"template\": {" + + "\"template_string\":\"{\\\"match_{{template}}\\\": {}}\"," + + "\"template_vars\":{\"template\":\"all\"}}" + "}"; + + Settings settings = ImmutableSettings.Builder.EMPTY_SETTINGS; + + Index index = new Index("test"); + Injector injector = new ModulesBuilder().add( + new SettingsModule(settings), + new CacheRecyclerModule(settings), + new CodecModule(settings), + new ThreadPoolModule(settings), + new IndicesQueriesModule(), + new ScriptModule(settings), + new IndexSettingsModule(index, settings), + new IndexCacheModule(settings), + new AnalysisModule(settings), + new IndexEngineModule(settings), + new SimilarityModule(settings), + new IndexNameModule(index), + new IndexQueryParserModule(settings), + new FunctionScoreModule(), + new AbstractModule() { + @Override + protected void configure() { + bind(ClusterService.class).toProvider(Providers.of((ClusterService) null)); + bind(CircuitBreakerService.class).to(DummyCircuitBreakerService.class); + } + } + ).createInjector(); + + IndexQueryParserService queryParserService = injector.getInstance(IndexQueryParserService.class); + + XContentParser templateSourceParser = XContentFactory.xContent(templateString).createParser(templateString); + QueryParseContext context = new QueryParseContext(index, queryParserService); + context.reset(templateSourceParser); + + TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class); + Query query = parser.parse(context); + assertTrue("Parsing template query failed.", query instanceof ConstantScoreQuery); + } +} diff --git a/src/test/java/org/elasticsearch/index/query/TemplateQueryPluginTest.java b/src/test/java/org/elasticsearch/index/query/TemplateQueryPluginTest.java new file mode 100644 index 0000000000000..0417796513954 --- /dev/null +++ b/src/test/java/org/elasticsearch/index/query/TemplateQueryPluginTest.java @@ -0,0 +1,91 @@ +/** + * 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.index.query; + +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.junit.Test; + +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 { + + @Test + public void testTemplateInBody() { + createIndex("test"); + ensureGreen(); + + client().prepareIndex("test", "testtype").setId("1") + .setSource("text", "value1").get(); + client().prepareIndex("test", "testtype").setId("2") + .setSource("text", "value2").get(); + refresh(); + + Map vars = new HashMap(); + vars.put("template", "all"); + + TemplateQueryBuilder builder = new TemplateQueryBuilder( + "{\"match_{{template}}\": {}}\"", vars); + SearchResponse sr = client().prepareSearch().setQuery(builder) + .execute().actionGet(); + assertEquals("Template query didn't return correct number of hits.", 2, + sr.getHits().totalHits()); + } + + @Test + public void testTemplateInFile() { + createIndex("test"); + ensureGreen(); + + client().prepareIndex("test", "testtype").setId("1") + .setSource("text", "value1").get(); + client().prepareIndex("test", "testtype").setId("2") + .setSource("text", "value2").get(); + refresh(); + + Map vars = new HashMap(); + vars.put("template", "all"); + TemplateQueryBuilder builder = new TemplateQueryBuilder( + "storedTemplate", vars); + SearchResponse sr = client().prepareSearch().setQuery(builder) + .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() + .getResource("config").getPath(); + + Settings settings = ImmutableSettings + .settingsBuilder() + .put("path.conf", scriptPath).build(); + + return settings; + } +} diff --git a/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTest.java b/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTest.java new file mode 100644 index 0000000000000..2d86257afe5e0 --- /dev/null +++ b/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTest.java @@ -0,0 +1,53 @@ +/** + * 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.script.mustache; + +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.Map; + +import org.elasticsearch.common.settings.ImmutableSettings; +import org.junit.Before; +import org.junit.Test; + +/** + * Mustache based templating test + * */ +public class MustacheScriptEngineTest { + private MustacheScriptEngineService qe; + + private static String TEMPLATE = "GET _search {\"query\": " + "{\"boosting\": {" + "\"positive\": {\"match\": {\"body\": \"gift\"}}," + + "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}" + "}}, \"negative_boost\": {{boost_val}} } }}"; + + @Before + public void setup() { + qe = new MustacheScriptEngineService(ImmutableSettings.Builder.EMPTY_SETTINGS); + } + + @Test + public void testSimpleParameterReplace() { + Map vars = new HashMap(); + vars.put("boost_val", "0.3"); + Object o = qe.execute(qe.compile(TEMPLATE), vars); + assertEquals("GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}}," + + "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}}}, \"negative_boost\": 0.3 } }}", ((String) o)); + } + +} diff --git a/src/test/java/org/elasticsearch/script/mustache/MustacheTest.java b/src/test/java/org/elasticsearch/script/mustache/MustacheTest.java new file mode 100644 index 0000000000000..d887fa1d14c41 --- /dev/null +++ b/src/test/java/org/elasticsearch/script/mustache/MustacheTest.java @@ -0,0 +1,58 @@ +/** + * 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.script.mustache; + +import static org.junit.Assert.*; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.HashMap; + +import org.junit.Test; + +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; +import com.github.mustachejava.MustacheFactory; + +/** + * Figure out how Mustache works for the simplest use case. Leaving in here for now for reference. + * */ +public class MustacheTest { + + @Test + public void test() { + HashMap scopes = new HashMap(); + scopes.put("boost_val", "0.2"); + + String template = "GET _search {\"query\": " + "{\"boosting\": {" + + "\"positive\": {\"match\": {\"body\": \"gift\"}}," + + "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}" + + "}}, \"negative_boost\": {{boost_val}} } }}"; + MustacheFactory f = new DefaultMustacheFactory(); + Mustache mustache = f.compile(new StringReader(template), "example"); + StringWriter writer = new StringWriter(); + mustache.execute(writer, scopes); + writer.flush(); + assertEquals( + "Mustache templating broken", + "GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}}," + + "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}}}, \"negative_boost\": 0.2 } }}", + writer.toString()); + } +} diff --git a/src/test/resources/org/elasticsearch/index/query/config/scripts/storedTemplate.mustache b/src/test/resources/org/elasticsearch/index/query/config/scripts/storedTemplate.mustache new file mode 100644 index 0000000000000..a779da7c467c5 --- /dev/null +++ b/src/test/resources/org/elasticsearch/index/query/config/scripts/storedTemplate.mustache @@ -0,0 +1,3 @@ +{ + "match_{{template}}": {} +}