Elasticsearch:使用新的 field API 简化 Painless 语法和文档字段访问 – Elastic Stack 8.1
2022年4月12日 | by mebius
在 8.1 中,我们引入了从 Painless 脚本轻松访问文档中的字段的功能。 你可以使用新的 fieldAPI 访问文档以自动处理缺失值,而不是使用需要逻辑来检查字段及其值是否存在的 doc 变量方法。
字段 API 返回一个字段对象,该对象对具有多个值的字段进行迭代,通过 get() 方法以及类型转换和辅助方法提供对基础值的访问。 它还返回你指定的默认值,无论该字段是否存在或是否具有文档的任何值。 这意味着字段 API 可以处理缺失值,而无需额外的逻辑。
field(‘name’).get()
为了使脚本更具可读性,你还可以使用新的 $ 快捷方式。 确保包含 $ 符号、字段名称和在字段不存在时要获取的默认值:
$(‘field’, )
借助这些增强的功能和简化的语法,你可以编写更短且更易于阅读的脚本。 例如,以下脚本使用过时的语法:
if (!doc.containsKey('myfield') || doc['myfield'].empty) { rtgcodeeturn "unavailable" } else { return doc['myfield'].value }
使用 fieldAPI,你现在可以更简洁地编写相同的脚本,而无需额外的逻辑来确定字段是否存在,然后再对它们进行操作:
$(‘myfield’, ‘unavailable’)
展示
在下面,我们来通过一些例子来进行展示。我们首先选择在之前的文章 “Elasticsearch:Painless scripting 编程实践” 文章中的例子来进行展示。
PUT employee/_bulk?refresh
{"index":{"_id": 1}}
{ "salary" : 5000, "bonus": 500, "@timestamp" : "2021-02-28", "weight": 60, "height": 175, "name" : "Peter", "occupation": "software engineer","hobbies": ["dancing", "badminton"]}
{"index":{"_id": 2}}
{ "salary" : 6000, "bonus": 500, "@timestamp" : "2020-02-01", "weight": 50, "name" : "John", "occupation": "sales", "hobbies":["singing", "volleyball"]}
{"index":{"_id": 3}}
{ "salary" : 7000, "bonus": 600, "@timestamp" : "2019-03-01", "weight": 55, "height": 172, "name" : "mary", "occupation": "manager", "hobbies":["dancing", "tennis"]}
{"index":{"_id": 4}}
{ "salary" : 8000, "bonus": 700, "@timestamp" : "2018-02-28", "weight": 45, "height": 166, "name" : "jerry", "occupation": "sales", "hobbies":["biking", "swimming"]}
{"index":{"_id": 5}}
{ "salary" : 9000, "bonus": 800, "@timestamp" : "2017-02-01", "weight": 60, "height": 170, "name" : "ctgcodeathy", "occupation": "manager", "hobbies":["climbing", "jigging"]}
{"index":{"_id": 6}}
{ "salary" : 7500, "bonus": 500, "@timestamp" : "2017-03-01", "weight": 40, "height": 158, "name" : "cherry", "occupation": "software engineer", "hobbies":["basketball", "yoga"]}
在上面,我需要特别指出的是针对 id 为 2 的文档:
{ “salary” : 6000, “bonus”: 500, “@timestamp” : “2020-02-01”, “weight”: 50, “name” : “Johntgcode“, “occupation”: “sales”, “hobbies”:[“singing”, “volleyball”]}
我特别有意地省去了它的 height 字段,也即其它的文档都含有这个 height 字段。
GET employee/_doc/2
{
"_index" : "employee",
"_id" : "2",
"_version" : 1,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"salary" : 6000,
"bonus" : 500,
"@timestamp" : "2020-02-01",
"weight" : 50,
"name" : "John",
"occupation" : "sales",
"hobbies" : [
"singing",
"volleyball"
]
}
}
我们接下来想得到这些员工的 BMI 值。我们可以参考之前文章“Elasticsearch:Painless scripting 编程实践” 中介绍的方法来创建一个 scripted field:
GET employee/_search
{
"script_fields": {
"BMI": {
"script": {
"source": """
double height = (float)doc['height'].value/100.0;
return doc['weight'].value / (height*height)
"""
}
}
}
}
如果我们运行上面的代码,我们会发现如下的错误信息:
很显然这个错误的信息是由于我们的一个文档缺少 height 这个字段而造成的。一种修改这种问题的办法是添加一些代码来完成,比如:
GET employee/_search?filter_path=**.hits
{
"script_fields": {
"BMI": {
"script": {
"source": """
double height = 0;
if(doc['height'].size() == 0) {
height = 170/100.0;
} else {
height = (double)doc['height'].value/100.0;
}
return doc['weight'].value / (height*height);
"""
}
}
}
}
在上面,当 height 字段缺失的情况下,我们设置一个默认值为 170,这样避免了在计算中的缺失。上面的命令返回的结果为:
{
"hits" : {
"hits" : [
{
"_index" : "employee",
"_id" : "1",
"_score" : 1.0,
"fields" : {
"BMI" : [
19.591836734693878
]
}
},
{
"_index" : "employee",
"_id" : "2",
"_score" : 1.0,
"fields" : {
"BMI" : [
17.301038062283737
]
}
},
{
"_index" : "employee",
"_id" : "3",
"_score" : 1.0,
"fields" : {
"BMI" : [
18.591130340724717
]
}
},
{
"_index" : "employee",
"_id" : "4",
"_score" : 1.0,
"fields" : {
"BMI" : [
16.330381768036002
]
}
},
{
"_index" : "employee",
"_id" : "5",
"_score" : 1.0,
"fields" : {
"BMI" : [
20.761245674740486
]
}
},
{
"_index" : "employee",
"_id" : "6",
"_score" : 1.0,
"fields" : {
"BMI" : [
16.023073225444637
]
}
}
]
}
}
针对 id 为 2 的情况,它计算出来的 BMI 为17.301038062283737。
由于有了新的 field API,那么我们可以简化上面的计算步骤为:
GET employee/_search?filter_path=**.hits
{
"script_fields": {
"BMI": {
"script": {
"source": """
double height = (double)$('height', 170)/100.0;
return doc['weight'].value / (height*height)
"""
}
}
}
}
或者:
GET employee/_search?filter_path=**.hits
{
"script_fields": {
"BMI": {
"script": {
"source": """
double height = (double)field('height').get(170)/100.0;
return doc['weight'].value / (height*height)
"""
}
}
}
}
上面的代码非常之简单明了。当 height 字段不存在时,我们使用 170 来代替。上面运行的结果为:
{
"hits" : {
"hits" : [
{
"_index" : "employee",
"_id" : "1",
"_score" : 1.0,
"fields" : {
"BMI" : [
19.591836734693878
]
}
},
{
"_index" : "employee",
"_id" : "2",
"_score" : 1.0,
"fields" : {
"BMI" : [
17.301038062283737
]
}
},
{
"_index" : "employee",
"_id" : "3",
"_score" : 1.0,
"fields" : {
"BMI" : [
18.591130340724717
]
}
},
{
"_index" : "employee",
"_id" : "4",
"_score" : 1.0,
"fields" : {
"BMI" : [
16.330381768036002
]
}
},
{
"_index" : "employee",
"_id" : "5",
"_score" : 1.0,
"fields" : {
"BMI" : [
20.761245674740486
]
}
},
{
"_index" : "employee",
"_id" : "6",
"_score" : 1.0,
"fields" : {
"BMI" : [
16.023073225444637
]
}
}
]
}
}
从上面的结果中,我们可以看出来它是一样的结果。
注意:field API 仍在开发中,应被视为 beta 功能。 API 可能会发生变化,并且此迭代可能不是最终状态。 对于功能状态,请参阅#78920。某些字段与 fieldAPI 不兼容,例如文本或地理字段。 继续使用 doc 访问字段 API 不支持的字段类型。
文章来源于互联网:Elasticsearch:使用新的 field API 简化 Painless 语法和文档字段访问 – Elastic Stack 8.1