user159211
user159211

Reputation: 171

How to search a json with jq for values?

I have a json of this structure:

{
  "nodes": {
    "60e327ee58a0": {
      "nodeinfo": {
        "network": {
          "mesh": {
            "bat0": {
              "interfaces": {
                "wireless": [
                  "<mac-address-removed>"
                ],
                "tunnel": [
                  "<mac-address-removed>"
                ]
              }
            }
          },
          "mac": "<mac removed>",
          "addresses": [
            "<ipv6 removed>",
            "<ipv6 removed>"
          ]
        },
        "hardware": {
          "model": "TP-Link TL-WR841N/ND v10",
          "nproc": 1
        },
        "software": {
          "batman-adv": {
            "compat": 15,
            "version": "2015.1"
          },
          "autoupdater": {
            "branch": "stable",
            "enabled": true
          },
          "firmware": {
            "release": "v2016.1+1.0.1",
            "base": "gluon-v2016.1"
          },
          "status-page": {
            "api": 1
          },
          "fastd": {
            "enabled": true,
            "version": "v17"
          }
        },
        "hostname": "Antoniusweg12",
        "system": {
          "site_code": "ffmsd03"
        },
        "node_id": "60e327ee58a0"
      },
      "lastseen": "2016-04-14T12:39:04",
      "flags": {
        "gateway": false,
        "online": true
      },
      "firstseen": "2016-03-16T15:14:04",
      "statistics": {
        "clients": 1,
        "gateway": "de:ad:be:ef:43:02",
        "rootfs_usage": 0.6041666666666667,
        "loadavg": 0.09,
        "uptime": 1822037.41,
        "memory_usage": 0.8124737210932025,
        "traffic": {
          "rx": {
            "packets": 50393821,
            "bytes": 5061895206
          },
          "forward": {
            "packets": 173,
            "bytes": 17417
          },
          "mgmt_rx": {
            "packets": 47453745,
            "bytes": 6623785282
          },
          "tx": {
            "packets": 1205695,
            "bytes": 173509528,
            "dropped": 5683
          },
          "mgmt_tx": {
            "packets": 37906725,
            "bytes": 11475209742
          }
        }
      }
    },
    "30b5c2b042f4": {
<next block...>

And I want to query it with jq for the hostname, the mac or the IPv6.

cat nodes.json |jq -c '.nodes[] | select(.nodes[]| contains("Antoniusweg12"))'

Most examples do not fit this kind of json structure as the objects have an index

Thanks for help in advance.

Upvotes: 2

Views: 15433

Answers (3)

jq170727
jq170727

Reputation: 14725

Here is a solution which searches for nodes where the specified $needle is present in any of the addresses, mac or hostname fields.

  "<ipv6 removed>" as $needle   # set to whatever you like

| foreach (.nodes|keys[]) as $k (
     .
   ; .
   ;    (      .nodes[$k].nodeinfo.network.addresses?
         + [   .nodes[$k].nodeinfo.network.mac?
             , .nodes[$k].nodeinfo.hostname?
           ]
        ) as $haystack

     | if $haystack | index($needle)
       then {($k): .nodes[$k]}
       else empty
       end
  )

EDIT: I now realize a filter of the form foreach E as $X (.; .; R) can almost always be rewritten as E as $X | R so the above is really just

  "<ipv6 removed>" as $needle

| (.nodes|keys[]) as $k
| (      .nodes[$k].nodeinfo.network.addresses?
   + [   .nodes[$k].nodeinfo.network.mac?
       , .nodes[$k].nodeinfo.hostname?
     ]
  ) as $haystack

| if $haystack | index($needle)
  then {($k): .nodes[$k]}
  else empty
  end

Upvotes: 0

peak
peak

Reputation: 117027

Here's another take on the question. Suppose you want to find all occurrences of the key "hostname" for which the value is "Antoniusweg12", no matter where the key/value combination occurs.

The following will reveal the path to the key/value combination of interest:

paths as $p
| select ( $p[-1] == "hostname" and getpath($p) == "Antoniusweg12" )
| $p

The result for the given input JSON:

[
  "nodes",
  "60e327ee58a0",
  "nodeinfo",
  "hostname"
]

If you wanted the path to the containing object, then replace the final $p with $p[0:-1]; and if you want the containing object itself: getpath($p[0:-1])

Upvotes: 1

Jeff Mercado
Jeff Mercado

Reputation: 134611

If you're going to filter, you need to drill down to the property that you want to check for and see if it matches your criteria. You can't expect to just give a name and you'll magically be presented with the results you want.

Searching by hostname, it is found on the .nodeinfo.hostname property of each node:

$ jq -c --arg hostname "Antoniusweg12" \
'.nodes[] | select(.nodeinfo.hostname == $hostname)' nodes.json

Similarly for the mac address, it's found on the .nodeinfo.network.mac property:

$ jq -c --arg mac "aa:bb:cc:dd:ee:ff" \
'.nodes[] | select(.nodeinfo.network.mac == $mac)' nodes.json

For the ip addresses, there's an array of them but it's not that much different in the query. They're found on the .nodeinfo.network.addresses property:

$ jq -c --arg ip "aaaa:bbbb:cccc:dddd::1" \
'.nodes[] | select(.nodeinfo.network.addresses[] == $ip)' nodes.json

Upvotes: 8

Related Questions