-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add hasValue()
aggregation inspection helpers
#36020
Conversation
This allows consumers of any InternalAggregation to easily determine if the agg "has a value". This is needed because InternalAggs represent "empty" in different manners according to convention. Some use NaN, +/- Inf, 0.0, etc. The XContent deals with all these nuances and converts them to `null`, but internal consumers of the objects are stuck decoding the conventions themselves. By adding `hasValue()`, the conventions can be handled internally and a simple interface is given for anything using InternalAggs. The definition of "having a value" varies depending on agg, but generally: - If it is a metric agg, the agg has collected at least one value - If it is a bucketing agg, at least one of the buckets had doc_count > 0 - Pipeline aggs behavior is variable and depends on the particular agg. E.g. derivatives always have a value because they are only added to the agg response if a value can be calculated. In contrast, a cumulative_sum will emit 0 even if no values are collected, so hasValue() depends on if any of the buckets it processes have a value themselves.
Pinging @elastic/es-analytics-geo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for picking this up @polyfractal ! Fwiw, LGTM!
Cheers!
@@ -150,6 +150,11 @@ MatrixStatsResults getResults() { | |||
return results; | |||
} | |||
|
|||
@Override | |||
public boolean hasValue() { | |||
return (getDocCount() == 0 && getStats() == null && getResults() == null) == false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe only results is needed since stats is just used internally for the results so the above might be simplified to:
getResults() != null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++
@Override | ||
public boolean hasValue() { | ||
// TODO this could be incorrect... e.g. +1 + -1 | ||
// Think we'll have to serialize count if we want to fix this... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another idea which might impact backwards compatibility would be to use an out of range value (Infinity or NaN) and check that instead of the count.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just took a look and that turns out to be tricky (which is a shame, after you mentioned it I agreed that would be best). Sum agg and a few others use Kahan summation for improved numerical stability... but part of that explicitly handles when the sum goes non-finite as part of the compensation scheme. So I think we're stuck with recording counts like the Avg does.
Jenkins, run the gradle build tests 1 |
Should we address this issue by returning This used to be a bit tricky backward-compatibility-wise, but as users are progressively migrating to the REST clients, they are now getting nulls anyway and changes to the transport client are getting easier? |
If we're ok with the bwc break that'd probably be cleaner. It'd be a fairly widespread break for any remaining TC users though. I don't have a strong opinion either way. |
As much as I rather like cleaning up tech debt sooner than later, maybe the easier path is to change responses to |
We chatted about this some more over slack. The main concern is adding to the complexity and technical debt to the agg framework (which is already sizable in places) for a short-ish term fix. @costin would this work for the SQL team:
? |
Sounds good. Thanks! |
Coolio. I'll fixup this PR (hopefully much reduced in size) and give you both a ping once the new version is ready for review :) |
Apologies for the delay. I removed the The main difference is that pipeline aggs are strictly worse now. Most of the pipeline aggs share I think we can improve this in the future, but in interest of keeping this PR as small as possible I just went with the coarsest definition of having value (not-NaN basically) |
Thanks. |
The issue is that Join and MatrixStats are in different plugins, so we can't reference them from a helper in core without adding them as a dependency. And I don't believe there's any central location that has all of them together in terms of dependencies... :( |
I see - then the smaller the number of |
We could also just move the helper into SQL itself, now that it's written. The three main helpers could be combined (Core, Join, MatrixStats), and the only one that needs to stay in core is the Metrics helper because that access package-private state. But it's not meant to be consumed anyway, the main |
That works too - we can do that as a separate PR to not postpone this one. |
Jenkins, run the gradle build tests 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall LGTM, but left a question
...va/org/elasticsearch/search/aggregations/matrix/stats/MatrixAggregationInspectionHelper.java
Show resolved
Hide resolved
...-join/src/main/java/org/elasticsearch/join/aggregations/JoinAggregationInspectionHelper.java
Show resolved
Hide resolved
Also awaits a matrix test because it fails if results are reduced. Variances become different, unclear if this is a problem with the test helper class ("MultiPassStats") or with incremental reduction of matrix stats.
Good call, thanks @talevy. Added uses of the helpers in the Matrix and Join tests. Discovered that the MatrixStats test fails when it reduces due to mismatched variances. I investigated briefly but couldn't tell if this was due to the test-specific class ( Muted the test, will create an issue to investigate and unmute. Didn't want to block this PR on fixing that test though. |
@elasticmachine test this please |
@elasticmachine run the gradle build tests 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM if bugUrl is updated and CI is happy
...test/java/org/elasticsearch/search/aggregations/matrix/stats/MatrixStatsAggregatorTests.java
Outdated
Show resolved
Hide resolved
@elasticmachine run the gradle build tests 1 |
2 similar comments
@elasticmachine run the gradle build tests 1 |
@elasticmachine run the gradle build tests 1 |
hasValue()
property to InternalAggregationhasValue()
aggregation inspection helpers
This allows consumers of any
InternalAggregation
to easily determine if the agg "has a value". This is needed because InternalAggs represent "empty" in different manners according to convention. Some useNaN
,+/- Inf
,0.0
, etc.The
XContent
deals with all these nuances and converts them tonull
, but internal consumers of the objects are stuck decoding the conventions themselves.By adding
hasValue()
, the conventions can be handled internally and a simple interface is given for anything using InternalAggs.The definition of "having a value" varies depending on agg, but generally:
So big :(
This PR turned out quite large... we have a lot of aggs apparently. About half is just adding assertions to tests. If it's too unmanageable I suppose I could add a concrete placeholder in
InternalAggregation
and then implement aggs in chunks? Open to suggestions.Didn't realize it would turn out so large when I started :)
Things not addressed in this PR
There are places where we can use the new
hasValue()
internally to deduplicate some code. This PR was already large (albeit simple changes), so I think we can tidy those up in followup PRs.This also doesn't introduce any sort of
hasValue()
in script contexts, so scripts are unable to determine if the values they are receiving are real values or just empty placeholders. That can also be done in a followup.Related to #34903
/cc @costin