Home > Software design >  Using jq to search for a value based on a key located deep in json file
Using jq to search for a value based on a key located deep in json file

Time:02-17

I am new to jq and I'm trying to use it to search for a value in a json file based on a key that is located deep in the json structure. Here is a sample of my json file:

{
  "data": {
    "inventory": {
      "location": "remote",
      "list": {
        "content": [
          {
            "item": {
              "name": "minivan"
            },
            "owner": {
              "id": "12345",
              "state": "CA"
            }
          },
          {
            "item": {
              "name": "sedan"
            },
            "owner": {
              "id": "67890",
              "state": "AZ"
            }
          }
        ]
      }
    }
  }
}

An example of search that I'm trying to do is:

select item.name where owner.id = "67890"

and the expected output would be:

item.name = "sedan"

I'm trying to run the following:

jq '.[] | select .owner.id = "67890" | .item.name' json

and it generates an error:

jq: error: select/0 is not defined at <top-level>, line 1:
.[] | select .owner.id = "67890" | .item.name      
jq: 1 compile error

Any pointers on how to do this in jq would be much appreciated! Thanks!

CodePudding user response:

First, you have to "navigate" to where you want to make the query. This seems to be an array.

.data.inventory.list.content
[
  {
    "item": {
      "name": "minivan"
    },
    "owner": {
      "id": "12345",
      "state": "CA"
    }
  },
  {
    "item": {
      "name": "sedan"
    },
    "owner": {
      "id": "67890",
      "state": "AZ"
    }
  }
]

Demo

Next, let's iterate over that array's items, which gives us a stream of objects.

.[]
{
  "item": {
    "name": "minivan"
  },
  "owner": {
    "id": "12345",
    "state": "CA"
  }
}
{
  "item": {
    "name": "sedan"
  },
  "owner": {
    "id": "67890",
    "state": "AZ"
  }
}

Demo

From these objects we select those that match your criteria.

select(.owner.id == "67890")
{
  "item": {
    "name": "sedan"
  },
  "owner": {
    "id": "67890",
    "state": "AZ"
  }
}

Demo

Finally, we extract the value you're interested in.

.item.name
"sedan"

Demo

Everything combined in a jq call would be:

jq '.data.inventory.list.content[] | select(.owner.id == "67890").item.name'
"sedan"

Demo

This output is still valid JSON document (containing nothing but a JSON string). If you want to process the output as raw text, use the --raw-output (or -r) option:

jq -r '.data.inventory.list.content[] | select(.owner.id == "67890").item.name'
sedan

Demo

CodePudding user response:

Here's a solution that avoids having to "navigate" to the right place, and which is also quite close to your SQL-like query:

..
| objects 
| select(.owner and 
         (.owner|type=="object" and .id == "67890"))
  .item.name

or more succinctly:

..|objects|select(.owner.id? == "67890").item.name
  • Related