fix: having filter removing payload (#2706)

This commit is contained in:
Yevhen Shevchenko 2023-05-18 14:14:34 +03:00 committed by GitHub
parent 679eb39256
commit 18fc1a2761
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 458 additions and 120 deletions

View File

@ -6,7 +6,11 @@ import createQueryParams from 'lib/createQueryParams';
import { ErrorResponse, SuccessResponse } from 'types/api';
// ** Types
import { IGetAggregateAttributePayload } from 'types/api/queryBuilder/getAggregatorAttribute';
import { IQueryAutocompleteResponse } from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
BaseAutocompleteData,
IQueryAutocompleteResponse,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { v4 as uuid } from 'uuid';
export const getAggregateAttribute = async ({
aggregateOperator,
@ -26,11 +30,15 @@ export const getAggregateAttribute = async ({
})}`,
);
const payload: BaseAutocompleteData[] =
response.data.data.attributeKeys?.map((item) => ({ ...item, id: uuid() })) ||
[];
return {
statusCode: 200,
error: null,
message: response.statusText,
payload: response.data.data,
payload: { attributeKeys: payload },
};
} catch (e) {
return ErrorResponseHandler(e as AxiosError);

View File

@ -5,7 +5,11 @@ import createQueryParams from 'lib/createQueryParams';
import { ErrorResponse, SuccessResponse } from 'types/api';
// ** Types
import { IGetAttributeKeysPayload } from 'types/api/queryBuilder/getAttributeKeys';
import { IQueryAutocompleteResponse } from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
BaseAutocompleteData,
IQueryAutocompleteResponse,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { v4 as uuid } from 'uuid';
export const getAggregateKeys = async ({
aggregateOperator,
@ -28,11 +32,15 @@ export const getAggregateKeys = async ({
})}&tagType=${tagType}`,
);
const payload: BaseAutocompleteData[] =
response.data.data.attributeKeys?.map((item) => ({ ...item, id: uuid() })) ||
[];
return {
statusCode: 200,
error: null,
message: response.statusText,
payload: response.data.data,
payload: { attributeKeys: payload },
};
} catch (e) {
return ErrorResponseHandler(e as AxiosError);

View File

@ -10,20 +10,27 @@ import {
import {
BoolOperators,
DataSource,
LogsAggregatorOperator,
MetricAggregateOperator,
NumberOperators,
PanelTypeKeys,
QueryAdditionalFilter,
ReduceOperators,
StringOperators,
TracesAggregatorOperator,
} from 'types/common/queryBuilder';
import { SelectOption } from 'types/common/select';
import { v4 as uuid } from 'uuid';
import {
logsAggregateOperatorOptions,
metricAggregateOperatorOptions,
tracesAggregateOperatorOptions,
} from './queryBuilderOperators';
export const MAX_FORMULAS = 20;
export const MAX_QUERIES = 26;
export const selectValueDivider = '--';
export const formulasNames: string[] = Array.from(
Array(MAX_FORMULAS),
(_, i) => `F${i + 1}`,
@ -37,10 +44,10 @@ export enum QueryBuilderKeys {
GET_ATTRIBUTE_KEY = 'GET_ATTRIBUTE_KEY',
}
export const mapOfOperators: Record<DataSource, string[]> = {
metrics: Object.values(MetricAggregateOperator),
logs: Object.values(LogsAggregatorOperator),
traces: Object.values(TracesAggregatorOperator),
export const mapOfOperators = {
metrics: metricAggregateOperatorOptions,
logs: logsAggregateOperatorOptions,
traces: tracesAggregateOperatorOptions,
};
export const mapOfFilters: Record<DataSource, QueryAdditionalFilter[]> = {
@ -78,6 +85,7 @@ export const initialHavingValues: HavingForm = {
};
export const initialAggregateAttribute: IBuilderQuery['aggregateAttribute'] = {
id: uuid(),
dataType: null,
key: '',
isColumn: null,

View File

@ -0,0 +1,304 @@
import {
LogsAggregatorOperator,
MetricAggregateOperator,
TracesAggregatorOperator,
} from 'types/common/queryBuilder';
import { SelectOption } from 'types/common/select';
export const metricAggregateOperatorOptions: SelectOption<string, string>[] = [
{
value: MetricAggregateOperator.NOOP,
label: 'NOOP',
},
{
value: MetricAggregateOperator.COUNT,
label: 'Count',
},
{
value: MetricAggregateOperator.COUNT_DISTINCT,
// eslint-disable-next-line sonarjs/no-duplicate-string
label: 'Count Distinct',
},
{
value: MetricAggregateOperator.SUM,
label: 'Sum',
},
{
value: MetricAggregateOperator.AVG,
label: 'Avg',
},
{
value: MetricAggregateOperator.MAX,
label: 'Max',
},
{
value: MetricAggregateOperator.MIN,
label: 'Min',
},
{
value: MetricAggregateOperator.P05,
label: 'P05',
},
{
value: MetricAggregateOperator.P10,
label: 'P10',
},
{
value: MetricAggregateOperator.P20,
label: 'P20',
},
{
value: MetricAggregateOperator.P25,
label: 'P25',
},
{
value: MetricAggregateOperator.P50,
label: 'P50',
},
{
value: MetricAggregateOperator.P75,
label: 'P75',
},
{
value: MetricAggregateOperator.P90,
label: 'P90',
},
{
value: MetricAggregateOperator.P95,
label: 'P95',
},
{
value: MetricAggregateOperator.P99,
label: 'P99',
},
{
value: MetricAggregateOperator.RATE,
label: 'Rate',
},
{
value: MetricAggregateOperator.SUM_RATE,
label: 'Sum_rate',
},
{
value: MetricAggregateOperator.AVG_RATE,
label: 'Avg_rate',
},
{
value: MetricAggregateOperator.MAX_RATE,
label: 'Max_rate',
},
{
value: MetricAggregateOperator.MIN_RATE,
label: 'Min_rate',
},
{
value: MetricAggregateOperator.RATE_SUM,
label: 'Rate_sum',
},
{
value: MetricAggregateOperator.RATE_AVG,
label: 'Rate_avg',
},
{
value: MetricAggregateOperator.RATE_MIN,
label: 'Rate_min',
},
{
value: MetricAggregateOperator.RATE_MAX,
label: 'Rate_max',
},
{
value: MetricAggregateOperator.HIST_QUANTILE_50,
label: 'Hist_quantile_50',
},
{
value: MetricAggregateOperator.HIST_QUANTILE_75,
label: 'Hist_quantile_75',
},
{
value: MetricAggregateOperator.HIST_QUANTILE_90,
label: 'Hist_quantile_90',
},
{
value: MetricAggregateOperator.HIST_QUANTILE_95,
label: 'Hist_quantile_95',
},
{
value: MetricAggregateOperator.HIST_QUANTILE_99,
label: 'Hist_quantile_99',
},
];
export const tracesAggregateOperatorOptions: SelectOption<string, string>[] = [
{
value: TracesAggregatorOperator.NOOP,
label: 'NOOP',
},
{
value: TracesAggregatorOperator.COUNT,
label: 'Count',
},
{
value: TracesAggregatorOperator.COUNT_DISTINCT,
label: 'Count Distinct',
},
{
value: TracesAggregatorOperator.SUM,
label: 'Sum',
},
{
value: TracesAggregatorOperator.AVG,
label: 'Avg',
},
{
value: TracesAggregatorOperator.MAX,
label: 'Max',
},
{
value: TracesAggregatorOperator.MIN,
label: 'Min',
},
{
value: TracesAggregatorOperator.P05,
label: 'P05',
},
{
value: TracesAggregatorOperator.P10,
label: 'P10',
},
{
value: TracesAggregatorOperator.P20,
label: 'P20',
},
{
value: TracesAggregatorOperator.P25,
label: 'P25',
},
{
value: TracesAggregatorOperator.P50,
label: 'P50',
},
{
value: TracesAggregatorOperator.P75,
label: 'P75',
},
{
value: TracesAggregatorOperator.P90,
label: 'P90',
},
{
value: TracesAggregatorOperator.P95,
label: 'P95',
},
{
value: TracesAggregatorOperator.P99,
label: 'P99',
},
{
value: TracesAggregatorOperator.RATE,
label: 'Rate',
},
{
value: TracesAggregatorOperator.RATE_SUM,
label: 'Rate_sum',
},
{
value: TracesAggregatorOperator.RATE_AVG,
label: 'Rate_avg',
},
{
value: TracesAggregatorOperator.RATE_MIN,
label: 'Rate_min',
},
{
value: TracesAggregatorOperator.RATE_MAX,
label: 'Rate_max',
},
];
export const logsAggregateOperatorOptions: SelectOption<string, string>[] = [
{
value: LogsAggregatorOperator.NOOP,
label: 'NOOP',
},
{
value: LogsAggregatorOperator.COUNT,
label: 'Count',
},
{
value: LogsAggregatorOperator.COUNT_DISTINCT,
label: 'Count Distinct',
},
{
value: LogsAggregatorOperator.SUM,
label: 'Sum',
},
{
value: LogsAggregatorOperator.AVG,
label: 'Avg',
},
{
value: LogsAggregatorOperator.MAX,
label: 'Max',
},
{
value: LogsAggregatorOperator.MIN,
label: 'Min',
},
{
value: LogsAggregatorOperator.P05,
label: 'P05',
},
{
value: LogsAggregatorOperator.P10,
label: 'P10',
},
{
value: LogsAggregatorOperator.P20,
label: 'P20',
},
{
value: LogsAggregatorOperator.P25,
label: 'P25',
},
{
value: LogsAggregatorOperator.P50,
label: 'P50',
},
{
value: LogsAggregatorOperator.P75,
label: 'P75',
},
{
value: LogsAggregatorOperator.P90,
label: 'P90',
},
{
value: LogsAggregatorOperator.P95,
label: 'P95',
},
{
value: LogsAggregatorOperator.P99,
label: 'P99',
},
{
value: LogsAggregatorOperator.RATE,
label: 'Rate',
},
{
value: LogsAggregatorOperator.RATE_SUM,
label: 'Rate_sum',
},
{
value: LogsAggregatorOperator.RATE_AVG,
label: 'Rate_avg',
},
{
value: LogsAggregatorOperator.RATE_MIN,
label: 'Rate_min',
},
{
value: LogsAggregatorOperator.RATE_MAX,
label: 'Rate_max',
},
];

View File

@ -2,14 +2,19 @@
import { AutoComplete, Spin } from 'antd';
// ** Api
import { getAggregateAttribute } from 'api/queryBuilder/getAggregateAttribute';
import { initialAggregateAttribute } from 'constants/queryBuilder';
import {
initialAggregateAttribute,
QueryBuilderKeys,
selectValueDivider,
} from 'constants/queryBuilder';
import { getFilterObjectValue } from 'lib/newQueryBuilder/getFilterObjectValue';
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
import React, { memo, useMemo, useState } from 'react';
import React, { memo, useCallback, useMemo, useState } from 'react';
import { useQuery } from 'react-query';
import { DataSource } from 'types/common/queryBuilder';
import { ExtendedSelectOption } from 'types/common/select';
import { transformToUpperCase } from 'utils/transformToUpperCase';
import { v4 as uuid } from 'uuid';
import { selectStyle } from '../QueryBuilderSearch/config';
// ** Types
@ -19,57 +24,60 @@ export const AggregatorFilter = memo(function AggregatorFilter({
onChange,
query,
}: AgregatorFilterProps): JSX.Element {
const [searchText, setSearchText] = useState<string>('');
const [optionsData, setOptionsData] = useState<ExtendedSelectOption[]>([]);
const { data, isFetching } = useQuery(
[
'GET_AGGREGATE_ATTRIBUTE',
searchText,
QueryBuilderKeys.GET_AGGREGATE_ATTRIBUTE,
query.aggregateAttribute.key,
query.aggregateOperator,
query.dataSource,
],
async () =>
getAggregateAttribute({
searchText,
searchText: query.aggregateAttribute.key,
aggregateOperator: query.aggregateOperator,
dataSource: query.dataSource,
}),
{ enabled: !!query.aggregateOperator && !!query.dataSource },
);
const handleSearchAttribute = (searchText: string): void => {
const { key } = getFilterObjectValue(searchText);
setSearchText(key);
};
const optionsData: ExtendedSelectOption[] =
{
enabled: !!query.aggregateOperator && !!query.dataSource,
onSuccess: (data) => {
const options: ExtendedSelectOption[] =
data?.payload?.attributeKeys?.map((item) => ({
label: transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
value: transformStringWithPrefix({
value: `${transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
key: transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
})}${selectValueDivider}${item.id || uuid()}`,
key: item.id || uuid(),
})) || [];
const handleChangeAttribute = (value: string): void => {
const { key, isColumn } = getFilterObjectValue(value);
setOptionsData(options);
},
},
);
const handleChangeAttribute = useCallback(
(
value: string,
option: ExtendedSelectOption | ExtendedSelectOption[],
): void => {
const currentOption = option as ExtendedSelectOption;
const { key } = getFilterObjectValue(value);
const currentAttributeObj = data?.payload?.attributeKeys?.find(
(item) => item.key === key && isColumn === item.isColumn,
(item) => currentOption.key === item.id,
) || { ...initialAggregateAttribute, key };
setSearchText('');
onChange(currentAttributeObj);
};
},
[data, onChange],
);
const value = useMemo(
() =>
@ -88,12 +96,10 @@ export const AggregatorFilter = memo(function AggregatorFilter({
return (
<AutoComplete
showSearch
placeholder={placeholder}
style={selectStyle}
showArrow={false}
filterOption={false}
onSearch={handleSearchAttribute}
notFoundContent={isFetching ? <Spin size="small" /> : null}
options={optionsData}
value={value}

View File

@ -1,15 +1,16 @@
import { Select, Spin } from 'antd';
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
// ** Constants
import { QueryBuilderKeys } from 'constants/queryBuilder';
import { QueryBuilderKeys, selectValueDivider } from 'constants/queryBuilder';
import { getFilterObjectValue } from 'lib/newQueryBuilder/getFilterObjectValue';
// ** Components
// ** Helpers
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
import React, { memo, useMemo, useState } from 'react';
import React, { memo, useState } from 'react';
import { useQuery } from 'react-query';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { ExtendedSelectOption } from 'types/common/select';
import { v4 as uuid } from 'uuid';
import { selectStyle } from '../QueryBuilderSearch/config';
import { GroupByFilterProps } from './GroupByFilter.interfaces';
@ -20,6 +21,7 @@ export const GroupByFilter = memo(function GroupByFilter({
disabled,
}: GroupByFilterProps): JSX.Element {
const [searchText, setSearchText] = useState<string>('');
const [optionsData, setOptionsData] = useState<ExtendedSelectOption[]>([]);
const [isFocused, setIsFocused] = useState<boolean>(false);
const { data, isFetching } = useQuery(
@ -31,7 +33,38 @@ export const GroupByFilter = memo(function GroupByFilter({
aggregateOperator: query.aggregateOperator,
searchText,
}),
{ enabled: !disabled && isFocused, keepPreviousData: true },
{
enabled: !disabled && isFocused,
onSuccess: (data) => {
const keys = query.groupBy.reduce<string[]>((acc, item) => {
acc.push(item.key);
return acc;
}, []);
const filteredOptions: BaseAutocompleteData[] =
data?.payload?.attributeKeys?.filter(
(attrKey) => !keys.includes(attrKey.key),
) || [];
const options: ExtendedSelectOption[] =
filteredOptions.map((item) => ({
label: transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
value: `${transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
})}${selectValueDivider}${item.id || uuid()}`,
key: item.id || uuid(),
title: item.key,
})) || [];
setOptionsData(options);
},
},
);
const handleSearchKeys = (searchText: string): void => {
@ -46,51 +79,27 @@ export const GroupByFilter = memo(function GroupByFilter({
setIsFocused(true);
};
const optionsData: ExtendedSelectOption[] = useMemo(() => {
if (data && data.payload && data.payload.attributeKeys) {
return data.payload.attributeKeys.map((item) => ({
label: transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
value: transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
key: transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
}));
}
return [];
}, [data]);
const handleChange = (values: ExtendedSelectOption[]): void => {
const groupByValues: BaseAutocompleteData[] = values.map((item) => {
const responseKeys = data?.payload?.attributeKeys || [];
const { key, isColumn } = getFilterObjectValue(item.value);
const { key } = getFilterObjectValue(item.value);
const existGroupResponse = responseKeys.find(
(group) => group.key === key && group.isColumn === isColumn,
(group) => group.id === item.key,
);
if (existGroupResponse) {
return existGroupResponse;
}
const existGroupQuery = query.groupBy.find(
(group) => group.key === key && group.isColumn === isColumn,
);
const existGroupQuery = query.groupBy.find((group) => group.id === item.key);
if (existGroupQuery) {
return existGroupQuery;
}
return {
id: uuid(),
isColumn: null,
key,
dataType: null,
@ -98,7 +107,6 @@ export const GroupByFilter = memo(function GroupByFilter({
};
});
setSearchText('');
onChange(groupByValues);
};
@ -108,16 +116,12 @@ export const GroupByFilter = memo(function GroupByFilter({
prefix: item.type || '',
condition: !item.isColumn,
}),
key: transformStringWithPrefix({
key: item.id || uuid(),
value: `${transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
value: transformStringWithPrefix({
str: item.key,
prefix: item.type || '',
condition: !item.isColumn,
}),
})}${selectValueDivider}${item.id || uuid()}`,
}));
return (

View File

@ -204,7 +204,9 @@ export function HavingFilter({
const handleDeselect = (value: string): void => {
const result = localValues.filter((item) => item !== value);
setLocalValues(result);
const having: Having[] = result.map(transformFromStringToHaving);
onChange(having);
resetChanges();
};
useEffect(() => {

View File

@ -1,7 +1,8 @@
import { SelectProps } from 'antd';
import { SelectOption } from 'types/common/select';
export type OperatorsSelectProps = Omit<SelectProps, 'onChange' | 'value'> & {
operators: string[];
operators: SelectOption<string, string>[];
onChange: (value: string) => void;
value: string;
};

View File

@ -1,10 +1,7 @@
import { Select } from 'antd';
import React, { memo } from 'react';
// ** Types
import { SelectOption } from 'types/common/select';
// ** Helpers
import { transformToUpperCase } from 'utils/transformToUpperCase';
// ** Types
import { selectStyle } from '../QueryBuilderSearch/config';
import { OperatorsSelectProps } from './OperatorsSelect.interfaces';
@ -14,16 +11,9 @@ export const OperatorsSelect = memo(function OperatorsSelect({
onChange,
...props
}: OperatorsSelectProps): JSX.Element {
const operatorsOptions: SelectOption<string, string>[] = operators.map(
(operator) => ({
label: transformToUpperCase(operator),
value: operator,
}),
);
return (
<Select
options={operatorsOptions}
options={operators}
value={value}
onChange={onChange}
style={selectStyle}

View File

@ -14,6 +14,7 @@ import {
UseQueryOperations,
} from 'types/common/operations.types';
import { DataSource } from 'types/common/queryBuilder';
import { SelectOption } from 'types/common/select';
export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
const {
@ -21,7 +22,7 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
removeEntityByIndex,
panelType,
} = useQueryBuilder();
const [operators, setOperators] = useState<string[]>([]);
const [operators, setOperators] = useState<SelectOption<string, string>[]>([]);
const [listOfAdditionalFilters, setListOfAdditionalFilters] = useState<
string[]
>([]);
@ -90,7 +91,7 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
...query,
...initCopyResult,
dataSource: nextSource,
aggregateOperator: newOperators[0],
aggregateOperator: newOperators[0].value,
};
setOperators(newOperators);

View File

@ -4,28 +4,30 @@ import {
BaseAutocompleteData,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
const isTypeExist = (str: string): boolean => {
const getType = (str: string): AutocompleteType | null => {
const types: AutocompleteType[] = ['tag', 'resource'];
let isExist = false;
let currentType: AutocompleteType | null = null;
types.forEach((type) => {
if (str.includes(type)) {
isExist = true;
currentType = type;
}
});
return isExist;
return currentType;
};
export const getFilterObjectValue = (
value: string,
): Omit<BaseAutocompleteData, 'dataType' | 'type'> => {
const isExist = isTypeExist(value);
): Omit<BaseAutocompleteData, 'dataType' | 'id'> => {
const type = getType(value);
if (!isExist) {
return { isColumn: true, key: value };
if (!type) {
return { isColumn: true, key: value, type: null };
}
const splittedValue = value.split(TYPE_ADDON_REGEXP);
return { isColumn: false, key: splittedValue[1] };
return { isColumn: false, key: splittedValue[1], type };
};

View File

@ -1,6 +1,7 @@
import { mapOfOperators, PANEL_TYPES } from 'constants/queryBuilder';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { SelectOption } from 'types/common/select';
type GetQueryOperatorsParams = {
dataSource: DataSource;
@ -11,12 +12,12 @@ type GetQueryOperatorsParams = {
export const getOperatorsBySourceAndPanelType = ({
dataSource,
panelType,
}: GetQueryOperatorsParams): string[] => {
}: GetQueryOperatorsParams): SelectOption<string, string>[] => {
let operatorsByDataSource = mapOfOperators[dataSource];
if (dataSource !== DataSource.METRICS && panelType !== PANEL_TYPES.LIST) {
operatorsByDataSource = operatorsByDataSource.filter(
(operator) => operator !== StringOperators.NOOP,
(operator) => operator.value !== StringOperators.NOOP,
);
}

View File

@ -111,7 +111,7 @@ export function QueryBuilderProvider({
aggregateOperator: getOperatorsBySourceAndPanelType({
dataSource: initialDataSource,
panelType,
})[0],
})[0].value,
}
: {}),
};

View File

@ -5,6 +5,7 @@ export type DataType = 'int64' | 'float64' | 'string' | 'bool';
export type AutocompleteType = 'tag' | 'resource';
export interface BaseAutocompleteData {
id?: string;
dataType: DataType | null;
isColumn: boolean | null;
key: string;
@ -12,5 +13,5 @@ export interface BaseAutocompleteData {
}
export interface IQueryAutocompleteResponse {
attributeKeys: BaseAutocompleteData[];
attributeKeys: BaseAutocompleteData[] | null;
}

View File

@ -3,6 +3,8 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { SelectOption } from './select';
type UseQueryOperationsParams = Pick<QueryProps, 'index' | 'query'>;
export type HandleChangeQueryData = <
@ -17,7 +19,7 @@ export type UseQueryOperations = (
params: UseQueryOperationsParams,
) => {
isMetricsDataSource: boolean;
operators: string[];
operators: SelectOption<string, string>[];
listOfAdditionalFilters: string[];
handleChangeOperator: (value: string) => void;
handleChangeAggregatorAttribute: (value: BaseAutocompleteData) => void;