Search code examples
djangoelasticsearchdjango-haystack

How to get multiple range queries to work in elasticsearch?


I'm trying to use multiple range queries in elasticsearch 2.4.5, but it doesn't seem to work when combined with each other.

I'm building an online store which must have the ability to search and filter products, and I'm using elasticsearch to add those functionalities. The store must be able to filter products by the following fields: price, num_in_stock, and popularity.

Here's how each field is store internally by elasticsearch:

  • popularity = float (i.e. 0.0098736465783, which will be converted to percent to indicate how popular the product is)

  • price = float (i.e. 5.890000000, which will be rounded and converted to proper price format)

  • num_in_stock = long (i.e. 10)

Here's what I know for sure:

  1. When I perform a single range query with any field individually (price, popularity, or num_in_stock), it works fine. For instance, when I filter by popularity -- let's say 0.05 (5%) - 0.75 (75%) -- it works fine.

  2. However, when I combine multiple range queries the popularity range query does not work. However the price and num_in_stock ranges do work. In the sample query below, the range price and num_in_stock works fine, but the range popularity does not work!

Here's a sample query:

{
  "query": {
    "query": {
      "constant_score": {
        "filter": {
          "bool": {
            "must": [
              {
                "bool": {
                  "must": {
                    "range": {
                      "popularity": {
                        "gte": 0,
                        "lte": 0.02
                      },
                      "price": {
                        "gte": 0,
                        "lte": 10.25
                      },
                      "num_in_stock": {
                        "gte": 0,
                        "lte": 15
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  "from": 0,
  "size": 1
}

Here's what I get for the response:

{
  "took": 10,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 9,
    "max_score": null,
    "hits": [
      {
        "_index": "store",
        "_type": "modelresult",
        "_id": "product.360",
        "_score": null,
        "_source": {
          "product_name": "Tide Laundry Detergent",
          "price": 5.15,
          "popularity" : 0.044386262065587822,
          "num_in_stock": 5
        }
      }
    ]
  }
}

As you can see, I wanted to filter by popularity from 0 to 0.02, but it gives me a results with popularity greater than that!

This is a big problem for me because the store must have the ability to filter by multiple ranges at the same time. What are some strategies to handle this problem?


Solution

  • the correct construction of range queries is something like

    "bool" : {
      "must" : [
        {
          "range" : {
            "popularity" : ...
          }
        },
        {
          "range" : {
            "price" : ...
          }
        },
        {
          "range" : {
            "num_in_stock" : ...
          }
        },
      ]
    }