【问题标题】:Aggregate the results of a jq query into a single array将 jq 查询的结果聚合到单个数组中
【发布时间】:2019-07-05 22:10:24
【问题描述】:

我有一个包含独立 JSON 对象(即不是数组)的输入文件,我想从每个对象中过滤一些字段并创建一个包含结果元素的数组。它基本上是 JSON 格式的日志语句列表。

我为此使用jq,它工作得很好,除了我无法将所有结果对象聚合到一个数组中。

输入是这样的:

{"name":"myname", "environment":"staging", "email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}
{"name":"myname", "environment":"staging", "email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}
...
{"name":"myname", "environment":"staging", "email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}
{"name":"myothername", "environment":"staging", "time":"2017-10-02T05:00:00.046Z"}

(请注意,最后一个条目没有email 字段,因此如果不过滤,它将返回null 值)

从此对象列表中,我只想获取字段emailtime,而忽略其余字段,因此我使用了以下jq 查询:

jq '{email: (.email | values), time: (.time | values)}' input.json

请注意,我使用 values 过滤器是因为日志消息是混合的,所以并非所有 json 对象都有 email 字段,所以我忽略了这些。

我现在的问题是,即使我得到了想要的结果,我又得到了一个列表,我想要一个数组。

即我得到类似的东西

{"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}
{"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}
...
{"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}

我想要它:

[
    {"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"},
    {"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"},
    ...,
    {"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}
]

我尝试了几种不同的方法,但我通常以错误 Cannot index array with string "email" 告终,这告诉我数组操作有问题。

我尝试将查询包装在map() 中,即map({.userEmail, .time}),尝试使用-s 处理数据,并尝试使用|+|= 运算符。

我也尝试将查询包装在数组括号内,如[{email: (.email|values), time:.time }],但我得到相同的结果对象,除了每个对象都单独包装在一个数组中,即

[{"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}]
[{"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}]
...
[{"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}]

看起来这可能是一件容易的事,或者至少是一个常见的操作,但我找不到正确的查询。

当输入不是数组时,将查询结果聚合到数组中的正确方法是什么?

【问题讨论】:

    标签: json jq


    【解决方案1】:

    更好...

    1. 根据您的样本数据,您的基本过滤器可以简化为{email, time}

    2. 一般来说,最好避免“啜饮”输入(例如,为了节省内存)。这可以通过使用带有 -n 命令行选项的 inputs 来完成。

    把它们放在一起:

    jq -n '[inputs | {email, time }]' input.json
    

    如果您想过滤掉某些输入,您可以使用select,例如

    jq -n '[inputs | select(.email) | {email, time } ]' input.json
    

    【讨论】:

    • 有趣!。因此,如果我理解正确,-n 选项用于避免读取任何输入,而是使用inputs 过滤器。这似乎可行,但不幸的是它仍然给我一个空值,我想避免。它几乎是等价的,除了 null 部分。
    • 显然您的实际数据包含一些与示例数据不同的项目。所以你必须过滤掉你不想要的元素。
    • 是的,很抱歉我没有将它添加到示例数据中。基本上,有些条目没有电子邮件但设置了时间字段,因此在这些情况下,我得到一些具有时间值但电子邮件为空的条目,这是我不想要的。不幸的是,速记版本{email, time} 不支持{(email | values), time}。我只是在最后添加了一个额外的条目,它没有 email 字段来说明这一点。无论如何,您的解决方案都非常好,除了我没有正确举例说明的这种边缘情况。
    【解决方案2】:

    又看了一些,找到了我想要的结果,就是slurp操作符和map的组合。

    我意识到查询

    jq -s 'map({email: (.email|values), time:.time })' input.json

    会将所有输入项读取为数组,然后读取为per the definition of map()

    对于任何过滤器 x,map(x) 将为输入数组的每个元素运行该过滤器,并在新数组中返回输出

    所以两者结合起来给了我我需要的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-29
      • 1970-01-01
      • 1970-01-01
      • 2014-10-22
      • 2018-12-11
      • 1970-01-01
      • 2014-02-23
      • 1970-01-01
      相关资源
      最近更新 更多