jq streaming - 过滤嵌套列表并保留全局结构

Posted

技术标签:

【中文标题】jq streaming - 过滤嵌套列表并保留全局结构【英文标题】:jq streaming - filter nested list and retain global structure 【发布时间】:2018-03-28 10:09:14 【问题描述】:

在一个大的 json 文件中,我想从嵌套列表中删除一些元素,但保留文档的整体结构。

我的示例输入它(但真实的足够大,可以要求流式传输)。


  "keep_untouched": 
    "keep_this": [
      "this",
      "list"
    ]
  ,
  "filter_this":
  [
    "keep" : "true",
    
      "keep": "true",
      "extra": "keeper"
     ,
    
      "keep": "false",
      "extra": "non-keeper"
    
  ]

所需的输出只删除了“filter_this”块的一个元素:


  "keep_untouched": 
    "keep_this": [
      "this",
      "list"
    ]
  ,
  "filter_this":
  [
    "keep" : "true",
    
      "keep": "true",
      "extra": "keeper"
     ,
  ]

处理此类情况的标准方法似乎是使用“truncate_stream”重构流对象,然后以通常的 jq 方式过滤这些对象。具体来说,命令:

jq -nc --stream 'fromstream(1|truncate_stream(inputs))' 

允许访问对象流:

"keep_this":["this","list"]
["keep":"true","keep":"true","extra":"keeper", 
 "keep":"false","extra":"non-keeper"]

此时很容易过滤出所需的对象。但是,这会从其父对象的上下文中剥离结果,这不是我想要的。

看流式结构:

[["keep_untouched","keep_this",0],"this"]
[["keep_untouched","keep_this",1],"list"]
[["keep_untouched","keep_this",1]]
[["keep_untouched","keep_this"]]
[["filter_this",0,"keep"],"true"]
[["filter_this",0,"keep"]]
[["filter_this",1,"keep"],"true"]
[["filter_this",1,"extra"],"keeper"]
[["filter_this",1,"extra"]]
[["filter_this",2,"keep"],"false"]
[["filter_this",2,"extra"],"non-keeper"]
[["filter_this",2,"extra"]]
[["filter_this",2]]
[["filter_this"]]

似乎我需要选择所有“filter_this”行,仅截断这些行(使用“truncate_stream”),将这些行重建为对象(使用“from_stream”),过滤它们,然后将对象转回流中数据格式(使用“tostream”)加入“保持不变”行的流,这些行仍然是流格式。那时,可以重新构建整个 json。如果这是正确的方法——这对我来说似乎过于复杂——我该怎么做?或者,还有更好的方法?

【问题讨论】:

最好的方法是尽可能避免使用流解析器。您是否有足够的 RAM 来在您的数据上运行类似“jq length”之类的东西? 【参考方案1】:

如果您的输入文件包含一个非常大的 JSON 实体,对于常规 jq 解析器在您的环境中无法处理,那么您很可能没有足够的内存来重构 JSON 文档。

有了这个警告,以下可能值得一试。关键的见解是可以使用reduce 完成重建。

为了清楚起见,下面使用了一堆临时文件:

TMP=/tmp/$$

jq -c --stream 'select(length==2)' input.json > $TMP.streamed

jq -c 'select(.[0][0] != "filter_this")' $TMP.streamed > $TMP.1

jq -c 'select(.[0][0] == "filter_this")' $TMP.streamed |
  jq -nc 'reduce inputs as [$p,$x] (null; setpath($p;$x))
          | .filter_this |= map(select(.keep=="true"))
          | tostream
          | select(length==2)' > $TMP.2

# Reconstruction
jq -n 'reduce inputs as [$p,$x] (null; setpath($p;$x))' $TMP.1 $TMP.2

输出


  "keep_untouched": 
    "keep_this": [
      "this",
      "list"
    ]
  ,
  "filter_this": [
    
      "keep": "true"
    ,
    
      "keep": "true",
      "extra": "keeper"
    
  ]

【讨论】:

谢谢@peak。我发现你的方法真的很有帮助。当尝试对文件的缩减版本(15M 而不是 8G)进行最后一步时,需要 30 多分钟才能完成。但是,窃取了您的一些想法,我想出了以下内容:【参考方案2】:

非常感谢@peak。我发现他的方法非常有用,但在性能方面不切实际。不过,窃取了@peak 的一些想法,我想出了以下内容:

提取“父”对象:

jq -c --stream 'select(length==2)' input.json | 
  jq -c 'select(.[0][0] != "filter_this")'  | 
  jq -n 'reduce inputs as [$p,$x] (null; setpath($p;$x))' > $TMP.parent

提取 'keepers' - 虽然这意味着读取文件两次 (:-

jq -nc --stream '[fromstream(2|truncate_stream(inputs))
                  | select(type == "object" and .keep == "true")]             
                ' input.json > $TMP.keepers

将过滤后的列表插入到父对象中。

jq -nc -s 'inputs as $items
           | $items[0] as $parent
           | $parent
           | .filter_this |= $items[1]
          '  $TMP.parent $TMP.keepers > result.json

【讨论】:

【参考方案3】:

这是@PeteC 脚本的简化版本。它需要少调用一次 jq。

在这两种情况下,请注意调用使用“2|truncate_stream(_)”的 jq 需要比 1.5 更新的 jq 版本。

TMP=/tmp/$$

INPUT=input.json

# Extract all but .filter_this
< $INPUT jq -c --stream 'select(length==2 and .[0][0] != "filter_this")' |
    jq -nc 'reduce inputs as [$p,$x] (null; setpath($p;$x))
           ' > $TMP.parent

# Need jq > 1.5
# Extract the 'keepers'
< $INPUT jq -n -c --stream '
  [fromstream(2|truncate_stream(inputs))
   | select(type == "object" and .keep == "true")]
  ' $INPUT > $TMP.keepers

# Insert the filtered list into the parent object:
jq -s '. as $in | .[0] | (.filter_this |= $in[1])
      ' $TMP.parent $TMP.keepers > result.json

【讨论】:

以上是关于jq streaming - 过滤嵌套列表并保留全局结构的主要内容,如果未能解决你的问题,请参考以下文章

JQ 过滤嵌套对象中的字段

Java 8 Stream 按键过滤对象列表。排除列表中的键并获取字符串

如何使用linq c#优化嵌套循环并从另一个列表中过滤

带有 jQ​​uery UI 1.8.2 的可排序 + 嵌套列表

使用 jq 将嵌套的 JSON 文件分解为具有唯一键的平面列表

R(purrr)展平命名列表列表以列出并保留名称