AWS Athena 将结构数组导出到 JSON
Posted
技术标签:
【中文标题】AWS Athena 将结构数组导出到 JSON【英文标题】:AWS Athena export array of structs to JSON 【发布时间】:2018-08-24 19:01:39 【问题描述】:我有一个 Athena 表,其中一些字段具有相当复杂的嵌套格式。 S3 中的支持记录是 JSON。沿着这些思路(但我们还有更多的嵌套级别):
CREATE EXTERNAL TABLE IF NOT EXISTS test (
timestamp double,
stats array<struct<time:double, mean:double, var:double>>,
dets array<struct<coords: array<double>, header:struct<frame:int,
seq:int, name:string>>>,
pos struct<x:double, y:double, theta:double>
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES ('ignore.malformed.json'='true')
LOCATION 's3://test-bucket/test-folder/'
现在我们需要能够查询数据并将结果导入 Python 进行分析。由于安全限制,我无法直接连接到 Athena;我需要能够向某人提供查询,然后他们会给我 CSV 结果。
如果我们只是直接选择 *,我们会以不完全 JSON 的格式返回结构/数组列。 这是一个示例输入文件条目:
"timestamp":1520640777.666096,"stats":["time":15,"mean":45.23,"var":0.31,"time":19,"mean":17.315,"var":2.612],"dets":["coords":[2.4,1.7,0.3], "header":"frame":1,"seq":1,"name":"hello"],"pos": "x":5,"y":1.4,"theta":0.04
示例输出:
select * from test
"timestamp","stats","dets","pos"
"1.520640777666096E9","[time=15.0, mean=45.23, var=0.31, time=19.0, mean=17.315, var=2.612]","[coords=[2.4, 1.7, 0.3], header=frame=1, seq=1, name=hello]","x=5.0, y=1.4, theta=0.04"
我希望以更方便的格式导出这些嵌套字段 - 以 JSON 格式导出它们会很棒。
不幸的是,转换为 JSON 似乎只适用于地图,而不适用于结构,因为它只是将所有内容扁平化为数组:
SELECT timestamp, cast(stats as JSON) as stats, cast(dets as JSON) as dets, cast(pos as JSON) as pos FROM "sampledb"."test"
"timestamp","stats","dets","pos"
"1.520640777666096E9","[[15.0,45.23,0.31],[19.0,17.315,2.612]]","[[[2.4,1.7,0.3],[1,1,""hello""]]]","[5.0,1.4,0.04]"
有没有一种好方法可以转换为 JSON(或其他易于导入的格式),还是我应该继续执行自定义解析功能?
【问题讨论】:
【参考方案1】:我浏览了所有文档,不幸的是,到目前为止似乎还没有办法做到这一点。唯一可能的解决方法是
converting a struct to a json when querying athena
SELECT
my_field,
my_field.a,
my_field.b,
my_field.c.d,
my_field.c.e
FROM
my_table
或者我会使用后处理将数据转换为 json。下面的脚本显示了如何
#!/usr/bin/env python
import io
import re
pattern1 = re.compile(r'(?<=)([a-z]+)=', re.I)
pattern2 = re.compile(r':([a-z][^,. [\]]+)', re.I)
pattern3 = re.compile(r'\\"', re.I)
with io.open("test.csv") as f:
headers = list(map(lambda f: f.strip(), f.readline().split(",")))
for line in f.readlines():
orig_line = line
data = []
for i, l in enumerate(line.split('","')):
data.append(headers[i] + ":" + re.sub('^"|"$', "", l))
line = "" + ','.join(data) + ""
line = pattern1.sub(r'"\1":', line)
line = pattern2.sub(r':"\1"', line)
print(line)
输入数据的输出是
"timestamp":1.520640777666096E9,"stats":["time":15.0, "mean":45.23, "var":0.31, "time":19.0, "mean":17.315, "var":2.612],"dets":["coords":[2.4, 1.7, 0.3], "header":"frame":1, "seq":1, "name":"hello"],"pos":"x":5.0, "y":1.4, "theta":0.04
这是一个有效的 JSON
【讨论】:
这在大多数情况下都能完美运行。我遇到的一个特殊情况是包含“=”的值。 e.b [id=dsdgfdgdfb=cdf=] 。有什么建议吗? @SameerGirolkar,通过修复更新了代码。模式 1 需要正面看待 谢谢@Tarun Lalwani!这有帮助。【参考方案2】:@tarun 的 python 代码几乎让我到达那里,但由于我的数据,我不得不以多种方式对其进行修改。特别是,我有:
在 Athena 中保存为字符串的 json 结构 包含多个单词的字符串,因此需要在双引号之间。其中一些包含“[]”和“”符号。这是对我有用的代码,希望对其他人有用:
#!/usr/bin/env python
import io
import re, sys
pattern1 = re.compile(r'(?<=)([a-z]+)=', re.I)
pattern2 = re.compile(r':([a-z][^,. [\]]+)', re.I)
pattern3 = re.compile(r'\\"', re.I)
with io.open(sys.argv[1]) as f:
headers = list(map(lambda f: f.strip(), f.readline().split(",")))
print(headers)
for line in f.readlines():
orig_line = line
#save the double quote cases, which mean there is a string with quotes inside
line = re.sub('""', "#", orig_line)
data = []
for i, l in enumerate(line.split('","')):
item = re.sub('^"|"$', "", l.rstrip())
if (item[0] == "" and item[-1] == "") or (item[0] == "[" and item[-1] == "]"):
data.append(headers[i] + ":" + item)
else: #we have a string
data.append(headers[i] + ": \"" + item + "\"")
line = "" + ','.join(data) + ""
line = pattern1.sub(r'"\1":', line)
line = pattern2.sub(r':"\1"', line)
#restate the double quotes to single ones, once inside the json
line = re.sub("#", '"', line)
print(line)
【讨论】:
【参考方案3】:此方法不是通过修改Query。
它的后处理对于 javascript/Nodejs 我们可以使用 npm 包 athena-struct-parser。
带示例的详细答案
https://***.com/a/67899845/6662952
参考 - https://www.npmjs.com/package/athena-struct-parser
【讨论】:
【参考方案4】:我使用了一种简单的方法来绕过 struct -> json Athena 限制。我创建了第二个表,其中 json 列保存为原始字符串。使用 presto json 和数组函数,我能够查询数据并将有效的 json 字符串返回到我的程序:
--Array transform functions too
select
json_extract_scalar(dd, '$.timestamp') as timestamp,
transform(cast(json_extract(json_parse(dd), '$.stats') as ARRAY<JSON>), x -> json_extract_scalar(x, '$.time')) as arr_stats_time,
transform(cast(json_extract(json_parse(dd), '$.stats') as ARRAY<JSON>), x -> json_extract_scalar(x, '$.mean')) as arr_stats_mean,
transform(cast(json_extract(json_parse(dd), '$.stats') as ARRAY<JSON>), x -> json_extract_scalar(x, '$.var')) as arr_stats_var
from
(select '"timestamp":1520640777.666096,"stats":["time":15,"mean":45.23,"var":0.31,"time":19,"mean":17.315,"var":2.612],"dets":["coords":[2.4,1.7,0.3], "header":"frame":1,"seq":1,"name":"hello"],"pos": "x":5,"y":1.4,"theta":0.04' as dd);
我知道查询需要更长的时间来执行,但有一些方法可以优化。
【讨论】:
以上是关于AWS Athena 将结构数组导出到 JSON的主要内容,如果未能解决你的问题,请参考以下文章
Athena 在另一个 json 结构数组中未嵌套 json 字符串数组
aws athena & java - 在结构类型列中获取数据
来自字符串字段的 AWS Athena json_extract 查询返回空值