Search code examples
syntaxjqdisjunction

Why does this jq expression work if used once, but not twice?


I have a json document

{
  "total": 2000,
  "start": 0,
  "entries": [
    {
      "updatedDate": "2021-03-12T13:00:05Z",
      "createdDate": "2010-06-17T05:34:42Z",
      "deleted": false,
      "suppressed": false,
      "names": [
        "Doe, John"
      ],
      "barcodes": [
        "23082599944"
      ],
      "expirationDate": "2022-05-06",
      "emails": [
        "[email protected]"
      ],
      "patronType": 14,
      "patronCodes": {
        "pcode1": "-",
        "pcode2": 0
      },
      "varFields": [
        {
          "fieldTag": "b",
          "content": "23082501799944"
        },
        {
          "fieldTag": "l",
          "content": "jdoe"
        }
      ]
    }
  ]
}

That I need to extract the .content where fieldTag is "b" or "l". In the example, I've removed many fields, and the fields I seek will not always be present.

If I cat the file into

cat myfile |jq -r '.entries[] |
        .varFields[] |select(.fieldTag=="l") | .content // ""
'

works exactly as expected but

cat myfile|jq -r '.entries[] |
        .varFields[] |select(.fieldTag=="l") | .content // "", 
        .varFields[] |select(.fieldTag=="b") | .content // "" 
'

returns jq: error (at <stdin>:37): Cannot index string with string "fieldTag". I seem to be able to select any field I want, so long as I only select one. Otherwise, I get the error. What am I missing?


Solution

  • In your query, you would need parentheses for correct grouping:

    .entries[] |
     (.varFields[] | select(.fieldTag=="l") | .content // ""), 
     (.varFields[] | select(.fieldTag=="b") | .content // "")
    

    or to avoid scanning .varFields redundantly:

    .entries[]
    | (INDEX(.varFields[]; .fieldTag) | map_values(.content)) as $dict
    | $dict["l","b"]
    | . // ""
    

    If ordering is unimportant and if both "l" and "b" are known to be present, you could alternatively write:

    .entries[]
    | .varFields[] 
    | select(.fieldTag | . == "l" or . == "b" ) 
    | .content // ""
    

    The disjunctive "select" immediately above could also be written even more DRYly as:

    select(.fieldTag | IN("l","b" ))