我在doc中有一个字段,它是ipv4(“ 1.2.3.4”)的字符串表示形式,该字段的名称为“ originating_ip”。 我正在尝试使用无痛语言编写脚本字段,以便添加新字段(originating_ip_calc)以具有上述IPv4的int(长整数)表示形式。
以下脚本可在groovy中使用(从我的理解中,该脚本基本上应该基本相同),但是在这种特定情况下,似乎几乎不是这样。
String[] ipAddressInArray = "1.2.3.4".split("\\.");
long result = 0;
for (int i = 0; i < ipAddressInArray.length; i++) {
int power = 3 - i;
int ip = Integer.parseInt(ipAddressInArray[i]);
long longIP = (ip * Math.pow(256, power)).toLong();
result = result + longIP;
}
return result;
我也在看this question,正如您从上面的代码中看到的那样,它基于那里的答案之一。
还尝试使用InetAddress,但没有运气。
答案 0 :(得分:4)
借助Elasticsearch轻松编写脚本,您可以使用如下代码:
POST ip_search/doc/_search
{
"query": {
"match_all": {}
},
"script_fields": {
"originating_ip_calc": {
"script": {
"source": """
String ip_addr = params['_source']['originating_ip'];
def ip_chars = ip_addr.toCharArray();
int chars_len = ip_chars.length;
long result = 0;
int cur_power = 0;
int last_dot = chars_len;
for(int i = chars_len -1; i>=-1; i--) {
if (i == -1 || ip_chars[i] == (char) '.' ){
result += (Integer.parseInt(ip_addr.substring(i+ 1, last_dot)) * Math.pow(256, cur_power));
last_dot = i;
cur_power += 1;
}
}
return result
""",
"lang": "painless"
}
}
},
"_source": ["originating_ip"]
}
(请注意,我使用Kibana console将请求发送到ES,在发送之前,它做了一些转义操作以使其成为有效的JSON。)
这将给出如下响应:
"hits": [
{
"_index": "ip_search",
"_type": "doc",
"_id": "2",
"_score": 1,
"_source": {
"originating_ip": "10.0.0.1"
},
"fields": {
"originating_ip_calc": [
167772161
]
}
},
{
"_index": "ip_search",
"_type": "doc",
"_id": "1",
"_score": 1,
"_source": {
"originating_ip": "1.2.3.4"
},
"fields": {
"originating_ip_calc": [
16909060
]
}
}
]
但是为什么一定要这样呢?
.split
的方法不起作用?如果您将问题中的代码发送给ES,则会以如下错误答复:
"script": "String[] ipAddressInArray = \"1.2.3.4\".split(\"\\\\.\");\n\nlong result = 0;\nfor (int i = 0; i < ipAddressInArray.length; i++) {\n int power = 3 - i;\n int ip = Integer.parseInt(ipAddressInArray[i]);\n long longIP = (ip * Math.pow(256, power)).toLong();\n result = result + longIP;\n}\nreturn result;",
"lang": "painless",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Unknown call [split] with [1] arguments on type [String]."
这主要是由于Java的String.split()
is not considered safe to use(因为它隐式创建了regex模式)。他们建议使用Pattern#split,但要这样做,您应该在索引中启用正则表达式。
默认情况下,它们被禁用:
"script": "String[] ipAddressInArray = /\\./.split(\"1.2.3.4\");...
"lang": "painless",
"caused_by": {
"type": "illegal_state_exception",
"reason": "Regexes are disabled. Set [script.painless.regex.enabled] to [true] in elasticsearch.yaml to allow them. Be careful though, regexes break out of Painless's protection against deep recursion and long loops."
(char) '.'
?因此,我们必须手动将字符串分割成点。简单的方法是将字符串的每个字符与'.'
(在Java中是char
文字,而不是String
)进行比较。
但是对于painless
,则表示String
。因此,我们必须对char
进行显式转换(因为我们要遍历一个char数组)。
因为显然painless
也不允许.length
的{{1}}方法:
String
"reason": {
"type": "script_exception",
"reason": "compile error",
"script_stack": [
"\"1.2.3.4\".length",
" ^---- HERE"
],
"script": "\"1.2.3.4\".length",
"lang": "painless",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Unknown field [length] for type [String]."
}
}
呢?尽管我在{@ {3}}和一些经验(如本答案中的上述内容)中进行了快速谷歌搜索,但找不到关于命名的任何历史记录,但我可以推断出它的设计旨在使无痛在生产中使用。
由于资源和documentation page的使用,它的前身Groovy是一颗滴答作响的炸弹。因此,Elasticsearch团队创建了Java / Groovy脚本的非常有限的子集,该子集具有可预测的性能并且不包含那些安全漏洞,因此将其命名为painless
。
如果关于painless
脚本语言有什么疑问,是受限和沙箱。
希望有帮助!