← Back to list

When Elasticsearch Says “Nested” — And Your Parser Doesn’t Listen

The ones that don’t fail — they lie.

veera gandhi · 2026-03-28 17:59 · 2 claps · 3.2 min read
#elasticsearch #dotnet-core #nest #software-engineering #backend-development
Open on Medium ↗
Wiki topics: 🌐 · Web Development

When Elasticsearch Says “Nested” — And Your Parser Doesn’t Listen

The ones that don’t fail — they lie.

Your system returns results confidently. They look correct. They pass tests.

And weeks later, you realize… they’re wrong.

This post is about one such bug in Elasticsearch — and how fixing it required understanding one of its most misunderstood features: nested fields.

TL;DR

If you query nested data in Elasticsearch without using a nested query, you can get logically incorrect results.

Because Elasticsearch flattens objects by default, relationships between fields are lost.

I explored this while contributing to Foundatio.Parsers, an open-source .NET library for building Elasticsearch queries.

The Setup

Foundatio.Parsers converts simple query strings like:

items.product:A items.quantity:>5

into Elasticsearch DSL queries and aggregations.

It handles:

  • Query parsing
  • Aggregation building
  • Field mapping
  • AST transformations

It’s well-structured — but nested field support wasn’t implemented yet. Nested fields were ignored

The Problem: Flattening Breaks Reality

{
  "items": [
    { "product": "A", "quantity": 2 },
    { "product": "B", "quantity": 10 }
  ]
}

Query:

items.product:A AND items.quantity:10

👉 This should NOT match.

But Elasticsearch matches it.

Why?

{
  "items.product": ["A", "B"],
  "items.quantity": [2, 10]
}

👉 The relationship between product and quantity is lost.

While this behavior is documented in Elasticsearch’s official nested field documentation (https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/nested), it’s easy to overlook when working through abstractions — which is where subtle bugs begin.

⚠️ The Dangerous Part

Elasticsearch:

  • Does not throw an error
  • Does not warn

👉 It just returns wrong results

The Fix: Nested Query

❌ Before

{
  "bool": {
    "must": [
      { "term": { "items.product": "A" }},
      { "term": { "items.quantity": 10 }}
    ]
  }
}

✅ After

{
  "nested": {
    "path": "items",
    "query": {
      "bool": {
        "must": [
          { "term": { "items.product": "A" }},
          { "term": { "items.quantity": 10 }}
        ]
      }
    }
  }
}

👉 Now both conditions apply to the same object

Aggregations: The Same Bug, Bigger Impact

This issue also affected aggregations.

❌ Before Fix

{
  "aggs": {
    "products": {
      "terms": {
        "field": "items.product"
      }
    }
  }
}

👉 Runs on flattened data → mixes unrelated values

✅ After Fix

{
  "aggs": {
    "items_nested": {
      "nested": {
        "path": "items"
      },
      "aggs": {
        "filtered_items": {
          "filter": {
            "range": {
              "items.quantity": { "gt": 5 }
            }
          },
          "aggs": {
            "products": {
              "terms": {
                "field": "items.product"
              }
            }
          }
        }
      }
    }
  }
}

👉 Now:

  • Aggregation respects object boundaries
  • Filtering and aggregation happen in the same scope

What the Fix Involved

At a high level:

Detect nested fields → group them → wrap them correctly

Detecting Nested Fields

Initial logic:

int dotIndex = fieldName.IndexOf('.');

⚠️ Limitation

  • status.keyword is NOT nested
  • Dot ≠ nested

👉 Mapping-based detection is a better long-term fix

Where DFS Came In (And What I Actually Did)

One interesting part of the fix was understanding how aggregation trees were already being traversed.

I didn’t introduce DFS — 👉 the library already used a Depth-First traversal.

My work involved:

  • Understanding this traversal
  • Extending it for nested aggregations
  • Ensuring correctness across all levels

Understanding the Aggregation Tree (Real Example)

Root
 └── Nested (items)
      └── Filter (quantity > 5)
           └── Terms (product)

How Traversal Happens (DFS)

Traversal order:

Root → Nested → Filter → Terms

👉 Go deep first, then backtrack.

Mapping This to Code

if (agg.Nested != null)
{
    target.Nested(name, n => n
        .Path(agg.Nested.Path)
        .Aggregations(a => CopyAggregations(a, agg.Nested.Aggregations)));
}

Why DFS Was Critical

Because aggregations are recursive:

  • Nested contains Aggregations
  • Those contain more Aggregations

Without proper traversal:

❌ Inner aggregations would be skipped ❌ Query would be incomplete

Edge Cases

1. .keyword Collision

Dot-based detection misclassifies fields like:

  • status.keyword

2. AND vs OR Issue

Nested groups default to AND, which can break logical intent.

3. Limited Aggregation Coverage

Some aggregation types were not handled:

  • This fix focused on making nested aggregations work correctly for supported types like Terms, Nested, Max.
  • More advanced aggregations such as DateHistogram, Avg, or Sum would require additional handling and were intentionally left as future improvements.

What I Learned

1. Mapping vs Query Is a Hidden Gap

Elasticsearch won’t protect you.

👉 You get wrong results — silently.

2. Abstractions Must Preserve Truth

Libraries like Foundatio.Parsers simplify query building.

But they must ensure correctness.

3. Understanding Existing Code Matters More Than Writing New Code

This fix wasn’t about adding complexity.

It was about:

  • Understanding traversal
  • Extending it correctly

Final Thought

If you’re using nested data in Elasticsearch:

👉 Double-check your queries and aggregations.

Because the worst bugs aren’t the ones that crash your system…

They’re the ones that quietly lie to you.

The full fix can be seen in this pull request: https://github.com/FoundatioFx/Foundatio.Parsers/pull/145 Big thanks to the maintainer for the review and guidance during this change.


메타데이터
post_id
698e83e57295
slug
when-elasticsearch-says-nested-and-your-parser-doesnt-listen-698e83e57295
url
https://medium.com/@veeraagandhi/when-elasticsearch-says-nested-and-your-parser-doesnt-listen-698e83e57295
canonical_url
https://medium.com/@veeraagandhi/when-elasticsearch-says-nested-and-your-parser-doesnt-listen-698e83e57295
author_url
https://medium.com/@veeraagandhi
status
ok
fetched_at
2026-06-20 20:29:01