Course contents
Query DSL · 12 / 28

Boolean logic

Translate business rules into bool trees and understand should defaults.

9 sectionsElasticsearch 9.4.3Not read yet

What you will learn

  • Four clause roles
  • Implicit defaults
  • Grouping and negation

Translate business rules into bool trees and understand should defaults.

course-products examples use the teaching mapping and product from chapter 5, “Mapping and field types”. Exercise walkthroughs below use the trainer’s separate public dataset.

How it works
  • Mandatory constraints
  • Alternative group
  • Eligibility + score

Translate business rules into bool trees and understand should defaults.

The course targets Elasticsearch 9.4.3. Exercise examples use the public products dataset; other requests illustrate separate teaching indices and may require setup or privileges unavailable in the trainer. Responses are shortened fragments; scores may vary. Check deployment and license requirements for advanced features.

01 / 09

Four clause roles

must requires a match and can score; filter requires a match without contributing score; must_not excludes. should provides alternatives or bonuses. Clauses in must or filter are combined with AND. A should array is not universally a mandatory OR: requirements depend on other bool clauses and minimum_should_match.

Check your understanding
02 / 09

Implicit defaults

With should clauses and no must or filter, the default required should count is one. When must or filter exists, it becomes zero. Adding a stock filter can accidentally turn required brands into optional bonuses. Explicitly set minimum_should_match or isolate required alternatives in a nested bool.

Check your understanding
03 / 09

Grouping and negation

“In stock AND (brand A OR price below 100)” requires a nested group. Excluding an entire product differs from excluding one nested offer. Write a small truth table of qualifying and rejected products before constructing complex JSON. Named queries can help diagnose branches but may add execution cost.

An explicit alternative requirement preserves meaning when filters are added.

POST /course-products/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "in_stock": true
          }
        }
      ],
      "should": [
        {
          "term": {
            "brand": "Sony"
          }
        },
        {
          "range": {
            "price": {
              "lt": 100
            }
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

How to read this result

An explicit alternative requirement preserves meaning when filters are added.

Check your understanding
04 / 09

Catalog reasoning example

For in_stock AND (Sony OR price<100), test Sony/150/in stock, Bose/50/in stock, Bose/150/in stock and Sony/50/out of stock. Only the first two qualify. If the third passes after adding filter, alternatives became optional. If the fourth passes, the stock constraint was lost. This truth table remains useful even when the JSON structure changes completely.

Check your understanding
05 / 09

Combine required conditions

Every clause in bool.must must match. In query context these clauses also contribute to scoring. A list of match and term clauses therefore requires both text relevance and an exact condition. The example searches for gaming in name and requires availability; it is an AND, not an alternative between a word and a boolean.

A single clause can be written as an object, while several clauses are normally written as an array. Choose a clear consistent style. In ordinary product search it is often better to move availability into filter, but this section isolates how must works; an exercise can deliberately require a particular context.

Try a different scenario

POST /products/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "gaming"
          }
        },
        {
          "term": {
            "in_stock": true
          }
        }
      ]
    }
  },
  "size": 2,
  "_source": [
    "name",
    "in_stock"
  ]
}
Elasticsearch response
Shortened response · JSON
{
  "hits": {
    "total": {
      "value": 20,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "4",
        "_score": 2.7367332,
        "_source": {
          "name": "Logitech Gaming Keyboard 4",
          "in_stock": true
        }
      },
      {
        "_id": "14",
        "_score": 2.7367332,
        "_source": {
          "name": "Samsung Gaming Keyboard 14",
          "in_stock": true
        }
      }
    ]
  }
}

How to read this result

Every hit matches the text and is in stock. The score comes from scored clauses rather than from the number of JSON fields in the request.

Apply this in exercise 6
06 / 09

Exclude discontinued products

A must_not clause rejects a document when the inner query matches. Multiple must_not clauses mean that none may match: excluding status A and excluding status B removes both populations. This is not an instruction to find documents that differ from just one of the values.

A bool containing only must_not begins with all documents and subtracts the matches. Missing values can therefore remain in the result: a document without status does not match term status: preorder and is not excluded by that clause. If presence is required, add exists as a separate required condition.

Try a different scenario

POST /products/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "term": {
            "status": "preorder"
          }
        }
      ]
    }
  },
  "size": 2,
  "_source": [
    "name",
    "status"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ]
}
Elasticsearch response
Shortened response · JSON
{
  "hits": {
    "total": {
      "value": 200,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "2",
        "_score": null,
        "_source": {
          "name": "Reference device",
          "status": "discontinued"
        }
      },
      {
        "_id": "3",
        "_score": null,
        "_source": {
          "name": "Sony Travel Speaker 3",
          "status": "active"
        }
      }
    ]
  }
}

How to read this result

No returned product has status preorder. This query intentionally keeps active and discontinued products; it teaches exclusion, not a complete availability policy.

Apply this in exercise 8
07 / 09

Match alternative brands

should expresses alternatives. Here the alternatives are categories rather than brands: either Audio or Accessories is enough. Setting minimum_should_match to 1 documents the intended OR even though a bool with only should already requires one match by default. It becomes especially important when adding other required clauses.

Each alternative can itself be a whole query tree. To express (A AND B) OR (C AND D), put two nested bool groups in should, not four independent leaves. The final lesson uses precisely that grouping for separate category-price policies.

ABA AND BA OR B
falsefalsefalsefalse
falsetruefalsetrue
truefalsefalsetrue
truetruetruetrue

Try a different scenario

