通过匹配多个对象,使用 jq 过滤 JSON 个对象列表

Filtering JSON object list with jq by matching multiple objects

我正在尝试过滤 'aws elb describe-tags' 的输出并通过匹配三个标签来获取 LoadBalancerName。我看不出如何 select 对象列表的特定元素并比较键和值。我要查看三个对象,如果它们都匹配,我需要 return LoadBalancerName.

这是三个负载均衡器的示例输出,只有一个具有正确的标签集。

{
    "TagDescriptions": [
        {
            "Tags": [
                {
                    "Value": "production",
                    "Key": "environment"
                },
                {
                    "Value": "widget",
                    "Key": "service"
                },
                {
                    "Value": "widget_xyz",
                    "Key": "customer_prefix"
                },
                {
                    "Value": "widget_xyz-widget-production",
                    "Key": "Name"
                }
            ],
            "LoadBalancerName": "widget-xyz-widget-prod"
        },
        {
            "Tags": [
                {
                    "Value": "widget-xyz-stage-widget-ConsulStack-DKJSADKJS",
                    "Key": "aws:cloudformation:stack-name"
                },
                {
                    "Value": "stage",
                    "Key": "environment"
                },
                {
                    "Value": "arn:aws:cloudformation:us-east-1:123456789:stack/widget-xyz-stage-widget-ConsulStack-DKJSADKJS/d46ad520-92e7-11e5-a975-500150b34c7c",
                    "Key": "aws:cloudformation:stack-id"
                },
                {
                    "Value": "widget",
                    "Key": "service"
                },
                {
                    "Value": "widget_xyz",
                    "Key": "customer_prefix"
                },
                {
                    "Value": "ELB",
                    "Key": "aws:cloudformation:logical-id"
                },
                {
                    "Value": "widget_xyz-widget-stage",
                    "Key": "Name"
                }
            ],
            "LoadBalancerName": "widget-xyz-ELB-SDKJSDKJSADKJAS"
        },
        {
            "Tags": [
                {
                    "Value": "widget-xyz-prod-widget-ConsulStack-DLFJEIJNWDKD",
                    "Key": "aws:cloudformation:stack-name"
                },
                {
                    "Value": "prod",
                    "Key": "environment"
                },
                {
                    "Value": "arn:aws:cloudformation:us-east-1:123456789:stack/widget-xyz-prod-widget-ConsulStack-DLFJEIJNWDKD/ab2292f0-9398-11e5-b0f6-50d501114c2c",
                    "Key": "aws:cloudformation:stack-id"
                },
                {
                    "Value": "widget",
                    "Key": "service"
                },
                {
                    "Value": "widget_xyz",
                    "Key": "customer_prefix"
                },
                {
                    "Value": "ELB",
                    "Key": "aws:cloudformation:logical-id"
                },
                {
                    "Value": "widget_xyz-widget-prod",
                    "Key": "Name"
                }
            ],
            "LoadBalancerName": "widget-xyz-ELB-SKDJSKDJSAKDJAS"
        }
    ]
}

我已经成功地实现了我的查询,但是不安全。只要任何三个值与我的搜索模式匹配,它就会 returning LoadBalancerName。我想搜索特定的Key,然后比较Value。

这是我的不安全查询,在 gist 的代码片段上成功。它 returns widget-xyz-widget-prod,这是我要获取的参数。

jq --raw-output '.TagDescriptions[] | select(.Tags[].Value=="widget_xyz") | select(.Tags[].Value=="widget") | select(.Tags[].Value=="production") | .LoadBalancerName'

如果三个条件都成立,应该return:

Key == "service" && Value == "widget"
Key == "environment" && Value == "production"
Key == "customer_prefix" && Value == "widget_xyz"

如您在上面的查询中所见,我只比较值。

更新:我已经能够构建一个查询来过滤匹配来自一个对象的键和值,但我仍在尝试匹配多个对象。

.TagDescriptions[] | select(.Tags[].Key=="customer_prefix" and .Tags[].Value == "widget_xyz") | .LoadBalancerName

另一个更新:好的,所以,我已经能够一起破解查询。我觉得好像我仍然遗漏了一块拼图,并且可以使用我尚未理解的 jq 的一些巧妙功能大大简化此查询。

.TagDescriptions[] | [select(.Tags[].Key == "customer_prefix" and .Tags[].Value == "widget_xyz")][] | [select(.Tags[].Key == "environment" and .Tags[].Value == "production")][] | [select(.Tags[].Key == "service" and .Tags[].Value == "widget")][] | .LoadBalancerName

给jq猫换皮的方法不止一种,但是主要的方法之一 获得清晰的解决方案是定义 helper 的能力 函数,实际上可以嵌套。无论如何,这是一个使用辅助函数的解决方案,该辅助函数确实具有内部函数。

# Does "obj" match any of the objects in the input array?
def anymatch(obj):
  # Do all the key-value pairs in obj also occur in the input?
  def match(obj):
    . as $in
    | obj as $o
    | all( $o|keys[]; $in[.] == $o[.]);
  any(.[]; match(obj));

.TagDescriptions[]
| select( .Tags
        | (anymatch({"Key":"customer_prefix", "Value": "widget_xyz"})
           and anymatch({"Key":"environment", "Value": "production"})
           and anymatch({"Key":"service", "Value": "widget"} ) ))
| .LoadBalancerName

对于给定的输入,这将产生: "widget-xyz-widget-prod"

(顺便说一句,我不认为你的 "ANOTHER UPDATE" 解决方案是对所述问题的有效解决方案,至少在我看来是这样。)

Tags 数组非常适合创建对象以便于访问。让自己轻松一些,然后这样做。然后访问这些值会容易得多。然后你就可以轻松测试一下你的条件是否满足了。

.TagDescriptions[] | select(
    .Tags | from_entries | [
        .service == "widget",
        .environment == "production",
        .customer_prefix == "widget_xyz"
    ] | all
).LoadBalancerName