The Elasticsearch nested query runs a subquery against objects stored under a nested field, treating each element of the nested array as its own indexed document. This preserves the relationship between fields inside the same object - a constraint that is lost when arrays of objects are stored with the default flat (object) mapping. Use the nested query when you need to match multiple conditions on the same array element, e.g. a comment with author=alice AND rating=5.
Syntax
GET /index/_search
{
"query": {
"nested": {
"path": "path_to_nested_field",
"query": { /* any query, scoped to path.* fields */ },
"score_mode": "avg",
"ignore_unmapped": false,
"inner_hits": {}
}
}
}
Parameters
| Parameter | Description | Required | Default |
|---|---|---|---|
path |
Path of the nested field to search. | Yes | - |
query |
Query to run against the nested objects. Field references must be prefixed with path.. |
Yes | - |
score_mode |
How to combine matching nested object scores into the parent score: avg, max, min, sum, none. |
No | avg |
ignore_unmapped |
If true, ignores indices that lack the nested mapping instead of erroring. |
No | false |
inner_hits |
Return the matching nested sub-documents alongside the parent hits. | No | - |
The target field must be mapped with "type": "nested". The query will not work on a plain object mapping; field relationships flatten and you will get false positives.
Examples
Find books that have a comment with rating greater than 4:
GET /books/_search
{
"query": {
"nested": {
"path": "comments",
"query": {
"range": { "comments.rating": { "gt": 4 } }
}
}
}
}
Multiple conditions on the same nested element:
GET /books/_search
{
"query": {
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{ "match": { "comments.author": "alice" } },
{ "range": { "comments.rating": { "gte": 5 } } }
]
}
},
"score_mode": "max"
}
}
}
Return the matching nested objects via inner_hits:
GET /books/_search
{
"query": {
"nested": {
"path": "comments",
"query": { "match": { "comments.body": "excellent" } },
"inner_hits": { "size": 3 }
}
}
}
Multi-level nesting (orders.items, items as nested under orders):
GET /customers/_search
{
"query": {
"nested": {
"path": "orders",
"query": {
"nested": {
"path": "orders.items",
"query": {
"bool": {
"must": [
{ "term": { "orders.items.sku": "AX-100" } },
{ "range": { "orders.items.quantity": { "gte": 2 } } }
]
}
}
}
}
}
}
}
Performance and Use Notes
Nested mappings cost more than object mappings. Each nested element is indexed as a hidden Lucene document under the same parent, so an index with 1 million parents and 10 nested elements per parent stores 11 million documents internally. This affects every operation that scans documents: search, aggregation, segment merging, and shard size all grow accordingly. The index.mapping.nested_fields.limit default is 50, and index.mapping.nested_objects.limit defaults to 10000 (per parent document).
Score combination is controlled by score_mode. The default avg averages all matching nested-object scores into one parent score; max keeps the best, min the worst, sum adds them, and none discards them. Choose none when you only need a filter and want to skip score arithmetic. Wrap nested queries in bool.filter whenever scoring is unneeded.
Hidden nested-document growth is a frequent source of unexpected shard size and slow aggregations. Manually correlating nested doc counts against shard size and slow-log entries to find the mapping that is silently dominating cluster cost is exactly the loop Pulse runs continuously.
Common Mistakes
- Querying an array of objects without mapping the field as
nested. With the defaultobjectmapping, allcomments.authorvalues flatten andauthor=alice AND rating=5will match if any element has each value separately - even on different elements. - Forgetting the path prefix in the inner query: write
comments.rating, not justrating. - Indexing many small nested elements per parent. Each one is a hidden Lucene document - watch index size and merge time.
- Deeply nested mappings (3+ levels). Query syntax becomes unwieldy and cost compounds. Consider parent/child or denormalization.
- Treating
inner_hitsas free. It runs a second query under the hood per parent hit; cap it withsizeand pagination.
Find Slow Nested Queries with Pulse
Pulse is an AI DBA for Elasticsearch and OpenSearch that continuously profiles production query traffic. For nested queries specifically, Pulse:
- Identifies nested queries running against large hidden-document sets where scoring is computed across every matching child, plus those issued in query context when
score_mode: noneinsidebool.filterwould skip score arithmetic entirely - Surfaces indices where hidden nested document count is dominating shard size, plus mappings approaching
index.mapping.nested_fields.limit(default 50) orindex.mapping.nested_objects.limit(default 10000 per parent) - Flags
inner_hitsblocks withoutsizecaps, where a secondary per-parent query is multiplying latency - Traces each slow nested query back to the calling service via slow-log and APM correlation
- Recommends concrete rewrites: wrap the nested clause in
bool.filterwithscore_mode: none, denormalize when child cardinality outgrows the parent, replace 3+ level nesting with parent/child join or denormalized fields, and capinner_hits.size - Tracks latency and shard-size impact after the mapping or query change ships
This converts the manual nested-mapping diagnosis loop into a continuous optimization workflow.
Frequently Asked Questions
Q: When should I use a nested query instead of a flat object query?
A: Use a nested query whenever you need multiple conditions to match on the same element of an array of objects. The default object mapping flattens arrays, so comments.author=alice AND comments.rating=5 will return documents where one comment is by Alice and a different comment has rating 5.
Q: What does score_mode do in a nested query?
A: score_mode decides how scores from matching nested objects are combined into a single score for the parent document. avg (the default) averages them, max and min take the best/worst, sum adds them, none discards them entirely.
Q: How do I retrieve the matching nested objects?
A: Add an inner_hits block to the nested query. The response then includes a inner_hits section per parent hit, listing the nested sub-documents that matched the query, with their own _score and offset.
Q: Are nested queries expensive?
A: They are more expensive than queries against flat objects, because each nested element is a hidden Lucene document. A 1M-parent index with 10 nested elements per parent stores 11M internal documents. Keep nested arrays small and prefer denormalization for high-cardinality children.
Q: Can I aggregate across nested fields?
A: Yes, with the nested aggregation. Wrap your metric or bucket aggregation under a nested aggregation pointing at the same path. Otherwise the aggregation treats each parent as one document and you lose per-element grouping.
Q: How deeply can I nest objects?
A: Elasticsearch enforces index.mapping.depth.limit (default 20) and index.mapping.nested_fields.limit (default 50). Practical performance falls off fast beyond 2-3 levels; consider denormalization or the parent/child join field instead.
Q: How do I find which nested queries are causing slow searches?
A: Pulse profiles Elasticsearch and OpenSearch slow logs, isolates nested queries scoring across large hidden-document sets and those with uncapped inner_hits, attributes each to the calling service, and recommends score_mode: none in bool.filter, denormalization when child cardinality is high, and concrete inner_hits.size caps.
Related Reading
- Elasticsearch Query Language: overview of the query DSL.
- Nested Field Data Type: mapping configuration for nested objects.
- Bool Query: combine multiple conditions inside the nested query.
- Range Query: common leaf clause inside a nested query.
- Exists Query: probe nested fields for presence/absence.