2
votes

I'm working on implementing elasticseach in a site I am working on and trying to build a multi-range filter. There are 2 fields that have ranges that I will need to filter by: price, and rating. Each one should be able to have multiple ranges selected at once. So for example, you should be able to filter by ratings of 80-85 OR 90-95 while also filtering by prices of 10-15 OR 20-25. I am using a post_filter for term and range filters, as I am building a faceted navigation for search results. Below you can see my structured json that I am sending.

{
  "from": 0,
  "size": 15,
  "query": {
    "multi_match": {
      "query": "taz 2003",
      "type": "most_fields",
      "operator": "or",
      "fields": [
        "name",
        "vintage",
        "brand^4",
        "review"
      ],
      "fuzziness": "1"
    }
  },
  "post_filter": {
    "bool": {
      "must": [
        {
          "term": {
            "vintage": "2009"
          }
        }
      ],
      "should": [
        {
          "range": {
            "price": {
              "gte": 10,
              "lt": 15
            }
          }
        },
        {
          "range": {
            "price": {
              "gte": 20,
              "lt": 25
            }
          }
        },
        {
          "range": {
            "rating": {
              "gte": 80,
              "lt": 85
            }
          }
        },
        {
          "range": {
            "rating": {
              "gte": 90,
              "lt": 95
            }
          }
        }
      ]
    }
  }
}

This almost works, however because all the ranges are within a should bool filter, it is using OR to combine them, so it's only matching on one of the filters. What I need is for it to do something like the following (in SQL):

SELECT * FROM `table` where 
vintage = 2009 AND
(
    (
        price BETWEEN 10 AND 15 OR
        price BETWEEN 20 AND 25
    ) AND
    (
        rating BETWEEN 80 AND 85 OR
        rating BETWEEN 90 AND 85
    )
);

I'm at a loss currently on how to accomplish this. Thanks in advance for help.

1
Did you try putting all the filters in the "must"? - Sloan Ahrens
Yes, but that will return no results because an entity cannot have a rating between 80-85 AND 90-95, and same with the price - ZeLoubs
Oh, I see. Should be able to nest bools. If you'll post some example data and desired output I'll see if I can work it out. - Sloan Ahrens

1 Answers

5
votes

One way would be to use nested bool query within the must

Example:

{
  "from": 0,
  "size": 15,
  "query": {
    "multi_match": {
      "query": "taz 2003",
      "type": "most_fields",
      "operator": "or",
      "fields": [
        "name",
        "vintage",
        "brand^4",
        "review"
      ],
      "fuzziness": "1"
    }
  },
  "post_filter": {
    "bool": {
      "must": [
        {
          "term": {
            "vintage": "2009"
          }
        },
        {
          "bool": {
            "should": [
              {
                "range": {
                  "price": {
                    "gte": 10,
                    "lt": 15
                  }
                }
              },
              {
                "range": {
                  "price": {
                    "gte": 20,
                    "lt": 25
                  }
                }
              }
            ]
          }
        },
        {
          "bool": {
            "should": [
              {
                "range": {
                  "rating": {
                    "gte": 80,
                    "lt": 85
                  }
                }
              },
              {
                "range": {
                  "rating": {
                    "gte": 90,
                    "lt": 95
                  }
                }
              }
            ]
          }
        }
      ]
    }
  }
}