Bujji
Bujji

Reputation: 1737

Is there any way we can apply "and" condition to the "terms filter" in Elastic Search?

In the below filter query it finds the documents which have any of the terms mentioned. But I am looking for the records that only match all the terms. my_languages is an array field in my index. Is there any simple way for it? I Can add term filters separately and can do it, But is there any simple "add" condition I can apply to these terms. Thank You

GET user_info/_search?pretty
    {
      "size": 30,
      "from": 0,
      "query": {
        "bool": {
          "filter": [
            {
              "terms": {
                "my_languages": [
                  8728431,
                  872854,
                  872845
                ]
              }
            }
          ]
        }
      }
    }

Mapping

  PUT user_info
    {
      "settings": {
        "number_of_shards": 1,
        "max_ngram_diff": 35
      },
      "mappings": {
        "properties": {
          "id": {
            "type": "keyword"
          },
          "email": {
            "type": "text"
          },
          "my_languages": {
            "type": "keyword"
          },
          "updated_by": {
            "type": "keyword"
          }
        }
      }
    }

Test Data

POST /user_info/_doc/
{
  "id": 10,
  "email": 2020,
  "my_languages": [
    8728431,
    872854,
    872845
  ]
}

Upvotes: 0

Views: 301

Answers (1)

LeBigCat
LeBigCat

Reputation: 1770

You need to made a bool query

{
  "query": {
    "bool": {
      "must": [ 
        {
          "match": {
            "my_languages": "8728431"
          }
        },
        {
          "match": {
            "my_languages": "872845"
          }
        }
,
        {
          "match": {
            "my_languages": "872854"
          }
        }
      ]
    }
  }
}

You can use query_string

{
  "size": 30,
  "from": 0,
  "query": {
    "query_string": {
      "query": "(8728431) AND (872854) AND (872845)",
      "default_field": "my_languages"
    }
  }
}

If you really want to pass a array, you could use script also, but you have to benchmark performance first, this seems to work:

{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "bool": {
          "must": {
            "script": {
              "script": {
                "lang": "painless",
                "source": "def x = doc['my_languages']; def list_to_check = ['8728431', '872854', '872845']; def do_not_return = true; for(int i = 0; i < list_to_check.length; i++) { if(!x.contains(list_to_check[i])) {do_not_return = false;}} return do_not_return;"
              }
            }
          }
        }
      }
    }
  }
}

Upvotes: 1

Related Questions