Each item in a loop is an array. How do I use one item in the array when looping?

huangapple go评论76阅读模式
英文:

Each item in a loop is an array. How do I use one item in the array when looping?

问题

Here's the translated code section:

我有一个任务,用来循环遍历一些JSON数据(这是有关Confluence页面及其附件的信息,来自Confluence REST API):

```json
{
  "results": [
    {
      "title": "page0",
      "children": {
        "attachment": {
          "results": [
            {
              "title": "page0attachment0",
              "history": {}
            },
            {
              "title": "page0attachment1",
              "history": {}
            }
          ]
        }
      }
    },
    {
      "title": "page1",
      "children": {
        "attachment": {
          "results": [
            {
              "title": "page1attachment0",
              "history": {}
            },
            {
              "title": "page1attachment1",
              "history": {}
            }
          ]
        }
      }
    }
  ]
}

这是我正在使用的循环过滤器:

var.json.results | subelements('children.attachment.results')

这个代码运行良好,但在循环时,页面信息是第一个元素,然后附件信息是第二个元素。

例如,在循环中如果我调用 {{ item.0.title }},我会得到以下结果:

"msg": "page0"
"msg": "page0"
"msg": "page1"
"msg": "page1"

如果我调用 {{ item.1.title }},我会得到:

"msg": "page0attachment0"
"msg": "page0attachment1"
"msg": "page1attachment0"
"msg": "page2attachment1"

因此,循环包含一个数组,页面信息是第一个元素,每个附件都是第二个元素。

再次强调,它在普通的Playbook中正常工作,例如:

- name: 获取项目信息
  amazon.aws.s3_object_info:
    region: "{{ region }}"
    bucket_name: "{{ bucket }}"
    object_name: "file/path/{{ item.1.title }}"
  loop: "{{ var.json.results | subelements('children.attachment.results') }}"

问题在于我希望任务被分成一个tasks目录,并使用include_tasks,像这样:

- name: 获取项目信息
  include_tasks: get_s3_object_info.yml
  loop: "{{ var.json.results | subelements('children.attachment.results') }}"

因此,由于我仍然需要在循环中捕获第二个元素,我不想在get_s3_object_info.yml任务中编写item.1.title,因为我希望能够在其他地方使用该任务,所以我希望只调用item.title

注意,我还需要访问history属性,这就是为什么我不能只获取附件标题的平面列表,我需要遍历包含titlehistory属性的对象列表。

最重要的是,无论是通过修改jinja模板,还是使用loop_control或其他方式,如何将item.1变为item

也许类似var.json.results | subelements('children.attachment.results') | foreach(index=1)的东西?

此外,每个项目都有一个type属性:pageattachment等。是否有办法添加一个类似where(type='attachment')的条件?


<details>
<summary>英文:</summary>

I have a task that loops through some JSON data (it&#39;s information about Confluence pages and their attachments, from the Confluence REST API):

```json
{
  &quot;results&quot;: [
    {
      &quot;title&quot;: &quot;page0&quot;,
      &quot;children&quot;: {
        &quot;attachment&quot;: {
          &quot;results&quot;: [
            {
              &quot;title&quot;: &quot;page0attachment0&quot;,
              &quot;history&quot;: {}
            },
            {
              &quot;title&quot;: &quot;page0attachment1&quot;,
              &quot;history&quot;: {}
            }
          ]
        }
      }
    },
    {
      &quot;title&quot;: &quot;page1&quot;,
      &quot;children&quot;: {
        &quot;attachment&quot;: {
          &quot;results&quot;: [
            {
              &quot;title&quot;: &quot;page1attachment0&quot;,
              &quot;history&quot;: {}
            },
            {
              &quot;title&quot;: &quot;page1attachment1&quot;,
              &quot;history&quot;: {}
            }
          ]
        }
      }
    }
  ]
}

This is the filter I'm using to loop:

