WARNING: Version 5.6 of Elasticsearch has passed its EOL date.
This documentation is no longer being maintained and may be removed. If you are running this version, we strongly advise you to upgrade. For the latest information, see the current release documentation.
Advanced scripts using script engines
editAdvanced scripts using script engines
editA ScriptEngine
is a backend for implementing a scripting language. It may also
be used to write scripts that need to use advanced internals of scripting. For example,
a script that wants to use term frequencies while scoring.
The plugin documentation has more information on
how to write a plugin so that Elasticsearch will properly load it. To register
the ScriptEngine
, your plugin should implement the ScriptPlugin
interface
and override the getScriptEngine(Settings settings)
method.
The following is an example of a custom ScriptEngine
which uses the language
name expert_scripts
. It implements a single script called pure_df
which
may be used as a search script to override each document’s score as
the document frequency of a provided term.
private static class MyExpertScriptEngine implements ScriptEngineService { @Override public String getType() { return "expert_scripts"; } @Override public Function<Map<String,Object>,SearchScript> compile(String scriptName, String scriptSource, Map<String, String> params) { // we use the script "source" as the script identifier if ("pure_df".equals(scriptSource)) { return p -> new SearchScript() { final String field; final String term; { if (p.containsKey("field") == false) { throw new IllegalArgumentException("Missing parameter [field]"); } if (p.containsKey("term") == false) { throw new IllegalArgumentException("Missing parameter [term]"); } field = p.get("field").toString(); term = p.get("term").toString(); } @Override public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IOException { PostingsEnum postings = context.reader().postings(new Term(field, term)); if (postings == null) { // the field and/or term don't exist in this segment, so always return 0 return () -> 0.0d; } return new LeafSearchScript() { int currentDocid = -1; @Override public void setDocument(int docid) { // advance has undefined behavior calling with a docid <= its current docid if (postings.docID() < docid) { try { postings.advance(docid); } catch (IOException e) { throw new UncheckedIOException(e); } } currentDocid = docid; } @Override public double runAsDouble() { if (postings.docID() != currentDocid) { // advance moved past the current doc, so this doc has no occurrences of the term return 0.0d; } try { return postings.freq(); } catch (IOException e) { throw new UncheckedIOException(e); } } }; } @Override public boolean needsScores() { return false; } }; } throw new IllegalArgumentException("Unknown script name " + scriptSource); } @Override @SuppressWarnings("unchecked") public SearchScript search(CompiledScript compiledScript, SearchLookup lookup, @Nullable Map<String, Object> params) { Function<Map<String,Object>,SearchScript> scriptFactory = (Function<Map<String,Object>,SearchScript>) compiledScript.compiled(); return scriptFactory.apply(params); } @Override public ExecutableScript executable(CompiledScript compiledScript, @Nullable Map<String, Object> params) { throw new UnsupportedOperationException(); } @Override public boolean isInlineScriptEnabled() { return true; } @Override public void close() {} }
You can execute the script by specifying its lang
as expert_scripts
, and the name
of the script as the the script source:
POST /_search { "query": { "function_score": { "query": { "match": { "body": "foo" } }, "functions": [ { "script_score": { "script": { "source": "pure_df", "lang" : "expert_scripts", "params": { "field": "body", "term": "foo" } } } } ] } } }