Elasticsearch Array Contains Search With Terms Filter

Here is a quick blog post on Elasticsearch and terms filter to achieve array contains search with terms filter
14 July 2015
3 minutes read

Related Posts

Here is a quick blog post on Elasticsearch and terms filter while I still remember how the hell it works :) Yes, this is possibly the 20th time that I looked for how to achieve array contains functionality in Elasticseach and it's a clear sign for me that I need to blog about it :)

I created the index called movies (mostly borrowed from Joel's great Elasticsearch 101 blog post) and here is its mapping:

PUT movies/_mapping/movie
{
  "movie": {
    "properties": {
       "director": {
          "type": "string"
       },
       "genres": {
          "type": "string",
          "index": "not_analyzed"
       },
       "title": {
          "type": "string"
       },
       "year": {
          "type": "long"
       }
    }
  }
}

The genres field mapping is important here as it needs to be not analyzed. I also indexed a few stuff in it:

POST movies/movie
{
    "title": "Apocalypse Now",
    "director": "Francis Ford Coppola",
    "year": 1979,
    "genres": ["Drama", "War", "Foo"]
}

POST movies/movie
{
    "title": "Apocalypse Now",
    "director": "Francis Ford Coppola",
    "year": 1979,
    "genres": ["Drama", "War", "Foo", "Bar"]
}

POST movies/movie
{
    "title": "Apocalypse Now",
    "director": "Francis Ford Coppola",
    "year": 1979,
    "genres": ["Drama", "Bar"]
}

Now, I am interested in finding out the movies which is in War or Foo genre. The way to achieve that is the terms filter as mentioned:

GET movies/movie/_search
{
  "query": {
    "filtered": {
      "query": {
        "match_all": {}
      },
      "filter": {
        "terms": {
          "genres": ["War", "Foo"]
        }
      }
    }
  }
}

We will get us the following result:

"hits": [
   {
      "_index": "movies",
      "_type": "movie",
      "_id": "AU6OkygJidzUtyfB9L2D",
      "_score": 1,
      "_source": {
         "title": "Apocalypse Now",
         "director": "Francis Ford Coppola",
         "year": 1979,
         "genres": [
            "Drama",
            "War",
            "Foo",
            "Bar"
         ]
      }
   },
   {
      "_index": "movies",
      "_type": "movie",
      "_id": "AU6OkwUeidzUtyfB9L1q",
      "_score": 1,
      "_source": {
         "title": "Apocalypse Now",
         "director": "Francis Ford Coppola",
         "year": 1979,
         "genres": [
            "Drama",
            "War",
            "Foo"
         ]
      }
   }
]

What if we want to see the movies which is in War, Foo and Bar genres at the same time? Well, there are probably other ways of doing this but here is how I hacked it together with bool and term filter:

GET movies/movie/_search
{
  "query": {
    "filtered": {
      "query": {
        "match_all": {}
      },
      "filter": {
        "bool": {
          "must": [
            { 
              "term": {
                "genres": "War"
              }
            }
          ],
          
          "must": [
            { 
              "term": {
                "genres": "Foo"
              }
            }
          ],
          
          "must": [
            { 
              "term": {
                "genres": "Bar"
              }
            }
          ]
        }
      }
    }
  }
}

The result:

"hits": [
   {
      "_index": "movies",
      "_type": "movie",
      "_id": "AU6OkygJidzUtyfB9L2D",
      "_score": 1,
      "_source": {
         "title": "Apocalypse Now",
         "director": "Francis Ford Coppola",
         "year": 1979,
         "genres": [
            "Drama",
            "War",
            "Foo",
            "Bar"
         ]
      }
   }
]

The exact match is a whole different story :)