Search code examples
pythonjmespath

Unable to filter for multiple features with JMESPath


My data looks as follows. How come I am able to filter for one word using contains but not a list of words I wish to filter for? both queries should produce the same output

import jmespath

data = {'collection': {'items': {'word': 'ice-cube'}}}
jmespath.search(
  'values(collection)[?word!=null && contains([`cube`,`-`],word)]', 
  data
) # returns []
jmespath.search(
  'values(collection)[?word!=null && contains(word,`cube`)]', 
  data
) # works

Solution

  • The signature of the function contains is

    boolean contains(array|string $subject, any $search)
    

    So, when you are doing

    contains([`cube`, `-`], word)
    

    You are actually looking for the value of the property word in the array [`cube`, `-`], and not the other way around, as you are doing it in

    contains(word, `cube`)
    

    Where you are indeed searching for cube in the value of the property word.

    Furthermore:

    If $subject is an array, this function returns true if one of the elements in the array is equal to the provided $search value.

    Source: https://jmespath.org/specification.html#contains

    Which means you have to have exact match of what you are searching for in the subject, which is not what you are trying to do here.


    For your requirement, you will have to construct the query with multiple contains:

    collection.* | [?word && contains(word, `-`) && contains(word, `cube`)]
    

    Which you can easily do, since you are doing your query with the Python library.

    For example:

    import jmespath
    
    data = {'collection': {'items': {'word': 'ice-cube'}}}
    terms = {'cube', '-'}
    contains = [f'contains(word, `{term}`)' for term in terms]
    print(
      jmespath.search(
        f'collection.* | [?word && {" && ".join(contains)}]',
        data
      )
    )
    

    Would yield:

    [{'word': 'ice-cube'}]