1

I have the following JSON structure:

[
    {
        "stack": [
            "datasync"
        ],
        "env": [
            "dev",
            "test"
        ],
        "runOnBranch": [
            "feature/",
            "bugfix/",
            "develop"
        ]
    },
    {
        "stack": [
            "datasync"
        ],
        "env": [
            "val",
            "prod"
        ],
        "runOnBranch": [
            "main"
        ]
    }
]

And I would like to filter the list based on if a given string starts with one of the strings defined in the runOnBranch attribute.

My best guess so far doesn't work:

[?runOnBranch.starts_with(@, `feature/`) == `true`]

The error I get is:

"Search parse error": TypeError: starts_with() expected argument 1 to be type 2 but received type 3 instead.

The result I would like to get is:

[
    {
        "stack": [
            "datasync"
        ],
        "env": [
            "dev",
            "test"
        ],
        "runOnBranch": [
            "feature/",
            "bugfix/",
            "develop"
        ]
    }
]

What am I missing?

WolfgangM
  • 243
  • 2
  • 14
  • Your attempt does not work because `starts_with` needs a string (`boolean starts_with(string $subject, string $prefix)`), while you are passing an array to it. – β.εηοιτ.βε Sep 02 '22 at 17:56

2 Answers2

1

In order to assess that there is one element in the array that starts with something you will need to:

  1. assess each string in the array, effectively creating a list of boolean, so, with starts_with but targeting each string, not the whole array:
    runOnBranch[].starts_with(@, `feature/`),
    
  2. assess that there is at least one true value contained in the resulting array, with the help of the contains function
    contains(runOnBranch[].starts_with(@, `feature/`), `true`)
    
  3. and finally put all this in your filter

So, we end with the query:

[?contains(runOnBranch[].starts_with(@, `feature/`), `true`)]

Which yields:

[
  {
    "stack": [
      "datasync"
    ],
    "env": [
      "dev",
      "test"
    ],
    "runOnBranch": [
      "feature/",
      "bugfix/",
      "develop"
    ]
  }
]

And to be more coherent in notation, this can also be written as:

[?(runOnBranch[].starts_with(@, `feature/`)).contains(@, `true`)]

Side note: simplify those kind of filter:

[?runOnBranch.starts_with(@, `feature/`) == `true`]

to

[?runOnBranch.starts_with(@, `feature/`)]

as starts_with already returns a boolean, as documented in the function signature:

boolean starts_with(string $subject, string $prefix)

Source: https://jmespath.org/specification.html#starts-with

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
1

Q: "Filter the list on attribute's runOnBranch any item starts with a given string."

A: Put the below declarations into the vars

_query: '[?runOnBranch[?starts_with(@, `feature/`)]]'
result: "{{ data|json_query(_query) }}"

gives

result:
  - env: [dev, test]
    runOnBranch: [feature/, bugfix/, develop]
    stack: [datasync]

Example of a complete playbook for testing

- hosts: localhost

  vars:

    data:
      - env: [dev, test]
        runOnBranch: [feature/, bugfix/, develop]
        stack: [datasync]
      - env: [val, prod]
        runOnBranch: [main]
        stack: [datasync]

    _query: '[?runOnBranch[?starts_with(@, `feature/`)]]'
    result: "{{ data|json_query(_query) }}"

  tasks:

    - debug:
        var: result|to_yaml

You can put the string into a variable. For example,

    pattern: feature/
    _query: '[?runOnBranch[?starts_with(@, `{{ pattern }}`)]]'

Then, you can override the string on the command line. For example,

shell> ansible-playbook playbook.yml -e pattern=ma

PLAY [localhost] *****************************************************************************

TASK [debug] *********************************************************************************
ok: [localhost] => 
  feature|to_yaml: |-
    - env: [val, prod]
      runOnBranch: [main]
      stack: [datasync]

PLAY RECAP ***********************************************************************************
localhost: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63