mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 23:38:59 +08:00
feat: choose autocomplete group by (#3108)
This commit is contained in:
parent
915738e1f7
commit
149fdebfaa
1
frontend/src/constants/queryBuilderFilterConfig.ts
Normal file
1
frontend/src/constants/queryBuilderFilterConfig.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const DEBOUNCE_DELAY = 200;
|
@ -31,8 +31,8 @@ function LogExplorerDetailedView({
|
|||||||
|
|
||||||
const existAutocompleteKey = chooseAutocompleteFromCustomValue(
|
const existAutocompleteKey = chooseAutocompleteFromCustomValue(
|
||||||
keysAutocomplete,
|
keysAutocomplete,
|
||||||
[fieldKey],
|
fieldKey,
|
||||||
)[0];
|
);
|
||||||
|
|
||||||
const nextQuery: Query = {
|
const nextQuery: Query = {
|
||||||
...currentQuery,
|
...currentQuery,
|
||||||
|
@ -4,22 +4,21 @@ import { AutoComplete, Spin } from 'antd';
|
|||||||
import { getAggregateAttribute } from 'api/queryBuilder/getAggregateAttribute';
|
import { getAggregateAttribute } from 'api/queryBuilder/getAggregateAttribute';
|
||||||
import {
|
import {
|
||||||
baseAutoCompleteIdKeysOrder,
|
baseAutoCompleteIdKeysOrder,
|
||||||
idDivider,
|
|
||||||
initialAutocompleteData,
|
|
||||||
QueryBuilderKeys,
|
QueryBuilderKeys,
|
||||||
selectValueDivider,
|
selectValueDivider,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
|
import { DEBOUNCE_DELAY } from 'constants/queryBuilderFilterConfig';
|
||||||
import useDebounce from 'hooks/useDebounce';
|
import useDebounce from 'hooks/useDebounce';
|
||||||
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
||||||
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
||||||
import { getAutocompleteValueAndType } from 'lib/newQueryBuilder/getAutocompleteValueAndType';
|
import { getAutocompleteValueAndType } from 'lib/newQueryBuilder/getAutocompleteValueAndType';
|
||||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||||
import { memo, useCallback, useMemo, useState } from 'react';
|
import { memo, useCallback, useMemo, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery, useQueryClient } from 'react-query';
|
||||||
|
import { SuccessResponse } from 'types/api';
|
||||||
import {
|
import {
|
||||||
AutocompleteType,
|
|
||||||
BaseAutocompleteData,
|
BaseAutocompleteData,
|
||||||
DataType,
|
IQueryAutocompleteResponse,
|
||||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
import { ExtendedSelectOption } from 'types/common/select';
|
import { ExtendedSelectOption } from 'types/common/select';
|
||||||
@ -34,19 +33,9 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
|||||||
disabled,
|
disabled,
|
||||||
onChange,
|
onChange,
|
||||||
}: AgregatorFilterProps): JSX.Element {
|
}: AgregatorFilterProps): JSX.Element {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const [optionsData, setOptionsData] = useState<ExtendedSelectOption[]>([]);
|
const [optionsData, setOptionsData] = useState<ExtendedSelectOption[]>([]);
|
||||||
const [searchText, setSearchText] = useState<string>(
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
query.aggregateAttribute.key,
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleChangeAttribute = useCallback(
|
|
||||||
(data: BaseAutocompleteData[]) => {
|
|
||||||
const attribute = chooseAutocompleteFromCustomValue(data, [searchText]);
|
|
||||||
|
|
||||||
onChange(attribute[0]);
|
|
||||||
},
|
|
||||||
[onChange, searchText],
|
|
||||||
);
|
|
||||||
|
|
||||||
const debouncedSearchText = useMemo(() => {
|
const debouncedSearchText = useMemo(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars
|
||||||
@ -55,7 +44,7 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
|||||||
return value;
|
return value;
|
||||||
}, [searchText]);
|
}, [searchText]);
|
||||||
|
|
||||||
const debouncedValue = useDebounce(debouncedSearchText, 300);
|
const debouncedValue = useDebounce(debouncedSearchText, DEBOUNCE_DELAY);
|
||||||
const { isFetching } = useQuery(
|
const { isFetching } = useQuery(
|
||||||
[
|
[
|
||||||
QueryBuilderKeys.GET_AGGREGATE_ATTRIBUTE,
|
QueryBuilderKeys.GET_AGGREGATE_ATTRIBUTE,
|
||||||
@ -86,8 +75,6 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
|||||||
key: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder),
|
key: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder),
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
handleChangeAttribute(data.payload?.attributeKeys || []);
|
|
||||||
|
|
||||||
setOptionsData(options);
|
setOptionsData(options);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -102,60 +89,79 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
|||||||
? `${transformToUpperCase(query.dataSource)} name`
|
? `${transformToUpperCase(query.dataSource)} name`
|
||||||
: 'Aggregate attribute';
|
: 'Aggregate attribute';
|
||||||
|
|
||||||
const handleSelect = (
|
const getAttributes = useCallback(
|
||||||
value: string,
|
(): BaseAutocompleteData[] =>
|
||||||
option: ExtendedSelectOption | ExtendedSelectOption[],
|
queryClient.getQueryData<SuccessResponse<IQueryAutocompleteResponse>>(
|
||||||
): void => {
|
[QueryBuilderKeys.GET_AGGREGATE_ATTRIBUTE],
|
||||||
const currentOption = option as ExtendedSelectOption;
|
{ exact: false },
|
||||||
|
)?.payload.attributeKeys || [],
|
||||||
|
[queryClient],
|
||||||
|
);
|
||||||
|
|
||||||
if (currentOption.key) {
|
const handleChangeCustomValue = useCallback(
|
||||||
const [key, dataType, type, isColumn] = currentOption.key.split(idDivider);
|
(value: string) => {
|
||||||
const attribute: BaseAutocompleteData = {
|
const aggregateAttributes = getAttributes();
|
||||||
key,
|
|
||||||
dataType: dataType as DataType,
|
|
||||||
type: type as AutocompleteType,
|
|
||||||
isColumn: isColumn === 'true',
|
|
||||||
};
|
|
||||||
|
|
||||||
const text = transformStringWithPrefix({
|
const customAttribute: BaseAutocompleteData = chooseAutocompleteFromCustomValue(
|
||||||
str: attribute.key,
|
aggregateAttributes,
|
||||||
prefix: attribute.type || '',
|
value,
|
||||||
condition: !attribute.isColumn,
|
);
|
||||||
});
|
|
||||||
|
|
||||||
setSearchText(text);
|
|
||||||
|
|
||||||
onChange(attribute);
|
|
||||||
} else {
|
|
||||||
const customAttribute: BaseAutocompleteData = {
|
|
||||||
...initialAutocompleteData,
|
|
||||||
key: value,
|
|
||||||
};
|
|
||||||
|
|
||||||
const text = transformStringWithPrefix({
|
|
||||||
str: customAttribute.key,
|
|
||||||
prefix: customAttribute.type || '',
|
|
||||||
condition: !customAttribute.isColumn,
|
|
||||||
});
|
|
||||||
|
|
||||||
setSearchText(text);
|
|
||||||
|
|
||||||
onChange(customAttribute);
|
onChange(customAttribute);
|
||||||
|
},
|
||||||
|
[getAttributes, onChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleBlur = useCallback(() => {
|
||||||
|
if (searchText) {
|
||||||
|
handleChangeCustomValue(searchText);
|
||||||
}
|
}
|
||||||
};
|
}, [handleChangeCustomValue, searchText]);
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(
|
||||||
|
value: string,
|
||||||
|
option: ExtendedSelectOption | ExtendedSelectOption[],
|
||||||
|
): void => {
|
||||||
|
const currentOption = option as ExtendedSelectOption;
|
||||||
|
|
||||||
|
const aggregateAttributes = getAttributes();
|
||||||
|
|
||||||
|
if (currentOption.key) {
|
||||||
|
const attribute = aggregateAttributes.find(
|
||||||
|
(item) => item.id === currentOption.key,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (attribute) {
|
||||||
|
onChange(attribute);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleChangeCustomValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSearchText('');
|
||||||
|
},
|
||||||
|
[getAttributes, handleChangeCustomValue, onChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
const value = transformStringWithPrefix({
|
||||||
|
str: query.aggregateAttribute.key,
|
||||||
|
prefix: query.aggregateAttribute.type || '',
|
||||||
|
condition: !query.aggregateAttribute.isColumn,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
style={selectStyle}
|
style={selectStyle}
|
||||||
showArrow={false}
|
showArrow={false}
|
||||||
searchValue={searchText}
|
|
||||||
onSearch={handleSearchText}
|
|
||||||
filterOption={false}
|
filterOption={false}
|
||||||
|
onSearch={handleSearchText}
|
||||||
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
||||||
options={optionsData}
|
options={optionsData}
|
||||||
value={searchText}
|
value={value}
|
||||||
onSelect={handleSelect}
|
onBlur={handleBlur}
|
||||||
|
onChange={handleChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -3,21 +3,22 @@ import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
|||||||
// ** Constants
|
// ** Constants
|
||||||
import {
|
import {
|
||||||
idDivider,
|
idDivider,
|
||||||
initialAutocompleteData,
|
|
||||||
QueryBuilderKeys,
|
QueryBuilderKeys,
|
||||||
selectValueDivider,
|
selectValueDivider,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
|
import { DEBOUNCE_DELAY } from 'constants/queryBuilderFilterConfig';
|
||||||
import useDebounce from 'hooks/useDebounce';
|
import useDebounce from 'hooks/useDebounce';
|
||||||
|
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
||||||
// ** Components
|
// ** Components
|
||||||
// ** Helpers
|
// ** Helpers
|
||||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||||
import { isEqual, uniqWith } from 'lodash-es';
|
import { isEqual, uniqWith } from 'lodash-es';
|
||||||
import { memo, useCallback, useEffect, useState } from 'react';
|
import { memo, useCallback, useEffect, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery, useQueryClient } from 'react-query';
|
||||||
|
import { SuccessResponse } from 'types/api';
|
||||||
import {
|
import {
|
||||||
AutocompleteType,
|
|
||||||
BaseAutocompleteData,
|
BaseAutocompleteData,
|
||||||
DataType,
|
IQueryAutocompleteResponse,
|
||||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { SelectOption } from 'types/common/select';
|
import { SelectOption } from 'types/common/select';
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
onChange,
|
onChange,
|
||||||
disabled,
|
disabled,
|
||||||
}: GroupByFilterProps): JSX.Element {
|
}: GroupByFilterProps): JSX.Element {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const [searchText, setSearchText] = useState<string>('');
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
const [optionsData, setOptionsData] = useState<SelectOption<string, string>[]>(
|
const [optionsData, setOptionsData] = useState<SelectOption<string, string>[]>(
|
||||||
[],
|
[],
|
||||||
@ -38,7 +40,7 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
);
|
);
|
||||||
const [isFocused, setIsFocused] = useState<boolean>(false);
|
const [isFocused, setIsFocused] = useState<boolean>(false);
|
||||||
|
|
||||||
const debouncedValue = useDebounce(searchText, 300);
|
const debouncedValue = useDebounce(searchText, DEBOUNCE_DELAY);
|
||||||
|
|
||||||
const { isFetching } = useQuery(
|
const { isFetching } = useQuery(
|
||||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, debouncedValue, isFocused],
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS, debouncedValue, isFocused],
|
||||||
@ -81,6 +83,15 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getAttributeKeys = useCallback(
|
||||||
|
(): BaseAutocompleteData[] =>
|
||||||
|
queryClient.getQueryData<SuccessResponse<IQueryAutocompleteResponse>>(
|
||||||
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS],
|
||||||
|
{ exact: false },
|
||||||
|
)?.payload.attributeKeys || [],
|
||||||
|
[queryClient],
|
||||||
|
);
|
||||||
|
|
||||||
const handleSearchKeys = (searchText: string): void => {
|
const handleSearchKeys = (searchText: string): void => {
|
||||||
setSearchText(searchText);
|
setSearchText(searchText);
|
||||||
};
|
};
|
||||||
@ -97,21 +108,17 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
const handleChange = (values: SelectOption<string, string>[]): void => {
|
const handleChange = (values: SelectOption<string, string>[]): void => {
|
||||||
const groupByValues: BaseAutocompleteData[] = values.map((item) => {
|
const groupByValues: BaseAutocompleteData[] = values.map((item) => {
|
||||||
const [currentValue, id] = item.value.split(selectValueDivider);
|
const [currentValue, id] = item.value.split(selectValueDivider);
|
||||||
if (id && id.includes(idDivider)) {
|
const keys = getAttributeKeys();
|
||||||
const [key, dataType, type, isColumn] = id.split(idDivider);
|
|
||||||
|
|
||||||
return {
|
if (id && id.includes(idDivider)) {
|
||||||
id,
|
const attribute = keys.find((item) => item.id === id);
|
||||||
key: key || currentValue,
|
|
||||||
dataType: (dataType as DataType) || initialAutocompleteData.dataType,
|
if (attribute) {
|
||||||
type: (type as AutocompleteType) || initialAutocompleteData.type,
|
return attribute;
|
||||||
isColumn: isColumn
|
}
|
||||||
? isColumn === 'true'
|
|
||||||
: initialAutocompleteData.isColumn,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...initialAutocompleteData, key: currentValue };
|
return chooseAutocompleteFromCustomValue(keys, currentValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = uniqWith(groupByValues, isEqual);
|
const result = uniqWith(groupByValues, isEqual);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
||||||
import { getAttributesValues } from 'api/queryBuilder/getAttributesValues';
|
import { getAttributesValues } from 'api/queryBuilder/getAttributesValues';
|
||||||
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||||
|
import { DEBOUNCE_DELAY } from 'constants/queryBuilderFilterConfig';
|
||||||
import {
|
import {
|
||||||
getRemovePrefixFromKey,
|
getRemovePrefixFromKey,
|
||||||
getTagToken,
|
getTagToken,
|
||||||
@ -54,7 +55,7 @@ export const useFetchKeysAndValues = (
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const searchParams = useDebounceValue(memoizedSearchParams, 300);
|
const searchParams = useDebounceValue(memoizedSearchParams, DEBOUNCE_DELAY);
|
||||||
|
|
||||||
const isQueryEnabled = useMemo(
|
const isQueryEnabled = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -3,17 +3,14 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
|
|||||||
|
|
||||||
export const chooseAutocompleteFromCustomValue = (
|
export const chooseAutocompleteFromCustomValue = (
|
||||||
sourceList: BaseAutocompleteData[],
|
sourceList: BaseAutocompleteData[],
|
||||||
values: string[],
|
value: string,
|
||||||
): BaseAutocompleteData[] => {
|
): BaseAutocompleteData => {
|
||||||
console.log({ sourceList });
|
const firstBaseAutoCompleteValue = sourceList.find(
|
||||||
return values.map((value) => {
|
(sourceAutoComplete) => value === sourceAutoComplete.key,
|
||||||
const firstBaseAutoCompleteValue = sourceList.find(
|
);
|
||||||
(sourceAutoComplete) => value === sourceAutoComplete.key,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!firstBaseAutoCompleteValue)
|
if (!firstBaseAutoCompleteValue)
|
||||||
return { ...initialAutocompleteData, key: value };
|
return { ...initialAutocompleteData, key: value };
|
||||||
|
|
||||||
return firstBaseAutoCompleteValue;
|
return firstBaseAutoCompleteValue;
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user