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?

1
  • 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. Commented Sep 2, 2022 at 17:56

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

1 Comment

What can I say... great explanation! Thanks a ton!
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.