mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-29 16:14:42 +00:00
feat: added dynamic variable suggestion in where clause
This commit is contained in:
parent
4fe1d1b3f7
commit
dbbd7c24ac
@ -12,6 +12,7 @@ import {
|
|||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
import { DEBOUNCE_DELAY } from 'constants/queryBuilderFilterConfig';
|
import { DEBOUNCE_DELAY } from 'constants/queryBuilderFilterConfig';
|
||||||
import { LogsExplorerShortcuts } from 'constants/shortcuts/logsExplorerShortcuts';
|
import { LogsExplorerShortcuts } from 'constants/shortcuts/logsExplorerShortcuts';
|
||||||
|
import { useGetDynamicVariables } from 'hooks/dashboard/useGetDynamicVariables';
|
||||||
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||||
import { WhereClauseConfig } from 'hooks/queryBuilder/useAutoComplete';
|
import { WhereClauseConfig } from 'hooks/queryBuilder/useAutoComplete';
|
||||||
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
|
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
|
||||||
@ -246,6 +247,8 @@ function QueryBuilderSearchV2(
|
|||||||
return false;
|
return false;
|
||||||
}, [currentState, query.aggregateAttribute?.dataType, query.dataSource]);
|
}, [currentState, query.aggregateAttribute?.dataType, query.dataSource]);
|
||||||
|
|
||||||
|
const { dynamicVariables } = useGetDynamicVariables();
|
||||||
|
|
||||||
const { data, isFetching } = useGetAggregateKeys(
|
const { data, isFetching } = useGetAggregateKeys(
|
||||||
{
|
{
|
||||||
searchText: searchValue?.split(' ')[0],
|
searchText: searchValue?.split(' ')[0],
|
||||||
@ -800,6 +803,19 @@ function QueryBuilderSearchV2(
|
|||||||
const dataType = currentFilterItem?.key?.dataType || DataTypes.String;
|
const dataType = currentFilterItem?.key?.dataType || DataTypes.String;
|
||||||
const key = DATA_TYPE_VS_ATTRIBUTE_VALUES_KEY[dataType];
|
const key = DATA_TYPE_VS_ATTRIBUTE_VALUES_KEY[dataType];
|
||||||
values.push(...(attributeValues?.payload?.[key] || []));
|
values.push(...(attributeValues?.payload?.[key] || []));
|
||||||
|
|
||||||
|
// here we want to suggest the variable name matching with the key here, we will go over the dynamic variables for the keys
|
||||||
|
const variableName = dynamicVariables.find(
|
||||||
|
(variable) =>
|
||||||
|
variable?.dynamicVariablesAttribute === currentFilterItem?.key?.key,
|
||||||
|
)?.name;
|
||||||
|
|
||||||
|
if (variableName) {
|
||||||
|
const variableValue = `$${variableName}`;
|
||||||
|
if (!values.includes(variableValue)) {
|
||||||
|
values.unshift(variableValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setDropdownOptions(
|
setDropdownOptions(
|
||||||
@ -819,6 +835,8 @@ function QueryBuilderSearchV2(
|
|||||||
searchValue,
|
searchValue,
|
||||||
suggestionsData?.payload?.attributes,
|
suggestionsData?.payload?.attributes,
|
||||||
operatorConfigKey,
|
operatorConfigKey,
|
||||||
|
currentFilterItem?.key?.key,
|
||||||
|
dynamicVariables,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// keep the query in sync with the selected tags in logs explorer page
|
// keep the query in sync with the selected tags in logs explorer page
|
||||||
|
|||||||
55
frontend/src/hooks/dashboard/useGetDynamicVariables.tsx
Normal file
55
frontend/src/hooks/dashboard/useGetDynamicVariables.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import getDashboard from 'api/dashboard/get';
|
||||||
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
|
export interface DynamicVariable extends IDashboardVariable {
|
||||||
|
dashboardName: string;
|
||||||
|
dashboardId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseGetDynamicVariablesProps {
|
||||||
|
dashboardId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetDynamicVariables = (
|
||||||
|
props?: UseGetDynamicVariablesProps,
|
||||||
|
): {
|
||||||
|
dynamicVariables: DynamicVariable[];
|
||||||
|
isLoading: boolean;
|
||||||
|
isError: boolean;
|
||||||
|
refetch: () => void;
|
||||||
|
} => {
|
||||||
|
const { dashboardId: dashboardIdFromProps } = props || {};
|
||||||
|
|
||||||
|
const { dashboardId: dashboardIdFromDashboard } = useDashboard();
|
||||||
|
|
||||||
|
const dashboardId = dashboardIdFromProps || dashboardIdFromDashboard;
|
||||||
|
|
||||||
|
const { data: dashboard, isLoading, isError, refetch } = useQuery({
|
||||||
|
queryFn: () => getDashboard({ uuid: dashboardId }),
|
||||||
|
queryKey: [REACT_QUERY_KEY.DASHBOARD_BY_ID, dashboardId],
|
||||||
|
});
|
||||||
|
|
||||||
|
const dynamicVariables = useMemo(() => {
|
||||||
|
if (!dashboard?.data?.variables) return [];
|
||||||
|
|
||||||
|
const variables: DynamicVariable[] = [];
|
||||||
|
|
||||||
|
Object.entries(dashboard.data.variables).forEach(([, variable]) => {
|
||||||
|
if (variable.type === 'DYNAMIC') {
|
||||||
|
variables.push({
|
||||||
|
...variable,
|
||||||
|
dashboardName: dashboard.data.title,
|
||||||
|
dashboardId: dashboard.uuid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return variables;
|
||||||
|
}, [dashboard]);
|
||||||
|
|
||||||
|
return { dynamicVariables, isLoading, isError, refetch };
|
||||||
|
};
|
||||||
@ -3,6 +3,7 @@ import {
|
|||||||
getTagToken,
|
getTagToken,
|
||||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||||
import { Option } from 'container/QueryBuilder/type';
|
import { Option } from 'container/QueryBuilder/type';
|
||||||
|
import { useGetDynamicVariables } from 'hooks/dashboard/useGetDynamicVariables';
|
||||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||||
import { isEmpty } from 'lodash-es';
|
import { isEmpty } from 'lodash-es';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
@ -25,10 +26,19 @@ export const useOptions = (
|
|||||||
result: string[],
|
result: string[],
|
||||||
isFetching: boolean,
|
isFetching: boolean,
|
||||||
whereClauseConfig?: WhereClauseConfig,
|
whereClauseConfig?: WhereClauseConfig,
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
): Option[] => {
|
): Option[] => {
|
||||||
const [options, setOptions] = useState<Option[]>([]);
|
const [options, setOptions] = useState<Option[]>([]);
|
||||||
const operators = useOperators(key, keys);
|
const operators = useOperators(key, keys);
|
||||||
|
|
||||||
|
// get matching dynamic variables to suggest
|
||||||
|
const { dynamicVariables } = useGetDynamicVariables();
|
||||||
|
const variableName = dynamicVariables.find(
|
||||||
|
(variable) => variable?.dynamicVariablesAttribute === key,
|
||||||
|
)?.name;
|
||||||
|
|
||||||
|
const variableAsValue = variableName ? `$${variableName}` : '';
|
||||||
|
|
||||||
const getLabel = useCallback(
|
const getLabel = useCallback(
|
||||||
(data: BaseAutocompleteData): Option['label'] =>
|
(data: BaseAutocompleteData): Option['label'] =>
|
||||||
transformStringWithPrefix({
|
transformStringWithPrefix({
|
||||||
@ -63,6 +73,11 @@ export const useOptions = (
|
|||||||
const getOptionsWithValidOperator = useCallback(
|
const getOptionsWithValidOperator = useCallback(
|
||||||
(key: string, results: string[], searchValue: string) => {
|
(key: string, results: string[], searchValue: string) => {
|
||||||
const hasAllResults = results.every((value) => result.includes(value));
|
const hasAllResults = results.every((value) => result.includes(value));
|
||||||
|
|
||||||
|
if (!isEmpty(variableAsValue)) {
|
||||||
|
results.unshift(variableAsValue);
|
||||||
|
}
|
||||||
|
|
||||||
const values = getKeyOpValue(results);
|
const values = getKeyOpValue(results);
|
||||||
|
|
||||||
return hasAllResults
|
return hasAllResults
|
||||||
@ -80,7 +95,7 @@ export const useOptions = (
|
|||||||
...values,
|
...values,
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[getKeyOpValue, result],
|
[getKeyOpValue, result, variableAsValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
const getKeyOperatorOptions = useCallback(
|
const getKeyOperatorOptions = useCallback(
|
||||||
@ -128,7 +143,10 @@ export const useOptions = (
|
|||||||
newOptions = getKeyOperatorOptions(key);
|
newOptions = getKeyOperatorOptions(key);
|
||||||
} else if (key && operator) {
|
} else if (key && operator) {
|
||||||
if (isMulti) {
|
if (isMulti) {
|
||||||
newOptions = results.map((item) => ({
|
const resultsWithVariable = isEmpty(variableAsValue)
|
||||||
|
? results
|
||||||
|
: [variableAsValue, ...results];
|
||||||
|
newOptions = resultsWithVariable.map((item) => ({
|
||||||
label: checkCommaInValue(String(item)),
|
label: checkCommaInValue(String(item)),
|
||||||
value: String(item),
|
value: String(item),
|
||||||
}));
|
}));
|
||||||
@ -161,6 +179,7 @@ export const useOptions = (
|
|||||||
getKeyOperatorOptions,
|
getKeyOperatorOptions,
|
||||||
getOptionsWithValidOperator,
|
getOptionsWithValidOperator,
|
||||||
isFetching,
|
isFetching,
|
||||||
|
variableAsValue,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
|
|||||||
@ -26,6 +26,8 @@ export const getDashboardVariables = (
|
|||||||
variablesTuple[value.name] =
|
variablesTuple[value.name] =
|
||||||
value?.type === 'DYNAMIC' &&
|
value?.type === 'DYNAMIC' &&
|
||||||
value?.allSelected &&
|
value?.allSelected &&
|
||||||
|
value?.showALLOption &&
|
||||||
|
value?.multiSelect &&
|
||||||
!value?.haveCustomValuesSelected
|
!value?.haveCustomValuesSelected
|
||||||
? '__all__'
|
? '__all__'
|
||||||
: value?.selectedValue;
|
: value?.selectedValue;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user