var.json.results | subelements(&#39;children.attachment.results&#39;)

This works fine, but when looping for some reason, the page information is the first element then the attachment information is the second element.

For example, in the loop if I call {{ item.0.title }} I'll get the following:

&quot;msg&quot;: &quot;page0&quot;
&quot;msg&quot;: &quot;page0&quot;
&quot;msg&quot;: &quot;page1&quot;
&quot;msg&quot;: &quot;page1&quot;

And if I call {{ item.1.title }} I get:

&quot;msg&quot;: &quot;page0attachment0&quot;
&quot;msg&quot;: &quot;page0attachment1&quot;
&quot;msg&quot;: &quot;page1attachment0&quot;
&quot;msg&quot;: &quot;page2attachment1&quot;

So it appears that the loop contains an array with the page information as the first element, repeated for each attachment and the attachment as the second element.

Again, it's working fine in a regular playbook with a task like this:

- name: Get item info
  amazon.aws.s3_object_info:
    region: &quot;{{ region }}&quot;
    bucket_name: &quot;{{ bucket }}&quot;
    object_name: &quot;file/path/{{ item.1.title }}&quot;
  loop: &quot;{{ var.json.results | subelements(&#39;children.attachment.results&#39;) }}&quot;

The problem is I want the task to be separated into a tasks directory and use include_tasks like this:

- name: Get item info
  include_tasks: get_s3_object_info.yml
  loop: &quot;{{ var.json.results | subelements(&#39;children.attachment.results&#39;) }}&quot;

So, since I still need to capture the second element within the loop I don't want to have to write the item.1.title thing into the get_s3_object_info.yml task because I want to be able to use that task elsewhere so I want to just call item.title within that task.

NOTE I also need to access the history attribute, which is why I can't just get a flat list of attachment titles, I need to iterate through a list of objects that contain both the title and history attributes.

Bottom line--whether by modifying the jinja template, or using loop_control or otherwise, how can I turn item.1 into just item?

Maybe something like var.json.results | subelements(&#39;children.attachment.results&#39;) | foreach(index=1) or something like that?

Also, each item has a type attribute: page, attachment, etc. Is there a way I can add a where(type=&#39;attachment&#39;) or something similar?

答案1

得分: 2

Here's the translated content:

预备说明: 使用名为 var 的变量绝不是一个好主意,因为它没有意义,可能与Ansible/Python中的其他名称冲突。我在下面的答案中将其更改为 confuence_api_page_request。根据您的需要进行适应。


在您的包含任务中,只需按照您要使用的方式精确描述变量,例如在 get_s3_object_info.yml 中:

- name: 从S3获取附件信息
  amazon.aws.s3_object_info:
    region: &quot;{{ region }}&quot;
    bucket_name: &quot;{{ bucket }}&quot;
    object_name: &quot;file/path/{{ attachment.title }}&quot;

然后将变量按照需要传递给您的包含任务文件:

- name: 获取附件信息
  vars:
    attachment: &quot;{{ item.1 }}&quot;
  include_tasks: get_s3_object_info.yml
  loop: &quot;{{ confuence_api_page_request.json.results
    | subelements(&#39;children.attachment.results&#39;) }}&quot;

此外,由于您在上述任务中没有在任何地方使用顶层元素页面信息,您可以轻松地从您的JSON中提取它作为一个单一的列表,并直接在附件信息上进行循环。保持上述相同的包含文件,只需更改 include_tasks 循环为:

- name: 获取附件信息
  include_tasks: get_s3_object_info.yml
  loop: &quot;{{ confuence_api_page_request.json.results
    | map(attribute=&#39;children.attachment.results&#39;) | flatten }}&quot;
  loop_control:
    loop_var: attachment

在所有情况下,您可以使用 attachment.history 从包含文件中访问历史记录。

还请注意,第二个解决方案将允许您在任何包含的任务中使用默认的 item 循环变量(如果需要),因为包含循环变量不会与其冲突。

英文:

preliminary note: having a variable called var is anything but a good idea as it is not meaningful and might conflict with other names in ansible/python. I changed the name to confuence_api_page_request in the below answer. Adapt to your need


In your included task, just describe the variable exactly as you want to use it, e.g. in get_s3_object_info.yml:

- name: Get attachment info from S3
  amazon.aws.s3_object_info:
    region: &quot;{{ region }}&quot;
    bucket_name: &quot;{{ bucket }}&quot;
    object_name: &quot;file/path/{{ attachment.title }}&quot;

Then pass the variable exactly as needed to your included task file:

- name: Get attachment info
  vars:
    attachment: &quot;{{ item.1 }}&quot;
  include_tasks: get_s3_object_info.yml
  loop: &quot;{{ confuence_api_page_request.json.results
    | subelements(&#39;children.attachment.results&#39;) }}&quot;

Moreover, since you are not using the top element page information anywhere in the above tasks, you can easily extract that as a single list from your json and loop directly on the attachement information. Keeping the exact same include file above, just change the include_tasks loop to:

- name: Get attachment info
  include_tasks: get_s3_object_info.yml
  loop: &quot;{{ confuence_api_page_request.json.results
    | map(attribute=&#39;children.attachment.results&#39;) | flatten }}&quot;
  loop_control:
    loop_var: attachment

In all cases, you can access history from the included file with attachment.history

Note also the second solution will let you use the default item loop var in any of your included tasks if needed as the include loop var will not conflict with it.

huangapple
  • 本文由 发表于 2023年5月13日 11:09:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76240913.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定