mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 14:25:56 +08:00
feat: add suggestion to order by filter (#3162)
* feat: add suggestion to order by filter * fix: column name for order by * fix: mapper for order by * fix: render order by for different panels * fix: order by timestamp and aggrigate value --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com> Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
This commit is contained in:
parent
5a2a987a9b
commit
98a2ef4080
72
frontend/src/container/ExplorerOrderBy/index.tsx
Normal file
72
frontend/src/container/ExplorerOrderBy/index.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { Select, Spin } from 'antd';
|
||||
import { OrderByFilterProps } from 'container/QueryBuilder/filters/OrderByFilter/OrderByFilter.interfaces';
|
||||
import { useOrderByFilter } from 'container/QueryBuilder/filters/OrderByFilter/useOrderByFilter';
|
||||
import { selectStyle } from 'container/QueryBuilder/filters/QueryBuilderSearch/config';
|
||||
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { StringOperators } from 'types/common/queryBuilder';
|
||||
|
||||
function ExplorerOrderBy({ query, onChange }: OrderByFilterProps): JSX.Element {
|
||||
const {
|
||||
debouncedSearchText,
|
||||
selectedValue,
|
||||
aggregationOptions,
|
||||
generateOptions,
|
||||
createOptions,
|
||||
handleChange,
|
||||
handleSearchKeys,
|
||||
} = useOrderByFilter({ query, onChange });
|
||||
|
||||
const { data, isFetching } = useGetAggregateKeys(
|
||||
{
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
searchText: debouncedSearchText,
|
||||
},
|
||||
{
|
||||
keepPreviousData: true,
|
||||
},
|
||||
);
|
||||
|
||||
const options = useMemo(() => {
|
||||
const keysOptions = createOptions(data?.payload?.attributeKeys || []);
|
||||
|
||||
const customOptions = createOptions([
|
||||
{ key: 'timestamp', isColumn: true, type: null, dataType: null },
|
||||
]);
|
||||
|
||||
const baseOptions = [
|
||||
...customOptions,
|
||||
...(query.aggregateOperator === StringOperators.NOOP
|
||||
? []
|
||||
: aggregationOptions),
|
||||
...keysOptions,
|
||||
];
|
||||
|
||||
return generateOptions(baseOptions);
|
||||
}, [
|
||||
aggregationOptions,
|
||||
createOptions,
|
||||
data?.payload?.attributeKeys,
|
||||
generateOptions,
|
||||
query.aggregateOperator,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Select
|
||||
mode="tags"
|
||||
style={selectStyle}
|
||||
onSearch={handleSearchKeys}
|
||||
showSearch
|
||||
showArrow={false}
|
||||
value={selectedValue}
|
||||
labelInValue
|
||||
options={options}
|
||||
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(ExplorerOrderBy);
|
@ -1,13 +1,15 @@
|
||||
import { Button } from 'antd';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ExplorerOrderBy from 'container/ExplorerOrderBy';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { OrderByFilterProps } from 'container/QueryBuilder/filters/OrderByFilter/OrderByFilter.interfaces';
|
||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import { ButtonWrapperStyled } from 'pages/LogsExplorer/styles';
|
||||
import { prepareQueryWithDefaultTimestamp } from 'pages/LogsExplorer/utils';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
function LogExplorerQuerySection(): JSX.Element {
|
||||
@ -23,6 +25,7 @@ function LogExplorerQuerySection(): JSX.Element {
|
||||
}, [updateAllQueriesOperators]);
|
||||
|
||||
useShareBuilderUrl(defaultValue);
|
||||
|
||||
const filterConfigs: QueryBuilderProps['filterConfigs'] = useMemo(() => {
|
||||
const isTable = panelTypes === PANEL_TYPES.TABLE;
|
||||
const config: QueryBuilderProps['filterConfigs'] = {
|
||||
@ -32,11 +35,26 @@ function LogExplorerQuerySection(): JSX.Element {
|
||||
return config;
|
||||
}, [panelTypes]);
|
||||
|
||||
const renderOrderBy = useCallback(
|
||||
({ query, onChange }: OrderByFilterProps): JSX.Element => (
|
||||
<ExplorerOrderBy query={query} onChange={onChange} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
const queryComponents = useMemo(
|
||||
(): QueryBuilderProps['queryComponents'] => ({
|
||||
...(panelTypes === PANEL_TYPES.LIST ? { renderOrderBy } : {}),
|
||||
}),
|
||||
[panelTypes, renderOrderBy],
|
||||
);
|
||||
|
||||
return (
|
||||
<QueryBuilder
|
||||
panelType={panelTypes}
|
||||
config={{ initialDataSource: DataSource.LOGS, queryVariant: 'static' }}
|
||||
filterConfigs={filterConfigs}
|
||||
queryComponents={queryComponents}
|
||||
actions={
|
||||
<ButtonWrapperStyled>
|
||||
<Button type="primary" onClick={handleRunQuery}>
|
||||
|
@ -3,6 +3,7 @@ import LogDetail from 'components/LogDetail';
|
||||
import TabLabel from 'components/TabLabel';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import {
|
||||
initialAutocompleteData,
|
||||
initialQueriesMap,
|
||||
OPERATORS,
|
||||
PANEL_TYPES,
|
||||
@ -17,6 +18,7 @@ import LogsExplorerChart from 'container/LogsExplorerChart';
|
||||
import LogsExplorerList from 'container/LogsExplorerList';
|
||||
import LogsExplorerTable from 'container/LogsExplorerTable';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { SIGNOZ_VALUE } from 'container/QueryBuilder/filters/OrderByFilter/constants';
|
||||
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
||||
@ -73,6 +75,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
stagedQuery,
|
||||
panelType,
|
||||
updateAllQueriesOperators,
|
||||
updateQueriesData,
|
||||
redirectWithQueryBuilderData,
|
||||
} = useQueryBuilder();
|
||||
|
||||
@ -175,26 +178,40 @@ function LogsExplorerViews(): JSX.Element {
|
||||
setActiveLog(null);
|
||||
}, []);
|
||||
|
||||
const getUpdateQuery = useCallback(
|
||||
(newPanelType: GRAPH_TYPES): Query => {
|
||||
let query = updateAllQueriesOperators(
|
||||
currentQuery,
|
||||
newPanelType,
|
||||
DataSource.TRACES,
|
||||
);
|
||||
|
||||
if (newPanelType === PANEL_TYPES.LIST) {
|
||||
query = updateQueriesData(query, 'queryData', (item) => ({
|
||||
...item,
|
||||
orderBy: item.orderBy.filter((item) => item.columnName !== SIGNOZ_VALUE),
|
||||
aggregateAttribute: initialAutocompleteData,
|
||||
}));
|
||||
}
|
||||
|
||||
return query;
|
||||
},
|
||||
[currentQuery, updateAllQueriesOperators, updateQueriesData],
|
||||
);
|
||||
|
||||
const handleChangeView = useCallback(
|
||||
(newPanelType: string) => {
|
||||
(type: string) => {
|
||||
const newPanelType = type as GRAPH_TYPES;
|
||||
|
||||
if (newPanelType === panelType) return;
|
||||
|
||||
const query = updateAllQueriesOperators(
|
||||
currentQuery,
|
||||
newPanelType as GRAPH_TYPES,
|
||||
DataSource.LOGS,
|
||||
);
|
||||
const query = getUpdateQuery(newPanelType);
|
||||
|
||||
redirectWithQueryBuilderData(query, {
|
||||
[queryParamNamesMap.panelTypes]: newPanelType,
|
||||
});
|
||||
},
|
||||
[
|
||||
currentQuery,
|
||||
panelType,
|
||||
updateAllQueriesOperators,
|
||||
redirectWithQueryBuilderData,
|
||||
],
|
||||
[panelType, getUpdateQuery, redirectWithQueryBuilderData],
|
||||
);
|
||||
|
||||
const getRequestData = useCallback(
|
||||
|
@ -3,12 +3,12 @@ import getFromLocalstorage from 'api/browser/localstorage/get';
|
||||
import setToLocalstorage from 'api/browser/localstorage/set';
|
||||
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
|
||||
import useDebounce from 'hooks/useDebounce';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useQueries, useQuery } from 'react-query';
|
||||
import { useQueries } from 'react-query';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import {
|
||||
BaseAutocompleteData,
|
||||
@ -116,16 +116,12 @@ const useOptionsMenu = ({
|
||||
const {
|
||||
data: searchedAttributesData,
|
||||
isFetching: isSearchedAttributesFetching,
|
||||
} = useQuery(
|
||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, debouncedSearchText, isFocused],
|
||||
async () =>
|
||||
getAggregateKeys({
|
||||
...initialQueryParams,
|
||||
searchText: debouncedSearchText,
|
||||
}),
|
||||
} = useGetAggregateKeys(
|
||||
{
|
||||
enabled: isFocused,
|
||||
...initialQueryParams,
|
||||
searchText: debouncedSearchText,
|
||||
},
|
||||
{ queryKey: [debouncedSearchText, isFocused], enabled: isFocused },
|
||||
);
|
||||
|
||||
const searchedAttributeKeys = useMemo(
|
||||
|
@ -3,6 +3,8 @@ import { ReactNode } from 'react';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { OrderByFilterProps } from './filters/OrderByFilter/OrderByFilter.interfaces';
|
||||
|
||||
export type QueryBuilderConfig =
|
||||
| {
|
||||
queryVariant: 'static';
|
||||
@ -17,4 +19,5 @@ export type QueryBuilderProps = {
|
||||
filterConfigs?: Partial<
|
||||
Record<keyof IBuilderQuery, { isHidden: boolean; isDisabled: boolean }>
|
||||
>;
|
||||
queryComponents?: { renderOrderBy?: (props: OrderByFilterProps) => ReactNode };
|
||||
};
|
||||
|
@ -16,6 +16,7 @@ export const QueryBuilder = memo(function QueryBuilder({
|
||||
panelType: newPanelType,
|
||||
actions,
|
||||
filterConfigs = {},
|
||||
queryComponents,
|
||||
}: QueryBuilderProps): JSX.Element {
|
||||
const {
|
||||
currentQuery,
|
||||
@ -74,6 +75,7 @@ export const QueryBuilder = memo(function QueryBuilder({
|
||||
queryVariant={config?.queryVariant || 'dropdown'}
|
||||
query={query}
|
||||
filterConfigs={filterConfigs}
|
||||
queryComponents={queryComponents}
|
||||
/>
|
||||
</Col>
|
||||
))}
|
||||
|
@ -6,4 +6,4 @@ export type QueryProps = {
|
||||
isAvailableToDisable: boolean;
|
||||
query: IBuilderQuery;
|
||||
queryVariant: 'static' | 'dropdown';
|
||||
} & Pick<QueryBuilderProps, 'filterConfigs'>;
|
||||
} & Pick<QueryBuilderProps, 'filterConfigs' | 'queryComponents'>;
|
||||
|
@ -36,6 +36,7 @@ export const Query = memo(function Query({
|
||||
queryVariant,
|
||||
query,
|
||||
filterConfigs,
|
||||
queryComponents,
|
||||
}: QueryProps): JSX.Element {
|
||||
const { panelType } = useQueryBuilder();
|
||||
const {
|
||||
@ -110,6 +111,17 @@ export const Query = memo(function Query({
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const renderOrderByFilter = useCallback((): ReactNode => {
|
||||
if (queryComponents?.renderOrderBy) {
|
||||
return queryComponents.renderOrderBy({
|
||||
query,
|
||||
onChange: handleChangeOrderByKeys,
|
||||
});
|
||||
}
|
||||
|
||||
return <OrderByFilter query={query} onChange={handleChangeOrderByKeys} />;
|
||||
}, [queryComponents, query, handleChangeOrderByKeys]);
|
||||
|
||||
const renderAggregateEveryFilter = useCallback(
|
||||
(): JSX.Element | null =>
|
||||
!filterConfigs?.stepInterval?.isHidden ? (
|
||||
@ -167,9 +179,7 @@ export const Query = memo(function Query({
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="Order by" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<OrderByFilter query={query} onChange={handleChangeOrderByKeys} />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">{renderOrderByFilter()}</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
@ -225,9 +235,7 @@ export const Query = memo(function Query({
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="Order by" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<OrderByFilter query={query} onChange={handleChangeOrderByKeys} />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">{renderOrderByFilter()}</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
@ -238,11 +246,11 @@ export const Query = memo(function Query({
|
||||
}
|
||||
}, [
|
||||
panelType,
|
||||
query,
|
||||
isMetricsDataSource,
|
||||
handleChangeHavingFilter,
|
||||
query,
|
||||
handleChangeLimit,
|
||||
handleChangeOrderByKeys,
|
||||
handleChangeHavingFilter,
|
||||
renderOrderByFilter,
|
||||
renderAggregateEveryFilter,
|
||||
]);
|
||||
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
selectValueDivider,
|
||||
} from 'constants/queryBuilder';
|
||||
import { DEBOUNCE_DELAY } from 'constants/queryBuilderFilterConfig';
|
||||
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
|
||||
import useDebounce from 'hooks/useDebounce';
|
||||
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
||||
// ** Components
|
||||
@ -14,7 +15,7 @@ import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAut
|
||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||
import { isEqual, uniqWith } from 'lodash-es';
|
||||
import { memo, useCallback, useEffect, useState } from 'react';
|
||||
import { useQuery, useQueryClient } from 'react-query';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
@ -38,16 +39,15 @@ export const GroupByFilter = memo(function GroupByFilter({
|
||||
|
||||
const debouncedValue = useDebounce(searchText, DEBOUNCE_DELAY);
|
||||
|
||||
const { isFetching } = useQuery(
|
||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, debouncedValue, isFocused],
|
||||
async () =>
|
||||
getAggregateKeys({
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
searchText: debouncedValue,
|
||||
}),
|
||||
const { isFetching } = useGetAggregateKeys(
|
||||
{
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
searchText: debouncedValue,
|
||||
},
|
||||
{
|
||||
queryKey: [debouncedValue, isFocused],
|
||||
enabled: !disabled && isFocused,
|
||||
onSuccess: (data) => {
|
||||
const keys = query.groupBy.reduce<string[]>((acc, item) => {
|
||||
|
@ -1,208 +1,57 @@
|
||||
import { Select, Spin } from 'antd';
|
||||
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
||||
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||
import { IOption } from 'hooks/useResourceAttribute/types';
|
||||
import { uniqWith } from 'lodash-es';
|
||||
import * as Papa from 'papaparse';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { OrderByPayload } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
|
||||
import { useMemo } from 'react';
|
||||
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
|
||||
|
||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||
import { getRemoveOrderFromValue } from '../QueryBuilderSearch/utils';
|
||||
import { FILTERS } from './config';
|
||||
import { OrderByFilterProps } from './OrderByFilter.interfaces';
|
||||
import {
|
||||
checkIfKeyPresent,
|
||||
getLabelFromValue,
|
||||
mapLabelValuePairs,
|
||||
orderByValueDelimiter,
|
||||
splitOrderByFromString,
|
||||
transformToOrderByStringValues,
|
||||
} from './utils';
|
||||
import { useOrderByFilter } from './useOrderByFilter';
|
||||
|
||||
export function OrderByFilter({
|
||||
query,
|
||||
onChange,
|
||||
}: OrderByFilterProps): JSX.Element {
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
const [selectedValue, setSelectedValue] = useState<IOption[]>(
|
||||
transformToOrderByStringValues(query.orderBy),
|
||||
const {
|
||||
debouncedSearchText,
|
||||
selectedValue,
|
||||
aggregationOptions,
|
||||
generateOptions,
|
||||
createOptions,
|
||||
handleChange,
|
||||
handleSearchKeys,
|
||||
} = useOrderByFilter({ query, onChange });
|
||||
|
||||
const { data, isFetching } = useGetAggregateKeys(
|
||||
{
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
searchText: debouncedSearchText,
|
||||
},
|
||||
{
|
||||
enabled: !!query.aggregateAttribute.key,
|
||||
keepPreviousData: true,
|
||||
},
|
||||
);
|
||||
|
||||
const { data, isFetching } = useQuery(
|
||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchText],
|
||||
async () =>
|
||||
getAggregateKeys({
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
searchText,
|
||||
}),
|
||||
{ enabled: !!query.aggregateAttribute.key, keepPreviousData: true },
|
||||
);
|
||||
|
||||
const handleSearchKeys = useCallback(
|
||||
(searchText: string): void => setSearchText(searchText),
|
||||
[],
|
||||
);
|
||||
|
||||
const noAggregationOptions = useMemo(
|
||||
() =>
|
||||
data?.payload?.attributeKeys
|
||||
? mapLabelValuePairs(data?.payload?.attributeKeys).flat()
|
||||
: [],
|
||||
[data?.payload?.attributeKeys],
|
||||
);
|
||||
|
||||
const aggregationOptions = useMemo(
|
||||
() =>
|
||||
mapLabelValuePairs(query.groupBy)
|
||||
.flat()
|
||||
.concat([
|
||||
{
|
||||
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) ${FILTERS.ASC}`,
|
||||
value: `${query.aggregateOperator}(${query.aggregateAttribute.key})${orderByValueDelimiter}${FILTERS.ASC}`,
|
||||
},
|
||||
{
|
||||
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) ${FILTERS.DESC}`,
|
||||
value: `${query.aggregateOperator}(${query.aggregateAttribute.key})${orderByValueDelimiter}${FILTERS.DESC}`,
|
||||
},
|
||||
]),
|
||||
[query.aggregateAttribute.key, query.aggregateOperator, query.groupBy],
|
||||
);
|
||||
|
||||
const customValue: IOption[] = useMemo(() => {
|
||||
if (!searchText) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
label: `${searchText} ${FILTERS.ASC}`,
|
||||
value: `${searchText}${orderByValueDelimiter}${FILTERS.ASC}`,
|
||||
},
|
||||
{
|
||||
label: `${searchText} ${FILTERS.DESC}`,
|
||||
value: `${searchText}${orderByValueDelimiter}${FILTERS.DESC}`,
|
||||
},
|
||||
];
|
||||
}, [searchText]);
|
||||
|
||||
const optionsData = useMemo(() => {
|
||||
const keyOptions = createOptions(data?.payload?.attributeKeys || []);
|
||||
const groupByOptions = createOptions(query.groupBy);
|
||||
const options =
|
||||
query.aggregateOperator === MetricAggregateOperator.NOOP
|
||||
? noAggregationOptions
|
||||
: aggregationOptions;
|
||||
? keyOptions
|
||||
: [...groupByOptions, ...aggregationOptions];
|
||||
|
||||
const resultOption = [...customValue, ...options];
|
||||
|
||||
return resultOption.filter(
|
||||
(option) =>
|
||||
!getLabelFromValue(selectedValue).includes(
|
||||
getRemoveOrderFromValue(option.value),
|
||||
),
|
||||
);
|
||||
return generateOptions(options);
|
||||
}, [
|
||||
aggregationOptions,
|
||||
customValue,
|
||||
noAggregationOptions,
|
||||
createOptions,
|
||||
data?.payload?.attributeKeys,
|
||||
generateOptions,
|
||||
query.aggregateOperator,
|
||||
selectedValue,
|
||||
query.groupBy,
|
||||
]);
|
||||
|
||||
const getUniqValues = useCallback((values: IOption[]): IOption[] => {
|
||||
const modifiedValues = values.map((item) => {
|
||||
const match = Papa.parse(item.value, { delimiter: orderByValueDelimiter });
|
||||
if (!match) return { label: item.label, value: item.value };
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars
|
||||
const [_, order] = match.data.flat() as string[];
|
||||
if (order)
|
||||
return {
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
};
|
||||
|
||||
return {
|
||||
label: `${item.value} ${FILTERS.ASC}`,
|
||||
value: `${item.value}${orderByValueDelimiter}${FILTERS.ASC}`,
|
||||
};
|
||||
});
|
||||
|
||||
return uniqWith(
|
||||
modifiedValues,
|
||||
(current, next) =>
|
||||
getRemoveOrderFromValue(current.value) ===
|
||||
getRemoveOrderFromValue(next.value),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const getValidResult = useCallback(
|
||||
(result: IOption[]): IOption[] =>
|
||||
result.reduce<IOption[]>((acc, item) => {
|
||||
if (item.value === FILTERS.ASC || item.value === FILTERS.DESC) return acc;
|
||||
|
||||
if (item.value.includes(FILTERS.ASC) || item.value.includes(FILTERS.DESC)) {
|
||||
const splittedOrderBy = splitOrderByFromString(item.value);
|
||||
|
||||
if (splittedOrderBy) {
|
||||
acc.push({
|
||||
label: `${splittedOrderBy.columnName} ${splittedOrderBy.order}`,
|
||||
value: `${splittedOrderBy.columnName}${orderByValueDelimiter}${splittedOrderBy.order}`,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
acc.push(item);
|
||||
|
||||
return acc;
|
||||
}, []),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleChange = (values: IOption[]): void => {
|
||||
const validResult = getValidResult(values);
|
||||
const result = getUniqValues(validResult);
|
||||
|
||||
const orderByValues: OrderByPayload[] = result.map((item) => {
|
||||
const match = Papa.parse(item.value, { delimiter: orderByValueDelimiter });
|
||||
|
||||
if (!match) {
|
||||
return {
|
||||
columnName: item.value,
|
||||
order: 'asc',
|
||||
};
|
||||
}
|
||||
|
||||
const [columnName, order] = match.data.flat() as string[];
|
||||
|
||||
const columnNameValue = checkIfKeyPresent(
|
||||
columnName,
|
||||
query.aggregateAttribute.key,
|
||||
)
|
||||
? '#SIGNOZ_VALUE'
|
||||
: columnName;
|
||||
|
||||
const orderValue = order ?? 'asc';
|
||||
|
||||
return {
|
||||
columnName: columnNameValue,
|
||||
order: orderValue,
|
||||
};
|
||||
});
|
||||
|
||||
const selectedValue: IOption[] = orderByValues.map((item) => ({
|
||||
label: `${item.columnName} ${item.order}`,
|
||||
value: `${item.columnName} ${item.order}`,
|
||||
}));
|
||||
|
||||
setSelectedValue(selectedValue);
|
||||
|
||||
setSearchText('');
|
||||
onChange(orderByValues);
|
||||
};
|
||||
|
||||
const isDisabledSelect = useMemo(
|
||||
() =>
|
||||
!query.aggregateAttribute.key ||
|
||||
|
@ -0,0 +1 @@
|
||||
export const SIGNOZ_VALUE = '#SIGNOZ_VALUE';
|
@ -0,0 +1,199 @@
|
||||
import { DEBOUNCE_DELAY } from 'constants/queryBuilderFilterConfig';
|
||||
import useDebounce from 'hooks/useDebounce';
|
||||
import { IOption } from 'hooks/useResourceAttribute/types';
|
||||
import { isEqual, uniqWith } from 'lodash-es';
|
||||
import * as Papa from 'papaparse';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { OrderByPayload } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { getRemoveOrderFromValue } from '../QueryBuilderSearch/utils';
|
||||
import { FILTERS } from './config';
|
||||
import { SIGNOZ_VALUE } from './constants';
|
||||
import { OrderByFilterProps } from './OrderByFilter.interfaces';
|
||||
import {
|
||||
getLabelFromValue,
|
||||
mapLabelValuePairs,
|
||||
orderByValueDelimiter,
|
||||
splitOrderByFromString,
|
||||
transformToOrderByStringValues,
|
||||
} from './utils';
|
||||
|
||||
type UseOrderByFilterResult = {
|
||||
searchText: string;
|
||||
debouncedSearchText: string;
|
||||
selectedValue: IOption[];
|
||||
aggregationOptions: IOption[];
|
||||
generateOptions: (options: IOption[]) => IOption[];
|
||||
createOptions: (data: BaseAutocompleteData[]) => IOption[];
|
||||
handleChange: (values: IOption[]) => void;
|
||||
handleSearchKeys: (search: string) => void;
|
||||
};
|
||||
|
||||
export const useOrderByFilter = ({
|
||||
query,
|
||||
onChange,
|
||||
}: OrderByFilterProps): UseOrderByFilterResult => {
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
|
||||
const debouncedSearchText = useDebounce(searchText, DEBOUNCE_DELAY);
|
||||
|
||||
const handleSearchKeys = useCallback(
|
||||
(searchText: string): void => setSearchText(searchText),
|
||||
[],
|
||||
);
|
||||
|
||||
const getUniqValues = useCallback((values: IOption[]): IOption[] => {
|
||||
const modifiedValues = values.map((item) => {
|
||||
const match = Papa.parse(item.value, { delimiter: orderByValueDelimiter });
|
||||
if (!match) return { label: item.label, value: item.value };
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars
|
||||
const [_, order] = match.data.flat() as string[];
|
||||
if (order)
|
||||
return {
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
};
|
||||
|
||||
return {
|
||||
label: `${item.value} ${FILTERS.ASC}`,
|
||||
value: `${item.value}${orderByValueDelimiter}${FILTERS.ASC}`,
|
||||
};
|
||||
});
|
||||
|
||||
return uniqWith(
|
||||
modifiedValues,
|
||||
(current, next) =>
|
||||
getRemoveOrderFromValue(current.value) ===
|
||||
getRemoveOrderFromValue(next.value),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const customValue: IOption[] = useMemo(() => {
|
||||
if (!searchText) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
label: `${searchText} ${FILTERS.ASC}`,
|
||||
value: `${searchText}${orderByValueDelimiter}${FILTERS.ASC}`,
|
||||
},
|
||||
{
|
||||
label: `${searchText} ${FILTERS.DESC}`,
|
||||
value: `${searchText}${orderByValueDelimiter}${FILTERS.DESC}`,
|
||||
},
|
||||
];
|
||||
}, [searchText]);
|
||||
|
||||
const selectedValue = useMemo(() => transformToOrderByStringValues(query), [
|
||||
query,
|
||||
]);
|
||||
|
||||
const generateOptions = useCallback(
|
||||
(options: IOption[]): IOption[] => {
|
||||
const currentCustomValue = options.find(
|
||||
(keyOption) =>
|
||||
getRemoveOrderFromValue(keyOption.value) === debouncedSearchText,
|
||||
)
|
||||
? []
|
||||
: customValue;
|
||||
|
||||
const result = [...currentCustomValue, ...options];
|
||||
|
||||
const uniqResult = uniqWith(result, isEqual);
|
||||
|
||||
return uniqResult.filter(
|
||||
(option) =>
|
||||
!getLabelFromValue(selectedValue).includes(
|
||||
getRemoveOrderFromValue(option.value),
|
||||
),
|
||||
);
|
||||
},
|
||||
[customValue, debouncedSearchText, selectedValue],
|
||||
);
|
||||
|
||||
const getValidResult = useCallback(
|
||||
(result: IOption[]): IOption[] =>
|
||||
result.reduce<IOption[]>((acc, item) => {
|
||||
if (item.value === FILTERS.ASC || item.value === FILTERS.DESC) return acc;
|
||||
|
||||
if (item.value.includes(FILTERS.ASC) || item.value.includes(FILTERS.DESC)) {
|
||||
const splittedOrderBy = splitOrderByFromString(item.value);
|
||||
|
||||
if (splittedOrderBy) {
|
||||
acc.push({
|
||||
label: `${splittedOrderBy.columnName} ${splittedOrderBy.order}`,
|
||||
value: `${splittedOrderBy.columnName}${orderByValueDelimiter}${splittedOrderBy.order}`,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
acc.push(item);
|
||||
|
||||
return acc;
|
||||
}, []),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleChange = (values: IOption[]): void => {
|
||||
const validResult = getValidResult(values);
|
||||
const result = getUniqValues(validResult);
|
||||
|
||||
const orderByValues: OrderByPayload[] = result.map((item) => {
|
||||
const match = Papa.parse(item.value, { delimiter: orderByValueDelimiter });
|
||||
|
||||
if (!match) {
|
||||
return {
|
||||
columnName: item.value,
|
||||
order: 'asc',
|
||||
};
|
||||
}
|
||||
|
||||
const [columnName, order] = match.data.flat() as string[];
|
||||
|
||||
const columnNameValue =
|
||||
columnName === SIGNOZ_VALUE ? SIGNOZ_VALUE : columnName;
|
||||
|
||||
const orderValue = order ?? 'asc';
|
||||
|
||||
return {
|
||||
columnName: columnNameValue,
|
||||
order: orderValue,
|
||||
};
|
||||
});
|
||||
|
||||
setSearchText('');
|
||||
onChange(orderByValues);
|
||||
};
|
||||
|
||||
const createOptions = useCallback(
|
||||
(data: BaseAutocompleteData[]): IOption[] => mapLabelValuePairs(data).flat(),
|
||||
[],
|
||||
);
|
||||
|
||||
const aggregationOptions = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) ${FILTERS.ASC}`,
|
||||
value: `${SIGNOZ_VALUE}${orderByValueDelimiter}${FILTERS.ASC}`,
|
||||
},
|
||||
{
|
||||
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) ${FILTERS.DESC}`,
|
||||
value: `${SIGNOZ_VALUE}${orderByValueDelimiter}${FILTERS.DESC}`,
|
||||
},
|
||||
],
|
||||
[query],
|
||||
);
|
||||
|
||||
return {
|
||||
searchText,
|
||||
debouncedSearchText,
|
||||
selectedValue,
|
||||
aggregationOptions,
|
||||
createOptions,
|
||||
handleChange,
|
||||
handleSearchKeys,
|
||||
generateOptions,
|
||||
};
|
||||
};
|
@ -1,31 +1,32 @@
|
||||
import { IOption } from 'hooks/useResourceAttribute/types';
|
||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||
import * as Papa from 'papaparse';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { OrderByPayload } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
IBuilderQuery,
|
||||
OrderByPayload,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { FILTERS } from './config';
|
||||
import { SIGNOZ_VALUE } from './constants';
|
||||
|
||||
export const orderByValueDelimiter = '|';
|
||||
|
||||
export const transformToOrderByStringValues = (
|
||||
orderBy: OrderByPayload[],
|
||||
query: IBuilderQuery,
|
||||
): IOption[] => {
|
||||
const prepareSelectedValue: IOption[] = orderBy.reduce<IOption[]>(
|
||||
(acc, item) => {
|
||||
if (item.columnName === '#SIGNOZ_VALUE') return acc;
|
||||
|
||||
const option: IOption = {
|
||||
label: `${item.columnName} ${item.order}`,
|
||||
const prepareSelectedValue: IOption[] = query.orderBy.map((item) => {
|
||||
if (item.columnName === SIGNOZ_VALUE) {
|
||||
return {
|
||||
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) ${item.order}`,
|
||||
value: `${item.columnName}${orderByValueDelimiter}${item.order}`,
|
||||
};
|
||||
}
|
||||
|
||||
acc.push(option);
|
||||
|
||||
return acc;
|
||||
},
|
||||
[],
|
||||
);
|
||||
return {
|
||||
label: `${item.columnName} ${item.order}`,
|
||||
value: `${item.columnName}${orderByValueDelimiter}${item.order}`,
|
||||
};
|
||||
});
|
||||
|
||||
return prepareSelectedValue;
|
||||
};
|
||||
@ -34,20 +35,15 @@ export function mapLabelValuePairs(
|
||||
arr: BaseAutocompleteData[],
|
||||
): Array<IOption>[] {
|
||||
return arr.map((item) => {
|
||||
const label = transformStringWithPrefix({
|
||||
str: item.key,
|
||||
prefix: item.type || '',
|
||||
condition: !item.isColumn,
|
||||
});
|
||||
const value = item.key;
|
||||
return [
|
||||
{
|
||||
label: `${label} asc`,
|
||||
value: `${value}${orderByValueDelimiter}asc`,
|
||||
label: `${value} ${FILTERS.ASC}`,
|
||||
value: `${value}${orderByValueDelimiter}${FILTERS.ASC}`,
|
||||
},
|
||||
{
|
||||
label: `${label} desc`,
|
||||
value: `${value}${orderByValueDelimiter}desc`,
|
||||
label: `${value} ${FILTERS.DESC}`,
|
||||
value: `${value}${orderByValueDelimiter}${FILTERS.DESC}`,
|
||||
},
|
||||
];
|
||||
});
|
||||
@ -58,6 +54,7 @@ export function getLabelFromValue(arr: IOption[]): string[] {
|
||||
const match = Papa.parse(item.value, { delimiter: orderByValueDelimiter });
|
||||
if (match) {
|
||||
const [key] = match.data as string[];
|
||||
|
||||
return key[0];
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Button } from 'antd';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ExplorerOrderBy from 'container/ExplorerOrderBy';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { OrderByFilterProps } from 'container/QueryBuilder/filters/OrderByFilter/OrderByFilter.interfaces';
|
||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { ButtonWrapper, Container } from './styles';
|
||||
@ -22,6 +24,22 @@ function QuerySection(): JSX.Element {
|
||||
return config;
|
||||
}, []);
|
||||
|
||||
const renderOrderBy = useCallback(
|
||||
({ query, onChange }: OrderByFilterProps) => (
|
||||
<ExplorerOrderBy query={query} onChange={onChange} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
const queryComponents = useMemo((): QueryBuilderProps['queryComponents'] => {
|
||||
const shouldRenderCustomOrderBy =
|
||||
panelTypes === PANEL_TYPES.LIST || panelTypes === PANEL_TYPES.TRACE;
|
||||
|
||||
return {
|
||||
...(shouldRenderCustomOrderBy ? { renderOrderBy } : {}),
|
||||
};
|
||||
}, [panelTypes, renderOrderBy]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<QueryBuilder
|
||||
@ -31,6 +49,7 @@ function QuerySection(): JSX.Element {
|
||||
initialDataSource: DataSource.TRACES,
|
||||
}}
|
||||
filterConfigs={filterConfigs}
|
||||
queryComponents={queryComponents}
|
||||
actions={
|
||||
<ButtonWrapper>
|
||||
<Button onClick={handleRunQuery} type="primary">
|
||||
|
@ -1,6 +1,4 @@
|
||||
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,
|
||||
@ -10,12 +8,13 @@ import {
|
||||
import useDebounceValue from 'hooks/useDebounce';
|
||||
import { isEqual, uniqWith } from 'lodash-es';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useDebounce } from 'react-use';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { useGetAggregateKeys } from './useGetAggregateKeys';
|
||||
|
||||
type IuseFetchKeysAndValues = {
|
||||
keys: BaseAutocompleteData[];
|
||||
results: string[];
|
||||
@ -71,19 +70,15 @@ export const useFetchKeysAndValues = (
|
||||
],
|
||||
);
|
||||
|
||||
const { data, isFetching, status } = useQuery(
|
||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchParams],
|
||||
async () =>
|
||||
getAggregateKeys({
|
||||
searchText: searchKey,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
tagType: query.aggregateAttribute.type ?? null,
|
||||
}),
|
||||
const { data, isFetching, status } = useGetAggregateKeys(
|
||||
{
|
||||
enabled: isQueryEnabled,
|
||||
searchText: searchKey,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
tagType: query.aggregateAttribute.type ?? null,
|
||||
},
|
||||
{ queryKey: [searchParams], enabled: isQueryEnabled },
|
||||
);
|
||||
|
||||
/**
|
||||
|
34
frontend/src/hooks/queryBuilder/useGetAggregateKeys.ts
Normal file
34
frontend/src/hooks/queryBuilder/useGetAggregateKeys.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
||||
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||
import { useMemo } from 'react';
|
||||
import { useQuery, UseQueryOptions, UseQueryResult } from 'react-query';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { IGetAttributeKeysPayload } from 'types/api/queryBuilder/getAttributeKeys';
|
||||
import { IQueryAutocompleteResponse } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
type UseGetAttributeKeys = (
|
||||
requestData: IGetAttributeKeysPayload,
|
||||
options?: UseQueryOptions<
|
||||
SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse
|
||||
>,
|
||||
) => UseQueryResult<
|
||||
SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse
|
||||
>;
|
||||
|
||||
export const useGetAggregateKeys: UseGetAttributeKeys = (
|
||||
requestData,
|
||||
options,
|
||||
) => {
|
||||
const queryKey = useMemo(() => {
|
||||
if (options?.queryKey && Array.isArray(options.queryKey)) {
|
||||
return [QueryBuilderKeys.GET_AGGREGATE_KEYS, ...options.queryKey];
|
||||
}
|
||||
return [QueryBuilderKeys.GET_AGGREGATE_KEYS, requestData];
|
||||
}, [options?.queryKey, requestData]);
|
||||
|
||||
return useQuery<SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse>({
|
||||
queryKey,
|
||||
queryFn: () => getAggregateKeys(requestData),
|
||||
...options,
|
||||
});
|
||||
};
|
@ -2,11 +2,16 @@ import { Tabs } from 'antd';
|
||||
import axios from 'axios';
|
||||
import ExplorerCard from 'components/ExplorerCard';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import {
|
||||
initialAutocompleteData,
|
||||
initialQueriesMap,
|
||||
PANEL_TYPES,
|
||||
} from 'constants/queryBuilder';
|
||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
||||
import ROUTES from 'constants/routes';
|
||||
import ExportPanel from 'container/ExportPanel';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { SIGNOZ_VALUE } from 'container/QueryBuilder/filters/OrderByFilter/constants';
|
||||
import QuerySection from 'container/TracesExplorer/QuerySection';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
||||
@ -17,6 +22,7 @@ import history from 'lib/history';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { generatePath } from 'react-router-dom';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { ActionsWrapper, Container } from './styles';
|
||||
@ -29,6 +35,7 @@ function TracesExplorer(): JSX.Element {
|
||||
currentQuery,
|
||||
panelType,
|
||||
updateAllQueriesOperators,
|
||||
updateQueriesData,
|
||||
redirectWithQueryBuilderData,
|
||||
} = useQueryBuilder();
|
||||
|
||||
@ -141,26 +148,42 @@ function TracesExplorer(): JSX.Element {
|
||||
[exportDefaultQuery, notifications, updateDashboard],
|
||||
);
|
||||
|
||||
const handleTabChange = useCallback(
|
||||
(newPanelType: string): void => {
|
||||
if (panelType === newPanelType) return;
|
||||
|
||||
const query = updateAllQueriesOperators(
|
||||
const getUpdateQuery = useCallback(
|
||||
(newPanelType: GRAPH_TYPES): Query => {
|
||||
let query = updateAllQueriesOperators(
|
||||
currentQuery,
|
||||
newPanelType as GRAPH_TYPES,
|
||||
newPanelType,
|
||||
DataSource.TRACES,
|
||||
);
|
||||
|
||||
if (
|
||||
newPanelType === PANEL_TYPES.LIST ||
|
||||
newPanelType === PANEL_TYPES.TRACE
|
||||
) {
|
||||
query = updateQueriesData(query, 'queryData', (item) => ({
|
||||
...item,
|
||||
orderBy: item.orderBy.filter((item) => item.columnName !== SIGNOZ_VALUE),
|
||||
aggregateAttribute: initialAutocompleteData,
|
||||
}));
|
||||
}
|
||||
|
||||
return query;
|
||||
},
|
||||
[currentQuery, updateAllQueriesOperators, updateQueriesData],
|
||||
);
|
||||
|
||||
const handleTabChange = useCallback(
|
||||
(type: string): void => {
|
||||
const newPanelType = type as GRAPH_TYPES;
|
||||
if (panelType === newPanelType) return;
|
||||
|
||||
const query = getUpdateQuery(newPanelType);
|
||||
|
||||
redirectWithQueryBuilderData(query, {
|
||||
[queryParamNamesMap.panelTypes]: newPanelType,
|
||||
});
|
||||
},
|
||||
[
|
||||
currentQuery,
|
||||
panelType,
|
||||
redirectWithQueryBuilderData,
|
||||
updateAllQueriesOperators,
|
||||
],
|
||||
[getUpdateQuery, panelType, redirectWithQueryBuilderData],
|
||||
);
|
||||
|
||||
useShareBuilderUrl(defaultQuery);
|
||||
|
@ -70,6 +70,7 @@ export const QueryBuilderContext = createContext<QueryBuilderContextType>({
|
||||
handleRunQuery: () => {},
|
||||
resetStagedQuery: () => {},
|
||||
updateAllQueriesOperators: () => initialQueriesMap.metrics,
|
||||
updateQueriesData: () => initialQueriesMap.metrics,
|
||||
initQueryBuilderData: () => {},
|
||||
});
|
||||
|
||||
@ -222,6 +223,22 @@ export function QueryBuilderProvider({
|
||||
[getElementWithActualOperator],
|
||||
);
|
||||
|
||||
const updateQueriesData = useCallback(
|
||||
<T extends keyof QueryBuilderData>(
|
||||
query: Query,
|
||||
type: T,
|
||||
updateCallback: (
|
||||
item: QueryBuilderData[T][number],
|
||||
index: number,
|
||||
) => QueryBuilderData[T][number],
|
||||
): Query => {
|
||||
const result = query.builder[type].map(updateCallback);
|
||||
|
||||
return { ...query, builder: { ...query.builder, [type]: result } };
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const removeQueryBuilderEntityByIndex = useCallback(
|
||||
(type: keyof QueryBuilderData, index: number) => {
|
||||
setCurrentQuery((prevState) => {
|
||||
@ -567,6 +584,7 @@ export function QueryBuilderProvider({
|
||||
handleRunQuery,
|
||||
resetStagedQuery,
|
||||
updateAllQueriesOperators,
|
||||
updateQueriesData,
|
||||
initQueryBuilderData,
|
||||
}),
|
||||
[
|
||||
@ -588,6 +606,7 @@ export function QueryBuilderProvider({
|
||||
handleRunQuery,
|
||||
resetStagedQuery,
|
||||
updateAllQueriesOperators,
|
||||
updateQueriesData,
|
||||
initQueryBuilderData,
|
||||
],
|
||||
);
|
||||
|
@ -192,6 +192,14 @@ export type QueryBuilderContextType = {
|
||||
panelType: GRAPH_TYPES,
|
||||
dataSource: DataSource,
|
||||
) => Query;
|
||||
updateQueriesData: <T extends keyof QueryBuilderData>(
|
||||
query: Query,
|
||||
type: T,
|
||||
updateCallback: (
|
||||
item: QueryBuilderData[T][number],
|
||||
index: number,
|
||||
) => QueryBuilderData[T][number],
|
||||
) => Query;
|
||||
initQueryBuilderData: (query: Query) => void;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user