Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Guide [8.17] ›Скрипты

Расширенные скрипты с использованием движков сценариев

A ScriptEngine — это бэкенд для реализации языка сценариев. Его также можно использовать для написания скриптов, которым требуется доступ к расширенным внутренностям системы сценариев. Например, скрипт, который хочет использовать частоту терминов при оценке.

В документации плагина документации содержится дополнительная информация о том, как написать плагин, чтобы Elasticsearch его правильно загрузил. Для регистрации ScriptEngine ваш плагин должен реализовать интерфейс ScriptPlugin и переопределить метод getScriptEngine(Settings settings).

Ниже приведен пример пользовательского ScriptEngine, использующего язык expert_scripts. Он реализует один скрипт под названием pure_df, который можно использовать как скрипт поиска для переопределения оценки каждого документа в соответствии с частотой документа указанного термина.

private static class MyExpertScriptEngine implements ScriptEngine {
    @Override
    public String getType() {
        return "expert_scripts";
    }

    @Override
    public <T> T compile(
        String scriptName,
        String scriptSource,
        ScriptContext<T> context,
        Map<String, String> params
    ) {
        if (context.equals(ScoreScript.CONTEXT) == false) {
            throw new IllegalArgumentException(getType()
                    + " scripts cannot be used for context ["
                    + context.name + "]");
        }
        // we use the script "source" as the script identifier
        if ("pure_df".equals(scriptSource)) {
            ScoreScript.Factory factory = new PureDfFactory();
            return context.factoryClazz.cast(factory);
        }
        throw new IllegalArgumentException("Unknown script name "
                + scriptSource);
    }

    @Override
    public void close() {
        // optionally close resources
    }

    @Override
    public Set<ScriptContext<?>> getSupportedContexts() {
        return Set.of(ScoreScript.CONTEXT);
    }

    private static class PureDfFactory implements ScoreScript.Factory,
                                                  ScriptFactory {
        @Override
        public boolean isResultDeterministic() {
            // PureDfLeafFactory only uses deterministic APIs, this
            // implies the results are cacheable.
            return true;
        }

        @Override
        public LeafFactory newFactory(
            Map<String, Object> params,
            SearchLookup lookup
        ) {
            return new PureDfLeafFactory(params, lookup);
        }
    }

    private static class PureDfLeafFactory implements LeafFactory {
        private final Map<String, Object> params;
        private final SearchLookup lookup;
        private final String field;
        private final String term;

        private PureDfLeafFactory(
                    Map<String, Object> params, SearchLookup lookup) {
            if (params.containsKey("field") == false) {
                throw new IllegalArgumentException(
                        "Missing parameter [field]");
            }
            if (params.containsKey("term") == false) {
                throw new IllegalArgumentException(
                        "Missing parameter [term]");
            }
            this.params = params;
            this.lookup = lookup;
            field = params.get("field").toString();
            term = params.get("term").toString();
        }

        @Override
        public boolean needs_score() {
            return false;  // Return true if the script needs the score
        }

        @Override
        public boolean needs_termStats() {
            return false; // Return true if the script needs term statistics via get_termStats()
        }

        @Override
        public ScoreScript newInstance(DocReader docReader)
                throws IOException {
            DocValuesDocReader dvReader = DocValuesDocReader) docReader);             PostingsEnum postings = dvReader.getLeafReaderContext()                     .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 new ScoreScript(params, lookup, docReader) {
                    @Override
                    public double execute(
                        ExplanationHolder explanation
                    ) {
                        if(explanation != null) {
                            explanation.set("An example optional custom description to explain details for this script's execution; we'll provide a default one if you leave this out.");
                        }
                        return 0.0d;
                    }
                };
            }
            return new ScoreScript(params, lookup, docReader) {
                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 execute(ExplanationHolder explanation) {
                    if(explanation != null) {
                        explanation.set("An example optional custom description to explain details for this script's execution; we'll provide a default one if you leave this out.");
                    }
                    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);
                    }
                }
            };
        }
    }
}

Вы можете выполнить скрипт, указав его lang как expert_scripts, а имя скрипта — в качестве исходного кода скрипта:

resp = client.search(
    query={
        "function_score": {
            "query": {
                "match": {
                    "body": "foo"
                }
            },
            "functions": [
                {
                    "script_score": {
                        "script": {
                            "source": "pure_df",
                            "lang": "expert_scripts",
                            "params": {
                                "field": "body",
                                "term": "foo"
                            }
                        }
                    }
                }
            ]
        }
    },
)
print(resp)
const response = await client.search({
  query: {
    function_score: {
      query: {
        match: {
          body: "foo",
        },
      },
      functions: [
        {
          script_score: {
            script: {
              source: "pure_df",
              lang: "expert_scripts",
              params: {
                field: "body",
                term: "foo",
              },
            },
          },
        },
      ],
    },
  },
});
console.log(response);
POST /_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "body": "foo"
        }
      },
      "functions": [
        {
          "script_score": {
            "script": {
                "source": "pure_df",
                "lang" : "expert_scripts",
                "params": {
                    "field": "body",
                    "term": "foo"
                }
            }
          }
        }
      ]
    }
  }
}

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/modules-scripting-engine.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API