feat(eap): Support collecting distinct values into an array on TraceItemTable - #8256
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5904a20. Configure here.
wmak
left a comment
There was a problem hiding this comment.
As Discussed, would be good to know if this is a function we can expose to customers publicly via the general Events Query Syntax, or if this is something we want to keep private for use only on the specific AI conversations endpoint.
| assert [v.WhichOneof("value") for v in collected] == ["val_array"] | ||
| # Only red rows contribute; "mobile" appears once despite two red occurrences, | ||
| # and "frontend" (blue) is filtered out. groupUniqArray gives no element ordering. | ||
| assert sorted(e.val_str for e in collected[0].val_array.values) == ["backend", "mobile"] |
There was a problem hiding this comment.
for my sanity, would it be possible to add one more test that checks more than two elements? maybe 3-4?
… rounded_sampling_weight into variables
5904a20 to
1ebdf84
Compare
Co-Authored-By: Prajjwal Bhandari <prajjwal.bhandari@sentry.io>
|
@wmak I think this is probably best left internal only for now. |

Summary
Adds support for
FUNCTION_COLLECT_UNIQUEin the TraceItemTable endpoint. Unlike every other aggregation — which reduces a group to a single scalar — this one gathers each group's distinct scalar values into an array, returned as aval_arrayin the response. It maps to ClickHouse'sgroupUniqArray.Related
FUNCTION_COLLECT_UNIQUEenum value this consumes): feat(snuba): Add FUNCTION_COLLECT_UNIQUE for array-returning aggregations sentry-protos#393Changes
Aggregation expression (
aggregation.py)FUNCTION_COLLECT_UNIQUEtogroupUniqArrayIf(max_array_size), in both the plain andFUNCTION_ANY-style non-rounded branches (the result is an array, soround()must not be applied).FUNCTION_COLLECT_UNIQUE: scaling a set of values by a sampling weight is meaningless, soget_extrapolated_functionraisesBadSnubaRPCRequestExceptionrather than silently producing a nonsensical result.get_extrapolated_functionto hoist the sharedconditionandrounded_sampling_weightsub-expressions out of the per-function map, removing the repeated inlineand_cond(...)/ cast boilerplate.Response conversion (
trace_item_table.py)_get_aggregate_convertercentralizes the aggregate-result →AttributeValuemapping:FUNCTION_COLLECT_UNIQUE→ array converter,FUNCTION_ANY→ key-typed converter, everything else → double. Replaces the duplicatedFUNCTION_ANYspecial-casing across theaggregationandconditional_aggregationbranches.Config (
schema.json)snuba_query_max_array_sizeoption (default 1000). Bounds how many distinct valuesgroupUniqArrayaccumulates per group. Unlikeuniq,groupUniqArrayholds every distinct value on the aggregating node, so this caps its memory footprint; a group exceeding the limit is truncated rather than allowed to grow unbounded.Tests
test_aggregation.py:FUNCTION_COLLECT_UNIQUEis rejected under every extrapolation mode, and produces agroupUniqArrayIfexpression when extrapolation is off.test_endpoint_trace_item_table.py: end-to-end coverage for the aggregation (returnsval_arrayper group) and for the conditional-aggregation variant (collects distinct values of one column filtered on another).🤖 Generated with Claude Code