我在_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安装或配置的一部分,我无法添加或编辑任何内容。
答案 0 :(得分:0)
一种可能的方法是使用其他可用的脚本选项。除非启用动态脚本,否则似乎无法使用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;
}
}
<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>
修改elasticsearch.yml
并添加
script.native.my_script.type: com.foo.script.MyScriptNativeScriptFactory
重启ES节点。
{
"aggs": {
"max_score": {
"max": {
"script": "my_script",
"lang": "native"
}
}
}
}
上面的示例只返回_score
作为脚本,但当然,它可以在更高级的场景中使用。
编辑:如果您不被允许触摸实例,那么我认为您没有任何选择。
答案 1 :(得分:0)
ElasticSearch至少版本1.7.1以及可能更早版本也提供了Lucene的Expression脚本语言的使用 - 并且由于Expression默认使用沙盒,因此它可以像Groovy一样用于动态内联脚本。在我们的情况下,我们的生产ES集群刚刚从1.4.1升级到1.7.1,我们决定不再使用Groovy,因为它具有非沙盒性质,尽管我们仍然希望使用动态脚本,因为随着我们继续微调我们的应用程序及其搜索层,它们易于部署并提供灵活性。
虽然编写本机Java脚本作为动态Groovy函数得分的替代,但在我们的案例中也可能有用,我们想看看使用Expression作为动态内联脚本语言的可行性。阅读完文档后,我发现我们只能在内联"groovy"
脚本和"expression"
属性中将“lang”属性从function_score
更改为script.inline: sandbox
在.../config/elasticsearch.yml
文件中设置 - 功能评分脚本无需任何其他修改即可运行。因此,我们现在可以继续在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的脚本选择会发生什么变化会很有趣,特别是考虑到版本2.0将完全删除Groovy的(显然无效的)沙盒选项。希望可以采用其他保护措施来实现动态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