Groovy Plugin: Allow to run use groovy for scripts (where applicable), closes #398.

This commit is contained in:
kimchy 2010-10-02 01:22:05 +02:00
parent 5c1c580b84
commit 9e8ebd46e8
28 changed files with 616 additions and 26 deletions

View File

@ -7,6 +7,8 @@
<content url="file://$MODULE_DIR$/../../plugins/lang/groovy">
<sourceFolder url="file://$MODULE_DIR$/../../plugins/lang/groovy/src/main/groovy" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../plugins/lang/groovy/src/test/groovy" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../../plugins/lang/groovy/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../plugins/lang/groovy/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/../../plugins/client/groovy/work" />
<excludeFolder url="file://$MODULE_DIR$/../../plugins/lang/groovy/build" />
</content>

View File

@ -263,6 +263,20 @@ public class SearchRequestBuilder extends BaseRequestBuilder<SearchRequest, Sear
return this;
}
/**
* Adds a script based field to load and return. The field does not have to be stored,
* but its recommended to use non analyzed or numeric fields.
*
* @param name The name that will represent this value in the return hit
* @param lang The language of the script
* @param script The script to use
* @param params Parameters that the script can use (can be <tt>null</tt>).
*/
public SearchRequestBuilder addScriptField(String name, String lang, String script, Map<String, Object> params) {
sourceBuilder().scriptField(name, lang, script, params);
return this;
}
/**
* Adds a sort against the given field name and the sort ordering.
*

View File

@ -36,6 +36,8 @@ public class CustomScoreQueryBuilder extends BaseQueryBuilder {
private String script;
private String lang;
private float boost = -1;
private Map<String, Object> params = null;
@ -57,6 +59,14 @@ public class CustomScoreQueryBuilder extends BaseQueryBuilder {
return this;
}
/**
* Sets the language of the script.
*/
public CustomScoreQueryBuilder lang(String lang) {
this.lang = lang;
return this;
}
/**
* Additional parameters that can be provided to the script.
*/
@ -94,6 +104,9 @@ public class CustomScoreQueryBuilder extends BaseQueryBuilder {
builder.field("query");
queryBuilder.toXContent(builder, params);
builder.field("script", script);
if (lang != null) {
builder.field("lang", lang);
}
if (this.params != null) {
builder.field("params", this.params);
}

View File

@ -35,6 +35,8 @@ public class ScriptFilterBuilder extends BaseFilterBuilder {
private Map<String, Object> params;
private String lang;
private String filterName;
public ScriptFilterBuilder(String script) {
@ -63,12 +65,23 @@ public class ScriptFilterBuilder extends BaseFilterBuilder {
return this;
}
/**
* Sets the script language.
*/
public ScriptFilterBuilder lang(String lang) {
this.lang = lang;
return this;
}
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(ScriptFilterParser.NAME);
builder.field("script", script);
if (this.params != null) {
builder.field("params", this.params);
}
if (this.lang != null) {
builder.field("lang", lang);
}
if (filterName != null) {
builder.field("_name", filterName);
}

View File

@ -33,7 +33,7 @@ public class CompiledScript {
this.compiled = compiled;
}
public String type() {
public String lang() {
return type;
}

View File

@ -0,0 +1,41 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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;
import org.elasticsearch.common.util.concurrent.NotThreadSafe;
import java.util.Map;
/**
* @author kimchy (shay.banon)
*/
@NotThreadSafe
public interface ExecutableScript {
/**
* Executes the script.
*/
Object run();
/**
* Executes the script.
*/
Object run(Map vars);
}

View File

@ -30,5 +30,7 @@ public interface ScriptEngineService {
Object compile(String script);
public Object execute(Object compiledScript, Map vars);
ExecutableScript executable(Object compiledScript, Map vars);
Object execute(Object compiledScript, Map vars);
}

View File

@ -0,0 +1,36 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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;
import org.elasticsearch.ElasticSearchException;
/**
* @author kimchy (shay.banon)
*/
public class ScriptException extends ElasticSearchException {
public ScriptException(String msg) {
super(msg);
}
public ScriptException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@ -37,7 +37,7 @@ import java.util.concurrent.ConcurrentMap;
*/
public class ScriptService extends AbstractComponent {
private final String defaultType;
private final String defaultLang;
private final ImmutableMap<String, ScriptEngineService> scriptEngines;
@ -53,7 +53,7 @@ public class ScriptService extends AbstractComponent {
@Inject public ScriptService(Settings settings, Set<ScriptEngineService> scriptEngines) {
super(settings);
this.defaultType = componentSettings.get("default_type", "mvel");
this.defaultLang = componentSettings.get("default_lang", "mvel");
ImmutableMap.Builder<String, ScriptEngineService> builder = ImmutableMap.builder();
for (ScriptEngineService scriptEngine : scriptEngines) {
@ -63,34 +63,42 @@ public class ScriptService extends AbstractComponent {
}
public CompiledScript compile(String script) {
return compile(defaultType, script);
return compile(defaultLang, script);
}
public CompiledScript compile(String type, String script) {
public CompiledScript compile(String lang, String script) {
CompiledScript compiled = cache.get(script);
if (compiled != null) {
return compiled;
}
if (type == null) {
type = defaultType;
if (lang == null) {
lang = defaultLang;
}
synchronized (cache) {
compiled = cache.get(script);
if (compiled != null) {
return compiled;
}
ScriptEngineService service = scriptEngines.get(type);
ScriptEngineService service = scriptEngines.get(lang);
if (service == null) {
throw new ElasticSearchIllegalArgumentException("script_type not supported [" + type + "]");
throw new ElasticSearchIllegalArgumentException("script_lang not supported [" + lang + "]");
}
compiled = new CompiledScript(type, service.compile(script));
compiled = new CompiledScript(lang, service.compile(script));
cache.put(script, compiled);
}
return compiled;
}
public ExecutableScript executable(String lang, String script, Map vars) {
return executable(compile(lang, script), vars);
}
public ExecutableScript executable(CompiledScript compiledScript, Map vars) {
return scriptEngines.get(compiledScript.lang()).executable(compiledScript.compiled(), vars);
}
public Object execute(CompiledScript compiledScript, Map vars) {
return scriptEngines.get(compiledScript.type()).execute(compiledScript.compiled(), vars);
return scriptEngines.get(compiledScript.lang()).execute(compiledScript.compiled(), vars);
}
public void clear() {

View File

@ -25,6 +25,7 @@ import org.elasticsearch.common.math.UnboxedMathUtils;
import org.elasticsearch.common.mvel2.MVEL;
import org.elasticsearch.common.mvel2.ParserContext;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptEngineService;
import java.lang.reflect.Method;
@ -65,4 +66,29 @@ public class MvelScriptEngineService extends AbstractComponent implements Script
@Override public Object execute(Object compiledScript, Map vars) {
return MVEL.executeExpression(compiledScript, vars);
}
@Override public ExecutableScript executable(Object compiledScript, Map vars) {
return new MvelExecutableScript(compiledScript, vars);
}
public static class MvelExecutableScript implements ExecutableScript {
private final Object compiledScript;
private final Map vars;
public MvelExecutableScript(Object compiledScript, Map vars) {
this.compiledScript = compiledScript;
this.vars = vars;
}
@Override public Object run() {
return MVEL.executeExpression(compiledScript, vars);
}
@Override public Object run(Map vars) {
vars.putAll(this.vars);
return MVEL.executeExpression(compiledScript, vars);
}
}
}

View File

@ -392,7 +392,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
String sSource = "_na_";
try {
sSource = Unicode.fromBytes(source, offset, length);
} catch (Exception e1) {
} catch (Error e1) {
// ignore
}
throw new SearchParseException(context, "Failed to parse [" + sSource + "]", e);

View File

@ -245,15 +245,41 @@ public class SearchSourceBuilder implements ToXContent {
return this;
}
/**
* Adds a script field under the given name with the provided script.
*
* @param name The name of the field
* @param script The script
*/
public SearchSourceBuilder scriptField(String name, String script) {
return scriptField(name, script, null);
return scriptField(name, null, script, null);
}
/**
* Adds a script field.
*
* @param name The name of the field
* @param script The script to execute
* @param params The script parameters
*/
public SearchSourceBuilder scriptField(String name, String script, Map<String, Object> params) {
return scriptField(name, null, script, params);
}
/**
* Adds a script field.
*
* @param name The name of the field
* @param lang The language of the script
* @param script The script to execute
* @param params The script parameters (can be <tt>null</tt>)
* @return
*/
public SearchSourceBuilder scriptField(String name, String lang, String script, Map<String, Object> params) {
if (scriptFields == null) {
scriptFields = Lists.newArrayList();
}
scriptFields.add(new ScriptField(name, script, params));
scriptFields.add(new ScriptField(name, lang, script, params));
return this;
}
@ -343,6 +369,9 @@ public class SearchSourceBuilder implements ToXContent {
for (ScriptField scriptField : scriptFields) {
builder.startObject(scriptField.fieldName());
builder.field("script", scriptField.script());
if (scriptField.lang() != null) {
builder.field("lang", scriptField.lang());
}
if (scriptField.params() != null) {
builder.field("params");
builder.map(scriptField.params());
@ -390,10 +419,12 @@ public class SearchSourceBuilder implements ToXContent {
private static class ScriptField {
private final String fieldName;
private final String script;
private final String lang;
private final Map<String, Object> params;
private ScriptField(String fieldName, String script, Map<String, Object> params) {
private ScriptField(String fieldName, String lang, String script, Map<String, Object> params) {
this.fieldName = fieldName;
this.lang = lang;
this.script = script;
this.params = params;
}
@ -406,6 +437,10 @@ public class SearchSourceBuilder implements ToXContent {
return script;
}
public String lang() {
return this.lang;
}
public Map<String, Object> params() {
return params;
}

View File

@ -59,6 +59,8 @@ public class GeoDistanceFacetBuilder extends AbstractFacetBuilder {
private String valueScript;
private String lang;
private List<Entry> entries = Lists.newArrayList();
/**
@ -92,6 +94,14 @@ public class GeoDistanceFacetBuilder extends AbstractFacetBuilder {
return this;
}
/**
* The language of the {@link #valueScript(String)} script.
*/
public GeoDistanceFacetBuilder lang(String lang) {
this.lang = lang;
return this;
}
/**
* Parameters for {@link #valueScript(String)} to improve performance when executing the same script with different parameters.
*/
@ -220,6 +230,9 @@ public class GeoDistanceFacetBuilder extends AbstractFacetBuilder {
if (valueScript != null) {
builder.field("value_script", valueScript);
if (lang != null) {
builder.field("lang", lang);
}
if (this.params != null) {
builder.field("params", this.params);
}

View File

@ -32,6 +32,7 @@ import java.util.Map;
* @author kimchy (shay.banon)
*/
public class HistogramScriptFacetBuilder extends AbstractFacetBuilder {
private String lang;
private String keyScript;
private String valueScript;
private Map<String, Object> params;
@ -42,6 +43,14 @@ public class HistogramScriptFacetBuilder extends AbstractFacetBuilder {
super(name);
}
/**
* The language of the script.
*/
public HistogramScriptFacetBuilder lang(String lang) {
this.lang = lang;
return this;
}
public HistogramScriptFacetBuilder keyScript(String keyScript) {
this.keyScript = keyScript;
return this;
@ -92,6 +101,9 @@ public class HistogramScriptFacetBuilder extends AbstractFacetBuilder {
builder.startObject(HistogramFacetCollectorParser.NAME);
builder.field("key_script", keyScript);
builder.field("value_script", valueScript);
if (lang != null) {
builder.field("lang", lang);
}
if (interval > 0) { // interval is optional in script facet, can be defined by the key script
builder.field("interval", interval);
}

View File

@ -35,6 +35,7 @@ import java.util.Map;
*/
public class RangeScriptFacetBuilder extends AbstractFacetBuilder {
private String lang;
private String keyScript;
private String valueScript;
private Map<String, Object> params;
@ -44,6 +45,14 @@ public class RangeScriptFacetBuilder extends AbstractFacetBuilder {
super(name);
}
/**
* The language of the script.
*/
public RangeScriptFacetBuilder lang(String lang) {
this.lang = lang;
return this;
}
public RangeScriptFacetBuilder keyScript(String keyScript) {
this.keyScript = keyScript;
return this;
@ -121,6 +130,9 @@ public class RangeScriptFacetBuilder extends AbstractFacetBuilder {
builder.startObject(RangeFacetCollectorParser.NAME);
builder.field("key_script", keyScript);
builder.field("value_script", valueScript);
if (lang != null) {
builder.field("lang", lang);
}
builder.startArray("ranges");
for (Entry entry : entries) {

View File

@ -32,6 +32,7 @@ import java.util.Map;
* @author kimchy (shay.banon)
*/
public class StatisticalScriptFacetBuilder extends AbstractFacetBuilder {
private String lang;
private String script;
private Map<String, Object> params;
@ -49,6 +50,14 @@ public class StatisticalScriptFacetBuilder extends AbstractFacetBuilder {
return this;
}
/**
* The language of the script.
*/
public StatisticalScriptFacetBuilder lang(String lang) {
this.lang = lang;
return this;
}
public StatisticalScriptFacetBuilder script(String script) {
this.script = script;
return this;
@ -70,6 +79,9 @@ public class StatisticalScriptFacetBuilder extends AbstractFacetBuilder {
builder.startObject(StatisticalFacetCollectorParser.NAME);
builder.field("script", script);
if (lang != null) {
builder.field("lang", lang);
}
if (this.params != null) {
builder.field("params", this.params);
}

View File

@ -40,6 +40,7 @@ public class TermsFacetBuilder extends AbstractFacetBuilder {
private int regexFlags = 0;
private TermsFacet.ComparatorType comparatorType;
private String script;
private String lang;
private Map<String, Object> params;
public TermsFacetBuilder(String name) {
@ -91,6 +92,14 @@ public class TermsFacetBuilder extends AbstractFacetBuilder {
return this;
}
/**
* The language of the script.
*/
public TermsFacetBuilder lang(String lang) {
this.lang = lang;
return this;
}
public TermsFacetBuilder param(String name, Object value) {
if (params == null) {
params = Maps.newHashMap();
@ -127,6 +136,9 @@ public class TermsFacetBuilder extends AbstractFacetBuilder {
if (script != null) {
builder.field("script", script);
if (lang != null) {
builder.field("lang", lang);
}
if (this.params != null) {
builder.field("params", this.params);
}

View File

@ -32,6 +32,8 @@ import java.util.Map;
*/
public class ScriptSortBuilder extends SortBuilder {
private String lang;
private final String script;
private final String type;
@ -73,6 +75,14 @@ public class ScriptSortBuilder extends SortBuilder {
return this;
}
/**
* The language of the script.
*/
public ScriptSortBuilder lang(String lang) {
this.lang = lang;
return this;
}
@Override public void toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("_script");
builder.field("script", script);

View File

@ -31,6 +31,7 @@ dependencies {
compile project(':elasticsearch')
groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.5'
distLib('org.codehaus.groovy:groovy-all:1.7.5') { transitive = false }
testCompile project(':test-testng')
testCompile('org.testng:testng:5.10:jdk15') { transitive = false }

View File

@ -0,0 +1 @@
plugin=org.elasticsearch.plugin.groovy.GroovyPlugin

View File

@ -0,0 +1,45 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.plugin.groovy;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.plugins.AbstractPlugin;
import org.elasticsearch.script.ScriptModule;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
/**
* @author kimchy (shay.banon)
*/
public class GroovyPlugin extends AbstractPlugin {
@Override public String name() {
return "lang-groovy";
}
@Override public String description() {
return "Groovy plugin allowing to add groovy scripting support";
}
@Override public void processModule(Module module) {
if (module instanceof ScriptModule) {
((ScriptModule) module).addScriptEngine(GroovyScriptEngineService.class);
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.groovy;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.Script;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptEngineService;
import org.elasticsearch.script.ScriptException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author kimchy (shay.banon)
*/
public class GroovyScriptEngineService extends AbstractComponent implements ScriptEngineService {
private final AtomicLong counter = new AtomicLong();
private final GroovyClassLoader loader;
@Inject public GroovyScriptEngineService(Settings settings) {
super(settings);
this.loader = new GroovyClassLoader(settings.getClassLoader());
}
@Override public String type() {
return "groovy";
}
@Override public Object compile(String script) {
return loader.parseClass(script, generateScriptName());
}
@Override public ExecutableScript executable(Object compiledScript, Map vars) {
try {
Class scriptClass = (Class) compiledScript;
Script scriptObject = (Script) scriptClass.newInstance();
Binding binding = new Binding();
if (vars != null) {
binding.getVariables().putAll(vars);
}
scriptObject.setBinding(binding);
return new GroovyExecutableScript(scriptObject);
} catch (Exception e) {
throw new ScriptException("failed to build executable script", e);
}
}
@Override public Object execute(Object compiledScript, Map vars) {
try {
Class scriptClass = (Class) compiledScript;
Script scriptObject = (Script) scriptClass.newInstance();
Binding binding = new Binding(vars);
scriptObject.setBinding(binding);
return scriptObject.run();
} catch (Exception e) {
throw new ScriptException("failed to execute script", e);
}
}
private String generateScriptName() {
return "Script" + counter.incrementAndGet() + ".groovy";
}
public static class GroovyExecutableScript implements ExecutableScript {
private final Script script;
public GroovyExecutableScript(Script script) {
this.script = script;
}
@Override public Object run() {
return script.run();
}
@Override public Object run(Map vars) {
script.getBinding().getVariables().putAll(vars);
return script.run();
}
}
}

View File

@ -67,7 +67,7 @@ class BuilderActionsTests {
}).gexecute()
assertThat indexR.response.index, equalTo("test")
assertThat indexR.response.type, equalTo("type1")
assertThat indexR.response.lang, equalTo("type1")
assertThat indexR.response.id, equalTo("1")
node.client.admin.indices.refresh {}.actionGet()
@ -86,7 +86,7 @@ class BuilderActionsTests {
def delete = node.client.prepareDelete("test", "type1", "1").gexecute()
assertThat delete.response.index, equalTo("test")
assertThat delete.response.type, equalTo("type1")
assertThat delete.response.lang, equalTo("type1")
assertThat delete.response.id, equalTo("1")
def refresh = node.client.admin.indices.refresh {}

View File

@ -80,7 +80,7 @@ class DifferentApiExecutionTests {
def getR = node.client.get {
index "test"
type "type1"
lang "type1"
id "1"
}
assertThat getR.response.exists, equalTo(true)
@ -133,7 +133,7 @@ class DifferentApiExecutionTests {
indexR = node.client.index {
index "test"
type "type1"
lang "type1"
id "1"
source {
test = "value"

View File

@ -64,7 +64,7 @@ class SimpleActionsTests {
def indexR = node.client.index {
index "test"
type "type1"
lang "type1"
id "1"
source {
test = "value"
@ -80,7 +80,7 @@ class SimpleActionsTests {
def delete = node.client.delete {
index "test"
type "type1"
lang "type1"
id "1"
}
assertThat delete.response.index, equalTo("test")
@ -92,14 +92,14 @@ class SimpleActionsTests {
def get = node.client.get {
index "test"
type "type1"
lang "type1"
id "1"
}
assertThat get.response.exists, equalTo(false)
indexR = node.client.index {
index "test"
type "type1"
lang "type1"
id "1"
source {
test = "value"
@ -154,7 +154,7 @@ class SimpleActionsTests {
get = node.client.get {
index "test"
type "type1"
lang "type1"
id "1"
}
assertThat get.response.exists, equalTo(false)

View File

@ -23,7 +23,7 @@ println "Node started"
future = node.client.index {
index "twitter"
type "tweet"
lang "tweet"
id "1"
source {
user = "kimchy"

View File

@ -0,0 +1,116 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.groovy;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.common.xcontent.XContentFactory.*;
import static org.elasticsearch.index.query.xcontent.FilterBuilders.*;
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
/**
* @author kimchy (shay.banon)
*/
public class GroovyScriptFilterSearchTests {
protected final ESLogger logger = Loggers.getLogger(getClass());
private Node node;
private Client client;
@BeforeMethod public void createNodes() throws Exception {
node = NodeBuilder.nodeBuilder().settings(ImmutableSettings.settingsBuilder().put("gateway.type", "none").put("number_of_shards", 1)).node();
client = node.client();
}
@AfterMethod public void closeNodes() {
client.close();
node.close();
}
@Test public void testGroovyScriptFilter() throws Exception {
client.admin().indices().prepareCreate("test").execute().actionGet();
client.prepareIndex("test", "type1", "1")
.setSource(jsonBuilder().startObject().field("test", "value beck").field("num1", 1.0f).endObject())
.execute().actionGet();
client.admin().indices().prepareFlush().execute().actionGet();
client.prepareIndex("test", "type1", "2")
.setSource(jsonBuilder().startObject().field("test", "value beck").field("num1", 2.0f).endObject())
.execute().actionGet();
client.admin().indices().prepareFlush().execute().actionGet();
client.prepareIndex("test", "type1", "3")
.setSource(jsonBuilder().startObject().field("test", "value beck").field("num1", 3.0f).endObject())
.execute().actionGet();
client.admin().indices().refresh(refreshRequest()).actionGet();
logger.info("running doc['num1'].value > 1");
SearchResponse response = client.prepareSearch()
.setQuery(filtered(matchAllQuery(), scriptFilter("doc['num1'].value > 1").lang("groovy")))
.addSort("num1", SortOrder.ASC)
.addScriptField("sNum1", "groovy", "doc['num1'].value", null)
.execute().actionGet();
assertThat(response.hits().totalHits(), equalTo(2l));
assertThat(response.hits().getAt(0).id(), equalTo("2"));
assertThat((Double) response.hits().getAt(0).fields().get("sNum1").values().get(0), equalTo(2.0));
assertThat(response.hits().getAt(1).id(), equalTo("3"));
assertThat((Double) response.hits().getAt(1).fields().get("sNum1").values().get(0), equalTo(3.0));
logger.info("running doc['num1'].value > param1");
response = client.prepareSearch()
.setQuery(filtered(matchAllQuery(), scriptFilter("doc['num1'].value > param1").lang("groovy").addParam("param1", 2)))
.addSort("num1", SortOrder.ASC)
.addScriptField("sNum1", "groovy", "doc['num1'].value", null)
.execute().actionGet();
assertThat(response.hits().totalHits(), equalTo(1l));
assertThat(response.hits().getAt(0).id(), equalTo("3"));
assertThat((Double) response.hits().getAt(0).fields().get("sNum1").values().get(0), equalTo(3.0));
logger.info("running doc['num1'].value > param1");
response = client.prepareSearch()
.setQuery(filtered(matchAllQuery(), scriptFilter("doc['num1'].value > param1").lang("groovy").addParam("param1", -1)))
.addSort("num1", SortOrder.ASC)
.addScriptField("sNum1", "groovy", "doc['num1'].value", null)
.execute().actionGet();
assertThat(response.hits().totalHits(), equalTo(3l));
assertThat(response.hits().getAt(0).id(), equalTo("1"));
assertThat((Double) response.hits().getAt(0).fields().get("sNum1").values().get(0), equalTo(1.0));
assertThat(response.hits().getAt(1).id(), equalTo("2"));
assertThat((Double) response.hits().getAt(1).fields().get("sNum1").values().get(0), equalTo(2.0));
assertThat(response.hits().getAt(2).id(), equalTo("3"));
assertThat((Double) response.hits().getAt(2).fields().get("sNum1").values().get(0), equalTo(3.0));
}
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.groovy;
import org.elasticsearch.common.StopWatch;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.script.ExecutableScript;
import java.util.HashMap;
import java.util.Map;
/**
* @author kimchy (shay.banon)
*/
public class SimpleBench {
public static void main(String[] args) {
GroovyScriptEngineService se = new GroovyScriptEngineService(ImmutableSettings.Builder.EMPTY_SETTINGS);
Object compiled = se.compile("x + y");
Map<String, Integer> vars = new HashMap<String, Integer>();
// warm up
for (int i = 0; i < 1000; i++) {
vars.put("x", i);
vars.put("y", i + 1);
se.execute(compiled, vars);
}
final long ITER = 100000;
StopWatch stopWatch = new StopWatch().start();
for (long i = 0; i < ITER; i++) {
se.execute(compiled, vars);
}
System.out.println("Execute Took: " + stopWatch.stop().lastTaskTime());
stopWatch = new StopWatch().start();
ExecutableScript executableScript = se.executable(compiled, vars);
for (long i = 0; i < ITER; i++) {
executableScript.run();
}
System.out.println("Executable Took: " + stopWatch.stop().lastTaskTime());
}
}