Separate eligibility from scoring and read Query DSL as a tree.
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.
- A tree of clauses
- Two questions for a document
- Constant scores and caching
Separate eligibility from scoring and read Query DSL as a tree.
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.
A tree of clauses
A leaf query checks a condition such as term, range or match. A compound query combines queries or changes their scoring, as bool, dis_max and constant_score do. JSON describes a tree, not an instruction sequence. Filter array order does not guarantee execution order: the engine optimizes internally.
Check your understandingTwo questions for a document
Query context can score how well a document matches. Filter context decides eligibility. Stock, tenant_id and price bounds are normally constraints. Moving match into filter does not change its analyzer, but removes its scoring contribution. This is a semantic decision, not just a mechanical optimization.
Check your understandingConstant scores and caching
match_all selects every document; match_none selects none. constant_score wraps a filter with a uniform contribution. Filter context can make results eligible for caching, but caching depends on usage, segments and internal policies. A filter is not guaranteed to be cached or faster on the second request.
Exact-brand matches receive a uniform score contribution.
{
"query": {
"constant_score": {
"filter": {
"term": {
"brand": "Sony"
}
}
}
}
}How to read this result
Exact-brand matches receive a uniform score contribution.
Catalog reasoning example
For “wireless” with price<100, name matching supports relevance comparison while price determines budget eligibility. A product costing 20 is not automatically more textually relevant than one costing 90. Put match in must and range in filter. A user-selected price sort is a separate ordering contract. Putting every clause in must can add unwanted scoring and make results harder to explain.
Check your understandingRetrieve all products
The root of a search body is a JSON object. query defines which documents match. match_all with an empty object imposes no restriction, so it is useful for exploring a dataset before adding conditions. size controls the number of returned hits, not how many documents match; sort makes this small demonstration reproducible. JSON requires double quotes and does not allow comments or a trailing comma.
hits.total.value reports the number of matching documents; relation: eq means this count is exact. hits.hits is the returned page. _id identifies a hit, _source contains requested original fields, and _score is the relevance metadata. took is measured in milliseconds and varies between runs. Our fragments omit took, shard information and index metadata; that omission is presentation, not another Elasticsearch response format.
| Field | Mapping | Purpose |
|---|---|---|
| id | integer | Stable product identifier and tie-breaker |
| name / description | text | Analyzed full-text search |
| brand / brand.keyword | text / keyword | Text search / exact brand value |
| category / status | keyword | Case-sensitive exact values |
| price / rating | double / float | Numeric values; rating is optional |
| in_stock / created_at | boolean / date | Availability / creation date |
Try a different scenario
{
"query": {
"match_all": {}
},
"size": 2,
"_source": [
"name",
"price"
],
"sort": [
{
"id": "asc"
}
]
}Elasticsearch response
{
"hits": {
"total": {
"value": 300,
"relation": "eq"
},
"hits": [
{
"_id": "1",
"_score": null,
"_source": {
"name": "Wireless",
"price": 100
}
},
{
"_id": "2",
"_score": null,
"_source": {
"name": "Reference device",
"price": 100
}
}
]
}
}How to read this result
Two hits are returned, but total is 300. The id order comes from sort, not from relevance. _source is limited to name and price to keep the example readable. Later lessons explain these output controls in detail.
Filter without scoring
bool.filter requires every listed restriction without adding a relevance contribution. Exact brand, stock and price restrictions are good examples: an available product should not gain relevance just because it is available. With no scored clause, a filter-only bool normally returns score zero.
Context applies to the whole clause below it. A nested bool with two filters represents a grouped AND. Keep query structure readable rather than using extra levels without purpose. Filter clauses can be considered for caching by Elasticsearch, but correctness must never depend on a cache hit or a particular execution order.
| Clause | Required? | Adds score in query context? |
|---|---|---|
| must | Yes | Yes |
| filter | Yes | No |
| must_not | Must not match | No |
| should | Depends on minimum_should_match | Yes |
Try a different scenario
{
"query": {
"bool": {
"filter": [
{
"term": {
"brand.keyword": "Sony"
}
},
{
"term": {
"in_stock": false
}
},
{
"range": {
"price": {
"lt": 1200
}
}
}
]
}
},
"size": 2,
"_source": [
"name",
"brand",
"price",
"in_stock"
],
"sort": [
{
"id": "asc"
}
]
}Elasticsearch response
{
"hits": {
"total": {
"value": 40,
"relation": "eq"
},
"hits": [
{
"_id": "3",
"_score": null,
"_source": {
"name": "Sony Travel Speaker 3",
"brand": "Sony",
"price": 100,
"in_stock": false
}
},
{
"_id": "15",
"_score": null,
"_source": {
"name": "Sony USB Cable 15",
"brand": "Sony",
"price": 499,
"in_stock": false
}
}
]
}
}How to read this result
The products satisfy all three restrictions. Sorting by id makes inspection stable; the zero or null score is not an error in a filter-only search.
Combine relevance and filters
A practical search often combines a scored match in must with stock and price restrictions in filter. All conditions remain mandatory, but only the text query contributes to relevance. The example searches gaming and restricts products to in-stock items below 800.
Putting the text clause under filter can return the same matching set while discarding text ranking. Putting business restrictions under must can introduce scoring contributions. Preserve the distinction even when a small sample makes the outputs appear similar.
Try a different scenario
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "gaming"
}
}
],
"filter": [
{
"term": {
"in_stock": true
}
},
{
"range": {
"price": {
"lt": 800
}
}
}
]
}
},
"size": 2,
"_source": [
"name",
"price",
"in_stock"
]
}Elasticsearch response
{
"hits": {
"total": {
"value": 20,
"relation": "eq"
},
"hits": [
{
"_id": "4",
"_score": 2.3304372,
"_source": {
"name": "Logitech Gaming Keyboard 4",
"price": 199,
"in_stock": true
}
},
{
"_id": "14",
"_score": 2.3304372,
"_source": {
"name": "Samsung Gaming Keyboard 14",
"price": 199,
"in_stock": true
}
}
]
}
}How to read this result
Returned products satisfy both filters, and their relative ranking is driven by the text match. total includes every matching eligible product, not just the small returned page.
Check your understanding
Answer before expanding each explanation.
1. What does Query DSL JSON describe?
A tree of clauses and search parameters.
2. Does filter remove text analysis?
No, match still analyzes input but does not contribute a score.
3. Where should tenant_id go?
In a required constraint, normally server-controlled bool.filter.
4. Is filter always cached?
No, cache policy and segment conditions apply.
5. When is constant_score useful?
When all eligible documents should receive the same contribution.
Key takeaways
- A tree of clauses and search parameters.
- No, match still analyzes input but does not contribute a score.
- In a required constraint, normally server-controlled bool.filter.
- No, cache policy and segment conditions apply.
- When all eligible documents should receive the same contribution.
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.