mirror of
https://github.com/grafana/grafana.git
synced 2025-01-24 15:27:01 -06:00
Tempo: Select performance improvements (#91732)
* Tempo select performance improvements * Update type * Tidy up and simplify * Update tagValueOptions * Update GroupBy options
This commit is contained in:
parent
d72846790e
commit
d779dfb0a2
@ -1,17 +1,17 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { AccessoryButton } from '@grafana/experimental';
|
||||
import { HorizontalGroup, Select, useStyles2 } from '@grafana/ui';
|
||||
import { HorizontalGroup, InputActionMeta, Select, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
|
||||
import { TempoDatasource } from '../datasource';
|
||||
import { TempoQuery } from '../types';
|
||||
|
||||
import InlineSearchField from './InlineSearchField';
|
||||
import { withTemplateVariableOptions } from './SearchField';
|
||||
import { maxOptions, withTemplateVariableOptions } from './SearchField';
|
||||
import { replaceAt } from './utils';
|
||||
|
||||
interface Props {
|
||||
@ -26,6 +26,7 @@ export const GroupByField = (props: Props) => {
|
||||
const { datasource, onChange, query, isTagsLoading, addVariablesToOptions } = props;
|
||||
const styles = useStyles2(getStyles);
|
||||
const generateId = () => uuidv4().slice(0, 8);
|
||||
const [tagQuery, setTagQuery] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!query.groupBy || query.groupBy.length === 0) {
|
||||
@ -41,9 +42,18 @@ export const GroupByField = (props: Props) => {
|
||||
}
|
||||
}, [onChange, query]);
|
||||
|
||||
const getTags = (f: TraceqlFilter) => {
|
||||
return datasource!.languageProvider.getMetricsSummaryTags(f.scope);
|
||||
};
|
||||
const tagOptions = useMemo(
|
||||
() => (f: TraceqlFilter) => {
|
||||
const tags = datasource!.languageProvider.getMetricsSummaryTags(f.scope);
|
||||
if (tagQuery.length === 0) {
|
||||
return tags.slice(0, maxOptions);
|
||||
}
|
||||
|
||||
const queryLowerCase = tagQuery.toLowerCase();
|
||||
return tags.filter((tag) => tag.toLowerCase().includes(queryLowerCase)).slice(0, maxOptions);
|
||||
},
|
||||
[datasource, tagQuery]
|
||||
);
|
||||
|
||||
const addFilter = () => {
|
||||
updateFilter({
|
||||
@ -74,8 +84,8 @@ export const GroupByField = (props: Props) => {
|
||||
<InlineSearchField label="Aggregate by" tooltip="Select one or more tags to see the metrics summary.">
|
||||
<>
|
||||
{query.groupBy?.map((f, i) => {
|
||||
const tags = getTags(f)
|
||||
?.concat(f.tag !== undefined && !getTags(f)?.includes(f.tag) ? [f.tag] : [])
|
||||
const tags = tagOptions(f)
|
||||
?.concat(f.tag !== undefined && !tagOptions(f)?.includes(f.tag) ? [f.tag] : [])
|
||||
.map((t) => ({
|
||||
label: t,
|
||||
value: t,
|
||||
@ -102,6 +112,12 @@ export const GroupByField = (props: Props) => {
|
||||
updateFilter({ ...f, tag: v?.value });
|
||||
}}
|
||||
options={addVariablesToOptions ? withTemplateVariableOptions(tags) : tags}
|
||||
onInputChange={(value: string, { action }: InputActionMeta) => {
|
||||
if (action === 'input-change') {
|
||||
setTagQuery(value);
|
||||
}
|
||||
}}
|
||||
onCloseMenu={() => setTagQuery('')}
|
||||
placeholder="Select tag"
|
||||
value={f.tag || ''}
|
||||
/>
|
||||
|
@ -6,7 +6,7 @@ import useAsync from 'react-use/lib/useAsync';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { TemporaryAlert } from '@grafana/o11y-ds-frontend';
|
||||
import { FetchError, getTemplateSrv, isFetchError } from '@grafana/runtime';
|
||||
import { Select, HorizontalGroup, useStyles2 } from '@grafana/ui';
|
||||
import { Select, HorizontalGroup, useStyles2, InputActionMeta } from '@grafana/ui';
|
||||
|
||||
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
|
||||
import { TempoDatasource } from '../datasource';
|
||||
@ -59,6 +59,8 @@ const SearchField = ({
|
||||
// there's only one value selected, so we store the previous operator and value
|
||||
const [prevOperator, setPrevOperator] = useState(filter.operator);
|
||||
const [prevValue, setPrevValue] = useState(filter.value);
|
||||
const [tagQuery, setTagQuery] = useState<string>('');
|
||||
const [tagValuesQuery, setTagValuesQuery] = useState<string>('');
|
||||
|
||||
const updateOptions = async () => {
|
||||
try {
|
||||
@ -127,14 +129,42 @@ const SearchField = ({
|
||||
case 'float':
|
||||
operatorList = numberOperators;
|
||||
}
|
||||
|
||||
const tagOptions = (filter.tag !== undefined ? uniq([filter.tag, ...tags]) : tags).map((t) => ({
|
||||
label: t,
|
||||
value: t,
|
||||
}));
|
||||
|
||||
const operatorOptions = operatorList.map(operatorSelectableValue);
|
||||
|
||||
const formatTagOptions = (tags: string[], filterTag: string | undefined) => {
|
||||
return (filterTag !== undefined ? uniq([filterTag, ...tags]) : tags).map((t) => ({ label: t, value: t }));
|
||||
};
|
||||
|
||||
const tagOptions = useMemo(() => {
|
||||
if (tagQuery.length === 0) {
|
||||
return formatTagOptions(tags.slice(0, maxOptions), filter.tag);
|
||||
}
|
||||
|
||||
const queryLowerCase = tagQuery.toLowerCase();
|
||||
const filterdOptions = tags.filter((tag) => tag.toLowerCase().includes(queryLowerCase)).slice(0, maxOptions);
|
||||
return formatTagOptions(filterdOptions, filter.tag);
|
||||
}, [filter.tag, tagQuery, tags]);
|
||||
|
||||
const tagValueOptions = useMemo(() => {
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tagValuesQuery.length === 0) {
|
||||
return options.slice(0, maxOptions);
|
||||
}
|
||||
|
||||
const queryLowerCase = tagValuesQuery.toLowerCase();
|
||||
return options
|
||||
.filter((tag) => {
|
||||
if (tag.value && tag.value.length > 0) {
|
||||
return tag.value.toLowerCase().includes(queryLowerCase);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.slice(0, maxOptions);
|
||||
}, [tagValuesQuery, options]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HorizontalGroup spacing={'none'} width={'auto'}>
|
||||
@ -144,9 +174,7 @@ const SearchField = ({
|
||||
inputId={`${filter.id}-scope`}
|
||||
options={addVariablesToOptions ? withTemplateVariableOptions(scopeOptions) : scopeOptions}
|
||||
value={filter.scope}
|
||||
onChange={(v) => {
|
||||
updateFilter({ ...filter, scope: v?.value });
|
||||
}}
|
||||
onChange={(v) => updateFilter({ ...filter, scope: v?.value })}
|
||||
placeholder="Select scope"
|
||||
aria-label={`select ${filter.id} scope`}
|
||||
/>
|
||||
@ -158,10 +186,14 @@ const SearchField = ({
|
||||
isLoading={isTagsLoading}
|
||||
// Add the current tag to the list if it doesn't exist in the tags prop, otherwise the field will be empty even though the state has a value
|
||||
options={addVariablesToOptions ? withTemplateVariableOptions(tagOptions) : tagOptions}
|
||||
value={filter.tag}
|
||||
onChange={(v) => {
|
||||
updateFilter({ ...filter, tag: v?.value, value: [] });
|
||||
onInputChange={(value: string, { action }: InputActionMeta) => {
|
||||
if (action === 'input-change') {
|
||||
setTagQuery(value);
|
||||
}
|
||||
}}
|
||||
onCloseMenu={() => setTagQuery('')}
|
||||
onChange={(v) => updateFilter({ ...filter, tag: v?.value, value: [] })}
|
||||
value={filter.tag}
|
||||
placeholder="Select tag"
|
||||
isClearable
|
||||
aria-label={`select ${filter.id} tag`}
|
||||
@ -174,9 +206,7 @@ const SearchField = ({
|
||||
inputId={`${filter.id}-operator`}
|
||||
options={addVariablesToOptions ? withTemplateVariableOptions(operatorOptions) : operatorOptions}
|
||||
value={filter.operator}
|
||||
onChange={(v) => {
|
||||
updateFilter({ ...filter, operator: v?.value });
|
||||
}}
|
||||
onChange={(v) => updateFilter({ ...filter, operator: v?.value })}
|
||||
isClearable={false}
|
||||
aria-label={`select ${filter.id} operator`}
|
||||
allowCustomValue={true}
|
||||
@ -193,8 +223,14 @@ const SearchField = ({
|
||||
className={styles.dropdown}
|
||||
inputId={`${filter.id}-value`}
|
||||
isLoading={isLoadingValues}
|
||||
options={addVariablesToOptions ? withTemplateVariableOptions(options) : options}
|
||||
options={addVariablesToOptions ? withTemplateVariableOptions(tagValueOptions) : tagValueOptions}
|
||||
value={filter.value}
|
||||
onInputChange={(value: string, { action }: InputActionMeta) => {
|
||||
if (action === 'input-change') {
|
||||
setTagValuesQuery(value);
|
||||
}
|
||||
}}
|
||||
onCloseMenu={() => setTagValuesQuery('')}
|
||||
onChange={(val) => {
|
||||
if (Array.isArray(val)) {
|
||||
updateFilter({
|
||||
@ -231,4 +267,7 @@ export const withTemplateVariableOptions = (options: SelectableValue[] | undefin
|
||||
return [...(options || []), ...templateVariables.map((v) => ({ label: `$${v.name}`, value: `$${v.name}` }))];
|
||||
};
|
||||
|
||||
// Limit maximum options in select dropdowns for performance reasons
|
||||
export const maxOptions = 10000;
|
||||
|
||||
export default SearchField;
|
||||
|
Loading…
Reference in New Issue
Block a user