Search code examples
jsonjmespath

JMESPath filter for elements with nested values starting with certain string


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?


Solution

  • 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