Skip to content

Commit d40be40

Browse files
authored
feat(explore): Enabling searching attributes on group by (#104003)
This PR updates the Group By dropdown to support searching attributes via the API dealing with the 3000 attribute limit issue.
1 parent 7ee9621 commit d40be40

File tree

6 files changed

+145
-17
lines changed

6 files changed

+145
-17
lines changed

static/app/views/explore/components/toolbar/toolbarGroupBy/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ interface ToolbarGroupByDropdownProps {
4040
onColumnChange: (column: string) => void;
4141
onColumnDelete: () => void;
4242
options: Array<SelectOption<string>>;
43+
loading?: boolean;
44+
onClose?: () => void;
45+
onSearch?: (search: string) => void;
4346
}
4447

4548
export function ToolbarGroupByDropdown({
@@ -48,6 +51,9 @@ export function ToolbarGroupByDropdown({
4851
onColumnChange,
4952
onColumnDelete,
5053
options,
54+
onSearch,
55+
loading,
56+
onClose,
5157
}: ToolbarGroupByDropdownProps) {
5258
const {attributes, listeners, setNodeRef, transform, transition} = useSortable({
5359
id: column.id,
@@ -88,6 +94,10 @@ export function ToolbarGroupByDropdown({
8894
searchable
8995
triggerProps={{children: label, style: {width: '100%'}}}
9096
menuWidth="300px"
97+
menuTitle="Group By"
98+
onSearch={onSearch}
99+
onClose={onClose}
100+
loading={loading}
91101
/>
92102
{canDelete ? (
93103
<Button

static/app/views/explore/contexts/traceItemAttributeContext.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type TraceItemAttributeConfig = {
3737
enabled: boolean;
3838
traceItemType: TraceItemDataset;
3939
projects?: Project[];
40+
search?: string;
4041
};
4142

4243
type TraceItemAttributeProviderProps = {
@@ -48,11 +49,13 @@ export function TraceItemAttributeProvider({
4849
traceItemType,
4950
enabled,
5051
projects,
52+
search,
5153
}: TraceItemAttributeProviderProps) {
5254
const typedAttributesResult = useTraceItemAttributeConfig({
5355
traceItemType,
5456
enabled,
5557
projects,
58+
search,
5659
});
5760

5861
return (
@@ -66,13 +69,15 @@ function useTraceItemAttributeConfig({
6669
traceItemType,
6770
enabled,
6871
projects,
72+
search,
6973
}: TraceItemAttributeConfig) {
7074
const {attributes: numberAttributes, isLoading: numberAttributesLoading} =
7175
useTraceItemAttributeKeys({
7276
enabled,
7377
type: 'number',
7478
traceItemType,
7579
projects,
80+
search,
7681
});
7782

7883
const {attributes: stringAttributes, isLoading: stringAttributesLoading} =
@@ -81,6 +86,7 @@ function useTraceItemAttributeConfig({
8186
type: 'string',
8287
traceItemType,
8388
projects,
89+
search,
8490
});
8591

8692
const allNumberAttributes = useMemo(() => {
@@ -106,7 +112,6 @@ function useTraceItemAttributeConfig({
106112
tag,
107113
{key: tag, name: tag, kind: FieldKind.TAG},
108114
]);
109-
110115
const secondaryAliases: TagCollection = Object.fromEntries(
111116
Object.values(stringAttributes ?? {})
112117
.flatMap(value => value.secondaryAliases ?? [])
@@ -117,7 +122,7 @@ function useTraceItemAttributeConfig({
117122
attributes: {...stringAttributes, ...Object.fromEntries(tags)},
118123
secondaryAliases,
119124
};
120-
}, [traceItemType, stringAttributes]);
125+
}, [stringAttributes, traceItemType]);
121126

122127
return {
123128
number: allNumberAttributes.attributes,

static/app/views/explore/hooks/useTraceItemAttributeKeys.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {UseTraceItemAttributeBaseProps} from 'sentry/views/explore/types';
1414
interface UseTraceItemAttributeKeysProps extends UseTraceItemAttributeBaseProps {
1515
enabled?: boolean;
1616
query?: string;
17+
search?: string;
1718
}
1819

1920
export function useTraceItemAttributeKeys({
@@ -22,6 +23,7 @@ export function useTraceItemAttributeKeys({
2223
traceItemType,
2324
projects,
2425
query,
26+
search,
2527
}: UseTraceItemAttributeKeysProps) {
2628
const {selection} = usePageFilters();
2729

@@ -53,8 +55,8 @@ export function useTraceItemAttributeKeys({
5355

5456
const {data, isFetching, error} = useQuery<TagCollection>({
5557
enabled,
56-
queryKey,
57-
queryFn: () => getTraceItemAttributeKeys(),
58+
queryKey: [...queryKey, search],
59+
queryFn: () => getTraceItemAttributeKeys(search),
5860
});
5961

6062
const previous = usePrevious(data, isFetching);

static/app/views/explore/spans/spansTab.spec.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,24 @@ describe('SpansTabContent', () => {
121121
},
122122
}),
123123
});
124+
MockApiClient.addMockResponse({
125+
url: `/organizations/${organization.slug}/trace-items/attributes/`,
126+
method: 'GET',
127+
body: [],
128+
match: [MockApiClient.matchQuery({attributeType: 'number'})],
129+
});
130+
MockApiClient.addMockResponse({
131+
url: `/organizations/${organization.slug}/trace-items/attributes/`,
132+
method: 'GET',
133+
body: [
134+
{
135+
key: 'project',
136+
name: 'project',
137+
attributeSource: {source_type: 'sentry'},
138+
},
139+
],
140+
match: [MockApiClient.matchQuery({attributeType: 'string'})],
141+
});
124142
});
125143

126144
it('should fire analytics once per change', async () => {

static/app/views/explore/toolbar/index.spec.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,29 @@ describe('ExploreToolbar', () => {
6161
url: `/organizations/${organization.slug}/trace-items/attributes/`,
6262
method: 'GET',
6363
body: [],
64+
match: [MockApiClient.matchQuery({attributeType: 'number'})],
65+
});
66+
MockApiClient.addMockResponse({
67+
url: `/organizations/${organization.slug}/trace-items/attributes/`,
68+
method: 'GET',
69+
body: [
70+
{
71+
key: 'span.op',
72+
name: 'span.op',
73+
attributeSource: {source_type: 'sentry'},
74+
},
75+
{
76+
key: 'span.description',
77+
name: 'span.description',
78+
attributeSource: {source_type: 'sentry'},
79+
},
80+
{
81+
key: 'project',
82+
name: 'project',
83+
attributeSource: {source_type: 'sentry'},
84+
},
85+
],
86+
match: [MockApiClient.matchQuery({attributeType: 'string'})],
6487
});
6588
});
6689

static/app/views/explore/toolbar/toolbarGroupBy.tsx

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {useCallback} from 'react';
1+
import {useCallback, useState} from 'react';
22

33
import type {SelectOption} from 'sentry/components/core/compactSelect';
4+
import {useDebouncedValue} from 'sentry/utils/useDebouncedValue';
45
import {
56
ToolbarFooter,
67
ToolbarSection,
@@ -13,6 +14,8 @@ import {
1314
import {DragNDropContext} from 'sentry/views/explore/contexts/dragNDropContext';
1415
import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode';
1516
import {useTraceItemTags} from 'sentry/views/explore/contexts/spanTagsContext';
17+
import {TraceItemAttributeProvider} from 'sentry/views/explore/contexts/traceItemAttributeContext';
18+
import type {Column} from 'sentry/views/explore/hooks/useDragNDropColumns';
1619
import {useGroupByFields} from 'sentry/views/explore/hooks/useGroupByFields';
1720
import {TraceItemDataset} from 'sentry/views/explore/types';
1821

@@ -22,16 +25,6 @@ interface ToolbarGroupByProps {
2225
}
2326

2427
export function ToolbarGroupBy({groupBys, setGroupBys}: ToolbarGroupByProps) {
25-
const {tags: numberTags} = useTraceItemTags('number');
26-
const {tags: stringTags} = useTraceItemTags('string');
27-
28-
const options: Array<SelectOption<string>> = useGroupByFields({
29-
groupBys,
30-
numberTags,
31-
stringTags,
32-
traceItemType: TraceItemDataset.SPANS,
33-
});
34-
3528
const setGroupBysWithOp = useCallback(
3629
(columns: string[], op: 'insert' | 'update' | 'delete' | 'reorder') => {
3730
// automatically switch to aggregates mode when a group by is inserted/updated
@@ -50,13 +43,13 @@ export function ToolbarGroupBy({groupBys, setGroupBys}: ToolbarGroupByProps) {
5043
<ToolbarSection data-test-id="section-group-by">
5144
<ToolbarGroupByHeader />
5245
{editableColumns.map((column, i) => (
53-
<ToolbarGroupByDropdown
46+
<ToolbarGroupByItem
5447
key={column.id}
5548
canDelete={editableColumns.length > 1}
5649
column={column}
5750
onColumnChange={c => updateColumnAtIndex(i, c)}
5851
onColumnDelete={() => deleteColumnAtIndex(i)}
59-
options={options}
52+
groupBys={groupBys}
6053
/>
6154
))}
6255
<ToolbarFooter>
@@ -67,3 +60,80 @@ export function ToolbarGroupBy({groupBys, setGroupBys}: ToolbarGroupByProps) {
6760
</DragNDropContext>
6861
);
6962
}
63+
64+
interface ToolbarGroupByItemProps {
65+
canDelete: boolean;
66+
column: Column<string>;
67+
groupBys: readonly string[];
68+
onColumnChange: (column: string) => void;
69+
onColumnDelete: () => void;
70+
}
71+
72+
function ToolbarGroupByItem({
73+
groupBys,
74+
canDelete,
75+
column,
76+
onColumnChange,
77+
onColumnDelete,
78+
}: ToolbarGroupByItemProps) {
79+
const [search, setSearch] = useState<string>('');
80+
const debouncedSearch = useDebouncedValue(search, 200);
81+
82+
return (
83+
<TraceItemAttributeProvider
84+
enabled
85+
traceItemType={TraceItemDataset.SPANS}
86+
search={debouncedSearch}
87+
>
88+
<ToolbarGroupByItemContent
89+
canDelete={canDelete}
90+
column={column}
91+
onColumnChange={onColumnChange}
92+
onColumnDelete={onColumnDelete}
93+
groupBys={groupBys}
94+
onSearch={setSearch}
95+
onClose={() => setSearch('')}
96+
/>
97+
</TraceItemAttributeProvider>
98+
);
99+
}
100+
101+
interface ToolbarGroupByItemContentProps extends ToolbarGroupByItemProps {
102+
onClose: () => void;
103+
onSearch: (search: string) => void;
104+
}
105+
106+
function ToolbarGroupByItemContent({
107+
groupBys,
108+
canDelete,
109+
column,
110+
onColumnChange,
111+
onColumnDelete,
112+
onSearch,
113+
onClose,
114+
}: ToolbarGroupByItemContentProps) {
115+
const {tags: numberTags, isLoading: numberTagsLoading} = useTraceItemTags('number');
116+
const {tags: stringTags, isLoading: stringTagsLoading} = useTraceItemTags('string');
117+
118+
const options: Array<SelectOption<string>> = useGroupByFields({
119+
groupBys,
120+
numberTags,
121+
stringTags,
122+
traceItemType: TraceItemDataset.SPANS,
123+
});
124+
125+
const loading = numberTagsLoading || stringTagsLoading;
126+
127+
return (
128+
<ToolbarGroupByDropdown
129+
column={column}
130+
options={options}
131+
loading={loading}
132+
onClose={onClose}
133+
onSearch={onSearch}
134+
canDelete={canDelete}
135+
onColumnChange={onColumnChange}
136+
onColumnDelete={onColumnDelete}
137+
/>
138+
);
139+
}

0 commit comments

Comments
 (0)