POST /products/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "category": "Audio"
          }
        },
        {
          "term": {
            "category": "Accessories"
          }
        }
      ],
      "minimum_should_match": 1
    }
  },
  "size": 2,
  "_source": [
    "name",
    "category"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ]
}
Elasticsearch response
Shortened response · JSON
{
  "hits": {
    "total": {
      "value": 150,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "2",
        "_score": null,
        "_source": {
          "name": "Reference device",
          "category": "Audio"
        }
      },
      {
        "_id": "4",
        "_score": null,
        "_source": {
          "name": "Logitech Gaming Keyboard 4",
          "category": "Accessories"
        }
      }
    ]
  }
}

How to read this result

Each hit belongs to at least one allowed category. A product is not required to belong to both, which would be impossible for our single-valued category field.

Apply this in exercise 9
08 / 09

Require optional matches

minimum_should_match belongs to the bool whose should clauses it governs. With must or filter present, the default required count is zero; without either, it is one. Set the count explicitly when the requirement is "at least two properties". The example requires two of three words in name while also filtering stock.

This counts matching should clauses, not the number of terms produced by one match query. A single match containing several words is still one should clause. Build separate clauses for separately counted features; inside each match, the text analyzer still handles case and tokenization.

Try a different scenario

POST /products/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "in_stock": true
          }
        }
      ],
      "should": [
        {
          "match": {
            "name": "wireless"
          }
        },
        {
          "match": {
            "name": "premium"
          }
        },
        {
          "match": {
            "name": "mouse"
          }
        }
      ],
      "minimum_should_match": 2
    }
  },
  "size": 2,
  "_source": [
    "name",
    "in_stock"
  ]
}
Elasticsearch response
Shortened response · JSON
{
  "hits": {
    "total": {
      "value": 39,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "7",
        "_score": 3.2707968,
        "_source": {
          "name": "Apple Wireless Mouse 7",
          "in_stock": true
        }
      },
      {
        "_id": "17",
        "_score": 3.2707968,
        "_source": {
          "name": "Dell Wireless Mouse 17",
          "in_stock": true
        }
      }
    ]
  }
}

How to read this result

A qualifying title contains at least two of the requested word features. A document that only passes the stock filter is not enough.

Apply this in exercise 10
09 / 09

Complex business rules

Start with the formula S AND (A OR B). S is shared eligibility: stock and permitted statuses. Each branch combines its own category with its own price interval. In this example S uses only preorder status, A is Accessories below 200 and B is Smartphones from 200 inclusive to 900 exclusive.

Place the shared restrictions in the outer filter, the two branches in outer should and minimum_should_match: 1 at that same outer level. Within each branch use a nested bool.filter for category AND price. Flattening all four branch leaves into one should lets a cheap product from the wrong category pass.

Test the lower boundary, upper boundary, a document satisfying only category, a document satisfying only price and an out-of-stock document satisfying a full branch. These cases distinguish a correctly grouped expression from an accidentally broad query. Apply the same reasoning with the different values specified in the practical task.

Shared SBranch ABranch BAccept?
falsetruetrueNo
truefalsefalseNo
truetruefalseYes
truefalsetrueYes

Try a different scenario

POST /products/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "in_stock": true
          }
        },
        {
          "term": {
            "status": "preorder"
          }
        }
      ],
      "should": [
        {
          "bool": {
            "filter": [
              {
                "term": {
                  "category": "Accessories"
                }
              },
              {
                "range": {
                  "price": {
                    "lt": 200
                  }
                }
              }
            ]
          }
        },
        {
          "bool": {
            "filter": [
              {
                "term": {
                  "category": "Smartphones"
                }
              },
              {
                "range": {
                  "price": {
                    "gte": 200,
                    "lt": 900
                  }
                }
              }
            ]
          }
        }
      ],
      "minimum_should_match": 1
    }
  },
  "size": 2,
  "_source": [
    "name",
    "category",
    "price",
    "status",
    "in_stock"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ]
}
Elasticsearch response
Shortened response · JSON
{
  "hits": {
    "total": {
      "value": 15,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "4",
        "_score": null,
        "_source": {
          "name": "Logitech Gaming Keyboard 4",
          "category": "Accessories",
          "price": 199,
          "in_stock": true,
          "status": "preorder"
        }
      },
      {
        "_id": "25",
        "_score": null,
        "_source": {
          "name": "Apple USB Cable 25",
          "category": "Smartphones",
          "price": 499,
          "in_stock": true,
          "status": "preorder"
        }
      }
    ]
  }
}

How to read this result

Each hit passes the shared restrictions and one complete category-price branch. A condition from branch A cannot be paired with an unrelated condition from branch B.

Apply this in exercise 26

Check your understanding

Answer before expanding each explanation.

1. When is should required by default?

When should exists without must or filter, one match is required.

2. What changes when filter is added?

The implicit minimum_should_match becomes zero.

3. How does must differ from filter?

Both require matches, but filter does not contribute score.

4. How do you express parentheses?

Use a nested bool with explicit alternative requirements.

5. How do you validate a business rule?

Use positive and negative examples, especially at condition boundaries.

Key takeaways

  • When should exists without must or filter, one match is required.
  • The implicit minimum_should_match becomes zero.
  • Both require matches, but filter does not contribute score.
  • Use a nested bool with explicit alternative requirements.
  • Use positive and negative examples, especially at condition boundaries.

Official reference

Ready to put this into practice?

Confirm reading to save your place. Reading and exercise progress are tracked separately in this browser using your guest cookie.