A filter context does not score, and is cached

Every clause in a query context computes a relevance score, including the ones that are yes-or-no questions — in stock, in this category, under this price. Scoring them is wasted work and prevents caching.

{
  "query": {
    "bool": {
      "must":   [ { "match": { "name": "aviator" } } ],
      "filter": [
        { "term":  { "in_stock": true } },
        { "range": { "price_cents": { "lte": 20000 } } }
      ]
    }
  }
}

Clauses under filter produce no score and are cached as a bitset per segment, so the second identical filter is nearly free. The rule is simple enough to apply mechanically: if the answer is a boolean, it belongs in filter. On a faceted catalogue this is usually the single largest query improvement available.