在 elasticsearch/kibana 脚本字段中将 IP(字符串)转换为 long
Convert IP (string) into long in elasticsearch/kibana scripted fields
我在文档中有一个字段是 ipv4 ("1.2.3.4") 的字符串表示,该字段的名称是 "originating_ip"。
我正在尝试使用无痛语言的脚本字段来添加一个新字段 (originating_ip_calc) 以具有所述 IPv4 的 int (long) 表示形式。
以下脚本在 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;
我也在查看 ,正如您从上面的代码中看到的那样,它基于那里的一个答案。
也尝试过使用 InetAddress 但没有成功。
借助 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 (because it creates regex Pattern implicitly). They suggest to use 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
也不允许 String
的 .length
方法:
"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]."
}
}
那为什么叫painless
呢?
虽然我在快速谷歌搜索后找不到任何关于命名的历史记录,但从 documentation page 和一些经验(如上面的答案)我可以推断它被设计为无痛 在生产中使用.
它的前身,Groovy, was a ticking bomb due to resources usage and security vulnerabilities。因此,Elasticsearch 团队创建了一个非常有限的 Java/Groovy 脚本子集,它具有可预测的性能并且不会包含那些安全漏洞,并将其称为 painless
.
如果说 painless
脚本语言有什么是真的,那就是它是 limited 和 sandboxed.
我在文档中有一个字段是 ipv4 ("1.2.3.4") 的字符串表示,该字段的名称是 "originating_ip"。 我正在尝试使用无痛语言的脚本字段来添加一个新字段 (originating_ip_calc) 以具有所述 IPv4 的 int (long) 表示形式。
以下脚本在 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;
我也在查看
也尝试过使用 InetAddress 但没有成功。
借助 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 (because it creates regex Pattern implicitly). They suggest to use 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
也不允许 String
的 .length
方法:
"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]."
}
}
那为什么叫painless
呢?
虽然我在快速谷歌搜索后找不到任何关于命名的历史记录,但从 documentation page 和一些经验(如上面的答案)我可以推断它被设计为无痛 在生产中使用.
它的前身,Groovy, was a ticking bomb due to resources usage and security vulnerabilities。因此,Elasticsearch 团队创建了一个非常有限的 Java/Groovy 脚本子集,它具有可预测的性能并且不会包含那些安全漏洞,并将其称为 painless
.
如果说 painless
脚本语言有什么是真的,那就是它是 limited 和 sandboxed.