mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 14:05:55 +08:00
feat: lowercase operators support in the where clause is updated (#3657)
* feat: lowercase operators suuport in the where clause is updated * feat: options is now updated * chore: log message is updated * chore: auto completed is updated * chore: tagRegex is updated * feat: update regex to math operators and text operators * chore: operator is updated * chore: options is updated --------- Co-authored-by: Yunus A M <myounis.ar@live.com>
This commit is contained in:
parent
587034f573
commit
0e04b779a9
@ -278,23 +278,35 @@ export const QUERY_BUILDER_SEARCH_VALUES = {
|
|||||||
|
|
||||||
export const OPERATORS = {
|
export const OPERATORS = {
|
||||||
IN: 'IN',
|
IN: 'IN',
|
||||||
|
in: 'in',
|
||||||
NIN: 'NOT_IN',
|
NIN: 'NOT_IN',
|
||||||
|
not_in: 'not_in',
|
||||||
LIKE: 'LIKE',
|
LIKE: 'LIKE',
|
||||||
|
like: 'like',
|
||||||
NLIKE: 'NOT_LIKE',
|
NLIKE: 'NOT_LIKE',
|
||||||
|
not_like: 'not_like',
|
||||||
REGEX: 'REGEX',
|
REGEX: 'REGEX',
|
||||||
|
regex: 'regex',
|
||||||
NREGEX: 'NOT_REGEX',
|
NREGEX: 'NOT_REGEX',
|
||||||
|
nregex: 'not_regex',
|
||||||
'=': '=',
|
'=': '=',
|
||||||
'!=': '!=',
|
'!=': '!=',
|
||||||
EXISTS: 'EXISTS',
|
EXISTS: 'EXISTS',
|
||||||
|
exists: 'exists',
|
||||||
NOT_EXISTS: 'NOT_EXISTS',
|
NOT_EXISTS: 'NOT_EXISTS',
|
||||||
|
not_exists: 'not_exists',
|
||||||
CONTAINS: 'CONTAINS',
|
CONTAINS: 'CONTAINS',
|
||||||
|
contains: 'contains',
|
||||||
NOT_CONTAINS: 'NOT_CONTAINS',
|
NOT_CONTAINS: 'NOT_CONTAINS',
|
||||||
|
not_contains: 'not_contains',
|
||||||
'>=': '>=',
|
'>=': '>=',
|
||||||
'>': '>',
|
'>': '>',
|
||||||
'<=': '<=',
|
'<=': '<=',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
HAS: 'HAS',
|
HAS: 'HAS',
|
||||||
|
has: 'has',
|
||||||
NHAS: 'NHAS',
|
NHAS: 'NHAS',
|
||||||
|
nhas: 'nhas',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QUERY_BUILDER_OPERATORS_BY_TYPES = {
|
export const QUERY_BUILDER_OPERATORS_BY_TYPES = {
|
||||||
|
@ -3,25 +3,42 @@ import { parse } from 'papaparse';
|
|||||||
|
|
||||||
import { orderByValueDelimiter } from '../OrderByFilter/utils';
|
import { orderByValueDelimiter } from '../OrderByFilter/utils';
|
||||||
|
|
||||||
|
const operators = /=|!=|>=|>|<=|<$/;
|
||||||
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
export const tagRegexp = /^\s*(.*?)\s*(IN|NOT_IN|LIKE|NOT_LIKE|REGEX|NOT_REGEX|=|!=|EXISTS|NOT_EXISTS|CONTAINS|NOT_CONTAINS|>=|>|<=|<|HAS|NHAS)\s*(.*)$/g;
|
export const tagRegexpV1 = /^\s*(.*?)\s*(IN|in|NOT_IN|nin|LIKE|like|NOT_LIKE|nlike|REGEX|regex|NOT_REGEX|nregex|=|!=|EXISTS|exists|NOT_EXISTS|nexists|CONTAINS|contains|NOT_CONTAINS|ncontains|>=|>|<=|<|HAS|has|NHAS|nhas)\s*(.*)$/g;
|
||||||
|
|
||||||
|
export const tagRegexpV2 = /^\s*(.+?)\s+(IN|in|NOT_IN|nin|LIKE|like|NOT_LIKE|nlike|REGEX|regex|NOT_REGEX|nregex|EXISTS|exists|NOT_EXISTS|nexists|CONTAINS|contains|NOT_CONTAINS|ncontains|HAS|has|NHAS|nhas|=|!=|>=|>|<=|<)\s*(.*)$/g;
|
||||||
|
|
||||||
export function isInNInOperator(value: string): boolean {
|
export function isInNInOperator(value: string): boolean {
|
||||||
return value === OPERATORS.IN || value === OPERATORS.NIN;
|
return value === OPERATORS.IN || value === OPERATORS.NIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function endsWithOperator(inputString: string): boolean {
|
||||||
|
return operators.test(inputString);
|
||||||
|
}
|
||||||
|
|
||||||
interface ITagToken {
|
interface ITagToken {
|
||||||
tagKey: string;
|
tagKey: string;
|
||||||
tagOperator: string;
|
tagOperator: string;
|
||||||
tagValue: string[];
|
tagValue: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMatchRegex(str: string): RegExp {
|
||||||
|
if (endsWithOperator(str)) {
|
||||||
|
return tagRegexpV1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagRegexpV2;
|
||||||
|
}
|
||||||
|
|
||||||
export function getTagToken(tag: string): ITagToken {
|
export function getTagToken(tag: string): ITagToken {
|
||||||
const matches = tag?.matchAll(tagRegexp);
|
const matches = tag?.matchAll(getMatchRegex(tag));
|
||||||
const [match] = matches ? Array.from(matches) : [];
|
const [match] = matches ? Array.from(matches) : [];
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
const [, matchTagKey, matchTagOperator, matchTagValue] = match;
|
const [, matchTagKey, matchTagOperator, matchTagValue] = match;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tagKey: matchTagKey,
|
tagKey: matchTagKey,
|
||||||
tagOperator: matchTagOperator,
|
tagOperator: matchTagOperator,
|
||||||
@ -51,65 +68,11 @@ export function getRemovePrefixFromKey(tag: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getOperatorValue(op: string): string {
|
export function getOperatorValue(op: string): string {
|
||||||
switch (op) {
|
return op.toLocaleLowerCase();
|
||||||
case 'IN':
|
|
||||||
return 'in';
|
|
||||||
case 'NOT_IN':
|
|
||||||
return 'nin';
|
|
||||||
case OPERATORS.REGEX:
|
|
||||||
return 'regex';
|
|
||||||
case OPERATORS.HAS:
|
|
||||||
return 'has';
|
|
||||||
case OPERATORS.NHAS:
|
|
||||||
return 'nhas';
|
|
||||||
case OPERATORS.NREGEX:
|
|
||||||
return 'nregex';
|
|
||||||
case 'LIKE':
|
|
||||||
return 'like';
|
|
||||||
case 'NOT_LIKE':
|
|
||||||
return 'nlike';
|
|
||||||
case 'EXISTS':
|
|
||||||
return 'exists';
|
|
||||||
case 'NOT_EXISTS':
|
|
||||||
return 'nexists';
|
|
||||||
case 'CONTAINS':
|
|
||||||
return 'contains';
|
|
||||||
case 'NOT_CONTAINS':
|
|
||||||
return 'ncontains';
|
|
||||||
default:
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOperatorFromValue(op: string): string {
|
export function getOperatorFromValue(op: string): string {
|
||||||
switch (op) {
|
return op.toLocaleLowerCase();
|
||||||
case 'in':
|
|
||||||
return 'IN';
|
|
||||||
case 'nin':
|
|
||||||
return 'NOT_IN';
|
|
||||||
case 'like':
|
|
||||||
return 'LIKE';
|
|
||||||
case 'regex':
|
|
||||||
return OPERATORS.REGEX;
|
|
||||||
case 'nregex':
|
|
||||||
return OPERATORS.NREGEX;
|
|
||||||
case 'nlike':
|
|
||||||
return 'NOT_LIKE';
|
|
||||||
case 'exists':
|
|
||||||
return 'EXISTS';
|
|
||||||
case 'nexists':
|
|
||||||
return 'NOT_EXISTS';
|
|
||||||
case 'contains':
|
|
||||||
return 'CONTAINS';
|
|
||||||
case 'ncontains':
|
|
||||||
return 'NOT_CONTAINS';
|
|
||||||
case 'has':
|
|
||||||
return OPERATORS.HAS;
|
|
||||||
case 'nhas':
|
|
||||||
return OPERATORS.NHAS;
|
|
||||||
default:
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replaceStringWithMaxLength(
|
export function replaceStringWithMaxLength(
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
|
getMatchRegex,
|
||||||
getRemovePrefixFromKey,
|
getRemovePrefixFromKey,
|
||||||
getTagToken,
|
getTagToken,
|
||||||
replaceStringWithMaxLength,
|
replaceStringWithMaxLength,
|
||||||
tagRegexp,
|
|
||||||
} 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 { parse } from 'papaparse';
|
import { parse } from 'papaparse';
|
||||||
@ -33,7 +33,7 @@ export const useAutoComplete = (
|
|||||||
searchKey,
|
searchKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [key, operator, result] = useSetCurrentKeyAndOperator(searchValue, keys);
|
const [key, operator, result] = useSetCurrentKeyAndOperator(searchValue);
|
||||||
|
|
||||||
const handleSearch = (value: string): void => {
|
const handleSearch = (value: string): void => {
|
||||||
const prefixFreeValue = getRemovePrefixFromKey(getTagToken(value).tagKey);
|
const prefixFreeValue = getRemovePrefixFromKey(getTagToken(value).tagKey);
|
||||||
@ -58,7 +58,7 @@ export const useAutoComplete = (
|
|||||||
(value: string): void => {
|
(value: string): void => {
|
||||||
if (isMulti) {
|
if (isMulti) {
|
||||||
setSearchValue((prev: string) => {
|
setSearchValue((prev: string) => {
|
||||||
const matches = prev?.matchAll(tagRegexp);
|
const matches = prev?.matchAll(getMatchRegex(prev));
|
||||||
const [match] = matches ? Array.from(matches) : [];
|
const [match] = matches ? Array.from(matches) : [];
|
||||||
const [, , , matchTagValue] = match;
|
const [, , , matchTagValue] = match;
|
||||||
const data = parse(matchTagValue).data.flat();
|
const data = parse(matchTagValue).data.flat();
|
||||||
|
@ -8,23 +8,35 @@ export type OperatorType =
|
|||||||
|
|
||||||
const operatorTypeMapper: Record<string, OperatorType> = {
|
const operatorTypeMapper: Record<string, OperatorType> = {
|
||||||
[OPERATORS.IN]: 'MULTIPLY_VALUE',
|
[OPERATORS.IN]: 'MULTIPLY_VALUE',
|
||||||
|
[OPERATORS.in]: 'MULTIPLY_VALUE',
|
||||||
[OPERATORS.NIN]: 'MULTIPLY_VALUE',
|
[OPERATORS.NIN]: 'MULTIPLY_VALUE',
|
||||||
|
[OPERATORS.not_in]: 'MULTIPLY_VALUE',
|
||||||
[OPERATORS.EXISTS]: 'NON_VALUE',
|
[OPERATORS.EXISTS]: 'NON_VALUE',
|
||||||
|
[OPERATORS.exists]: 'NON_VALUE',
|
||||||
[OPERATORS.NOT_EXISTS]: 'NON_VALUE',
|
[OPERATORS.NOT_EXISTS]: 'NON_VALUE',
|
||||||
|
[OPERATORS.not_exists]: 'NON_VALUE',
|
||||||
[OPERATORS['<=']]: 'SINGLE_VALUE',
|
[OPERATORS['<=']]: 'SINGLE_VALUE',
|
||||||
[OPERATORS['<']]: 'SINGLE_VALUE',
|
[OPERATORS['<']]: 'SINGLE_VALUE',
|
||||||
[OPERATORS['>=']]: 'SINGLE_VALUE',
|
[OPERATORS['>=']]: 'SINGLE_VALUE',
|
||||||
[OPERATORS['>']]: 'SINGLE_VALUE',
|
[OPERATORS['>']]: 'SINGLE_VALUE',
|
||||||
[OPERATORS.LIKE]: 'SINGLE_VALUE',
|
[OPERATORS.LIKE]: 'SINGLE_VALUE',
|
||||||
|
[OPERATORS.like]: 'SINGLE_VALUE',
|
||||||
[OPERATORS.NLIKE]: 'SINGLE_VALUE',
|
[OPERATORS.NLIKE]: 'SINGLE_VALUE',
|
||||||
|
[OPERATORS.not_like]: 'SINGLE_VALUE',
|
||||||
[OPERATORS.REGEX]: 'SINGLE_VALUE',
|
[OPERATORS.REGEX]: 'SINGLE_VALUE',
|
||||||
|
[OPERATORS.regex]: 'SINGLE_VALUE',
|
||||||
[OPERATORS.NREGEX]: 'SINGLE_VALUE',
|
[OPERATORS.NREGEX]: 'SINGLE_VALUE',
|
||||||
|
[OPERATORS.nregex]: 'SINGLE_VALUE',
|
||||||
[OPERATORS.CONTAINS]: 'SINGLE_VALUE',
|
[OPERATORS.CONTAINS]: 'SINGLE_VALUE',
|
||||||
|
[OPERATORS.contains]: 'SINGLE_VALUE',
|
||||||
[OPERATORS.NOT_CONTAINS]: 'SINGLE_VALUE',
|
[OPERATORS.NOT_CONTAINS]: 'SINGLE_VALUE',
|
||||||
|
[OPERATORS.not_contains]: 'SINGLE_VALUE',
|
||||||
[OPERATORS['=']]: 'SINGLE_VALUE',
|
[OPERATORS['=']]: 'SINGLE_VALUE',
|
||||||
[OPERATORS['!=']]: 'SINGLE_VALUE',
|
[OPERATORS['!=']]: 'SINGLE_VALUE',
|
||||||
[OPERATORS.HAS]: 'SINGLE_VALUE',
|
[OPERATORS.HAS]: 'SINGLE_VALUE',
|
||||||
|
[OPERATORS.has]: 'SINGLE_VALUE',
|
||||||
[OPERATORS.NHAS]: 'SINGLE_VALUE',
|
[OPERATORS.NHAS]: 'SINGLE_VALUE',
|
||||||
|
[OPERATORS.nhas]: 'SINGLE_VALUE',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useOperatorType = (operator: string): OperatorType =>
|
export const useOperatorType = (operator: string): OperatorType =>
|
||||||
|
@ -81,8 +81,8 @@ export const useOptions = (
|
|||||||
const getKeyOperatorOptions = useCallback(
|
const getKeyOperatorOptions = useCallback(
|
||||||
(key: string) => {
|
(key: string) => {
|
||||||
const operatorsOptions = operators?.map((operator) => ({
|
const operatorsOptions = operators?.map((operator) => ({
|
||||||
value: `${key} ${operator} `,
|
value: `${key} ${operator.toLowerCase()} `,
|
||||||
label: `${key} ${operator} `,
|
label: `${key} ${operator.toLowerCase()} `,
|
||||||
}));
|
}));
|
||||||
if (whereClauseConfig) {
|
if (whereClauseConfig) {
|
||||||
return [
|
return [
|
||||||
@ -148,22 +148,24 @@ export const useOptions = (
|
|||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
(
|
options
|
||||||
options.filter(
|
.filter(
|
||||||
(option, index, self) =>
|
(option, index, self) =>
|
||||||
index ===
|
index ===
|
||||||
self.findIndex(
|
self.findIndex(
|
||||||
(o) => o.label === option.label && o.value === option.value, // to remove duplicate & empty options from list
|
(o) => o.label === option.label && o.value === option.value, // to remove duplicate & empty options from list
|
||||||
) && option.value !== '',
|
) && option.value !== '',
|
||||||
) || []
|
)
|
||||||
).map((option) => {
|
.map((option) => {
|
||||||
const { tagValue } = getTagToken(searchValue);
|
const { tagValue } = getTagToken(searchValue);
|
||||||
if (isMulti) {
|
if (isMulti) {
|
||||||
return {
|
return {
|
||||||
...option,
|
...option,
|
||||||
selected: tagValue
|
selected: Array.isArray(tagValue)
|
||||||
.filter((i) => i.trim().replace(/^\s+/, '') === option.value)
|
? tagValue
|
||||||
.includes(option.value),
|
?.filter((i) => i.trim().replace(/^\s+/, '') === option.value)
|
||||||
|
?.includes(option.value)
|
||||||
|
: String(tagValue).includes(option.value),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return option;
|
return option;
|
||||||
|
@ -1,32 +1,24 @@
|
|||||||
import {
|
import { getTagToken } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||||
getRemovePrefixFromKey,
|
import { useMemo, useRef } from 'react';
|
||||||
getTagToken,
|
|
||||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
|
||||||
|
|
||||||
type ICurrentKeyAndOperator = [string, string, string[]];
|
type ICurrentKeyAndOperator = [string, string, string[]];
|
||||||
|
|
||||||
export const useSetCurrentKeyAndOperator = (
|
export const useSetCurrentKeyAndOperator = (
|
||||||
value: string,
|
value: string,
|
||||||
keys: BaseAutocompleteData[],
|
|
||||||
): ICurrentKeyAndOperator => {
|
): ICurrentKeyAndOperator => {
|
||||||
const [key, operator, result] = useMemo(() => {
|
const keyRef = useRef<string>('');
|
||||||
let key = '';
|
const operatorRef = useRef<string>('');
|
||||||
let operator = '';
|
|
||||||
|
const result = useMemo(() => {
|
||||||
let result: string[] = [];
|
let result: string[] = [];
|
||||||
const { tagKey, tagOperator, tagValue } = getTagToken(value);
|
const { tagKey, tagOperator, tagValue } = getTagToken(value);
|
||||||
const isSuggestKey = keys?.some(
|
|
||||||
(el) => el?.key === getRemovePrefixFromKey(tagKey),
|
keyRef.current = tagKey || '';
|
||||||
);
|
operatorRef.current = tagOperator || '';
|
||||||
if (isSuggestKey || keys.length === 0) {
|
|
||||||
key = tagKey || '';
|
|
||||||
operator = tagOperator || '';
|
|
||||||
result = tagValue || [];
|
result = tagValue || [];
|
||||||
}
|
|
||||||
|
|
||||||
return [key, operator, result];
|
return result;
|
||||||
}, [value, keys]);
|
}, [value]);
|
||||||
|
|
||||||
return [key, operator, result];
|
return [keyRef.current, operatorRef.current, result];
|
||||||
};
|
};
|
||||||
|
@ -74,6 +74,7 @@ export const useTag = (
|
|||||||
const handleAddTag = useCallback(
|
const handleAddTag = useCallback(
|
||||||
(value: string): void => {
|
(value: string): void => {
|
||||||
const { tagKey } = getTagToken(value);
|
const { tagKey } = getTagToken(value);
|
||||||
|
|
||||||
const [key, id] = tagKey.split('-');
|
const [key, id] = tagKey.split('-');
|
||||||
|
|
||||||
if (id === 'custom') {
|
if (id === 'custom') {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user