Split/Slice 大 JSON 使用 jq
Split/Slice large JSON using jq
想根据数组大小(10000/50000 等)将一个巨大的 json 文件 ~20GB 切成较小的数据块..
输入:
{"recDt":"2021-01-05",
"country":"US",
"name":"ABC",
"number":"9828",
"add": [
{"evnCd":"O","rngNum":"1","state":"TX","city":"ANDERSON","postal":"77830"},
{"evnCd":"O","rngNum":"2","state":"TX","city":"ANDERSON","postal":"77832"},
{"evnCd":"O","rngNum":"3","state":"TX","city":"ANDERSON","postal":"77831"},
{"evnCd":"O","rngNum":"4","state":"TX","city":"ANDERSON","postal":"77834"}
]
}
目前 运行 在一个循环中通过递增 x/y 值来获得所需的输出,但性能非常慢,迭代需要 8-20 秒,具体取决于文件的大小完成拆分过程。目前使用的是 1.6 版本,是否有任何替代方法可以使结果低于 result
预期输出:数组中 2 个对象的切片
{"recDt":"2021-01-05","country":"US","name":"ABC","number":"9828","add":[{"rngNum":"1","state":"TX","city":"ANDERSON","postal":"77830"},{"rngNum":"2","state":"TX","city":"ANDERSON","postal":"77832"}]}
{"recDt":"2021-01-05","country":"US","name":"ABC","number":"9828","add":[{"rngNum":"3","state":"TX","city":"ANDERSON","postal":"77831"},{"rngNum":"4","state":"TX","city":"ANDERSON","postal":"77834"}]}
尝试过
cat $inFile | jq -cn --stream 'fromstream(1|truncate_stream(inputs))' | jq --arg x $x --arg y $y -c '{recDt: .recDt, country: .country, name: .name, number: .number, add: .add[$x|tonumber:$y|tonumber]}' >> $outFile
cat $inFile | jq --arg x $x --arg y $y -c '{recDt: .recDt, country: .country, name: .name, number: .number, add: .add[$x|tonumber:$y|tonumber]}' >> $outFile
如果有可用的替代品,请分享..
在这个只调用 jq 一次的响应中,我假设您的计算机有足够的内存来读取整个 JSON。我还假设您想为每个切片创建单独的文件,并且您希望 JSON 漂亮地打印在每个文件中。
假设块大小为 2,并且输出文件将使用模板部分 -N.json 命名,您可以这样写:
< input.json jq -r --argjson size 2 '
del(.add) as $object
| (.add|_nwise($size) | ("\t", $object + {add:.} ))
' | awk '
/^\t/ {fn++; next}
{ print >> "part-" fn ".json"}'
这里使用的技巧是有效的 JSON 不能包含制表符。
以下假设输入 JSON 太大而无法读入内存,因此使用 jq 的 --stream
命令行选项。
为简单起见,我将重点关注 .add
数组的“切片”,而不会担心其他键、漂亮打印和其他细节,您可以根据您的需要轻松调整以下内容:
< input.json jq -nc --stream --argjson size 2 '
def regroup(stream; $n):
foreach (stream, null) as $x ({a:[]};
if $x == null then .emit = .a
elif .a|length == $n then .emit = .a | .a = [$x]
else .emit=null | .a += [$x] end;
select(.emit).emit);
regroup(fromstream( 2 | truncate_stream(inputs | select(.[0][0] == "add")) );
$size)' |
awk '{fn++; print > fn ".json"}'
这会将数组写入文件,文件名格式为 N.json
想根据数组大小(10000/50000 等)将一个巨大的 json 文件 ~20GB 切成较小的数据块..
输入:
{"recDt":"2021-01-05",
"country":"US",
"name":"ABC",
"number":"9828",
"add": [
{"evnCd":"O","rngNum":"1","state":"TX","city":"ANDERSON","postal":"77830"},
{"evnCd":"O","rngNum":"2","state":"TX","city":"ANDERSON","postal":"77832"},
{"evnCd":"O","rngNum":"3","state":"TX","city":"ANDERSON","postal":"77831"},
{"evnCd":"O","rngNum":"4","state":"TX","city":"ANDERSON","postal":"77834"}
]
}
目前 运行 在一个循环中通过递增 x/y 值来获得所需的输出,但性能非常慢,迭代需要 8-20 秒,具体取决于文件的大小完成拆分过程。目前使用的是 1.6 版本,是否有任何替代方法可以使结果低于 result
预期输出:数组中 2 个对象的切片
{"recDt":"2021-01-05","country":"US","name":"ABC","number":"9828","add":[{"rngNum":"1","state":"TX","city":"ANDERSON","postal":"77830"},{"rngNum":"2","state":"TX","city":"ANDERSON","postal":"77832"}]}
{"recDt":"2021-01-05","country":"US","name":"ABC","number":"9828","add":[{"rngNum":"3","state":"TX","city":"ANDERSON","postal":"77831"},{"rngNum":"4","state":"TX","city":"ANDERSON","postal":"77834"}]}
尝试过
cat $inFile | jq -cn --stream 'fromstream(1|truncate_stream(inputs))' | jq --arg x $x --arg y $y -c '{recDt: .recDt, country: .country, name: .name, number: .number, add: .add[$x|tonumber:$y|tonumber]}' >> $outFile
cat $inFile | jq --arg x $x --arg y $y -c '{recDt: .recDt, country: .country, name: .name, number: .number, add: .add[$x|tonumber:$y|tonumber]}' >> $outFile
如果有可用的替代品,请分享..
在这个只调用 jq 一次的响应中,我假设您的计算机有足够的内存来读取整个 JSON。我还假设您想为每个切片创建单独的文件,并且您希望 JSON 漂亮地打印在每个文件中。
假设块大小为 2,并且输出文件将使用模板部分 -N.json 命名,您可以这样写:
< input.json jq -r --argjson size 2 '
del(.add) as $object
| (.add|_nwise($size) | ("\t", $object + {add:.} ))
' | awk '
/^\t/ {fn++; next}
{ print >> "part-" fn ".json"}'
这里使用的技巧是有效的 JSON 不能包含制表符。
以下假设输入 JSON 太大而无法读入内存,因此使用 jq 的 --stream
命令行选项。
为简单起见,我将重点关注 .add
数组的“切片”,而不会担心其他键、漂亮打印和其他细节,您可以根据您的需要轻松调整以下内容:
< input.json jq -nc --stream --argjson size 2 '
def regroup(stream; $n):
foreach (stream, null) as $x ({a:[]};
if $x == null then .emit = .a
elif .a|length == $n then .emit = .a | .a = [$x]
else .emit=null | .a += [$x] end;
select(.emit).emit);
regroup(fromstream( 2 | truncate_stream(inputs | select(.[0][0] == "add")) );
$size)' |
awk '{fn++; print > fn ".json"}'
这会将数组写入文件,文件名格式为 N.json