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