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