8

I have documents that look like:

{
    "tags" => [
        "tag1",
        "tag2",
    ],
    "name" => "Example 1"
}

{
    "tags" => [
        "tag1",
        "tag3",
        "tag4"
    ],
    "name" => "Example 2"
}

What I now want is to do a terms search where given array might look like:

[tag1, tag3]

where expected hit should be:

{
    "tags" => [
        "tag1",
        "tag3",
        "tag4"
    ],
    "name" => "Example 2"
}

However, when I do a query like:

GET _search
{
    "query": {
        "filtered": {
           "query": {
               "match_all": {}
           },
           "filter": {
               "bool": {
                   "must": [
                      {
                          "terms": {
                             "tags": [
                                "tag1",
                                "tag3"
                             ]
                          }
                      }
                   ]
               }
           }
       }
    }
}

I get both "Example 1" and "Example 2" as hits since both Example 1 and Example 2 contains either tag1 or tag3. By looking at the documentation for terms I figured out that terms is actually a contains query.

How can I in this case make sure that Example 2 is the only hit when querying with tag1 and tag3?

Ekenstein
  • 531
  • 1
  • 8
  • 18

3 Answers3

6

For those who are looking at this in 2020, you might have noticed that minimum_should_match is deprecated long back.

There is an alternative currently available, which is to use terms_set.

For eg:

{
  "query": {
    "terms_set": {
      "programming_languages": {
        "terms": [ "c++", "java", "php" ],
        "minimum_should_match_field": "required_matches"
      }
    }
  }
}

The above example assumes a field required_matches exists which contains an integer, that defines how many matches should be there.

What is more useful is the alternative field minimum_should_match_script.

See the example below:

{
  "query": {
    "terms_set": {
      "programming_languages": {
        "terms": [ "c++", "java", "php" ],
        "minimum_should_match_script": {
          "source": "2"
        },
      }
    }
  }
}

You can always use the inside a filter context to make it works a filter.

Read more here

Abdul Vajid
  • 1,291
  • 1
  • 15
  • 25
  • 1
    I'm not sure in what context `minimum_should_match` might be deprecated, but Google will only show me that happening to `minimum_number_should_match`. – Noumenon Apr 23 '21 at 01:48
4

You need to set the execution mode to "and" by adding "execution": "and" to the terms filter so that all terms must be contained within a document to be considered a match

GET _search
{
   "query": {
      "filtered": {
         "query": {
            "match_all": {}
         },
         "filter": {
            "terms": {
               "tags": [
                  "tag1",
                  "tag3"
               ],
               "execution": "and"
            }
         }
      }
   }
}

This is effectively the same as building a bool must filter with the conjunction of all terms, but in a more compact form.

Russ Cam
  • 124,184
  • 33
  • 204
  • 266
  • this should now be written with a `must` query because `filtered` queries are deprecated since ES v5.0 as stated [here](https://stackoverflow.com/a/40521602/6114088) – Louis Oct 26 '18 at 14:14
  • 1
    @Louis entire query needs to be written quite differently for 5 onwards as execution mode on terms query is gone as well – Russ Cam Oct 29 '18 at 22:00
0

You can set minimum_should_match to match your array:

{
    "query": {
        "filtered": {
           "query": {
               "match_all": {}
           },
           "filter": {
               "bool": {
                   "must": [
                      {
                          "terms": {
                             "tags": ["tag1","tag3"],
                             "minimum_should_match": 2
                          }
                      }
                   ]
               }
           }
       }
    }
}
chengpohi
  • 14,064
  • 1
  • 24
  • 42