てくなべ (tekunabe)

ansible / network automation / 学習メモ

【Ansible】データから「select * from users where name="hoge"」的な抽出ができる selectattr

SQL の where 句のように ディクショナリのリストから抽出するには selectattr を利用

以下のようなディクショナリのリストがあるとします。DBにおけるテーブルのデータのような構造です。

users:
  - name: yamada
    age: 42
  - name: tanaka
    age: 26
  - name: suzuki
    age: 32
  - name: sato
    age: 27

ここで、「 namesato であるディクショナリのリスト」を抽出したい場合は、 selectattr を利用して以下のようにフィルター(Jinja2の機能)します。

  • selectattr によるフィルタ
- debug:
    msg: "{{ users | selectattr('name', '==', 'sato') | list }}"
  • 結果
    "msg": [
        {
            "age": 27,
            "name": "sato"
        }
    ]

無地に1件が抽出されました。 SQL でいうと select * from users where name='sato' のようなイメージです。


■ 応用例: 部分一致、以上、以下など指定もできる

先ほど使用した == (または qeualtoeq)では完全一致でしたが、他にもあります。

!= または ne: 〜ではない

- debug:
    msg: "{{ users | selectattr('name', '!=', 'sato') | list }}"
    "msg": [
        {
            "age": 42,
            "name": "yamada"
        },
        {
            "age": 26,
            "name": "tanaka"
        },
        {
            "age": 32,
            "name": "suzuki"
        }
    ]

in: 〜に含まれる

- debug:
    msg: "{{ users | selectattr('name', 'in', 'sssssatoooo') | list }}"
    "msg": [
        {
            "age": 27,
            "name": "sato"
        }
    ]

> または gt: 〜より大きい

- debug:
    msg: "{{ users | selectattr('age', '>', 27) | list }}"
    "msg": [
        {
            "age": 42,
            "name": "yamada"
        },
        {
            "age": 32,
            "name": "suzuki"
        }
    ]

>= または ge: 〜以上

- debug:
    msg: "{{ users | selectattr('age', '>=', 27) | list }}"
    "msg": [
        {
            "age": 42,
            "name": "yamada"
        },
        {
            "age": 32,
            "name": "suzuki"
        },
        {
            "age": 27,
            "name": "sato"
        }
    ]

< または lt: 〜以下

- debug:
    msg: "{{ users | selectattr('age', '<', 27) | list }}"
    "msg": [
        {
            "age": 26,
            "name": "tanaka"
        }
    ]

<= または le: 〜以下

- debug:
    msg: "{{ users | selectattr('age', '<=', 27) | list }}"
    "msg": [
        {
            "age": 26,
            "name": "tanaka"
        },
        {
            "age": 27,
            "name": "sato"
        }
    ]


■ 補足: ループと when の組み合わせとの比較

selectattr のように抽出してから何かを処理する方法の他にも、全件ループと when によって処理条件を指定する方法があります。 条件に合わなかった分のタスクは skipping になります。

  • Playbook: ループと when の組み合わせる場合
- debug:
    msg: "{{ item }}"
  when:
    - item.age <= 27
  loop: "{{ users }}"
  • 結果
skipping: [localhost] => (item={u'age': 42, u'name': u'yamada'})
ok: [localhost] => (item={u'age': 26, u'name': u'tanaka'}) => {
    "msg": {
        "age": 26,
        "name": "tanaka"
    }
}
skipping: [localhost] => (item={u'age': 32, u'name': u'suzuki'})
ok: [localhost] => (item={u'age': 27, u'name': u'sato'}) => {
    "msg": {
        "age": 27,
        "name": "sato"
    }
}

先に selectattr でフィルタしたほうがログがスッキリして、処理速度も早いかもしれません。

Playbook としてのシンプルさという点は、ループと when の組み合わせのほうがよさそうです。

参考: エラーが発生する場合

TemplateRuntimeError: no test named 'equalto' のようなエラーが発生し場合は、 Jinja2 のバージョンを確認してください。利用のバージョンで、指定した評価式が対応していないことがあります。

stackoverflow.com