使用 json_query 过滤嵌套列表中的元素

Filtering elements in nested list with json_query

我想根据键的存在从嵌套列表中过滤掉条目。给定以下字典:

hostvars:
  host1:
    backups:
      - src: somesource
        target: sometarget
      - src: anothersource
  host2:
    backups:
      - src: somesource for host2
        target: fancy target
  host3:
    backups:
      - src: yet another src
      - src: and another one

target 键不存在时,我想过滤掉 backups 列表中的所有元素。

我最接近的是:

- set_fact:
    data: "{{ hostvars | dict2items | json_query(query) }}"
  vars:
    query: "[?value.backups[?target]]"

这导致

hostvars:
  host1:
    backups:
      - src: somesource
        target: sometarget
      - src: anothersource
  host2:
    backups:
      - src: somesource for host2
        target: fancy target

所以我已经成功过滤掉 host3,它在备份列表中没有包含 target 键的元素。
但是,我还想从 host1backups 列表中删除第二个元素(也不包含 target 键)。

非常感谢任何指点。

例如

    - set_fact:
        data: "{{ hostvars|
                  dict2items|
                  json_query(_query)|
                  selectattr('value')|
                  items2dict }}"
      vars:
        _query: '[].{key: key, value: value.backups[?target]}'
      run_once: true

给予

  data:
    host1:
    - src: somesource
      target: sometarget
    host2:
    - src: somesource for host2
      target: fancy target

下一个选项是添加默认属性 target: None(如果缺少),例如

    - set_fact:
        backups: "{{ [{'target': None}]|product(backups)|map('combine') }}"
    - debug:
        var: backups

给予

TASK [debug] ****************************************************
ok: [host1] => 
  backups:
  - src: somesource
    target: sometarget
  - src: anothersource
    target: null
ok: [host2] => 
  backups:
  - src: somesource for host2
    target: fancy target
ok: [host3] => 
  backups:
  - src: yet another src
    target: null
  - src: and another one
    target: null

然后,select 'not null' 目标

    - set_fact:
        data: "{{ hostvars|
                  json_query('*.backups')|
                  map('selectattr', 'target')|
                  flatten }}"
      run_once: true
    - debug:
        var: data
      run_once: true

给予

TASK [debug] ****************************************************
ok: [host1] => 
  data:
  - src: somesource
    target: sometarget
  - src: somesource for host2
    target: fancy target

要识别主机,请将此属性也添加到列表中,例如

    - set_fact:
        backups: "{{ [{'target': None, 'host': inventory_hostname}]|
                     product(backups)|map('combine') }}"

给出结果

TASK [debug] ****************************************************
ok: [host1] => 
  data:
  - host: host1
    src: somesource
    target: sometarget
  - host: host2
    src: somesource for host2
    target: fancy target

以纯 JMESPath 方式,您可以重新创建 value 属性 对实际值进行 merge 过滤 backups 属性查询:

[].{
  key: key, 
  value: merge(value, {backups: value.backups[?target]})
} | [?value.backups]

因此,鉴于任务:

- debug:
    var: hostvars | dict2items | json_query(query) | items2dict
  vars:
    query: >-
      [].{
        key: key,
        value: merge(value, {backups: value.backups[?target]})
      } | [?value.backups]

这产生:

hostvars | dict2items | json_query(query) | items2dict:
  host1:
    backups:
    - src: somesource
      target: sometarget
  host2:
    backups:
    - src: somesource for host2
      target: fancy target