You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ansible:当字典值为字典列表时识别对应Key

问题背景与需求

现有如下结构的Ansible变量字典:

grouped:
  10000:
  - {Id: 10001, Name: North_America, Parent_Id: 10000, Type: Country}
  10001:
  - {Id: 10011, Name: Maine, Parent_Id: 10001, Type: State}
  - {Id: 10012, Name: Colorado, Parent_Id: 10001, Type: State}
  - {Id: 10013, Name: Texas, Parent_Id: 10001, Type: State}
  10011:
  - {Id: 10101, Name: Augusta, Parent_Id: 10011, Type: City}
  - {Id: 10102, Name: Portland, Parent_Id: 10011, Type: City}
  10012:
  - {Id: 10103, Name: Denver, Parent_Id: 10012, Type: City}
  10013:
  - {Id: 10104, Name: Austin, Parent_Id: 10013, Type: City}
  - {Id: 10105, Name: Houston, Parent_Id: 10013, Type: City}
  10101:
  - {Id: 11001, Name: First_st, Parent_Id: 10101, Type: Street}
  - {Id: 11002, Name: Second_st, Parent_Id: 10101, Type: Street}
  10102:
  - {Id: 11003, Name: First_st, Parent_Id: 10102, Type: Street}
  - {Id: 11004, Name: Second_st, Parent_Id: 10102, Type: Street}
  10104:
  - {Id: 11005, Name: First_st, Parent_Id: 10104, Type: Street}
  - {Id: 11006, Name: Second_st, Parent_Id: 10104, Type: Street}

需要实现的功能:
给定类似North_America-Texas-Austin-First_st的层级字符串,按Country-State-City-Street顺序完成以下操作:

  • 逐级查找对应名称的项,获取其ID作为下一级的查询键
  • 若某层级的名称不存在,则将其添加到字典中

当前在第一步查找North_America的ID时遇到问题,尝试的代码及错误如下:

尝试的代码

---
hosts: localhost
gather_facts: false
connection: local
vars:
  target: "North_America-Colorado-Denver-First_st"

tasks:
  - name: Get the id using json filter and jmespath query
    set_fact:
      group_id: "{{ grouped | json_query(ID_query) | map(attribute='key') }}"
    vars:
      ID_query: '[?[*].Name == `North-America`]'
  - debug: var=group_id

  - name: Get the id using selectattr
    set_fact:
      group_id2: "{{ grouped | dict2items | selectattr('Name', 'search', 'North_America') }}"
  - debug: var=group_id2

错误输出

第一个任务的debug结果为空列表:

ok: [localhost] => {
    "group_id": []
}

第二个任务报错,提示字典对象不存在Name属性:

fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'Name'..."}

解决方案

1. 错误原因分析

  • json_query问题:原查询存在两处错误:一是拼写错误(目标名称为North_America而非North-America);二是未正确遍历字典键值对,需先将字典转为键值对列表,再筛选值中包含目标Name的项。
  • selectattr问题:dict2items转换后,每个元素是{key: "...", value: [...]}结构,没有直接的Name属性,需先展开value列表,再筛选其中的Name字段。

2. 正确的逐级查找实现

以下是完整的任务示例,先拆分目标字符串,再逐级查找ID:

---
hosts: localhost
gather_facts: false
connection: local
vars:
  target: "North_America-Colorado-Denver-First_st"
  target_levels: "{{ target.split('-') }}"
  current_parent_id: "10000"  # 根节点ID

tasks:
  - name: 遍历层级查找对应ID
    set_fact:
      current_parent_id: >-
        {% set found = grouped[current_parent_id] | selectattr('Name', 'equalto', item) | first | default(none) %}
        {% if found is not none %}
          {{ found.Id | string }}
        {% else %}
          {{ current_parent_id }}
        {% endif %}
    loop: "{{ target_levels }}"
    register: level_results

  - name: 获取最终查找结果
    set_fact:
      final_id: "{{ level_results.results | last | json_query('ansible_facts.current_parent_id') }}"

  - name: 检查所有层级是否存在
    set_fact:
      all_exists: >-
        {% set exists = true %}
        {% for res in level_results.results %}
          {% if res.ansible_facts.current_parent_id == current_parent_id %}
            {% set exists = false %}
          {% endif %}
        {% endfor %}
        {{ exists }}

  - debug:
      msg: "目标层级存在,最终ID: {{ final_id }}"
    when: all_exists

  - debug:
      msg: "目标层级不存在,需要添加"
    when: not all_exists

3. 缺失层级的添加逻辑(示例)

若需添加不存在的层级,可在遍历过程中记录缺失项,生成新ID后添加到字典:

- name: 记录缺失的层级及父ID
    set_fact:
      missing_levels: "{{ missing_levels | default([]) + [{ 'parent_id': current_parent_id, 'name': item }] }}"
    loop: "{{ target_levels }}"
    when: >-
      grouped[current_parent_id] | selectattr('Name', 'equalto', item) | list | length == 0

  - name: 添加缺失层级到grouped字典
    set_fact:
      grouped: >-
        {% set new_id = (grouped | dict2items | map(attribute='key') | map('int') | max + 1) | string %}
        {% set new_item = [{ 'Id': new_id, 'Name': item.name, 'Parent_Id': item.parent_id, 'Type': level_type }] %}
        {% set updated_grouped = grouped | combine({ item.parent_id: grouped[item.parent_id] + new_item }, recursive=true) %}
        {{ updated_grouped | combine({ new_id: [] }) }}
    loop: "{{ missing_levels }}"
    vars:
      level_index: "{{ loop.index0 }}"
      level_type: >-
        {{ 'Country' if level_index == 0 else 'State' if level_index == 1 else 'City' if level_index == 2 else 'Street' }}

内容的提问来源于stack exchange,提问作者piercjs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 03:07:04