mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-29 06:11:59 +08:00
fix: issue of new query builder v3 (#2697)
* fix: position of where caluse is changed for metrics * fix: by default enabled for logs & traces * fix: to many api call for key on search * fix: make chip on enter for exists/nexists * fix: flickering issue on selection of option * fix: text change * fix: orderby payload issue * fix: removed replace logic * fix: responsive qb & disabled issue --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com> Co-authored-by: Ankit Nayan <ankit@signoz.io>
This commit is contained in:
parent
a0c320e47e
commit
ca3ff04f7d
@ -14,11 +14,11 @@ import {
|
|||||||
GroupByFilter,
|
GroupByFilter,
|
||||||
HavingFilter,
|
HavingFilter,
|
||||||
OperatorsSelect,
|
OperatorsSelect,
|
||||||
|
OrderByFilter,
|
||||||
ReduceToFilter,
|
ReduceToFilter,
|
||||||
} from 'container/QueryBuilder/filters';
|
} from 'container/QueryBuilder/filters';
|
||||||
import AggregateEveryFilter from 'container/QueryBuilder/filters/AggregateEveryFilter';
|
import AggregateEveryFilter from 'container/QueryBuilder/filters/AggregateEveryFilter';
|
||||||
import LimitFilter from 'container/QueryBuilder/filters/LimitFilter/LimitFilter';
|
import LimitFilter from 'container/QueryBuilder/filters/LimitFilter/LimitFilter';
|
||||||
import { OrderByFilter } from 'container/QueryBuilder/filters/OrderByFilter';
|
|
||||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryOperations';
|
import { useQueryOperations } from 'hooks/queryBuilder/useQueryOperations';
|
||||||
@ -233,6 +233,25 @@ export const Query = memo(function Query({
|
|||||||
<FilterLabel label={transformToUpperCase(query.dataSource)} />
|
<FilterLabel label={transformToUpperCase(query.dataSource)} />
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
|
{isMetricsDataSource && (
|
||||||
|
<Col span={12}>
|
||||||
|
<Row gutter={[11, 5]}>
|
||||||
|
<Col flex="5.93rem">
|
||||||
|
<OperatorsSelect
|
||||||
|
value={query.aggregateOperator}
|
||||||
|
onChange={handleChangeOperator}
|
||||||
|
operators={operators}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col flex="1 1 12.5rem">
|
||||||
|
<AggregatorFilter
|
||||||
|
onChange={handleChangeAggregatorAttribute}
|
||||||
|
query={query}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
<Col flex="1 1 20rem">
|
<Col flex="1 1 20rem">
|
||||||
<Row gutter={[11, 5]}>
|
<Row gutter={[11, 5]}>
|
||||||
{isMetricsDataSource && (
|
{isMetricsDataSource && (
|
||||||
@ -247,25 +266,26 @@ export const Query = memo(function Query({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={11}>
|
{!isMetricsDataSource && (
|
||||||
<Row gutter={[11, 5]}>
|
<Col span={11}>
|
||||||
<Col flex="5.93rem">
|
<Row gutter={[11, 5]}>
|
||||||
<OperatorsSelect
|
<Col flex="5.93rem">
|
||||||
value={query.aggregateOperator}
|
<OperatorsSelect
|
||||||
onChange={handleChangeOperator}
|
value={query.aggregateOperator}
|
||||||
operators={operators}
|
onChange={handleChangeOperator}
|
||||||
/>
|
operators={operators}
|
||||||
</Col>
|
/>
|
||||||
<Col flex="1 1 12.5rem">
|
</Col>
|
||||||
<AggregatorFilter
|
<Col flex="1 1 12.5rem">
|
||||||
onChange={handleChangeAggregatorAttribute}
|
<AggregatorFilter
|
||||||
query={query}
|
onChange={handleChangeAggregatorAttribute}
|
||||||
/>
|
query={query}
|
||||||
</Col>
|
/>
|
||||||
</Row>
|
</Col>
|
||||||
</Col>
|
</Row>
|
||||||
|
</Col>
|
||||||
<Col span={11} offset={2}>
|
)}
|
||||||
|
<Col span={11} offset={isMetricsDataSource ? 0 : 2}>
|
||||||
<Row gutter={[11, 5]}>
|
<Row gutter={[11, 5]}>
|
||||||
<Col flex="5.93rem">
|
<Col flex="5.93rem">
|
||||||
<FilterLabel
|
<FilterLabel
|
||||||
|
@ -4,6 +4,7 @@ import React, { useMemo } from 'react';
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||||
@ -41,11 +42,16 @@ function AggregateEveryFilter({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isMetricsDataSource = useMemo(
|
||||||
|
() => query.dataSource === DataSource.METRICS,
|
||||||
|
[query.dataSource],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter in seconds"
|
placeholder="Enter in seconds"
|
||||||
disabled={!query.aggregateAttribute.key}
|
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||||
style={selectStyle}
|
style={selectStyle}
|
||||||
defaultValue={query.stepInterval ?? stepInterval}
|
defaultValue={query.stepInterval ?? stepInterval}
|
||||||
onChange={(event): void => onChange(Number(event.target.value))}
|
onChange={(event): void => onChange(Number(event.target.value))}
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { Having, HavingForm } from 'types/api/queryBuilder/queryBuilderData';
|
import { Having, HavingForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
import { SelectOption } from 'types/common/select';
|
import { SelectOption } from 'types/common/select';
|
||||||
|
|
||||||
// ** Types
|
// ** Types
|
||||||
@ -214,6 +215,11 @@ export function HavingFilter({
|
|||||||
setLocalValues(transformHavingToStringValue(having));
|
setLocalValues(transformHavingToStringValue(having));
|
||||||
}, [having]);
|
}, [having]);
|
||||||
|
|
||||||
|
const isMetricsDataSource = useMemo(
|
||||||
|
() => query.dataSource === DataSource.METRICS,
|
||||||
|
[query.dataSource],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
autoClearSearchValue={false}
|
autoClearSearchValue={false}
|
||||||
@ -223,7 +229,7 @@ export function HavingFilter({
|
|||||||
tagRender={tagRender}
|
tagRender={tagRender}
|
||||||
value={localValues}
|
value={localValues}
|
||||||
data-testid="havingSelect"
|
data-testid="havingSelect"
|
||||||
disabled={!query.aggregateAttribute.key}
|
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
notFoundContent={currentFormValue.value.length === 0 ? undefined : null}
|
notFoundContent={currentFormValue.value.length === 0 ? undefined : null}
|
||||||
placeholder="Count(operation) > 5"
|
placeholder="Count(operation) > 5"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { InputNumber } from 'antd';
|
import { InputNumber } from 'antd';
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||||
|
|
||||||
@ -20,12 +21,17 @@ function LimitFilter({ onChange, query }: LimitFilterProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isMetricsDataSource = useMemo(
|
||||||
|
() => query.dataSource === DataSource.METRICS,
|
||||||
|
[query.dataSource],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
min={1}
|
min={1}
|
||||||
type="number"
|
type="number"
|
||||||
defaultValue={query.limit ?? 1}
|
defaultValue={query.limit ?? 1}
|
||||||
disabled={!query.aggregateAttribute.key}
|
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||||
style={selectStyle}
|
style={selectStyle}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import {
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
IBuilderQuery,
|
||||||
|
OrderByPayload,
|
||||||
|
} from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
export type OrderByFilterProps = {
|
export type OrderByFilterProps = {
|
||||||
query: IBuilderQuery;
|
query: IBuilderQuery;
|
||||||
onChange: (values: BaseAutocompleteData[]) => void;
|
onChange: (values: OrderByPayload[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OrderByFilterValue = {
|
export type OrderByFilterValue = {
|
||||||
|
@ -1,25 +1,28 @@
|
|||||||
import { Select, Spin } from 'antd';
|
import { Select, Spin } from 'antd';
|
||||||
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
||||||
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
import * as Papa from 'papaparse';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { OrderByPayload } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { MetricAggregateOperator } from 'types/common/queryBuilder';
|
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||||
|
import { getRemoveOrderFromValue } from '../QueryBuilderSearch/utils';
|
||||||
|
import { OrderByFilterProps } from './OrderByFilter.interfaces';
|
||||||
import {
|
import {
|
||||||
OrderByFilterProps,
|
checkIfKeyPresent,
|
||||||
OrderByFilterValue,
|
getLabelFromValue,
|
||||||
} from './OrderByFilter.interfaces';
|
mapLabelValuePairs,
|
||||||
import { getLabelFromValue, mapLabelValuePairs } from './utils';
|
orderByValueDelimiter,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
export function OrderByFilter({
|
export function OrderByFilter({
|
||||||
query,
|
query,
|
||||||
onChange,
|
onChange,
|
||||||
}: OrderByFilterProps): JSX.Element {
|
}: OrderByFilterProps): JSX.Element {
|
||||||
const [searchText, setSearchText] = useState<string>('');
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
const [selectedValue, setSelectedValue] = useState<OrderByFilterValue[]>([]);
|
const [selectedValue, setSelectedValue] = useState<string[]>([]);
|
||||||
|
|
||||||
const { data, isFetching } = useQuery(
|
const { data, isFetching } = useQuery(
|
||||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchText],
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchText],
|
||||||
@ -41,14 +44,9 @@ export function OrderByFilter({
|
|||||||
const noAggregationOptions = useMemo(
|
const noAggregationOptions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
data?.payload?.attributeKeys
|
data?.payload?.attributeKeys
|
||||||
? mapLabelValuePairs(data?.payload?.attributeKeys)
|
? mapLabelValuePairs(data?.payload?.attributeKeys).flat()
|
||||||
.flat()
|
|
||||||
.filter(
|
|
||||||
(option) =>
|
|
||||||
!getLabelFromValue(selectedValue).includes(option.label.split(' ')[0]),
|
|
||||||
)
|
|
||||||
: [],
|
: [],
|
||||||
[data?.payload?.attributeKeys, selectedValue],
|
[data?.payload?.attributeKeys],
|
||||||
);
|
);
|
||||||
|
|
||||||
const aggregationOptions = useMemo(
|
const aggregationOptions = useMemo(
|
||||||
@ -58,78 +56,56 @@ export function OrderByFilter({
|
|||||||
.concat([
|
.concat([
|
||||||
{
|
{
|
||||||
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) asc`,
|
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) asc`,
|
||||||
value: `${query.aggregateOperator}(${query.aggregateAttribute.key}) asc`,
|
value: `${query.aggregateOperator}(${query.aggregateAttribute.key})${orderByValueDelimiter}asc`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) desc`,
|
label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) desc`,
|
||||||
value: `${query.aggregateOperator}(${query.aggregateAttribute.key}) desc`,
|
value: `${query.aggregateOperator}(${query.aggregateAttribute.key})${orderByValueDelimiter}desc`,
|
||||||
},
|
},
|
||||||
])
|
]),
|
||||||
.filter(
|
[query.aggregateAttribute.key, query.aggregateOperator, query.groupBy],
|
||||||
(option) =>
|
|
||||||
!getLabelFromValue(selectedValue).includes(option.label.split(' ')[0]),
|
|
||||||
),
|
|
||||||
[
|
|
||||||
query.aggregateAttribute.key,
|
|
||||||
query.aggregateOperator,
|
|
||||||
query.groupBy,
|
|
||||||
selectedValue,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const optionsData = useMemo(
|
const optionsData = useMemo(() => {
|
||||||
() =>
|
const options =
|
||||||
query.aggregateOperator === MetricAggregateOperator.NOOP
|
query.aggregateOperator === MetricAggregateOperator.NOOP
|
||||||
? noAggregationOptions
|
? noAggregationOptions
|
||||||
: aggregationOptions,
|
: aggregationOptions;
|
||||||
[aggregationOptions, noAggregationOptions, query.aggregateOperator],
|
return options.filter(
|
||||||
);
|
(option) =>
|
||||||
|
!getLabelFromValue(selectedValue).includes(
|
||||||
|
getRemoveOrderFromValue(option.value),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}, [
|
||||||
|
aggregationOptions,
|
||||||
|
noAggregationOptions,
|
||||||
|
query.aggregateOperator,
|
||||||
|
selectedValue,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleChange = (values: OrderByFilterValue[]): void => {
|
const handleChange = (values: string[]): void => {
|
||||||
setSelectedValue(values);
|
setSelectedValue(values);
|
||||||
const orderByValues: BaseAutocompleteData[] = values?.map((item) => {
|
const orderByValues: OrderByPayload[] = values.map((item) => {
|
||||||
const iterationArray = data?.payload?.attributeKeys || query.orderBy;
|
const match = Papa.parse(item, { delimiter: '|' });
|
||||||
const existingOrderValues = iterationArray.find(
|
if (match) {
|
||||||
(group) => group.key === item.value,
|
const [columnName, order] = match.data.flat() as string[];
|
||||||
);
|
return {
|
||||||
if (existingOrderValues) {
|
columnName: checkIfKeyPresent(columnName, query.aggregateAttribute.key)
|
||||||
return existingOrderValues;
|
? '#SIGNOZ_VALUE'
|
||||||
|
: columnName,
|
||||||
|
order,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isColumn: null,
|
columnName: item,
|
||||||
key: item.value,
|
order: '',
|
||||||
dataType: null,
|
|
||||||
type: null,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
onChange(orderByValues);
|
onChange(orderByValues);
|
||||||
};
|
};
|
||||||
|
|
||||||
const values: OrderByFilterValue[] = useMemo(
|
|
||||||
() =>
|
|
||||||
query.orderBy
|
|
||||||
.filter((order) =>
|
|
||||||
query.groupBy.find(
|
|
||||||
(group) =>
|
|
||||||
order.key.includes(group.key) ||
|
|
||||||
order.key.includes(query.aggregateOperator),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map((item) => ({
|
|
||||||
label: transformStringWithPrefix({
|
|
||||||
str: item.key,
|
|
||||||
prefix: item.type || '',
|
|
||||||
condition: !item.isColumn,
|
|
||||||
}),
|
|
||||||
key: item.key,
|
|
||||||
value: item.key,
|
|
||||||
disabled: undefined,
|
|
||||||
title: undefined,
|
|
||||||
})),
|
|
||||||
[query.aggregateOperator, query.groupBy, query.orderBy],
|
|
||||||
);
|
|
||||||
|
|
||||||
const isDisabledSelect = useMemo(
|
const isDisabledSelect = useMemo(
|
||||||
() =>
|
() =>
|
||||||
!query.aggregateAttribute.key ||
|
!query.aggregateAttribute.key ||
|
||||||
@ -137,18 +113,21 @@ export function OrderByFilter({
|
|||||||
[query.aggregateAttribute.key, query.aggregateOperator],
|
[query.aggregateAttribute.key, query.aggregateOperator],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isMetricsDataSource = useMemo(
|
||||||
|
() => query.dataSource === DataSource.METRICS,
|
||||||
|
[query.dataSource],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
mode="tags"
|
mode="tags"
|
||||||
style={selectStyle}
|
style={selectStyle}
|
||||||
onSearch={handleSearchKeys}
|
onSearch={handleSearchKeys}
|
||||||
showSearch
|
showSearch
|
||||||
disabled={isDisabledSelect}
|
disabled={isMetricsDataSource && isDisabledSelect}
|
||||||
showArrow={false}
|
showArrow={false}
|
||||||
filterOption={false}
|
filterOption={false}
|
||||||
options={optionsData}
|
options={optionsData}
|
||||||
labelInValue
|
|
||||||
value={values}
|
|
||||||
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { IOption } from 'hooks/useResourceAttribute/types';
|
import { IOption } from 'hooks/useResourceAttribute/types';
|
||||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||||
|
import * as Papa from 'papaparse';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
|
||||||
import { OrderByFilterValue } from './OrderByFilter.interfaces';
|
export const orderByValueDelimiter = '|';
|
||||||
|
|
||||||
export function mapLabelValuePairs(
|
export function mapLabelValuePairs(
|
||||||
arr: BaseAutocompleteData[],
|
arr: BaseAutocompleteData[],
|
||||||
@ -17,16 +18,27 @@ export function mapLabelValuePairs(
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: `${label} asc`,
|
label: `${label} asc`,
|
||||||
value: `${value} asc`,
|
value: `${value}${orderByValueDelimiter}asc`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: `${label} desc`,
|
label: `${label} desc`,
|
||||||
value: `${value} desc`,
|
value: `${value}${orderByValueDelimiter}desc`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLabelFromValue(arr: OrderByFilterValue[]): string[] {
|
export function getLabelFromValue(arr: string[]): string[] {
|
||||||
return arr.map((value) => value.label.split(' ')[0]);
|
return arr.flat().map((item) => {
|
||||||
|
const match = Papa.parse(item, { delimiter: orderByValueDelimiter });
|
||||||
|
if (match) {
|
||||||
|
const [key] = match.data as string[];
|
||||||
|
return key[0];
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkIfKeyPresent(str: string, valueToCheck: string): boolean {
|
||||||
|
return new RegExp(`\\(${valueToCheck}\\)`).test(str);
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
export const selectStyle = { width: '100%', minWidth: '10rem' };
|
export const selectStyle = { width: '100%', minWidth: '7.7rem' };
|
||||||
|
@ -88,15 +88,15 @@ function QueryBuilderSearch({
|
|||||||
if (isExistsNotExistsOperator(searchValue)) handleKeyDown(event);
|
if (isExistsNotExistsOperator(searchValue)) handleKeyDown(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isMatricsDataSource = useMemo(
|
const isMetricsDataSource = useMemo(
|
||||||
() => query.dataSource === DataSource.METRICS,
|
() => query.dataSource === DataSource.METRICS,
|
||||||
[query.dataSource],
|
[query.dataSource],
|
||||||
);
|
);
|
||||||
|
|
||||||
const queryTags = useMemo(() => {
|
const queryTags = useMemo(() => {
|
||||||
if (!query.aggregateAttribute.key && isMatricsDataSource) return [];
|
if (!query.aggregateAttribute.key && isMetricsDataSource) return [];
|
||||||
return tags;
|
return tags;
|
||||||
}, [isMatricsDataSource, query.aggregateAttribute.key, tags]);
|
}, [isMetricsDataSource, query.aggregateAttribute.key, tags]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialTagFilters: TagFilter = { items: [], op: 'AND' };
|
const initialTagFilters: TagFilter = { items: [], op: 'AND' };
|
||||||
@ -135,7 +135,7 @@ function QueryBuilderSearch({
|
|||||||
placeholder="Search Filter"
|
placeholder="Search Filter"
|
||||||
value={queryTags}
|
value={queryTags}
|
||||||
searchValue={searchValue}
|
searchValue={searchValue}
|
||||||
disabled={isMatricsDataSource && !query.aggregateAttribute.key}
|
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||||
style={selectStyle}
|
style={selectStyle}
|
||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
|
@ -2,6 +2,8 @@ import { OPERATORS } from 'constants/queryBuilder';
|
|||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import * as Papa from 'papaparse';
|
import * as Papa from 'papaparse';
|
||||||
|
|
||||||
|
import { orderByValueDelimiter } from '../OrderByFilter/utils';
|
||||||
|
|
||||||
export const tagRegexp = /([a-zA-Z0-9_.:@$()\-/\\]+)\s*(!=|=|<=|<|>=|>|IN|NOT_IN|LIKE|NOT_LIKE|EXISTS|NOT_EXISTS|CONTAINS|NOT_CONTAINS)\s*([\s\S]*)/g;
|
export const tagRegexp = /([a-zA-Z0-9_.:@$()\-/\\]+)\s*(!=|=|<=|<|>=|>|IN|NOT_IN|LIKE|NOT_LIKE|EXISTS|NOT_EXISTS|CONTAINS|NOT_CONTAINS)\s*([\s\S]*)/g;
|
||||||
|
|
||||||
export function isInNInOperator(value: string): boolean {
|
export function isInNInOperator(value: string): boolean {
|
||||||
@ -39,12 +41,13 @@ export function getTagToken(tag: string): ITagToken {
|
|||||||
export function isExistsNotExistsOperator(value: string): boolean {
|
export function isExistsNotExistsOperator(value: string): boolean {
|
||||||
const { tagOperator } = getTagToken(value);
|
const { tagOperator } = getTagToken(value);
|
||||||
return (
|
return (
|
||||||
tagOperator === OPERATORS.NOT_EXISTS || tagOperator === OPERATORS.EXISTS
|
tagOperator?.trim() === OPERATORS.NOT_EXISTS ||
|
||||||
|
tagOperator?.trim() === OPERATORS.EXISTS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRemovePrefixFromKey(tag: string): string {
|
export function getRemovePrefixFromKey(tag: string): string {
|
||||||
return tag?.replace(/^(tag_|resource_)/, '');
|
return tag?.replace(/^(tag_|resource_)/, '').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOperatorValue(op: string): string {
|
export function getOperatorValue(op: string): string {
|
||||||
@ -106,3 +109,12 @@ export function replaceStringWithMaxLength(
|
|||||||
export function checkCommaInValue(str: string): string {
|
export function checkCommaInValue(str: string): string {
|
||||||
return str.includes(',') ? `"${str}"` : str;
|
return str.includes(',') ? `"${str}"` : str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRemoveOrderFromValue(tag: string): string {
|
||||||
|
const match = Papa.parse(tag, { delimiter: orderByValueDelimiter });
|
||||||
|
if (match) {
|
||||||
|
const [key] = match.data.flat() as string[];
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
@ -2,4 +2,5 @@ export { AggregatorFilter } from './AggregatorFilter';
|
|||||||
export { GroupByFilter } from './GroupByFilter';
|
export { GroupByFilter } from './GroupByFilter';
|
||||||
export { HavingFilter } from './HavingFilter';
|
export { HavingFilter } from './HavingFilter';
|
||||||
export { OperatorsSelect } from './OperatorsSelect';
|
export { OperatorsSelect } from './OperatorsSelect';
|
||||||
|
export { OrderByFilter } from './OrderByFilter';
|
||||||
export { ReduceToFilter } from './ReduceToFilter';
|
export { ReduceToFilter } from './ReduceToFilter';
|
||||||
|
@ -73,11 +73,9 @@ export const useAutoComplete = (query: IBuilderQuery): IAutoComplete => {
|
|||||||
return replaceStringWithMaxLength(prev, data as string[], value);
|
return replaceStringWithMaxLength(prev, data as string[], value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!isMulti && isValidTag && !isExistsNotExistsOperator(value)) {
|
if (!isMulti) {
|
||||||
handleAddTag(value);
|
if (isExistsNotExistsOperator(value)) handleAddTag(value);
|
||||||
}
|
if (isValidTag && !isExistsNotExistsOperator(value)) handleAddTag(value);
|
||||||
if (!isMulti && isValidTag && isExistsNotExistsOperator(value)) {
|
|
||||||
handleAddTag(value);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleAddTag, isMulti, isValidTag],
|
[handleAddTag, isMulti, isValidTag],
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
getTagToken,
|
getTagToken,
|
||||||
isInNInOperator,
|
isInNInOperator,
|
||||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||||
|
import debounce from 'lodash-es/debounce';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { useDebounce } from 'react-use';
|
import { useDebounce } from 'react-use';
|
||||||
@ -34,6 +35,25 @@ export const useFetchKeysAndValues = (
|
|||||||
const [keys, setKeys] = useState<BaseAutocompleteData[]>([]);
|
const [keys, setKeys] = useState<BaseAutocompleteData[]>([]);
|
||||||
const [results, setResults] = useState<string[]>([]);
|
const [results, setResults] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const searchParams = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce(
|
||||||
|
() => [
|
||||||
|
searchKey,
|
||||||
|
query.dataSource,
|
||||||
|
query.aggregateOperator,
|
||||||
|
query.aggregateAttribute.key,
|
||||||
|
],
|
||||||
|
300,
|
||||||
|
),
|
||||||
|
[
|
||||||
|
query.aggregateAttribute.key,
|
||||||
|
query.aggregateOperator,
|
||||||
|
query.dataSource,
|
||||||
|
searchKey,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
const isQueryEnabled = useMemo(
|
const isQueryEnabled = useMemo(
|
||||||
() =>
|
() =>
|
||||||
query.dataSource === DataSource.METRICS
|
query.dataSource === DataSource.METRICS
|
||||||
@ -49,13 +69,7 @@ export const useFetchKeysAndValues = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { data, isFetching, status } = useQuery(
|
const { data, isFetching, status } = useQuery(
|
||||||
[
|
[QueryBuilderKeys.GET_ATTRIBUTE_KEY, searchParams()],
|
||||||
QueryBuilderKeys.GET_ATTRIBUTE_KEY,
|
|
||||||
searchKey,
|
|
||||||
query.dataSource,
|
|
||||||
query.aggregateOperator,
|
|
||||||
query.aggregateAttribute.key,
|
|
||||||
],
|
|
||||||
async () =>
|
async () =>
|
||||||
getAggregateKeys({
|
getAggregateKeys({
|
||||||
searchText: searchKey,
|
searchText: searchKey,
|
||||||
|
@ -52,41 +52,39 @@ export const useOptions = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let newOptions: Option[] = [];
|
||||||
|
|
||||||
if (!key) {
|
if (!key) {
|
||||||
setOptions(
|
newOptions = searchValue
|
||||||
searchValue
|
? [
|
||||||
? [
|
{ label: `${searchValue} `, value: `${searchValue} ` },
|
||||||
{ label: `${searchValue} `, value: `${searchValue} ` },
|
...getOptionsFromKeys(keys),
|
||||||
...getOptionsFromKeys(keys),
|
]
|
||||||
]
|
: getOptionsFromKeys(keys);
|
||||||
: getOptionsFromKeys(keys),
|
|
||||||
);
|
|
||||||
} else if (key && !operator) {
|
} else if (key && !operator) {
|
||||||
setOptions(
|
newOptions = operators?.map((operator) => ({
|
||||||
operators?.map((operator) => ({
|
value: `${key} ${operator} `,
|
||||||
value: `${key} ${operator} `,
|
label: `${key} ${operator} `,
|
||||||
label: `${key} ${operator} `,
|
}));
|
||||||
})),
|
|
||||||
);
|
|
||||||
} else if (key && operator) {
|
} else if (key && operator) {
|
||||||
if (isMulti) {
|
if (isMulti) {
|
||||||
setOptions(
|
newOptions = results.map((item) => ({
|
||||||
results.map((item) => ({
|
label: checkCommaInValue(String(item)),
|
||||||
label: checkCommaInValue(String(item)),
|
value: String(item),
|
||||||
value: String(item),
|
}));
|
||||||
})),
|
|
||||||
);
|
|
||||||
} else if (isExist) {
|
} else if (isExist) {
|
||||||
setOptions([]);
|
newOptions = [];
|
||||||
} else if (isValidOperator) {
|
} else if (isValidOperator) {
|
||||||
const hasAllResults = results.every((value) => result.includes(value));
|
const hasAllResults = results.every((value) => result.includes(value));
|
||||||
const values = getKeyOpValue(results);
|
const values = getKeyOpValue(results);
|
||||||
const options = hasAllResults
|
newOptions = hasAllResults
|
||||||
? [{ label: searchValue, value: searchValue }]
|
? [{ label: searchValue, value: searchValue }]
|
||||||
: [{ label: searchValue, value: searchValue }, ...values];
|
: [{ label: searchValue, value: searchValue }, ...values];
|
||||||
setOptions(options);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (newOptions.length > 0) {
|
||||||
|
setOptions(newOptions);
|
||||||
|
}
|
||||||
}, [
|
}, [
|
||||||
getKeyOpValue,
|
getKeyOpValue,
|
||||||
getOptionsFromKeys,
|
getOptionsFromKeys,
|
||||||
|
@ -42,7 +42,6 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
|
|||||||
...query,
|
...query,
|
||||||
aggregateOperator: value,
|
aggregateOperator: value,
|
||||||
having: [],
|
having: [],
|
||||||
orderBy: [],
|
|
||||||
limit: null,
|
limit: null,
|
||||||
filters: { items: [], op: 'AND' },
|
filters: { items: [], op: 'AND' },
|
||||||
...(shouldResetAggregateAttribute
|
...(shouldResetAggregateAttribute
|
||||||
|
@ -33,6 +33,11 @@ export type HavingForm = Omit<Having, 'value'> & {
|
|||||||
value: string[];
|
value: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OrderByPayload = {
|
||||||
|
columnName: string;
|
||||||
|
order: string;
|
||||||
|
};
|
||||||
|
|
||||||
// Type for query builder
|
// Type for query builder
|
||||||
export type IBuilderQuery = {
|
export type IBuilderQuery = {
|
||||||
queryName: string;
|
queryName: string;
|
||||||
@ -46,7 +51,7 @@ export type IBuilderQuery = {
|
|||||||
having: Having[];
|
having: Having[];
|
||||||
limit: number | null;
|
limit: number | null;
|
||||||
stepInterval: number;
|
stepInterval: number;
|
||||||
orderBy: BaseAutocompleteData[];
|
orderBy: OrderByPayload[];
|
||||||
reduceTo: ReduceOperators;
|
reduceTo: ReduceOperators;
|
||||||
legend: string;
|
legend: string;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user