mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 09:05:55 +08:00
fix: custom where clause value (#3209)
* fix: custom where clause value * fix: operations * fix: return suggestions for body --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
872759f579
commit
2c5c972801
@ -1,5 +1,9 @@
|
||||
import { Button } from 'antd';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import {
|
||||
initialQueriesMap,
|
||||
OPERATORS,
|
||||
PANEL_TYPES,
|
||||
} from 'constants/queryBuilder';
|
||||
import ExplorerOrderBy from 'container/ExplorerOrderBy';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { OrderByFilterProps } from 'container/QueryBuilder/filters/OrderByFilter/OrderByFilter.interfaces';
|
||||
@ -30,6 +34,10 @@ function LogExplorerQuerySection(): JSX.Element {
|
||||
const isTable = panelTypes === PANEL_TYPES.TABLE;
|
||||
const config: QueryBuilderProps['filterConfigs'] = {
|
||||
stepInterval: { isHidden: isTable, isDisabled: true },
|
||||
filters: {
|
||||
customKey: 'body',
|
||||
customOp: OPERATORS.CONTAINS,
|
||||
},
|
||||
};
|
||||
|
||||
return config;
|
||||
|
@ -1,10 +1,18 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { WhereClauseConfig } from 'hooks/queryBuilder/useAutoComplete';
|
||||
import { ReactNode } from 'react';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { OrderByFilterProps } from './filters/OrderByFilter/OrderByFilter.interfaces';
|
||||
|
||||
type FilterConfigs = {
|
||||
[Key in keyof Omit<IBuilderQuery, 'filters'>]: {
|
||||
isHidden: boolean;
|
||||
isDisabled: boolean;
|
||||
};
|
||||
} & { filters: WhereClauseConfig };
|
||||
|
||||
export type QueryBuilderConfig =
|
||||
| {
|
||||
queryVariant: 'static';
|
||||
@ -16,8 +24,6 @@ export type QueryBuilderProps = {
|
||||
config?: QueryBuilderConfig;
|
||||
panelType: PANEL_TYPES;
|
||||
actions?: ReactNode;
|
||||
filterConfigs?: Partial<
|
||||
Record<keyof IBuilderQuery, { isHidden: boolean; isDisabled: boolean }>
|
||||
>;
|
||||
filterConfigs?: Partial<FilterConfigs>;
|
||||
queryComponents?: { renderOrderBy?: (props: OrderByFilterProps) => ReactNode };
|
||||
};
|
||||
|
@ -305,7 +305,11 @@ export const Query = memo(function Query({
|
||||
</Col>
|
||||
)}
|
||||
<Col flex="1">
|
||||
<QueryBuilderSearch query={query} onChange={handleChangeTagFilters} />
|
||||
<QueryBuilderSearch
|
||||
query={query}
|
||||
onChange={handleChangeTagFilters}
|
||||
whereClauseConfig={filterConfigs?.filters}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { Select, Spin, Tag, Tooltip } from 'antd';
|
||||
import { useAutoComplete } from 'hooks/queryBuilder/useAutoComplete';
|
||||
import {
|
||||
useAutoComplete,
|
||||
WhereClauseConfig,
|
||||
} from 'hooks/queryBuilder/useAutoComplete';
|
||||
import { useFetchKeysAndValues } from 'hooks/queryBuilder/useFetchKeysAndValues';
|
||||
import {
|
||||
KeyboardEvent,
|
||||
@ -31,6 +34,7 @@ import {
|
||||
function QueryBuilderSearch({
|
||||
query,
|
||||
onChange,
|
||||
whereClauseConfig,
|
||||
}: QueryBuilderSearchProps): JSX.Element {
|
||||
const {
|
||||
updateTag,
|
||||
@ -45,7 +49,7 @@ function QueryBuilderSearch({
|
||||
isFetching,
|
||||
setSearchKey,
|
||||
searchKey,
|
||||
} = useAutoComplete(query);
|
||||
} = useAutoComplete(query, whereClauseConfig);
|
||||
|
||||
const { sourceKeys, handleRemoveSourceKey } = useFetchKeysAndValues(
|
||||
searchValue,
|
||||
@ -169,7 +173,7 @@ function QueryBuilderSearch({
|
||||
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<Select.Option key={option.label} value={option.label}>
|
||||
<Select.Option key={option.label} value={option.value}>
|
||||
{option.label}
|
||||
{option.selected && <StyledCheckOutlined />}
|
||||
</Select.Option>
|
||||
@ -181,8 +185,13 @@ function QueryBuilderSearch({
|
||||
interface QueryBuilderSearchProps {
|
||||
query: IBuilderQuery;
|
||||
onChange: (value: TagFilter) => void;
|
||||
whereClauseConfig?: WhereClauseConfig;
|
||||
}
|
||||
|
||||
QueryBuilderSearch.defaultProps = {
|
||||
whereClauseConfig: undefined,
|
||||
};
|
||||
|
||||
export interface CustomTagProps {
|
||||
label: ReactNode;
|
||||
value: string;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {
|
||||
getRemovePrefixFromKey,
|
||||
getTagToken,
|
||||
isExistsNotExistsOperator,
|
||||
replaceStringWithMaxLength,
|
||||
tagRegexp,
|
||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||
@ -16,7 +15,15 @@ import { useSetCurrentKeyAndOperator } from './useSetCurrentKeyAndOperator';
|
||||
import { useTag } from './useTag';
|
||||
import { useTagValidation } from './useTagValidation';
|
||||
|
||||
export const useAutoComplete = (query: IBuilderQuery): IAutoComplete => {
|
||||
export type WhereClauseConfig = {
|
||||
customKey: string;
|
||||
customOp: string;
|
||||
};
|
||||
|
||||
export const useAutoComplete = (
|
||||
query: IBuilderQuery,
|
||||
whereClauseConfig?: WhereClauseConfig,
|
||||
): IAutoComplete => {
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
const [searchKey, setSearchKey] = useState<string>('');
|
||||
|
||||
@ -40,11 +47,11 @@ export const useAutoComplete = (query: IBuilderQuery): IAutoComplete => {
|
||||
);
|
||||
|
||||
const { handleAddTag, handleClearTag, tags, updateTag } = useTag(
|
||||
key,
|
||||
isValidTag,
|
||||
handleSearch,
|
||||
query,
|
||||
setSearchKey,
|
||||
whereClauseConfig,
|
||||
);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
@ -59,11 +66,10 @@ export const useAutoComplete = (query: IBuilderQuery): IAutoComplete => {
|
||||
});
|
||||
}
|
||||
if (!isMulti) {
|
||||
if (isExistsNotExistsOperator(value)) handleAddTag(value);
|
||||
if (isValidTag && !isExistsNotExistsOperator(value)) handleAddTag(value);
|
||||
handleAddTag(value);
|
||||
}
|
||||
},
|
||||
[handleAddTag, isMulti, isValidTag],
|
||||
[handleAddTag, isMulti],
|
||||
);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
@ -102,6 +108,7 @@ export const useAutoComplete = (query: IBuilderQuery): IAutoComplete => {
|
||||
isExist,
|
||||
results,
|
||||
result,
|
||||
whereClauseConfig,
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -7,8 +7,11 @@ import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import { WhereClauseConfig } from './useAutoComplete';
|
||||
import { useOperators } from './useOperators';
|
||||
|
||||
export const WHERE_CLAUSE_CUSTOM_SUFFIX = '-custom';
|
||||
|
||||
export const useOptions = (
|
||||
key: string,
|
||||
keys: BaseAutocompleteData[],
|
||||
@ -19,6 +22,7 @@ export const useOptions = (
|
||||
isExist: boolean,
|
||||
results: string[],
|
||||
result: string[],
|
||||
whereClauseConfig?: WhereClauseConfig,
|
||||
): Option[] => {
|
||||
const [options, setOptions] = useState<Option[]>([]);
|
||||
const operators = useOperators(key, keys);
|
||||
@ -51,21 +55,64 @@ export const useOptions = (
|
||||
[key, operator],
|
||||
);
|
||||
|
||||
const getOptionsWithValidOperator = useCallback(
|
||||
(key: string, results: string[], searchValue: string) => {
|
||||
const hasAllResults = results.every((value) => result.includes(value));
|
||||
const values = getKeyOpValue(results);
|
||||
|
||||
return hasAllResults
|
||||
? [
|
||||
{
|
||||
label: searchValue,
|
||||
value: searchValue,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
label: searchValue,
|
||||
value: searchValue,
|
||||
},
|
||||
...values,
|
||||
];
|
||||
},
|
||||
[getKeyOpValue, result],
|
||||
);
|
||||
|
||||
const getKeyOperatorOptions = useCallback(
|
||||
(key: string) => {
|
||||
const operatorsOptions = operators?.map((operator) => ({
|
||||
value: `${key} ${operator} `,
|
||||
label: `${key} ${operator} `,
|
||||
}));
|
||||
if (whereClauseConfig) {
|
||||
return [
|
||||
{
|
||||
label: `${searchValue} `,
|
||||
value: `${searchValue}${WHERE_CLAUSE_CUSTOM_SUFFIX}`,
|
||||
},
|
||||
...operatorsOptions,
|
||||
];
|
||||
}
|
||||
return operatorsOptions;
|
||||
},
|
||||
[operators, searchValue, whereClauseConfig],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let newOptions: Option[] = [];
|
||||
|
||||
if (!key) {
|
||||
newOptions = searchValue
|
||||
? [
|
||||
{ label: `${searchValue} `, value: `${searchValue} ` },
|
||||
{
|
||||
label: `${searchValue} `,
|
||||
value: `${searchValue} `,
|
||||
},
|
||||
...getOptionsFromKeys(keys),
|
||||
]
|
||||
: getOptionsFromKeys(keys);
|
||||
} else if (key && !operator) {
|
||||
newOptions = operators?.map((operator) => ({
|
||||
value: `${key} ${operator} `,
|
||||
label: `${key} ${operator} `,
|
||||
}));
|
||||
newOptions = getKeyOperatorOptions(key);
|
||||
} else if (key && operator) {
|
||||
if (isMulti) {
|
||||
newOptions = results.map((item) => ({
|
||||
@ -75,17 +122,14 @@ export const useOptions = (
|
||||
} else if (isExist) {
|
||||
newOptions = [];
|
||||
} else if (isValidOperator) {
|
||||
const hasAllResults = results.every((value) => result.includes(value));
|
||||
const values = getKeyOpValue(results);
|
||||
newOptions = hasAllResults
|
||||
? [{ label: searchValue, value: searchValue }]
|
||||
: [{ label: searchValue, value: searchValue }, ...values];
|
||||
newOptions = getOptionsWithValidOperator(key, results, searchValue);
|
||||
}
|
||||
}
|
||||
if (newOptions.length > 0) {
|
||||
setOptions(newOptions);
|
||||
}
|
||||
}, [
|
||||
whereClauseConfig,
|
||||
getKeyOpValue,
|
||||
getOptionsFromKeys,
|
||||
isExist,
|
||||
@ -98,6 +142,8 @@ export const useOptions = (
|
||||
result,
|
||||
results,
|
||||
searchValue,
|
||||
getKeyOperatorOptions,
|
||||
getOptionsWithValidOperator,
|
||||
]);
|
||||
|
||||
return useMemo(
|
||||
|
@ -63,9 +63,18 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
|
||||
const getNewListOfAdditionalFilters = useCallback(
|
||||
(dataSource: DataSource): string[] => {
|
||||
const additionalFiltersKeys: (keyof Pick<
|
||||
IBuilderQuery,
|
||||
'orderBy' | 'limit' | 'having' | 'stepInterval'
|
||||
>)[] = ['having', 'limit', 'orderBy', 'stepInterval'];
|
||||
|
||||
const result: string[] = mapOfFilters[dataSource].reduce<string[]>(
|
||||
(acc, item) => {
|
||||
if (filterConfigs && filterConfigs[item.field]?.isHidden) {
|
||||
if (
|
||||
filterConfigs &&
|
||||
filterConfigs[item.field as typeof additionalFiltersKeys[number]]
|
||||
?.isHidden
|
||||
) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
|
@ -15,17 +15,14 @@ export const useSetCurrentKeyAndOperator = (
|
||||
let key = '';
|
||||
let operator = '';
|
||||
let result: string[] = [];
|
||||
|
||||
if (value) {
|
||||
const { tagKey, tagOperator, tagValue } = getTagToken(value);
|
||||
const isSuggestKey = keys?.some(
|
||||
(el) => el?.key === getRemovePrefixFromKey(tagKey),
|
||||
);
|
||||
if (isSuggestKey || keys.length === 0) {
|
||||
key = tagKey || '';
|
||||
operator = tagOperator || '';
|
||||
result = tagValue || [];
|
||||
}
|
||||
const { tagKey, tagOperator, tagValue } = getTagToken(value);
|
||||
const isSuggestKey = keys?.some(
|
||||
(el) => el?.key === getRemovePrefixFromKey(tagKey),
|
||||
);
|
||||
if (isSuggestKey || keys.length === 0) {
|
||||
key = tagKey || '';
|
||||
operator = tagOperator || '';
|
||||
result = tagValue || [];
|
||||
}
|
||||
|
||||
return [key, operator, result];
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {
|
||||
getOperatorFromValue,
|
||||
getTagToken,
|
||||
isExistsNotExistsOperator,
|
||||
isInNInOperator,
|
||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||
@ -8,6 +9,8 @@ import * as Papa from 'papaparse';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { WhereClauseConfig } from './useAutoComplete';
|
||||
|
||||
type IUseTag = {
|
||||
handleAddTag: (value: string) => void;
|
||||
handleClearTag: (value: string) => void;
|
||||
@ -24,11 +27,11 @@ type IUseTag = {
|
||||
*/
|
||||
|
||||
export const useTag = (
|
||||
key: string,
|
||||
isValidTag: boolean,
|
||||
handleSearch: (value: string) => void,
|
||||
query: IBuilderQuery,
|
||||
setSearchKey: (value: string) => void,
|
||||
whereClauseConfig?: WhereClauseConfig,
|
||||
): IUseTag => {
|
||||
const initTagsData = useMemo(
|
||||
() =>
|
||||
@ -57,15 +60,31 @@ export const useTag = (
|
||||
* Adds a new tag to the tag list.
|
||||
* @param {string} value - The tag value to be added.
|
||||
*/
|
||||
|
||||
const handleAddTag = useCallback(
|
||||
(value: string): void => {
|
||||
const { tagKey } = getTagToken(value);
|
||||
const [key, id] = tagKey.split('-');
|
||||
|
||||
if (id === 'custom') {
|
||||
const customValue = whereClauseConfig
|
||||
? `${whereClauseConfig.customKey} ${whereClauseConfig.customOp} ${key}`
|
||||
: '';
|
||||
setTags((prevTags) =>
|
||||
prevTags.includes(customValue) ? prevTags : [...prevTags, customValue],
|
||||
);
|
||||
handleSearch('');
|
||||
setSearchKey('');
|
||||
return;
|
||||
}
|
||||
|
||||
if ((value && key && isValidTag) || isExistsNotExistsOperator(value)) {
|
||||
setTags((prevTags) => [...prevTags, value]);
|
||||
handleSearch('');
|
||||
setSearchKey('');
|
||||
}
|
||||
},
|
||||
[key, isValidTag, handleSearch, setSearchKey],
|
||||
[whereClauseConfig, isValidTag, handleSearch, setSearchKey],
|
||||
);
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user