A static linter for aggregation queries: catching the ones that return 200 OK

Hi all,

I wrote a small open source tool that lints an aggs block against your mapping without a running cluster, and I’m sharing it here partly in case it’s useful and partly because I want to know which rules it’s missing.

What prompted it: a dashboard panel went blank in production. No error, no failed request, 200 OK. Someone had renamed a field and missed one saved aggregation still pointing at the old name.

That behaviour is correct, and that’s what makes it awkward. From inside the aggregation, a field that doesn’t exist and a field that no document happens to have look identical, and the second case is completely legitimate. So you get zero buckets and a 200, and nobody finds out until a human notices a blank chart.

The ones I kept hitting:

  • misspelled or renamed field: empty buckets, no error
  • terms on an analyzed text field: buckets tokens instead of values, or a fielddata error depending on how the cluster is configured
  • date_histogram with no calendar_interval or fixed_interval: 400, and usually first seen in production because the template only loads on one dashboard
  • a metric aggregation with sub-aggregations: 400
  • a large terms size with no matching shard_size

Every one of those is decidable from the query text plus the mapping, so none of them need a cluster to catch:

npx opensearch-agg-inspector dashboard.json --mapping mapping.json
✖ dashboard.json:3:5  error  invalid-date-histogram
  date_histogram is missing "calendar_interval" or "fixed_interval".
  → Add calendar_interval (e.g. "1d", "1M") or fixed_interval (e.g. "1h", "30m").

⚠ dashboard.json:6:17  warning  unknown-field
  Field "chanel" does not exist in the provided mapping. The aggregation will
  silently return no data.

1 error, 1 warning across 3 aggregations

Output is file:line:col and it exits non-zero, so the place it earns its keep is a lint step in CI over whatever saved aggregation JSON you keep in the repo. Eight rules so far, each one configurable down to a warning or off. There’s also an inspect() function if you build aggregations at runtime and would rather reject a broken one than return silent bad data.

MIT, zero runtime dependencies, not affiliated with anything: GitHub - ab1109/opensearch-agg-inspector · GitHub

I also wrote up the reasoning behind each rule, including the shard_size and doc_count_error_upper_bound side of this, here: Your OpenSearch aggregation is lying to you: it returns 200 OK - DEV Community

The actual question I have for this forum: which quiet aggregation mistakes have bitten you that aren’t in that list? I’m specifically interested in the ones that returned a 200 and a wrong answer rather than an error, since those are the ones worth encoding as rules. Happy to add anything reasonable people suggest.