使用 jq 处理大型 JSON 流

Process large JSON stream with jq

我从 curl 得到一个非常大的 JSON 流(几 GB)并尝试用 jq 处理它。

我想用jq解析的相关输出打包在一个表示结果结构的文档中:

{
  "results":[
    {
      "columns": ["n"],

      // get this
      "data": [    
        {"row": [{"key1": "row1", "key2": "row1"}], "meta": [{"key": "value"}]},
        {"row": [{"key1": "row2", "key2": "row2"}], "meta": [{"key": "value"}]}
      //  ... millions of rows      

      ]
    }
  ],
  "errors": []
}

我想用 jq 提取 row 数据。这很简单:

curl XYZ | jq -r -c '.results[0].data[0].row[]'

结果:

{"key1": "row1", "key2": "row1"}
{"key1": "row2", "key2": "row2"}

但是,这总是要等到 curl 完成。

我使用了 --stream 选项来处理这个问题。我尝试了以下命令,但也在等待从 curl:

返回完整对象
curl XYZ | jq -n --stream 'fromstream(1|truncate_stream(inputs)) | .[].data[].row[]'

有没有办法 'jump' 到 data 字段并开始一个接一个地解析 row 而无需等待结束标记?

(1) 您将使用的香草过滤器如下:

jq -r -c '.results[0].data[].row'

(2) 此处使用流解析器的一种方法是使用它来处理 .results[0].data 的输出,但这两个步骤的组合可能会比普通方法慢。

(3) 要生成您想要的输出,您可以 运行:

jq -nc --stream '
  fromstream(inputs
    | select( [.[0][0,2,4]] == ["results", "data", "row"])
    | del(.[0][0:5]) )'

(4) 或者,您可能希望尝试以下方法:

jq -nc --stream 'inputs
      | select(length==2)
      | select( [.[0][0,2,4]] == ["results", "data", "row"])
      | [ .[0][6], .[1]] '

对于说明性输入,最后一次调用的输出为:

["key1","row1"] ["key2","row1"] ["key1","row2"] ["key2","row2"]

获得:

{"key1": "row1", "key2": "row1"}
{"key1": "row2", "key2": "row2"}

发件人:

{
  "results":[
    {
      "columns": ["n"],
      "data": [    
        {"row": [{"key1": "row1", "key2": "row1"}], "meta": [{"key": "value"}]},
        {"row": [{"key1": "row2", "key2": "row2"}], "meta": [{"key": "value"}]}
      ]
    }
  ],
  "errors": []
}

执行以下操作,等效于 jq -c '.results[].data[].row[]',但使用流式处理:

jq -cn --stream 'fromstream(1|truncate_stream(inputs | select(.[0][0] == "results" and .[0][2] == "data" and .[0][4] == "row") | del(.[0][0:5])))'

这样做的是:

  • 将 JSON 转换为流(使用 --stream
  • Select路径.results[].data[].row[](含select(.[0][0] == "results" and .[0][2] == "data" and .[0][4] == "row"
  • 丢弃路径的那些初始部分,例如 "results",0,"data",0,"row"(带有 del(.[0][0:5])
  • 最后使用 jq FAQ
  • 中的 fromstream(1|truncate_stream(…)) 模式将生成的 jq 流转回预期的 JSON

例如:

echo '
  {
    "results":[
      {
        "columns": ["n"],
        "data": [    
          {"row": [{"key1": "row1", "key2": "row1"}], "meta": [{"key": "value"}]},
          {"row": [{"key1": "row2", "key2": "row2"}], "meta": [{"key": "value"}]}
        ]
      }
    ],
    "errors": []
  }
' | jq -cn --stream '
  fromstream(1|truncate_stream(
    inputs | select(
      .[0][0] == "results" and 
      .[0][2] == "data" and 
      .[0][4] == "row"
    ) | del(.[0][0:5])
  ))'

生成所需的输出。