ElasticSearch:在禁用 Groovy 的 _score 字段上进行聚合
Posted
技术标签:
【中文标题】ElasticSearch:在禁用 Groovy 的 _score 字段上进行聚合【英文标题】:ElasticSearch: aggregation on _score field w/ Groovy disabled 【发布时间】:2015-07-17 03:51:03 【问题描述】:我见过的每个对 _score 字段进行聚合或与之相关的示例(例如,ElasticSearch: aggregation on _score field?)似乎都需要使用脚本。出于安全原因,ElasticSearch 默认禁用动态脚本,有没有办法在不求助于将脚本文件加载到每个 ES 节点或重新启用动态脚本的情况下完成此操作?
我的原始聚合如下所示:
"aggs":
"terms_agg":
"terms":
"field": "field1",
"order": "max_score": "desc"
,
"aggs":
"max_score":
"max": "script": "_score"
,
"top_terms":
"top_hits": "size": 1
尝试将表达式指定为 lang 似乎不起作用,因为 ES 会引发错误,指出分数只能在用于排序时访问。我想不出任何其他按分数字段排序我的存储桶的方法。有人有什么想法吗?
编辑:澄清一下,我的限制是不能修改服务器端。即,作为 ES 安装或配置的一部分,我无法添加或编辑任何内容。
【问题讨论】:
【参考方案1】:一种可能的方法是使用其他可用的脚本选项。 mvel
似乎无法使用,除非启用动态脚本。而且,除非a more fine-grained control of scripting enable/disable 达到1.6 版本,否则我认为不可能为mvel
而不是groovy
启用动态脚本。
我们留下了默认启用的native
和mustache
(用于模板)。我不认为自定义脚本可以使用 mustache
完成,如果可能的话我没有找到方法,我们只剩下 native
(Java) 脚本。
这是我的看法:
创建NativeScriptFactory
的实现:
package com.foo.script;
import java.util.Map;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.NativeScriptFactory;
public class MyScriptNativeScriptFactory implements NativeScriptFactory
@Override
public ExecutableScript newScript(Map<String, Object> arg0)
return new MyScript();
AbstractFloatSearchScript
的实现例如:
package com.foo.script;
import java.io.IOException;
import org.elasticsearch.script.AbstractFloatSearchScript;
public class MyScript extends AbstractFloatSearchScript
@Override
public float runAsFloat()
try
return score();
catch (IOException e)
e.printStackTrace();
return 0;
或者,构建一个简单的 Maven 项目以将所有内容联系在一起。 pom.xml:
<properties>
<elasticsearch.version>1.5.2</elasticsearch.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>$elasticsearch.version</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
构建它并获取生成的 jar 文件。
将 jar 放在 [ES_folder]/lib 中
编辑 elasticsearch.yml
并添加
script.native.my_script.type: com.foo.script.MyScriptNativeScriptFactory
重启 ES 节点。
在聚合中使用它:
"aggs":
"max_score":
"max":
"script": "my_script",
"lang": "native"
我上面的示例只是将_score
作为脚本返回,但当然,它可以用于更高级的场景。
编辑:如果您不允许触摸实例,那么我认为您没有任何选择。
【讨论】:
不幸的是,这不是一个理想的解决方案。此外,编写一个 groovy 脚本并将其放在_score
并将其放在 scripts/ 中,它会被 ES 自动拾取。根据我的测试,我什至不必更改"script": "_score"
代码来使用“script_file”,它就可以工作。问题是,这个解决方案需要直接访问修改节点,我不能保证。native
脚本之外别无他法。干杯。
啊,那么我想我的限制不够明确,因为您的替代方案属于那个桶(没有双关语)。我的限制是我无法控制并且无法修改其配置的 ES 集群。我将编辑帖子以澄清。谢谢!【参考方案2】:
ElasticSearch 至少在 1.7.1 版本和可能更早的版本中还提供了 Lucene 的 Expression 脚本语言的使用 - 由于 Expression 默认情况下是沙盒化的,它可以用于动态内联脚本,其方式与 Groovy 大致相同。在我们的案例中,我们的生产 ES 集群刚刚从 1.4.1 升级到 1.7.1,我们决定不再使用 Groovy,因为它的非沙盒特性,尽管我们仍然想使用动态脚本,因为随着我们继续微调我们的应用程序及其搜索层,它们提供的易于部署和灵活性。
虽然编写本机 Java 脚本来替代我们的动态 Groovy 函数分数在我们的案例中可能也是可行的,但我们想看看将 Expression 用于我们的动态内联脚本语言的可行性。阅读完文档后,我发现我们可以简单地将“lang”属性从"groovy"
更改为"expression"
在内联function_score
脚本中,并在script.inline: sandbox
文件中设置script.inline: sandbox
属性 -功能评分脚本无需任何其他修改即可工作。因此,我们现在可以继续在 ElasticSearch 中使用动态内联脚本,并在启用沙盒的情况下这样做(因为 Expression 默认是沙盒)。显然,还应实施其他安全措施,例如在应用程序代理和防火墙后面运行 ES 集群,以确保外部用户无法直接访问您的 ES 节点或 ES API。但是,这是一个非常简单的更改,目前已经解决了 Groovy 缺乏沙盒的问题以及在不使用沙盒的情况下运行的问题。
虽然将您的动态脚本切换到 Expression 可能仅在某些情况下有效或适用(取决于您的内联动态脚本的复杂性),但似乎值得分享这些信息,希望它能帮助其他开发人员。
请注意,作为其他受支持的 ES 脚本语言之一,Mustache 似乎仅可用于在您的搜索查询中创建模板。它似乎不适用于任何更复杂的脚本需求,例如function_score
等,尽管我不确定在第一次阅读更新的 ES 文档时这是否完全明显。
最后,另一个需要注意的问题是,在最新的 ES 版本中,Lucene Expression 脚本的使用被标记为实验性功能,并且文档指出,由于这个脚本扩展正在经历重大此时的开发工作,其用法或功能可能会在 ES 的后续版本中发生变化。因此,如果您确实切换到对任何脚本(动态或其他)使用 Expression,则应在您的文档/开发人员说明中注明,以便在下次升级 ES 安装之前重新访问这些更改,以确保您的脚本保持兼容并作为预计。
至少在我们的情况下,除非我们愿意允许在最新版本的 ES 中再次启用非沙盒动态脚本(通过script.inline: on
选项),以便内联 Groovy 脚本可以继续运行,切换Lucene Expression 脚本似乎是目前最好的选择。
看看未来版本中 ES 的脚本选择会发生哪些变化将会很有趣,特别是考虑到 Groovy 的(显然无效的)沙盒选项将在 2.0 版中完全删除。希望可以实施其他保护措施来启用动态 Groovy 使用,或者 Lucene Expression 脚本可能会取代 Groovy 并启用开发人员已经在使用的所有类型的动态脚本。
有关 Lucene Expression 的更多说明,请参阅此处的 ES 文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html#_lucene_expressions_scripts - 此页面也是有关计划从 ES v2.0+ 中删除 Groovy 沙盒选项的说明的来源。更多 Lucene Expression 文档可以在这里找到:http://lucene.apache.org/core/4_9_0/expressions/index.html?org/apache/lucene/expressions/js/package-summary.html
【讨论】:
以上是关于ElasticSearch:在禁用 Groovy 的 _score 字段上进行聚合的主要内容,如果未能解决你的问题,请参考以下文章
Elasticsearch 无法使用 lang groovy 运行内联脚本 [doc ....]
Elasticsearch的Groovy Script自定义评分检索
Elasticsearch 顶尖高手(19)—基于groovy脚本执行partial update