Search code examples
ansiblejmespath

Multiple conditions in JMESPath query does not give any results


I am trying to query the following Infoblox data with Ansible and JMESPath json_query:

{
    "ip_records.json": {
        "result": [
            {
                "_ref": "fixedaddress/blabla",
                "ipv4addr": "10.10.10.10",
                "network_view": "Bla"
            },
            {
                "_ref": "record:host/blabla",
                "ipv4addrs": [
                    {
                        "_ref": "record:host_ipv4addr/blabla",
                        "host": "bla.bla.com",
                        "ipv4addr": "10.10.10.10"
                    }
                ],
                "name": "bla.bla.com",
                "view": " "
            },
            {
                "_ref": "record:a/blabla",
                "ipv4addr": "10.10.10.10",
                "name": "bla.bla.com",
                "view": "bla"
            }
        ]
    }
}

I want to get only the _ref value for the item with fixedaddress in the _ref value.

Forgot to add that there might also be multiple records with fixedaddress but different IP's. So I also want to filter on a specific IP as the same time.

I have created queries to filter

  • only on IP address given as input
  • the string fixedaddress
  • a combination of both

The first two work as expected. But, I want to combine both conditions and would expect to get the single item as output, but I get nothing. I tried using && and | to combine both, as showed below.

- name: "Search IP Record: Task 2.2: Filter Results."
  vars:
    jmesquery: "[] | [?ipv4addr==`{{ infoblox_ip }}`]._ref"
  set_fact:
    ip_records_refs: "{{ ip_records.json.result | json_query(jmesquery) }}"

- name: "Search IP Record: Task 2.4: Filter Results."
  vars:
    jmesquery: "[] | [?_ref.contains(@,`fixedaddress`)]._ref"
  set_fact:
    ip_records_refs: "{{ ip_records.json.result | to_json | from_json | json_query(jmesquery) }}"

- name: "Search IP Record: Task 2.6: Filter Results."
  vars:
    # jmesquery: "[] | ([?ipv4addr==`{{ infoblox_ip }}` && _ref.contains(@,`fixedaddress`)])._ref"
    jmesquery: "[] | [?ipv4addr==`{{ infoblox_ip }}`].ref | [?_ref.contains(@,`fixedaddress`)]._ref"
  set_fact:
    ip_records_refs: "{{ ip_records.json.result | to_json | from_json | json_query(jmesquery) }}"

Output:

TASK [Search IP Record: Task 2.3 Dump variable Content] ***********
ok: [localhost] => {
    "ip_records_refs": [
        "fixedaddress/blabla",
        "record:a/blabla"
    ]
}

TASK [Search IP Record: Task 2.5 Dump variable Content] ***********
ok: [localhost] => {
    "ip_records_refs": [
        "fixedaddress/blabla"
    ]
}

TASK [Search IP Record: Task 2.7 Dump variable Content] ***********
ok: [localhost] => {
    "ip_records_refs": []
}

Solution

  • You are misusing the pipe expression.

    From your trial, it is hard to tell exactly what you think it does, but here is a simple explanation: you might not see it, but a JMESPath filter on an array does not return you a JSON array, rather it returns you a projection.
    You cannot chain a filter on top of projection, you need to reset it first, in order to get the resulting JSON array, and this is what the pipe expression is meant for.

    In your case, you do not want to have a filter on top of a projection, you want a filter with multiple conditions, so, your last set_fact query should read:

    jmesquery: >-
      [?
        _ref.contains(@,`fixedaddress`) 
        && ipv4addr == `{{ infoblox_ip }}`
      ]._ref
    

    And your two first queries should be simplified to:

    jmesquery: "[?_ref.contains(@,`fixedaddress`)]._ref"
    

    and

    jmesquery: "[?ipv4addr == `{{ infoblox_ip }}`]._ref"