Groovy Plugin: Allow to run use groovy for scripts (where applicable), closes #398.
This commit is contained in:
parent
5c1c580b84
commit
9e8ebd46e8
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public class CompiledScript {
|
|||
this.compiled = compiled;
|
||||
}
|
||||
|
||||
public String type() {
|
||||
public String lang() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
plugin=org.elasticsearch.plugin.groovy.GroovyPlugin
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -23,7 +23,7 @@ println "Node started"
|
|||
|
||||
future = node.client.index {
|
||||
index "twitter"
|
||||
type "tweet"
|
||||
lang "tweet"
|
||||
id "1"
|
||||
source {
|
||||
user = "kimchy"
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue