Elasticsearch Range filter using should and must

Viewed 239

I just started working with ES I am facing an issue with the query body I need to filter results using two fields, price and discount. It works fine when I use them with must but some of the documents do not contain a discount field. how to get those too. I am confused about how to write the query. below is what I am using currently. It returns the response without discount but does not apply a range filter. Please help me to fix this issue and explain how it works.

"bool": {
      "filter": [
        {
            "bool": {
                "should": [
                   {
                   "range": {
                      "discount": {
                            "gte": $minDiscount,
                            "lte": $maxDiscount
                            }
                        }
                    }
                ],
                "must":[
                    {
                    "range": {
                       "price": {
                            "gte": $minPrice,
                            "lte": $maxPrice
                             }
                          }
                      }
                ]
            }
        },
       ]
     }
1 Answers

Let me explain it with an example, lets consider that you have following four documents in ES.

PUT your-index/_doc/1
{
    "price" : 5000,  -> This document price and discount will not be in range.
    "discount" : 80
}

{
    "price" : 100,
    "discount" : 10
}

{
    "price" : 200,
    "discount" : 20
}

{
  "price" : 300 --> this document doesn't have discount field
}

Now if you search the documents whose price range is between 10 to 1000, and discount is between 10 to 30, but you also want last document to also come, as it doesn't have discount, but price is under the range.

** Search query for above requirement**

POST your-index/_search
{
    "query": {
        "bool": {
            "should": [
                {
                    "range": {
                        "discount": {
                            "gte": 10,
                            "lte": 30
                        }
                    }
                },
                {
                    "range": {
                        "price": {
                            "gte": 10,
                            "lte": 1000
                        }
                    }
                }
            ]
        }
    }
}

And search result

"hits": [
            {
                "_index": "71671533",
                "_id": "1",
                "_score": 2.0,
                "_source": {
                    "price": 100,
                    "discount": 10
                }
            },
            {
                "_index": "71671533",
                "_id": "2",
                "_score": 2.0,
                "_source": {
                    "price": 200,
                    "discount": 20
                }
            },
            {
                "_index": "71671533",
                "_id": "3",
                "_score": 1.0,
                "_source": {
                    "price": 300
                }
            }
        ]

Things to note:

  1. I used should array for searching the documents with the discount and price range, this way any one condition matching the range will be returned, and as you can notice, fourth document doesn't have the discount field but its matching in the price range, so its coming in search result.
Related