使用jq,如何根据对象属性的值将对象的JSON流拆分为单独的文件?

时间:2019-02-16 16:16:34

标签: json bash stream jq partitioning

我有一个名为input.json的超大文件(压缩后超过20GB),其中包含如下所示的JSON对象流:

{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typea"
}
{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typea"
}
{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typeb"
}

我想根据其type属性typea.jsontypeb.json等将文件拆分为文件,每个文件都包含自己的json对象流,这些对象仅具有匹配的type属性

我已经设法解决了较小文件的问题,但是对于如此大的文件,我的AWS实例内存不足。我希望降低内存使用量,所以我知道我需要使用--stream,但我正在努力寻找如何实现这一目标的方法。

cat input.json | jq -c --stream 'select(.[0][0]=="type") | .[1]'将为我返回每个类型属性的值,但是如何使用它来过滤对象?

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:0)

假设文件中的JSON对象相对较小(不超过几个MB),则无需使用(相当复杂的)“-stream”命令行选项,当输入是(或包括)单个庞大的JSON实体。

但是仍然有几种选择。主要的方法在Split a JSON file into separate files中进行了描述,它们是一种多遍方法(对jq的N或(N + 1)个调用,其中N是输出文件的数量),而该方法仅涉及一个对jq,然后调用诸如awk之类的程序以执行实际的分区为文件的操作。每种方法都有其优缺点,但是如果可以接受N次读取输入文件的话,那么第一种方法可能会更好。

要估算所需的总计算资源,最好通过运行jq empty input.json

来衡量使用的资源。

(从您的简短撰写看来,您遇到内存问题的主要原因是文件解压缩。)

答案 1 :(得分:0)

使用jq分成NUL分隔的(类型,文档)对流,并使用本机bash(4.1或更高版本)使用一组持久的文件描述符写入这些文档:

#!/usr/bin/env bash
case $BASH_VERSION in ''|[1-3].*|4.0*) echo "ERROR: Bash 4.1 needed" >&2; exit 1;; esac

declare -A output_fds=( )

while IFS= read -r -d '' type && IFS= read -r -d '' content; do
  if [[ ${output_fds[$type]} ]]; then  # already have a file handle for this output file?
    curr_fd=${output_fds[$type]}       # reuse it, then.
  else
    exec {curr_fd}>"$type.json"        # open a new output file...
    output_fds[$type]=$curr_fd         # and store its file descriptor for use.
  fi
  printf '%s\n' "$content" >&"$curr_fd"
done < <(jq -j '(.type) + "\u0000" + (. | tojson) + "\u0000"')

这一次最多不会一次读取多条记录(当然,每个记录可能有多个副本),因此,只要记录的大小合理,它就可以处理任意大的文件。