Compare commits

..

86 Commits

Author SHA1 Message Date
ahrefabhi
070decb102 fix(query): added fix for multi value context and in-place replacement 2025-07-01 01:51:22 +05:30
ahrefabhi
0fbe60d68f feat: enhance IQueryPair to support multi-value operators and update query context handling 2025-06-29 17:26:43 +05:30
SagarRajput-7
7e877790d0 feat: new qb selectedfields changes for logs and traces 2025-06-27 18:36:09 +05:30
ahrefabhi
3b2d3c3849 fix: simplify condition for wrapping string values in quotes and comment out query context display 2025-06-27 18:29:28 +05:30
ahrefabhi
815d3b8bc0 feat: enhance query processing to support negation context and improve space handling 2025-06-27 18:29:28 +05:30
ahrefabhi
e529ad8d57 chore: added grammer parity for frontend grammer with main grammer 2025-06-27 18:29:28 +05:30
ahrefabhi
c8f36de6fa feat: add support for negation context in query processing 2025-06-27 18:29:28 +05:30
ahrefabhi
85caefa945 feat: enhance query context to support detection of values wrapped in quotes 2025-06-27 18:29:28 +05:30
ahrefabhi
62d8dd929a chore: removed IS_NULL and IS_NOT_NULL and added NOT_EXISTS 2025-06-27 18:29:28 +05:30
ahrefabhi
afceff33d6 feat: added IS_NULL and IS_NOT_NULL operators and fixed support for not value operators 2025-06-27 18:29:28 +05:30
ahrefabhi
ed77c6abd0 chore: updated grammer for value, added parsetree for finding current context 2025-06-27 18:29:27 +05:30
SagarRajput-7
5575334893 fix: implemented the filter retention across view switch in explorer pages 2025-06-27 18:29:27 +05:30
SagarRajput-7
137a3f6d27 fix: added metricName to the metric where clause keys api 2025-06-27 18:29:27 +05:30
SagarRajput-7
c332a3c48a fix: removed hadrcoded values suggestion for Having Filter 2025-06-27 18:29:27 +05:30
SagarRajput-7
84ff35100a fix: fixed infinite loop of states around operator 2025-06-27 18:29:27 +05:30
SagarRajput-7
67ce050f53 fix: handled qb - order, group and having's vertical expansion 2025-06-27 18:29:27 +05:30
SagarRajput-7
ceefe50d82 fix: removed noop from suggestions and default values 2025-06-27 18:29:26 +05:30
SagarRajput-7
04c9e852e6 fix: fixed metric having clause and traces order sorting 2025-06-27 18:29:26 +05:30
SagarRajput-7
97cd377fa6 feat: new query builder misc fixes (#8359)
* feat: qb fixes

* feat: fixed handlerunquery props

* feat: fixes logs list order by

* feat: fix logs order by issue

* feat: safety check and order by correction

* feat: updated version in new create dashboards

* feat: added new formatOptions for table and fixed the pie chart plotting

* feat: keyboard shortcut overriding issue and pie ch correction in dashboard views

* feat: fixed dashboard data state management across datasource * paneltypes

* feat: fixed explorer pages data management issues

* feat: integrated new backend payload/request diff, to the UI types

* feat: fixed the collapse behaviour of QB - queries

* feat: fix order by and default aggregation to count()
2025-06-27 18:29:26 +05:30
SagarRajput-7
157213defc feat: resolved conflicts 2025-06-27 18:29:25 +05:30
SagarRajput-7
8092df8961 Query builder misc - fixes (#8295)
* feat: trace and logs explorer fixes

* fix: ui fixes

* fix: handle multi arg aggregation

* feat: explorer pages fixes

* feat: added fixes for order by for datasource

* feat: metric order by issue

* feat: support for paneltype selectedview tab switch

* feat: qb v2 compatiblity with url's composite query

* feat: conversion fixes

* feat: where clause and aggregation fix

---------

Co-authored-by: Yunus M <myounis.ar@live.com>
2025-06-27 18:29:25 +05:30
Yunus M
3c895981d9 feat: fetch more keys is complete list not already fetched 2025-06-27 18:29:25 +05:30
SagarRajput-7
a058dac45b feat: query_range migration from v3/v4 -> v5 (#8192)
* feat: query_range migration from v3/v4 -> v5

* feat: cleanup files

* feat: cleanup code

* feat: metric payload improvements

* feat: metric payload improvements

* feat: data retention and qb v2 for dashboard cleanup

* feat: corrected datasource change daata updatation in qb v2

* feat: fix value panel plotting with new query v5

* feat: alert migration

* feat: fixed aggregation css

* feat: explorer pages migration

* feat: trace and logs explorer fixes
2025-06-27 18:29:25 +05:30
Yunus M
a18106f5d8 fix: responsiveness issues 2025-06-27 18:29:24 +05:30
Yunus M
ea88177936 feat: where clause key updates 2025-06-27 18:29:24 +05:30
Yunus M
84a17dd376 feat: update styles for light mode 2025-06-27 18:29:24 +05:30
Yunus M
f699773aec feat: show errors 2025-06-27 18:29:24 +05:30
Yunus M
d46d1a0f24 feat: update context and show suggestions on select 2025-06-27 18:29:24 +05:30
Yunus M
a3b66935d8 feat: add a space after selecting a value from suggestion 2025-06-27 18:29:24 +05:30
Yunus M
1f8c97cd5b feat: improve suggestion ux in query search 2025-06-27 18:29:24 +05:30
Yunus M
2659e03564 feat: ui improvements 2025-06-27 18:29:24 +05:30
Yunus M
121696c1d7 feat: handle close on blur 2025-06-27 18:29:24 +05:30
Yunus M
20be9dd600 feat: query search component clean up 2025-06-27 18:29:24 +05:30
Yunus M
45e4c65c9f feat: handle having option autocomplete ux 2025-06-27 18:29:24 +05:30
Yunus M
b7490fcf68 feat: disable clicking on placeholder items in suggestions 2025-06-27 18:29:24 +05:30
Yunus M
4bfd4e536c feat: improve having suggestions 2025-06-27 18:29:24 +05:30
Yunus M
f7d5a26403 feat: handle add ons 2025-06-27 18:29:24 +05:30
Yunus M
f87594243e feat: handle list panel type options 2025-06-27 18:29:24 +05:30
Yunus M
dacc3d6d9e feat: pass index to query addons 2025-06-27 18:29:24 +05:30
Yunus M
6b28ec2f7f feat: update qb elements based on panel type 2025-06-27 18:29:24 +05:30
Yunus M
9b757af028 feat: hide extra qb elements 2025-06-27 18:29:24 +05:30
Yunus M
ce87bcae71 feat: use qb-v2 in explorers and alerts 2025-06-27 18:29:24 +05:30
Yunus M
25fb8b6561 feat: update explorer views 2025-06-27 18:29:24 +05:30
Yunus M
428a16326a feat: update logs, metrics and traces qb 2025-06-27 18:29:24 +05:30
Yunus M
78fec2188d feat: query builder layout updates 2025-06-27 18:29:24 +05:30
Yunus M
c5650cc131 fix: minor fixes 2025-06-27 18:29:24 +05:30
Yunus M
f67213096c feat: create separate containers for traces, logs and metrics qbs 2025-06-27 18:29:24 +05:30
Yunus M
d71f85a8ec feat: metrics qb 2025-06-27 18:29:24 +05:30
Yunus M
9335261314 fix: update dropdown css 2025-06-27 18:29:24 +05:30
Yunus M
0f5c54cabb feat: remove () from suggestions 2025-06-27 18:29:24 +05:30
Yunus M
0204337396 feat: handle parenthesis and conjunction operators 2025-06-27 18:29:24 +05:30
Yunus M
8101fef874 feat: support multiple having key value pairs 2025-06-27 18:29:24 +05:30
Yunus M
2d223fe9e8 feat: move state to context 2025-06-27 18:29:24 +05:30
Yunus M
de464e6042 feat: handle having options creation 2025-06-27 18:29:24 +05:30
Yunus M
ea42e4db6b feat: hide already used variables 2025-06-27 18:29:24 +05:30
Yunus M
2b5d2f0061 fix: show operator suggestions only on manual trigger or valid key 2025-06-27 18:29:24 +05:30
Yunus M
a013cc0fd3 fix: handle autocomplete 2025-06-27 18:29:24 +05:30
Yunus M
e68d860adf fix: update styles 2025-06-27 18:29:23 +05:30
Yunus M
9ed93ae5ac fix: update css 2025-06-27 18:29:23 +05:30
Yunus M
9989af10d6 feat: handle multie select functions 2025-06-27 18:29:23 +05:30
Yunus M
1bc89c9d1a feat: handle field suggestions for aggregate operators 2025-06-27 18:29:23 +05:30
Yunus M
3fbe111bc0 feat: support aggregation function with values 2025-06-27 18:29:23 +05:30
Yunus M
c449d1da8e feat: add groupBy, having, order by, limit and legend format 2025-06-27 18:29:23 +05:30
Yunus M
4635da0ee8 feat: handle multie select values better 2025-06-27 18:29:23 +05:30
Yunus M
67453e27f7 feat: improve suggestions 2025-06-27 18:29:23 +05:30
Yunus M
fdcc6a6c92 feat: console log context based on cursor position 2025-06-27 18:29:23 +05:30
Yunus M
62c71e6306 fix: handle . notation keywords better 2025-06-27 18:29:23 +05:30
Yunus M
e2e535eaca feat: remove card container above where clause 2025-06-27 18:29:23 +05:30
Yunus M
2520718afb feat: use new qb in logs explorer 2025-06-27 18:29:23 +05:30
Yunus M
0ffa666903 feat: handle parenthesis 2025-06-27 18:29:23 +05:30
Yunus M
c653e83461 feat: handle value selection 2025-06-27 18:29:23 +05:30
Yunus M
b80cf96faf feat: styling updates 2025-06-27 18:29:23 +05:30
Yunus M
a2126ad22c feat: handle string and number values correctly 2025-06-27 18:29:23 +05:30
Yunus M
5a75df30e2 feat: handle async value fetching 2025-06-27 18:29:23 +05:30
Yunus M
aeca98b6aa feat: update the context with additonal properties 2025-06-27 18:29:23 +05:30
Yunus M
53b31ae516 feat: styling updates 2025-06-27 18:29:23 +05:30
Yunus M
209828de01 feat: update theme and syntax highlighting 2025-06-27 18:29:23 +05:30
Yunus M
491a0140e3 feat: handle context switch 2025-06-27 18:29:23 +05:30
Yunus M
b9494a3375 feat: handle multiple spaces 2025-06-27 18:29:23 +05:30
Yunus M
d4b379ccc0 feat: integrate the apis 2025-06-27 18:29:23 +05:30
Yunus M
a7ff27d30c feat: update context logic and return auto-suggestions based on context 2025-06-27 18:29:23 +05:30
Yunus M
6008e8df72 feat: add apis and hooks 2025-06-27 18:29:23 +05:30
Yunus M
27d5e16d18 feat: update context to recognise conjunction operator 2025-06-27 18:29:23 +05:30
Yunus M
24d6b48ad4 feat: add codemirror 2025-06-27 18:29:23 +05:30
Yunus M
78af24b4df feat: add types, base components 2025-06-27 18:29:23 +05:30
Yunus M
45fcf746b0 feat: add antlr4, parser files and grammar 2025-06-27 18:29:23 +05:30
61 changed files with 872 additions and 2722 deletions

View File

@@ -8,4 +8,10 @@ import {
export const getKeySuggestions = (
props: QueryKeyRequestProps,
): Promise<AxiosResponse<QueryKeySuggestionsResponseProps>> =>
axios.get(`/fields/keys?signal=${props.signal}&name=${props.name}`);
axios.get(
`/fields/keys?signal=${props.signal}&searchText=${
props.searchText
}&metricName=${props.metricName ?? ''}&fieldContext=${
props.fieldContext ?? ''
}&fieldDataType=${props.fieldDataType ?? ''}`,
);

View File

@@ -4,7 +4,6 @@ import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import getStartEndRangeTime from 'lib/getStartEndRangeTime';
import { mapQueryDataToApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi';
import { isEmpty } from 'lodash-es';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
IBuilderQuery,
QueryFunctionProps,
@@ -126,10 +125,12 @@ function createBaseSpec(
selectFields: isEmpty(queryData.selectColumns)
? undefined
: queryData.selectColumns?.map(
(column: BaseAutocompleteData): TelemetryFieldKey => ({
name: column.key,
fieldDataType: column?.dataType as FieldDataType,
fieldContext: column?.type as FieldContext,
(column: any): TelemetryFieldKey => ({
name: column.name ?? column.key,
fieldDataType:
column?.fieldDataType ?? (column?.dataType as FieldDataType),
fieldContext: column?.fieldContext ?? (column?.type as FieldContext),
signal: column?.signal ?? undefined,
}),
),
};

View File

@@ -410,18 +410,18 @@ export default function LogsFormatOptionsMenu({
)}
<div className="column-format">
{addColumn?.value?.map(({ key, id }) => (
<div className="column-name" key={id}>
{addColumn?.value?.map(({ name }) => (
<div className="column-name" key={name}>
<div className="name">
<Tooltip placement="left" title={key}>
{key}
<Tooltip placement="left" title={name}>
{name}
</Tooltip>
</div>
{addColumn?.value?.length > 1 && (
<X
className="delete-btn"
size={14}
onClick={(): void => addColumn.onRemove(id as string)}
onClick={(): void => addColumn.onRemove(name)}
/>
)}
</div>

View File

@@ -7,9 +7,11 @@ import SpaceAggregationOptions from 'container/QueryBuilder/components/SpaceAggr
import { GroupByFilter, OperatorsSelect } from 'container/QueryBuilder/filters';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
import { Info } from 'lucide-react';
import { memo, useCallback, useMemo } from 'react';
import { memo, useCallback, useEffect, useMemo } from 'react';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { useQueryBuilderV2Context } from '../../QueryBuilderV2Context';
const MetricsAggregateSection = memo(function MetricsAggregateSection({
query,
index,
@@ -21,6 +23,7 @@ const MetricsAggregateSection = memo(function MetricsAggregateSection({
version: string;
panelType: PANEL_TYPES | null;
}): JSX.Element {
const { setAggregationOptions } = useQueryBuilderV2Context();
const {
operators,
spaceAggregationOptions,
@@ -33,6 +36,20 @@ const MetricsAggregateSection = memo(function MetricsAggregateSection({
entityVersion: version,
});
useEffect(() => {
setAggregationOptions([
{
func: query.spaceAggregation || 'count',
arg: query.aggregateAttribute.key || '',
},
]);
}, [
query.spaceAggregation,
query.aggregateAttribute.key,
setAggregationOptions,
query,
]);
const handleChangeGroupByKeys = useCallback(
(value: IBuilderQuery['groupBy']) => {
handleChangeQueryData('groupBy', value);

View File

@@ -53,17 +53,6 @@ const havingOperators = [
},
];
// Add common value suggestions
const commonValues = [
{ label: '0', value: '0 ' },
{ label: '1', value: '1 ' },
{ label: '5', value: '5 ' },
{ label: '10', value: '10 ' },
{ label: '50', value: '50 ' },
{ label: '100', value: '100 ' },
{ label: '1000', value: '1000 ' },
];
const conjunctions = [
{ label: 'AND', value: 'AND ' },
{ label: 'OR', value: 'OR ' },
@@ -250,22 +239,18 @@ function HavingFilter({
};
}
// Show value suggestions after operator
// Close dropdown after operator to allow custom value entry
if (isAfterOperator(tokens)) {
return {
from: context.pos,
options: [
...commonValues.map((value) => ({
...value,
apply: applyValueCompletion,
})),
{
label: 'Enter a custom number value',
type: 'text',
apply: applyValueCompletion,
},
],
};
return null;
}
// Hide suggestions while typing a value after an operator
if (
!text.endsWith(' ') &&
tokens.length >= 2 &&
havingOperators.some((op) => op.value === tokens[tokens.length - 2])
) {
return null;
}
// Suggest key/operator pairs and ( for grouping
@@ -346,6 +331,7 @@ function HavingFilter({
havingAutocomplete,
javascript({ jsx: false, typescript: false }),
stopEventsExtension,
EditorView.lineWrapping,
keymap.of([
...completionKeymap,
{

View File

@@ -12,7 +12,7 @@ import {
import { javascript } from '@codemirror/lang-javascript';
import { Color } from '@signozhq/design-tokens';
import { copilot } from '@uiw/codemirror-theme-copilot';
import CodeMirror, { EditorView, keymap, Prec } from '@uiw/react-codemirror';
import CodeMirror, { EditorView, keymap } from '@uiw/react-codemirror';
import { Button, Card, Collapse, Popover, Tag } from 'antd';
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
import { getValueSuggestions } from 'api/querySuggestions/getValueSuggestion';
@@ -32,9 +32,13 @@ import {
queryOperatorSuggestions,
validateQuery,
} from 'utils/antlrQueryUtils';
import { getQueryContextAtCursor } from 'utils/queryContextUtils';
import {
getQueryContextAtCursor,
getCurrentValueIndexAtCursor,
} from 'utils/queryContextUtils';
import { queryExamples } from './constants';
import { isNull } from 'lodash-es';
const { Panel } = Collapse;
@@ -111,20 +115,12 @@ function QuerySearch({
const [isFocused, setIsFocused] = useState(false);
const [isCompleteKeysList, setIsCompleteKeysList] = useState(false);
const [isCompleteValuesList, setIsCompleteValuesList] = useState<boolean>(
false,
);
const [
isFetchingCompleteValuesList,
setIsFetchingCompleteValuesList,
] = useState<boolean>(false);
const lastPosRef = useRef<{ line: number; ch: number }>({ line: 0, ch: 0 });
// Reference to the editor view for programmatic autocompletion
const editorRef = useRef<EditorView | null>(null);
const lastKeyRef = useRef<string>('');
const lastValueRef = useRef<string>('');
const isMountedRef = useRef<boolean>(true);
// const {
@@ -151,7 +147,8 @@ function QuerySearch({
const fetchKeySuggestions = async (searchText?: string): Promise<void> => {
const response = await getKeySuggestions({
signal: dataSource,
name: searchText || '',
searchText: searchText || '',
metricName: queryData.aggregateAttribute.key ?? undefined,
});
if (response.data.data) {
@@ -166,7 +163,7 @@ function QuerySearch({
setKeySuggestions([]);
fetchKeySuggestions();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataSource]);
}, [dataSource, queryData.aggregateAttribute.key]);
// Add a state for tracking editing mode
const [editingMode, setEditingMode] = useState<
@@ -222,10 +219,7 @@ function QuerySearch({
// If we're already inside bracket list for IN operator and it's a string value
// just wrap in quotes but not brackets (we're already in brackets)
if (
(type === 'value' || type === 'keyword') &&
!/^[a-zA-Z0-9_][a-zA-Z0-9_.\[\]]*$/.test(value)
) {
if (type === 'value' || type === 'keyword') {
return wrapStringValueInQuotes(value);
}
@@ -244,29 +238,17 @@ function QuerySearch({
// Use callback to prevent dependency changes on each render
const fetchValueSuggestions = useCallback(
// eslint-disable-next-line sonarjs/cognitive-complexity
async ({
key,
searchText,
fetchingComplete = false,
}: {
key: string;
searchText?: string;
fetchingComplete?: boolean;
}): Promise<void> => {
async (key: string): Promise<void> => {
if (
!key ||
(key === activeKey && !isLoadingSuggestions && !fetchingComplete) ||
(key === activeKey && !isLoadingSuggestions) ||
!isMountedRef.current
)
return;
// Set loading state and store the key we're fetching for
setIsLoadingSuggestions(true);
if (fetchingComplete) {
setIsFetchingCompleteValuesList(true);
}
lastKeyRef.current = key;
lastValueRef.current = searchText || '';
setActiveKey(key);
setValueSuggestions([
@@ -278,21 +260,14 @@ function QuerySearch({
},
]);
const sanitizedSearchText = searchText ? searchText?.trim() : '';
try {
const response = await getValueSuggestions({
key,
searchText: sanitizedSearchText,
signal: dataSource,
});
// Skip updates if component unmounted or key changed
if (
!isMountedRef.current ||
lastKeyRef.current !== key ||
lastValueRef.current !== sanitizedSearchText
) {
if (!isMountedRef.current || lastKeyRef.current !== key) {
return; // Skip updating if key has changed or component unmounted
}
@@ -371,9 +346,6 @@ function QuerySearch({
]);
setIsLoadingSuggestions(false);
}
} finally {
setIsLoadingSuggestions(false);
setIsFetchingCompleteValuesList(false);
}
},
[activeKey, dataSource, isLoadingSuggestions],
@@ -503,8 +475,7 @@ function QuerySearch({
// Enhanced myCompletions function to better use context including query pairs
// eslint-disable-next-line sonarjs/cognitive-complexity
function autoSuggestions(context: CompletionContext): CompletionResult | null {
// This matches words before the cursor position
const word = context.matchBefore(/[a-zA-Z0-9_.:/?&=#%\-\[\]]*/);
const word = context.matchBefore(/[.\w]*/);
if (word?.from === word?.to && !context.explicit) return null;
// Get the query context at the cursor position
@@ -528,7 +499,7 @@ function QuerySearch({
completion: any,
from: number,
to: number,
shouldAddSpace: boolean = true,
shouldAddSpace = true,
): void => {
view.dispatch({
changes: {
@@ -556,24 +527,47 @@ function QuerySearch({
from: number,
to: number,
): void => {
if (queryContext.isInValue && option.type === 'value') {
if (
queryContext.currentPair?.position &&
queryContext.currentPair.position.valueStart &&
queryContext.currentPair.position.valueEnd
) {
const { valueStart, valueEnd } = queryContext.currentPair.position;
addSpaceAfterSelection(
view,
{ apply: originalApply },
valueStart,
valueEnd + 1,
false,
let shouldDefaultApply = true;
// Changes to replace the value in-place with the existing value
const isValueType = queryContext.isInValue && option.type === 'value';
const pair = queryContext.currentPair;
if (isValueType) {
if (queryContext.isInBracketList && pair?.valuesPosition) {
const idx = getCurrentValueIndexAtCursor(
pair.valuesPosition,
cursorPos.ch,
);
} else {
addSpaceAfterSelection(view, { apply: originalApply }, from, to);
if (!isNull(idx)) {
const { start, end } = pair.valuesPosition[idx];
if (typeof start === 'number' && typeof end === 'number') {
shouldDefaultApply = false;
addSpaceAfterSelection(
view,
{ apply: originalApply },
start,
end + 1,
false,
);
}
}
} else if (pair?.position) {
const { valueStart, valueEnd } = pair.position;
if (typeof valueStart === 'number' && typeof valueEnd === 'number') {
shouldDefaultApply = false;
addSpaceAfterSelection(
view,
{ apply: originalApply },
valueStart,
valueEnd + 1,
false,
);
}
}
} else {
}
if (shouldDefaultApply) {
addSpaceAfterSelection(view, { apply: originalApply }, from, to);
}
},
@@ -590,36 +584,21 @@ function QuerySearch({
return null;
}
const searchText = word?.text.toLowerCase().trim() ?? '';
options = (valueSuggestions || []).filter((option) =>
option.label.toLowerCase().includes(searchText),
);
if (
keyName &&
((options.length === 0 &&
(!isCompleteValuesList || lastValueRef.current !== searchText) &&
!isFetchingCompleteValuesList) ||
keyName !== activeKey ||
isLoadingSuggestions) &&
(keyName !== activeKey || isLoadingSuggestions) &&
!(isLoadingSuggestions && lastKeyRef.current === keyName)
) {
setTimeout(() => {
fetchValueSuggestions({
key: keyName,
searchText,
fetchingComplete: true,
});
}, 300);
fetchValueSuggestions(keyName);
}
// For values in bracket list, just add quotes without enclosing in brackets
const processedOptions = options.map((option) => {
const processedOptions = valueSuggestions.map((option) => {
// Clone the option to avoid modifying the original
const processedOption = { ...option };
// Skip processing for non-selectable items
if (!option.apply || typeof option.apply === 'function') {
if (option.apply === false || typeof option.apply === 'function') {
return option;
}
@@ -759,29 +738,14 @@ function QuerySearch({
if (!keyName) {
return null;
}
const searchText = word?.text.toLowerCase().trim() ?? '';
options = (valueSuggestions || []).filter((option) =>
option.label.toLowerCase().includes(searchText),
);
// Trigger fetch only if needed
if (
keyName &&
((options.length === 0 &&
(!isCompleteValuesList || lastValueRef.current !== searchText) &&
!isFetchingCompleteValuesList) ||
keyName !== activeKey ||
isLoadingSuggestions) &&
(keyName !== activeKey || isLoadingSuggestions) &&
!(isLoadingSuggestions && lastKeyRef.current === keyName)
) {
setTimeout(() => {
fetchValueSuggestions({
key: keyName,
searchText,
fetchingComplete: true,
});
}, 300);
fetchValueSuggestions(keyName);
}
// Process options to add appropriate formatting when selected
@@ -946,7 +910,7 @@ function QuerySearch({
// options: optionsWithSpace,
// };
//Don't show anything if no context detected
// Don't show anything if no context detected
return {
from: word?.from ?? 0,
options: [],
@@ -980,7 +944,7 @@ function QuerySearch({
// Only fetch if needed and if we have a valid key
if (key && key !== activeKey && !isLoadingSuggestions) {
fetchValueSuggestions({ key });
fetchValueSuggestions(key);
}
}
}, [queryContext, activeKey, isLoadingSuggestions, fetchValueSuggestions]);
@@ -1046,42 +1010,13 @@ function QuerySearch({
javascript({ jsx: false, typescript: false }),
EditorView.lineWrapping,
stopEventsExtension,
Prec.highest(
keymap.of([
...completionKeymap,
{
key: 'Escape',
run: closeCompletion,
},
{
key: 'Enter',
preventDefault: true,
// Prevent default behavior of Enter to add new line
// and instead run a custom action
run: (): boolean => {
return true;
},
},
{
key: 'Mod-Enter',
preventDefault: true,
// Prevent default behavior of Mod-Enter to add new line
// and instead run a custom action
// Mod-Enter is usually Ctrl-Enter or Cmd-Enter based on OS
run: (): boolean => {
return true;
},
},
{
key: 'Shift-Enter',
preventDefault: true,
// Prevent default behavior of Shift-Enter to add new line
run: (): boolean => {
return true;
},
},
]),
),
keymap.of([
...completionKeymap,
{
key: 'Escape',
run: closeCompletion,
},
]),
]}
placeholder="Enter your query (e.g., status = 'error' AND service = 'frontend')"
basicSetup={{

View File

@@ -6,10 +6,6 @@ import {
import { SelectOption } from 'types/common/select';
export const metricAggregateOperatorOptions: SelectOption<string, string>[] = [
{
value: MetricAggregateOperator.NOOP,
label: 'NOOP',
},
{
value: MetricAggregateOperator.COUNT,
label: 'Count',
@@ -130,10 +126,6 @@ export const metricAggregateOperatorOptions: SelectOption<string, string>[] = [
];
export const tracesAggregateOperatorOptions: SelectOption<string, string>[] = [
{
value: TracesAggregatorOperator.NOOP,
label: 'NOOP',
},
{
value: TracesAggregatorOperator.COUNT,
label: 'Count',
@@ -217,10 +209,6 @@ export const tracesAggregateOperatorOptions: SelectOption<string, string>[] = [
];
export const logsAggregateOperatorOptions: SelectOption<string, string>[] = [
{
value: LogsAggregatorOperator.NOOP,
label: 'NOOP',
},
{
value: LogsAggregatorOperator.COUNT,
label: 'Count',

View File

@@ -13,6 +13,7 @@ import {
Typography,
} from 'antd';
import logEvent from 'api/common/logEvent';
import { TelemetryFieldKey } from 'api/v5/v5';
import axios from 'axios';
import cx from 'classnames';
import { getViewDetailsUsingViewKey } from 'components/ExplorerCard/utils';
@@ -58,7 +59,6 @@ import {
} from 'react';
import { useHistory } from 'react-router-dom';
import { Dashboard } from 'types/api/dashboard/getAll';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { ViewProps } from 'types/api/saveViews/types';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
@@ -253,7 +253,7 @@ function ExplorerOptions({
const getUpdatedExtraData = (
extraData: string | undefined,
newSelectedColumns: BaseAutocompleteData[],
newSelectedColumns: TelemetryFieldKey[],
formattingOptions?: FormattingOptions,
): string => {
let updatedExtraData;
@@ -337,7 +337,7 @@ function ExplorerOptions({
const { handleExplorerTabChange } = useHandleExplorerTabChange();
type ExtraData = {
selectColumns?: BaseAutocompleteData[];
selectColumns?: TelemetryFieldKey[];
version?: number;
};

View File

@@ -87,8 +87,9 @@ function TableView({
}
});
} else {
// eslint-disable-next-line sonarjs/no-identical-functions
selectedOptions.selectColumns.forEach((val) => {
const path = findKeyPath(logData, val.key, '');
const path = findKeyPath(logData, val.name, '');
if (path) {
pinnedAttributes[path] = true;
}

View File

@@ -1,11 +1,11 @@
import { TelemetryFieldKey } from 'api/v5/v5';
import { IField } from 'types/api/logs/fields';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
export const convertKeysToColumnFields = (
keys: BaseAutocompleteData[],
keys: TelemetryFieldKey[],
): IField[] =>
keys.map((item) => ({
dataType: item.dataType as string,
name: item.key,
type: item.type as string,
dataType: item.fieldDataType ?? '',
name: item.name,
type: item.fieldContext ?? '',
}));

View File

@@ -1,5 +1,5 @@
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { defaultTraceSelectedColumns } from 'container/OptionsMenu/constants';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { LogsAggregatorOperator } from 'types/common/queryBuilder';
@@ -43,48 +43,7 @@ export const listViewInitialTraceQuery = {
orderBy: [{ columnName: 'timestamp', order: 'desc' }],
offset: 0,
pageSize: 10,
selectColumns: [
{
key: 'serviceName',
dataType: 'string',
type: 'tag',
isColumn: true,
isJSON: false,
id: 'serviceName--string--tag--true',
},
{
key: 'name',
dataType: 'string',
type: 'tag',
isColumn: true,
isJSON: false,
id: 'name--string--tag--true',
},
{
key: 'durationNano',
dataType: 'float64',
type: 'tag',
isColumn: true,
isJSON: false,
id: 'durationNano--float64--tag--true',
},
{
key: 'httpMethod',
dataType: 'string',
type: 'tag',
isColumn: true,
isJSON: false,
id: 'httpMethod--string--tag--true',
},
{
key: 'responseStatusCode',
dataType: 'string',
type: 'tag',
isColumn: true,
isJSON: false,
id: 'responseStatusCode--string--tag--true',
},
] as BaseAutocompleteData[],
selectColumns: defaultTraceSelectedColumns,
},
],
},

View File

@@ -25,9 +25,11 @@ function ExplorerAttributeColumns({
}
const filteredAttributeKeys =
data?.payload?.attributeKeys?.filter((attributeKey: any) =>
attributeKey.key.toLowerCase().includes(searchText.toLowerCase()),
) || [];
Object.values(data?.data?.data?.keys || {})
?.flat()
?.filter((attributeKey: any) =>
attributeKey.name.toLowerCase().includes(searchText.toLowerCase()),
) || [];
if (filteredAttributeKeys.length === 0) {
return (
<div className="attribute-columns">
@@ -36,16 +38,17 @@ function ExplorerAttributeColumns({
);
}
console.log('filteredAttributeKeys', filteredAttributeKeys, data);
return (
<div className="attribute-columns">
{filteredAttributeKeys.map((attributeKey: any) => (
<Checkbox
checked={isAttributeKeySelected(attributeKey.key)}
onChange={(): void => handleCheckboxChange(attributeKey.key)}
checked={isAttributeKeySelected(attributeKey.name)}
onChange={(): void => handleCheckboxChange(attributeKey.name)}
style={{ padding: 0 }}
key={attributeKey.key}
key={attributeKey.name}
>
{attributeKey.key}
{attributeKey.name}
</Checkbox>
))}
</div>

View File

@@ -1,3 +1,4 @@
/* eslint-disable sonarjs/no-identical-functions */
/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable react/jsx-props-no-spreading */
import './ExplorerColumnsRenderer.styles.scss';
@@ -5,9 +6,10 @@ import './ExplorerColumnsRenderer.styles.scss';
import { Color } from '@signozhq/design-tokens';
import { Button, Divider, Dropdown, Input, Tooltip, Typography } from 'antd';
import { MenuProps } from 'antd/lib';
import { FieldDataType } from 'api/v5/v5';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKeySuggestions';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useDebouncedFn from 'hooks/useDebouncedFunction';
import {
@@ -49,13 +51,27 @@ function ExplorerColumnsRenderer({
const initialDataSource = currentQuery.builder.queryData[0].dataSource;
const { data, isLoading, isError } = useGetAggregateKeys(
// const { data, isLoading, isError } = useGetAggregateKeys(
// {
// aggregateAttribute: '',
// dataSource: currentQuery.builder.queryData[0].dataSource,
// aggregateOperator: currentQuery.builder.queryData[0].aggregateOperator,
// searchText: querySearchText,
// tagType: '',
// },
// {
// queryKey: [
// currentQuery.builder.queryData[0].dataSource,
// currentQuery.builder.queryData[0].aggregateOperator,
// querySearchText,
// ],
// },
// );
const { data, isLoading, isError } = useGetQueryKeySuggestions(
{
aggregateAttribute: '',
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateOperator: currentQuery.builder.queryData[0].aggregateOperator,
searchText: querySearchText,
tagType: '',
signal: currentQuery.builder.queryData[0].dataSource,
},
{
queryKey: [
@@ -71,7 +87,7 @@ function ExplorerColumnsRenderer({
return selectedLogFields.some((field) => field.name === key);
}
if (initialDataSource === DataSource.TRACES && selectedTracesFields) {
return selectedTracesFields.some((field) => field.key === key);
return selectedTracesFields.some((field) => field.name === key);
}
return false;
};
@@ -99,18 +115,31 @@ function ExplorerColumnsRenderer({
initialDataSource === DataSource.TRACES &&
setSelectedTracesFields !== undefined
) {
const selectedField = data?.payload?.attributeKeys?.find(
(attributeKey) => attributeKey.key === key,
);
const selectedField = Object.values(data?.data?.data?.keys || {})
?.flat()
?.find((attributeKey) => attributeKey.name === key);
console.log('selectedField', selectedField);
if (selectedTracesFields) {
if (isAttributeKeySelected(key)) {
setSelectedTracesFields(
selectedTracesFields.filter((field) => field.key !== key),
selectedTracesFields.filter((field) => field.name !== key),
);
} else if (selectedField) {
setSelectedTracesFields([...selectedTracesFields, selectedField]);
setSelectedTracesFields([
...selectedTracesFields,
{
...selectedField,
fieldDataType: selectedField.fieldDataType as FieldDataType,
},
]);
}
} else if (selectedField) setSelectedTracesFields([selectedField]);
} else if (selectedField)
setSelectedTracesFields([
{
...selectedField,
fieldDataType: selectedField.fieldDataType as FieldDataType,
},
]);
}
setOpen(false);
};
@@ -175,7 +204,7 @@ function ExplorerColumnsRenderer({
selectedTracesFields
) {
setSelectedTracesFields(
selectedTracesFields.filter((field) => field.key !== name),
selectedTracesFields.filter((field) => field.name !== name),
);
}
};
@@ -218,6 +247,8 @@ function ExplorerColumnsRenderer({
const isDarkMode = useIsDarkMode();
console.log('selectedTracesFields', selectedTracesFields, selectedLogFields);
return (
<div className="explorer-columns-renderer">
<div className="title">
@@ -279,12 +310,14 @@ function ExplorerColumnsRenderer({
>
<div className="explorer-column-title">
<GripVertical size={12} color="#5A5A5A" />
{field.key}
{field?.name || (field as any)?.key}
</div>
<Trash2
size={12}
color="red"
onClick={(): void => removeSelectedLogField(field.key)}
onClick={(): void =>
removeSelectedLogField(field?.name || (field as any)?.key)
}
data-testid="trash-icon"
/>
</div>

View File

@@ -3,14 +3,12 @@
/* eslint-disable react/jsx-props-no-spreading */
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TelemetryFieldKey } from 'api/v5/v5';
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import React from 'react';
import { DropResult } from 'react-beautiful-dnd';
import {
BaseAutocompleteData,
DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
import ExplorerColumnsRenderer from '../ExplorerColumnsRenderer';
@@ -321,7 +319,11 @@ describe('ExplorerColumnsRenderer', () => {
selectedLogFields={[]}
setSelectedLogFields={mockSetSelectedLogFields}
selectedTracesFields={[
{ key: 'trace_attribute1', dataType: DataTypes.String, type: 'tag' },
{
name: 'trace_attribute1',
fieldDataType: DataTypes.String,
fieldContext: '',
},
]}
setSelectedTracesFields={mockSetSelectedTracesFields}
/>,
@@ -340,7 +342,11 @@ describe('ExplorerColumnsRenderer', () => {
selectedLogFields={[]}
setSelectedLogFields={mockSetSelectedLogFields}
selectedTracesFields={[
{ key: 'trace_attribute1', dataType: DataTypes.String, type: 'tag' },
{
name: 'trace_attribute1',
fieldDataType: DataTypes.String,
fieldContext: '',
},
]}
setSelectedTracesFields={mockSetSelectedTracesFields}
/>,
@@ -355,15 +361,15 @@ describe('ExplorerColumnsRenderer', () => {
it('reorders trace fields on drag and drop', () => {
const initialSelectedFields = [
{ key: 'trace_field1', dataType: 'string', type: 'tag' },
{ key: 'trace_field2', dataType: 'string', type: 'tag' },
{ name: 'trace_field1', fieldDataType: 'string', fieldContext: 'tag' },
{ name: 'trace_field2', fieldDataType: 'string', fieldContext: 'tag' },
];
render(
<ExplorerColumnsRenderer
selectedLogFields={[]}
setSelectedLogFields={mockSetSelectedLogFields}
selectedTracesFields={initialSelectedFields as BaseAutocompleteData[]}
selectedTracesFields={initialSelectedFields as TelemetryFieldKey[]}
setSelectedTracesFields={mockSetSelectedTracesFields}
/>,
);
@@ -383,8 +389,8 @@ describe('ExplorerColumnsRenderer', () => {
});
expect(mockSetSelectedTracesFields).toHaveBeenCalledWith([
{ key: 'trace_field2', dataType: 'string', type: 'tag' },
{ key: 'trace_field1', dataType: 'string', type: 'tag' },
{ name: 'trace_field2', fieldDataType: 'string', fieldContext: 'tag' },
{ name: 'trace_field1', fieldDataType: 'string', fieldContext: 'tag' },
]);
} else {
fail('DragDropContext or onDragEndMock not found');

View File

@@ -6,9 +6,12 @@ import {
} from 'constants/queryBuilder';
import {
listViewInitialLogQuery,
listViewInitialTraceQuery,
PANEL_TYPES_INITIAL_QUERY,
} from 'container/NewDashboard/ComponentsSlider/constants';
import {
defaultLogsSelectedColumns,
defaultTraceSelectedColumns,
} from 'container/OptionsMenu/constants';
import { categoryToSupport } from 'container/QueryBuilder/filters/BuilderUnitsFilter/config';
import { cloneDeep, defaultTo, isEmpty, isEqual, set, unset } from 'lodash-es';
import { Layout } from 'react-grid-layout';
@@ -503,7 +506,6 @@ export function handleQueryChange(
supersetQuery: Query,
currentPanelType: PANEL_TYPES,
): Query {
console.log('supersetQuery', supersetQuery);
return {
...supersetQuery,
builder: {
@@ -564,21 +566,12 @@ export const getDefaultWidgetData = (
timePreferance: 'GLOBAL_TIME',
softMax: null,
softMin: null,
selectedLogFields: [
{
dataType: 'string',
type: '',
name: 'body',
},
{
dataType: 'string',
type: '',
name: 'timestamp',
},
],
selectedTracesFields: [
...listViewInitialTraceQuery.builder.queryData[0].selectColumns,
],
selectedLogFields: defaultLogsSelectedColumns.map((field) => ({
...field,
type: field.fieldContext ?? '',
dataType: field.fieldDataType ?? '',
})),
selectedTracesFields: defaultTraceSelectedColumns,
});
export const PANEL_TYPE_TO_QUERY_TYPES: Record<PANEL_TYPES, EQueryType[]> = {

View File

@@ -42,10 +42,10 @@ function AddColumnField({ config }: AddColumnFieldProps): JSX.Element | null {
</SearchIconWrapper>
</Input.Group>
{config.value?.map(({ key, id }) => (
<AddColumnItem direction="horizontal" key={id}>
<Typography>{key}</Typography>
<DeleteOutlinedIcon onClick={(): void => config.onRemove(id as string)} />
{config.value?.map(({ name }) => (
<AddColumnItem direction="horizontal" key={name}>
<Typography>{name}</Typography>
<DeleteOutlinedIcon onClick={(): void => config.onRemove(name)} />
</AddColumnItem>
))}
</AddColumnWrapper>

View File

@@ -1,4 +1,4 @@
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TelemetryFieldKey } from 'api/v5/v5';
import { FontSize, OptionsQuery } from './types';
@@ -11,71 +11,56 @@ export const defaultOptionsQuery: OptionsQuery = {
fontSize: FontSize.SMALL,
};
export const defaultLogsSelectedColumns = [
export const defaultLogsSelectedColumns: TelemetryFieldKey[] = [
{
key: 'timestamp',
dataType: DataTypes.String,
type: 'tag',
name: 'timestamp',
signal: 'logs',
fieldContext: 'log',
fieldDataType: '',
isColumn: true,
isJSON: false,
id: 'timestamp--string--tag--true',
isIndexed: false,
},
{
key: 'body',
dataType: DataTypes.String,
type: 'tag',
name: 'body',
signal: 'logs',
fieldContext: 'log',
fieldDataType: '',
isColumn: true,
isJSON: false,
id: 'body--string--tag--true',
isIndexed: false,
},
];
export const defaultTraceSelectedColumns = [
export const defaultTraceSelectedColumns: TelemetryFieldKey[] = [
{
key: 'serviceName',
dataType: DataTypes.String,
type: 'tag',
isColumn: true,
isJSON: false,
id: 'serviceName--string--tag--true',
isIndexed: false,
name: 'service.name',
signal: 'traces',
fieldContext: 'resource',
fieldDataType: 'string',
},
{
key: 'name',
dataType: DataTypes.String,
type: 'tag',
isColumn: true,
isJSON: false,
id: 'name--string--tag--true',
isIndexed: false,
name: 'name',
signal: 'traces',
fieldContext: 'span',
fieldDataType: 'string',
},
{
key: 'durationNano',
dataType: DataTypes.Float64,
type: 'tag',
isColumn: true,
isJSON: false,
id: 'durationNano--float64--tag--true',
isIndexed: false,
name: 'duration_nano',
signal: 'traces',
fieldContext: 'span',
fieldDataType: '',
},
{
key: 'httpMethod',
dataType: DataTypes.String,
type: 'tag',
isColumn: true,
isJSON: false,
id: 'httpMethod--string--tag--true',
isIndexed: false,
name: 'http_method',
signal: 'traces',
fieldContext: 'span',
fieldDataType: '',
},
{
key: 'responseStatusCode',
dataType: DataTypes.String,
type: 'tag',
isColumn: true,
isJSON: false,
id: 'responseStatusCode--string--tag--true',
isIndexed: false,
name: 'response_status_code',
signal: 'traces',
fieldContext: 'span',
fieldDataType: '',
},
];

View File

@@ -1,6 +1,6 @@
import { InputNumberProps, RadioProps, SelectProps } from 'antd';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LogViewMode } from 'container/LogsTable';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
export enum FontSize {
SMALL = 'small',
@@ -13,7 +13,7 @@ interface FontSizeProps {
onChange: (val: FontSize) => void;
}
export interface OptionsQuery {
selectColumns: BaseAutocompleteData[];
selectColumns: TelemetryFieldKey[];
maxLines: number;
format: LogViewMode;
fontSize: FontSize;
@@ -36,7 +36,7 @@ export type OptionsMenuConfig = {
'options' | 'onSelect' | 'onFocus' | 'onSearch' | 'onBlur'
> & {
isFetching: boolean;
value: BaseAutocompleteData[];
value: TelemetryFieldKey[];
onRemove: (key: string) => void;
};
};

View File

@@ -1,21 +1,24 @@
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
import { TelemetryFieldKey } from 'api/v5/v5';
import { AxiosResponse } from 'axios';
import { LogViewMode } from 'container/LogsTable';
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKeySuggestions';
import useDebounce from 'hooks/useDebounce';
import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
import {
AllTraceFilterKeys,
AllTraceFilterKeyValue,
} from 'pages/TracesExplorer/Filter/filterUtils';
import { AllTraceFilterKeyValue } from 'pages/TracesExplorer/Filter/filterUtils';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueries } from 'react-query';
import { ErrorResponse, SuccessResponse } from 'types/api';
import {
BaseAutocompleteData,
IQueryAutocompleteResponse,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
QueryKeyRequestProps,
QueryKeySuggestionsResponseProps,
} from 'types/api/querySuggestions/types';
import {
FieldContext,
FieldDataType,
SignalType,
} from 'types/api/v5/queryRange';
import { DataSource } from 'types/common/queryBuilder';
import {
@@ -47,7 +50,7 @@ interface UseOptionsMenu {
const useOptionsMenu = ({
dataSource,
aggregateOperator,
// aggregateOperator,
initialOptions = {},
}: UseOptionsMenuProps): UseOptionsMenu => {
const { notifications } = useNotifications();
@@ -61,15 +64,23 @@ const useOptionsMenu = ({
const [isFocused, setIsFocused] = useState<boolean>(false);
const debouncedSearchText = useDebounce(searchText, 300);
const initialQueryParams = useMemo(
// const initialQueryParams = useMemo(
// () => ({
// searchText: '',
// aggregateAttribute: '',
// tagType: undefined,
// dataSource,
// aggregateOperator,
// }),
// [dataSource, aggregateOperator],
// );
const initialQueryParamsV5: QueryKeyRequestProps = useMemo(
() => ({
signal: dataSource,
searchText: '',
aggregateAttribute: '',
tagType: undefined,
dataSource,
aggregateOperator,
}),
[dataSource, aggregateOperator],
[dataSource],
);
const {
@@ -77,23 +88,37 @@ const useOptionsMenu = ({
redirectWithQuery: redirectWithOptionsData,
} = useUrlQueryData<OptionsQuery>(URL_OPTIONS, defaultOptionsQuery);
const initialQueries = useMemo(
// const initialQueries = useMemo(
// () =>
// initialOptions?.selectColumns?.map((column) => ({
// queryKey: column,
// queryFn: (): Promise<
// SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse
// > =>
// getAggregateKeys({
// ...initialQueryParams,
// searchText: column,
// }),
// enabled: !!column && !optionsQuery,
// })) || [],
// [initialOptions?.selectColumns, initialQueryParams, optionsQuery],
// );
const initialQueriesV5 = useMemo(
() =>
initialOptions?.selectColumns?.map((column) => ({
queryKey: column,
queryFn: (): Promise<
SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse
> =>
getAggregateKeys({
...initialQueryParams,
queryFn: (): Promise<AxiosResponse<QueryKeySuggestionsResponseProps>> =>
getKeySuggestions({
...initialQueryParamsV5,
searchText: column,
}),
enabled: !!column && !optionsQuery,
})) || [],
[initialOptions?.selectColumns, initialQueryParams, optionsQuery],
[initialOptions?.selectColumns, initialQueryParamsV5, optionsQuery],
);
const initialAttributesResult = useQueries(initialQueries);
const initialAttributesResult = useQueries(initialQueriesV5);
const isFetchedInitialAttributes = useMemo(
() => initialAttributesResult.every((result) => result.isFetched),
@@ -106,42 +131,52 @@ const useOptionsMenu = ({
}
const attributesData = initialAttributesResult?.reduce(
(acc, attributeResponse) => {
const data = attributeResponse?.data?.payload?.attributeKeys || [];
(acc: TelemetryFieldKey[], attributeResponse): TelemetryFieldKey[] => {
const suggestions =
Object.values(attributeResponse?.data?.data?.data?.keys || {}).flat() ||
[];
return [...acc, ...data];
const mappedSuggestions: TelemetryFieldKey[] = suggestions.map(
(suggestion) => ({
name: suggestion.name,
signal: suggestion.signal as SignalType,
fieldDataType: suggestion.fieldDataType as FieldDataType,
fieldContext: suggestion.fieldContext as FieldContext,
}),
);
return [...acc, ...mappedSuggestions];
},
[] as BaseAutocompleteData[],
[],
);
let initialSelected = initialOptions.selectColumns
?.map((column) => attributesData.find(({ key }) => key === column))
.filter(Boolean) as BaseAutocompleteData[];
let initialSelected: TelemetryFieldKey[] | undefined =
initialOptions.selectColumns
?.map((column) => attributesData.find(({ name }) => name === column))
.filter((e) => !!e) || [];
if (dataSource === DataSource.TRACES) {
initialSelected = initialSelected
?.map((col) => {
if (col && Object.keys(AllTraceFilterKeyValue).includes(col?.key)) {
if (col && Object.keys(AllTraceFilterKeyValue).includes(col?.name)) {
const metaData = defaultTraceSelectedColumns.find(
(coln) => coln.key === (col.key as AllTraceFilterKeys),
(coln) => coln.name === col.name,
);
return {
...metaData,
key: metaData?.key,
dataType: metaData?.dataType,
type: metaData?.type,
isColumn: metaData?.isColumn,
isJSON: metaData?.isJSON,
id: metaData?.id,
name: metaData?.name || '',
};
}
return col;
})
.filter(Boolean) as BaseAutocompleteData[];
.filter((e) => !!e);
if (!initialSelected || !initialSelected?.length) {
initialSelected = defaultTraceSelectedColumns;
initialSelected = defaultTraceSelectedColumns.map((e) => ({
...e,
name: e.name,
}));
}
}
@@ -154,41 +189,91 @@ const useOptionsMenu = ({
]);
const {
data: searchedAttributesData,
isFetching: isSearchedAttributesFetching,
} = useGetAggregateKeys(
data: searchedAttributesDataV5,
isFetching: isSearchedAttributesFetchingV5,
} = useGetQueryKeySuggestions(
{
...initialQueryParams,
...initialQueryParamsV5,
searchText: debouncedSearchText,
},
{ queryKey: [debouncedSearchText, isFocused], enabled: isFocused },
);
const searchedAttributeKeys = useMemo(() => {
if (searchedAttributesData?.payload?.attributeKeys?.length) {
// const {
// data: searchedAttributesData,
// isFetching: isSearchedAttributesFetching,
// } = useGetAggregateKeys(
// {
// ...initialQueryParams,
// searchText: debouncedSearchText,
// },
// { queryKey: [debouncedSearchText, isFocused], enabled: isFocused },
// );
const searchedAttributeKeys: TelemetryFieldKey[] = useMemo(() => {
const searchedAttributesDataList = Object.values(
searchedAttributesDataV5?.data.data.keys || {},
).flat();
if (searchedAttributesDataList.length) {
if (dataSource === DataSource.LOGS) {
const logsSelectedColumns: TelemetryFieldKey[] = defaultLogsSelectedColumns.map(
(e) => ({
...e,
name: e.name,
signal: e.signal as SignalType,
fieldContext: e.fieldContext as FieldContext,
fieldDataType: e.fieldDataType as FieldDataType,
}),
);
return [
...defaultLogsSelectedColumns,
...searchedAttributesData.payload.attributeKeys.filter(
(attribute) => attribute.key !== 'body',
),
...logsSelectedColumns,
...searchedAttributesDataList
.filter((attribute) => attribute.name !== 'body')
// eslint-disable-next-line sonarjs/no-identical-functions
.map((e) => ({
...e,
name: e.name,
signal: e.signal as SignalType,
fieldContext: e.fieldContext as FieldContext,
fieldDataType: e.fieldDataType as FieldDataType,
})),
];
}
return searchedAttributesData.payload.attributeKeys;
// eslint-disable-next-line sonarjs/no-identical-functions
return searchedAttributesDataList.map((e) => ({
...e,
name: e.name,
signal: e.signal as SignalType,
fieldContext: e.fieldContext as FieldContext,
fieldDataType: e.fieldDataType as FieldDataType,
}));
}
if (dataSource === DataSource.TRACES) {
return defaultTraceSelectedColumns;
return defaultTraceSelectedColumns.map((e) => ({
...e,
name: e.name,
}));
}
return [];
}, [dataSource, searchedAttributesData?.payload?.attributeKeys]);
}, [dataSource, searchedAttributesDataV5?.data.data.keys]);
const initialOptionsQuery: OptionsQuery = useMemo(() => {
let defaultColumns = defaultOptionsQuery.selectColumns;
let defaultColumns: TelemetryFieldKey[] = defaultOptionsQuery.selectColumns;
if (dataSource === DataSource.TRACES) {
defaultColumns = defaultTraceSelectedColumns;
defaultColumns = defaultTraceSelectedColumns.map((e) => ({
...e,
name: e.name,
}));
} else if (dataSource === DataSource.LOGS) {
defaultColumns = defaultLogsSelectedColumns;
// eslint-disable-next-line sonarjs/no-identical-functions
defaultColumns = defaultLogsSelectedColumns.map((e) => ({
...e,
name: e.name,
signal: e.signal as SignalType,
fieldContext: e.fieldContext as FieldContext,
fieldDataType: e.fieldDataType as FieldDataType,
}));
}
const finalSelectColumns = initialOptions?.selectColumns
@@ -203,14 +288,14 @@ const useOptionsMenu = ({
}, [dataSource, initialOptions, initialSelectedColumns]);
const selectedColumnKeys = useMemo(
() => preferences?.columns?.map(({ id }) => id) || [],
() => preferences?.columns?.map(({ name }) => name) || [],
[preferences?.columns],
);
const optionsFromAttributeKeys = useMemo(() => {
const filteredAttributeKeys = searchedAttributeKeys.filter((item) => {
if (dataSource !== DataSource.LOGS) {
return item.key !== 'body';
return item.name !== 'body';
}
return true;
});
@@ -232,11 +317,11 @@ const useOptionsMenu = ({
const column = [
...searchedAttributeKeys,
...(preferences?.columns || []),
].find(({ id }) => id === key);
].find(({ name }) => name === key);
if (!column) return acc;
return [...acc, column];
}, [] as BaseAutocompleteData[]);
}, [] as TelemetryFieldKey[]);
const optionsData: OptionsQuery = {
...defaultOptionsQuery,
@@ -261,7 +346,7 @@ const useOptionsMenu = ({
const handleRemoveSelectedColumn = useCallback(
(columnKey: string) => {
const newSelectedColumns = preferences?.columns?.filter(
({ id }) => id !== columnKey,
({ name }) => name !== columnKey,
);
if (!newSelectedColumns?.length && dataSource !== DataSource.LOGS) {
@@ -367,7 +452,7 @@ const useOptionsMenu = ({
const optionsMenuConfig: Required<OptionsMenuConfig> = useMemo(
() => ({
addColumn: {
isFetching: isSearchedAttributesFetching,
isFetching: isSearchedAttributesFetchingV5,
value: preferences?.columns || defaultOptionsQuery.selectColumns,
options: optionsFromAttributeKeys || [],
onFocus: handleFocus,
@@ -390,7 +475,7 @@ const useOptionsMenu = ({
},
}),
[
isSearchedAttributesFetching,
isSearchedAttributesFetchingV5,
preferences,
optionsFromAttributeKeys,
handleSelectColumns,

View File

@@ -1,13 +1,13 @@
import { SelectProps } from 'antd';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TelemetryFieldKey } from 'api/v5/v5';
export const getOptionsFromKeys = (
keys: BaseAutocompleteData[],
keys: TelemetryFieldKey[],
selectedKeys: (string | undefined)[],
): SelectProps['options'] => {
const options = keys.map(({ id, key }) => ({
label: key,
value: id,
const options = keys.map(({ name }) => ({
label: name,
value: name,
}));
return options.filter(

View File

@@ -1 +1,5 @@
export const selectStyle = { width: '100%', minWidth: '7.7rem' };
export const selectStyle = {
width: '100%',
minWidth: '7.7rem',
height: '100%',
};

View File

@@ -1,11 +1,12 @@
import { DEFAULT_PER_PAGE_OPTIONS } from 'hooks/queryPagination';
export const defaultSelectedColumns: string[] = [
'serviceName',
'service.name',
'name',
'durationNano',
'httpMethod',
'responseStatusCode',
'duration_nano',
'http_method',
'response_status_code',
'timestamp',
];
export const PER_PAGE_OPTIONS: number[] = [10, ...DEFAULT_PER_PAGE_OPTIONS];

View File

@@ -21,6 +21,7 @@ import useDragColumns from 'hooks/useDragColumns';
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { cloneDeep } from 'lodash-es';
import { ArrowUp10, Minus } from 'lucide-react';
import { useTimezone } from 'providers/Timezone';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
@@ -46,7 +47,7 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
const panelType = panelTypeFromQueryBuilder || PANEL_TYPES.LIST;
const [orderDirection, setOrderDirection] = useState<string>('asc');
const [orderDirection, setOrderDirection] = useState<string>('desc');
const {
selectedTime: globalSelectedTime,
@@ -64,6 +65,8 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
},
});
console.log('options', options);
const { draggedColumns, onDragColumns } = useDragColumns<RowData>(
LOCALSTORAGE.TRACES_LIST_COLUMNS,
);
@@ -74,6 +77,23 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
const paginationConfig =
paginationQueryData ?? getDefaultPaginationConfig(PER_PAGE_OPTIONS);
const requestQuery = useMemo(() => {
const query = stagedQuery
? cloneDeep(stagedQuery)
: cloneDeep(initialQueriesMap.traces);
if (query.builder.queryData[0]) {
query.builder.queryData[0].orderBy = [
{
columnName: 'timestamp',
order: orderDirection as 'asc' | 'desc',
},
];
}
return query;
}, [stagedQuery, orderDirection]);
const queryKey = useMemo(
() => [
REACT_QUERY_KEY.GET_QUERY_RANGE,
@@ -84,6 +104,7 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
panelType,
paginationConfig,
options?.selectColumns,
orderDirection,
],
[
stagedQuery,
@@ -93,12 +114,13 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
options?.selectColumns,
maxTime,
minTime,
orderDirection,
],
);
const { data, isFetching, isLoading, isError } = useGetQueryRange(
{
query: stagedQuery || initialQueriesMap.traces,
query: requestQuery,
graphType: panelType,
selectedTime: 'GLOBAL_TIME' as const,
globalSelectedInterval: globalSelectedTime as CustomTimeType,

View File

@@ -1,5 +1,6 @@
import { Tag, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { TelemetryFieldKey } from 'api/v5/v5';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import ROUTES from 'constants/routes';
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
@@ -9,7 +10,6 @@ import { RowData } from 'lib/query/createTableColumnsFromQuery';
import LineClampedText from 'periscope/components/LineClampedText/LineClampedText';
import { Link } from 'react-router-dom';
import { ILog } from 'types/api/logs/log';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { QueryDataV3 } from 'types/api/widgets/getQuery';
export function BlockLink({
@@ -47,7 +47,7 @@ export const getTraceLink = (record: RowData): string =>
})}`;
export const getListColumns = (
selectedColumns: BaseAutocompleteData[],
selectedColumns: TelemetryFieldKey[],
formatTimezoneAdjustedTimestamp: (
input: TimestampInput,
format?: string,
@@ -80,48 +80,58 @@ export const getListColumns = (
];
const columns: ColumnsType<RowData> =
selectedColumns.map(({ dataType, key, type }) => ({
title: key,
dataIndex: key,
key: `${key}-${dataType}-${type}`,
width: 145,
render: (value, item): JSX.Element => {
if (value === '') {
selectedColumns.map((props) => {
const name = props?.name || (props as any)?.key;
const fieldDataType = props?.fieldDataType || (props as any)?.dataType;
const fieldContext = props?.fieldContext || (props as any)?.type;
return {
title: name,
dataIndex: name,
key: `${name}-${fieldDataType}-${fieldContext}`,
width: 145,
render: (value, item): JSX.Element => {
if (value === '') {
return (
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
<Typography data-testid={name}>N/A</Typography>
</BlockLink>
);
}
if (
name === 'httpMethod' ||
name === 'responseStatusCode' ||
name === 'response_status_code' ||
name === 'http_method'
) {
return (
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
<Tag data-testid={name} color="magenta">
{value}
</Tag>
</BlockLink>
);
}
if (name === 'durationNano' || name === 'duration_nano') {
return (
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
<Typography data-testid={name}>{getMs(value)}ms</Typography>
</BlockLink>
);
}
return (
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
<Typography data-testid={key}>N/A</Typography>
<Typography data-testid={name}>
<LineClampedText text={value} lines={3} />
</Typography>
</BlockLink>
);
}
if (key === 'httpMethod' || key === 'responseStatusCode') {
return (
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
<Tag data-testid={key} color="magenta">
{value}
</Tag>
</BlockLink>
);
}
if (key === 'durationNano' || key === 'duration_nano') {
return (
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
<Typography data-testid={key}>{getMs(value)}ms</Typography>
</BlockLink>
);
}
return (
<BlockLink to={getTraceLink(item)} openInNewTab={false}>
<Typography data-testid={key}>
<LineClampedText text={value} lines={3} />
</Typography>
</BlockLink>
);
},
responsive: ['md'],
})) || [];
},
responsive: ['md'],
};
}) || [];
return [...initialColumns, ...columns];
};

View File

@@ -42,6 +42,8 @@ function TracesTableComponent({
});
const { safeNavigate } = useSafeNavigate();
console.log('widget.selectedTracesFields', widget.selectedTracesFields);
useEffect(() => {
setRequestData((prev) => ({
...prev,
@@ -79,6 +81,7 @@ function TracesTableComponent({
[queryTableData],
);
console.log('transformedQueryTableData', transformedQueryTableData, columns);
const handleRow = useCallback(
(record: RowData): HTMLAttributes<RowData> => ({
onClick: (event): void => {

View File

@@ -1,8 +1,8 @@
import { TelemetryFieldKey } from 'api/v5/v5';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { convertKeysToColumnFields } from 'container/LogsExplorerList/utils';
import { placeWidgetAtBottom } from 'container/NewWidget/utils';
import { Dashboard } from 'types/api/dashboard/getAll';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
const baseLogsSelectedColumns = {
@@ -16,7 +16,7 @@ export const addEmptyWidgetInDashboardJSONWithQuery = (
query: Query,
widgetId: string,
panelType?: PANEL_TYPES,
selectedColumns?: BaseAutocompleteData[] | null,
selectedColumns?: TelemetryFieldKey[] | null,
): Dashboard => {
const logsSelectedColumns = [
baseLogsSelectedColumns,

View File

@@ -1,3 +1,4 @@
import { ENTITY_VERSION_V5 } from 'constants/app';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
@@ -58,7 +59,8 @@ export const useGetExplorerQueryRange = (
query: requestData || initialQueriesMap.metrics,
params,
},
version,
// version,
ENTITY_VERSION_V5,
{
...options,
retry: false,

View File

@@ -369,13 +369,13 @@ export const useQueryOperations: UseQueryOperations = ({
setOperators(initialOperators);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
dataSource,
initialDataSource,
panelType,
entityVersion,
query,
operators,
handleMetricAggregateAtributeTypes,
]);

View File

@@ -1,19 +1,66 @@
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
import { AxiosError, AxiosResponse } from 'axios';
import { useQuery, UseQueryResult } from 'react-query';
import { QueryKeySuggestionsResponseProps } from 'types/api/querySuggestions/types';
import { useMemo } from 'react';
import { useQuery, UseQueryOptions, UseQueryResult } from 'react-query';
import {
QueryKeyRequestProps,
QueryKeySuggestionsResponseProps,
} from 'types/api/querySuggestions/types';
export const useGetQueryKeySuggestions = ({
signal,
name,
}: {
signal: string;
name: string;
}): UseQueryResult<
type UseGetQueryKeySuggestions = (
requestData: QueryKeyRequestProps,
options?: UseQueryOptions<
AxiosResponse<QueryKeySuggestionsResponseProps>,
AxiosError
>,
) => UseQueryResult<
AxiosResponse<QueryKeySuggestionsResponseProps>,
AxiosError
> =>
useQuery<AxiosResponse<QueryKeySuggestionsResponseProps>, AxiosError>({
queryKey: ['queryKeySuggestions', signal, name],
queryFn: () => getKeySuggestions({ signal, name }),
>;
export const useGetQueryKeySuggestions: UseGetQueryKeySuggestions = (
{
signal,
searchText,
fieldContext,
fieldDataType,
metricName,
}: QueryKeyRequestProps,
options?: UseQueryOptions<
AxiosResponse<QueryKeySuggestionsResponseProps>,
AxiosError
>,
) => {
const queryKey = useMemo(() => {
if (options?.queryKey && Array.isArray(options.queryKey)) {
return ['queryKeySuggestions', ...options.queryKey];
}
return [
'queryKeySuggestions',
signal,
searchText,
metricName,
fieldContext,
fieldDataType,
];
}, [
options?.queryKey,
signal,
searchText,
metricName,
fieldContext,
fieldDataType,
]);
return useQuery<AxiosResponse<QueryKeySuggestionsResponseProps>, AxiosError>({
queryKey,
queryFn: () =>
getKeySuggestions({
signal,
searchText,
metricName,
fieldContext,
fieldDataType,
}),
...options,
});
};

View File

@@ -3,6 +3,7 @@ import './LogsExplorer.styles.scss';
import * as Sentry from '@sentry/react';
import getLocalStorageKey from 'api/browser/localstorage/get';
import setLocalStorageApi from 'api/browser/localstorage/set';
import { TelemetryFieldKey } from 'api/v5/v5';
import cx from 'classnames';
import ExplorerCard from 'components/ExplorerCard/ExplorerCard';
import QuickFilters from 'components/QuickFilters/QuickFilters';
@@ -31,7 +32,7 @@ import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFall
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom-v5-compat';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import {
getExplorerViewForPanelType,
@@ -82,6 +83,8 @@ function LogsExplorer(): JSX.Element {
handleRunQuery,
handleSetConfig,
updateAllQueriesOperators,
currentQuery,
updateQueriesData,
} = useQueryBuilder();
const { handleExplorerTabChange } = useHandleExplorerTabChange();
@@ -94,6 +97,14 @@ function LogsExplorer(): JSX.Element {
const [shouldReset, setShouldReset] = useState(false);
const [defaultQuery, setDefaultQuery] = useState<Query>(() =>
updateAllQueriesOperators(
initialQueriesMap.logs,
PANEL_TYPES.LIST,
DataSource.LOGS,
),
);
const handleChangeSelectedView = useCallback(
(view: ExplorerViews): void => {
if (selectedView === ExplorerViews.LIST) {
@@ -101,6 +112,30 @@ function LogsExplorer(): JSX.Element {
}
if (view === ExplorerViews.LIST) {
if (
selectedView !== ExplorerViews.LIST &&
currentQuery?.builder?.queryData?.[0]
) {
const filterToRetain = currentQuery.builder.queryData[0].filter;
const newDefaultQuery = updateAllQueriesOperators(
initialQueriesMap.logs,
PANEL_TYPES.LIST,
DataSource.LOGS,
);
const newListQuery = updateQueriesData(
newDefaultQuery,
'queryData',
(item, index) => {
if (index === 0) {
return { ...item, filter: filterToRetain };
}
return item;
},
);
setDefaultQuery(newListQuery);
}
setShouldReset(true);
}
@@ -109,27 +144,34 @@ function LogsExplorer(): JSX.Element {
view === ExplorerViews.TIMESERIES ? PANEL_TYPES.TIME_SERIES : view,
);
},
[handleSetConfig, handleExplorerTabChange, selectedView],
);
const defaultListQuery = useMemo(
() =>
updateAllQueriesOperators(
initialQueriesMap.logs,
PANEL_TYPES.LIST,
DataSource.LOGS,
),
[updateAllQueriesOperators],
[
handleSetConfig,
handleExplorerTabChange,
selectedView,
currentQuery,
updateAllQueriesOperators,
updateQueriesData,
setSelectedView,
],
);
useShareBuilderUrl({
defaultValue: defaultListQuery,
defaultValue: defaultQuery,
forceReset: shouldReset,
});
useEffect(() => {
if (shouldReset) setShouldReset(false);
}, [shouldReset]);
if (shouldReset) {
setShouldReset(false);
setDefaultQuery(
updateAllQueriesOperators(
initialQueriesMap.logs,
PANEL_TYPES.LIST,
DataSource.LOGS,
),
);
}
}, [shouldReset, updateAllQueriesOperators]);
const handleFilterVisibilityChange = (): void => {
setLocalStorageApi(
@@ -158,11 +200,11 @@ function LogsExplorer(): JSX.Element {
// Check if the columns have the required columns (timestamp, body)
const hasRequiredColumns = useCallback(
(columns?: Array<{ key: string }> | null): boolean => {
(columns?: TelemetryFieldKey[] | null): boolean => {
if (!columns?.length) return false;
const hasTimestamp = columns.some((col) => col.key === 'timestamp');
const hasBody = columns.some((col) => col.key === 'body');
const hasTimestamp = columns.some((col) => col.name === 'timestamp');
const hasBody = columns.some((col) => col.name === 'body');
return hasTimestamp && hasBody;
},
@@ -171,7 +213,7 @@ function LogsExplorer(): JSX.Element {
// Merge the columns with the required columns (timestamp, body) if missing
const mergeWithRequiredColumns = useCallback(
(columns: BaseAutocompleteData[]): BaseAutocompleteData[] => [
(columns: TelemetryFieldKey[]): TelemetryFieldKey[] => [
// Add required columns (timestamp, body) if missing
...(!hasRequiredColumns(columns) ? defaultLogsSelectedColumns : []),
...columns,

View File

@@ -29,7 +29,7 @@ function MetricsExplorerPage(): JSX.Element {
[updateAllQueriesOperators],
);
useShareBuilderUrl(defaultQuery);
useShareBuilderUrl({ defaultValue: defaultQuery });
return (
<div className="metrics-explorer-page">

View File

@@ -12,18 +12,28 @@ import { DataSource } from 'types/common/queryBuilder';
export const AllTraceFilterKeyValue: Record<string, string> = {
durationNanoMin: 'Duration',
durationNano: 'Duration',
duration_nano: 'Duration',
durationNanoMax: 'Duration',
'deployment.environment': 'Environment',
hasError: 'Status',
has_error: 'Status',
serviceName: 'Service Name',
'service.name': 'service.name',
name: 'Operation / Name',
rpcMethod: 'RPC Method',
'rpc.method': 'RPC Method',
responseStatusCode: 'Status Code',
response_status_code: 'Status Code',
httpHost: 'HTTP Host',
http_host: 'HTTP Host',
httpMethod: 'HTTP Method',
http_method: 'HTTP Method',
httpRoute: 'HTTP Route',
'http.route': 'HTTP Route',
httpUrl: 'HTTP URL',
'http.url': 'HTTP URL',
traceID: 'Trace ID',
trace_id: 'Trace ID',
} as const;
export type AllTraceFilterKeys = keyof typeof AllTraceFilterKeyValue;

View File

@@ -51,6 +51,7 @@ function TracesExplorer(): JSX.Element {
handleRunQuery,
stagedQuery,
handleSetConfig,
updateQueriesData,
} = useQueryBuilder();
const { options } = useOptionsMenu({
@@ -94,6 +95,14 @@ function TracesExplorer(): JSX.Element {
const [shouldReset, setShouldReset] = useState(false);
const [defaultQuery, setDefaultQuery] = useState<Query>(() =>
updateAllQueriesOperators(
initialQueriesMap.traces,
PANEL_TYPES.LIST,
DataSource.TRACES,
),
);
const handleChangeSelectedView = useCallback(
(view: ExplorerViews): void => {
if (selectedView === ExplorerViews.LIST) {
@@ -101,6 +110,30 @@ function TracesExplorer(): JSX.Element {
}
if (view === ExplorerViews.LIST) {
if (
selectedView !== ExplorerViews.LIST &&
currentQuery?.builder?.queryData?.[0]
) {
const filterToRetain = currentQuery.builder.queryData[0].filter;
const newDefaultQuery = updateAllQueriesOperators(
initialQueriesMap.traces,
PANEL_TYPES.LIST,
DataSource.TRACES,
);
const newListQuery = updateQueriesData(
newDefaultQuery,
'queryData',
(item, index) => {
if (index === 0) {
return { ...item, filter: filterToRetain };
}
return item;
},
);
setDefaultQuery(newListQuery);
}
setShouldReset(true);
}
@@ -109,7 +142,15 @@ function TracesExplorer(): JSX.Element {
view === ExplorerViews.TIMESERIES ? PANEL_TYPES.TIME_SERIES : view,
);
},
[handleSetConfig, handleExplorerTabChange, selectedView],
[
handleSetConfig,
handleExplorerTabChange,
selectedView,
currentQuery,
updateAllQueriesOperators,
updateQueriesData,
setSelectedView,
],
);
const listQuery = useMemo(() => {
@@ -118,16 +159,6 @@ function TracesExplorer(): JSX.Element {
return stagedQuery.builder.queryData.find((item) => !item.disabled) || null;
}, [stagedQuery]);
const defaultQuery = useMemo(
() =>
updateAllQueriesOperators(
initialQueriesMap.traces,
PANEL_TYPES.LIST,
DataSource.TRACES,
),
[updateAllQueriesOperators],
);
const exportDefaultQuery = useMemo(
() =>
updateAllQueriesOperators(
@@ -186,8 +217,17 @@ function TracesExplorer(): JSX.Element {
useShareBuilderUrl({ defaultValue: defaultQuery, forceReset: shouldReset });
useEffect(() => {
if (shouldReset) setShouldReset(false);
}, [shouldReset]);
if (shouldReset) {
setShouldReset(false);
setDefaultQuery(
updateAllQueriesOperators(
initialQueriesMap.traces,
PANEL_TYPES.LIST,
DataSource.TRACES,
),
);
}
}, [shouldReset, updateAllQueriesOperators]);
const [isOpen, setOpen] = useState<boolean>(true);
const logEventCalledRef = useRef(false);

View File

@@ -144,7 +144,7 @@ export function QueryBuilderProvider({
.includes(queryData.aggregateOperator);
if (!isCurrentOperatorAvailableInList) {
return { ...queryData, aggregateOperator: initialOperators[0].value };
return { ...queryData, aggregateOperator: initialOperators[0]?.value };
}
return queryData;
@@ -247,6 +247,8 @@ export function QueryBuilderProvider({
getElementWithActualOperator(item, dataSource, panelType),
);
console.log('queryData', queryData, panelType, dataSource);
return { ...query, builder: { ...query.builder, queryData } };
},

View File

@@ -1,12 +1,12 @@
/* eslint-disable sonarjs/no-identical-functions */
import { render, screen } from '@testing-library/react';
import { TelemetryFieldKey } from 'api/v5/v5';
import {
FormattingOptions,
PreferenceMode,
Preferences,
} from 'providers/preferences/types';
import { MemoryRouter, Route, Switch } from 'react-router-dom';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
PreferenceContextProvider,
@@ -17,7 +17,7 @@ import {
jest.mock('../sync/usePreferenceSync', () => ({
usePreferenceSync: jest.fn().mockReturnValue({
preferences: {
columns: [] as BaseAutocompleteData[],
columns: [] as TelemetryFieldKey[],
formatting: {
maxLines: 2,
format: 'table',

View File

@@ -150,7 +150,7 @@ describe('logsLoaderConfig', () => {
const result = await logsLoaderConfig.default();
expect(result).toEqual({
columns: defaultLogsSelectedColumns as BaseAutocompleteData[],
columns: defaultLogsSelectedColumns,
formatting: {
maxLines: 2,
format: 'table' as LogViewMode,

View File

@@ -1,3 +1,4 @@
import { TelemetryFieldKey } from 'api/v5/v5';
import { LOCALSTORAGE } from 'constants/localStorage';
import { LogViewMode } from 'container/LogsTable';
import { defaultOptionsQuery } from 'container/OptionsMenu/constants';
@@ -7,10 +8,7 @@ import {
PreferenceMode,
Preferences,
} from 'providers/preferences/types';
import {
BaseAutocompleteData,
DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import getLogsUpdaterConfig from '../configs/logsUpdaterConfig';
@@ -65,11 +63,11 @@ describe('logsUpdaterConfig', () => {
setSavedViewPreferences,
);
const newColumns: BaseAutocompleteData[] = [
const newColumns: TelemetryFieldKey[] = [
{
key: 'new-column',
type: 'tag',
dataType: DataTypes.String,
name: 'new-column',
fieldContext: '',
fieldDataType: DataTypes.String,
isColumn: true,
},
];
@@ -114,11 +112,11 @@ describe('logsUpdaterConfig', () => {
setSavedViewPreferences,
);
const newColumns: BaseAutocompleteData[] = [
const newColumns: TelemetryFieldKey[] = [
{
key: 'new-column',
type: 'tag',
dataType: DataTypes.String,
name: 'new-column',
fieldContext: '',
fieldDataType: DataTypes.String,
isColumn: true,
},
];

View File

@@ -4,6 +4,7 @@ import {
BaseAutocompleteData,
DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
import tracesLoaderConfig from '../configs/tracesLoaderConfig';
@@ -125,7 +126,7 @@ describe('tracesLoaderConfig', () => {
const result = await tracesLoaderConfig.default();
expect(result).toEqual({
columns: defaultTraceSelectedColumns as BaseAutocompleteData[],
columns: defaultTraceSelectedColumns as TelemetryFieldKey[],
});
});
});

View File

@@ -1,9 +1,7 @@
import { TelemetryFieldKey } from 'api/v5/v5';
import { LOCALSTORAGE } from 'constants/localStorage';
import { defaultOptionsQuery } from 'container/OptionsMenu/constants';
import {
BaseAutocompleteData,
DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import getTracesUpdaterConfig from '../configs/tracesUpdaterConfig';
import { PreferenceMode } from '../types';
@@ -34,11 +32,11 @@ describe('tracesUpdaterConfig', () => {
const mockSetSavedViewPreferences = jest.fn();
// Test data
const mockColumns: BaseAutocompleteData[] = [
const mockColumns: TelemetryFieldKey[] = [
{
key: 'test-trace-column',
type: 'tag',
dataType: DataTypes.String,
name: 'test-trace-column',
fieldContext: '',
fieldDataType: DataTypes.String,
isColumn: true,
},
];

View File

@@ -1,5 +1,6 @@
/* eslint-disable sonarjs/no-identical-functions */
import { renderHook } from '@testing-library/react';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LogViewMode } from 'container/LogsTable';
import { FontSize } from 'container/OptionsMenu/types';
import {
@@ -8,10 +9,7 @@ import {
Preferences,
} from 'providers/preferences/types';
import { act } from 'react-dom/test-utils';
import {
BaseAutocompleteData,
DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceUpdater } from '../updater/usePreferenceUpdater';
@@ -81,11 +79,11 @@ describe('usePreferenceUpdater', () => {
it('should call the logs updater for updateColumns with logs dataSource', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newColumns: BaseAutocompleteData[] = [
const newColumns: TelemetryFieldKey[] = [
{
key: 'new-column',
type: 'tag',
dataType: DataTypes.String,
name: 'new-column',
fieldContext: '',
fieldDataType: DataTypes.String,
isColumn: true,
},
];
@@ -147,11 +145,11 @@ describe('usePreferenceUpdater', () => {
it('should call the traces updater for updateColumns with traces dataSource', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newColumns: BaseAutocompleteData[] = [
const newColumns: TelemetryFieldKey[] = [
{
key: 'new-trace-column',
type: 'tag',
dataType: DataTypes.String,
name: 'new-trace-column',
fieldContext: '',
fieldDataType: DataTypes.String,
isColumn: true,
},
];
@@ -227,9 +225,9 @@ describe('usePreferenceUpdater', () => {
act(() => {
result.current.updateColumns([
{
key: 'column',
type: 'tag',
dataType: DataTypes.String,
name: 'column',
fieldContext: '',
fieldDataType: DataTypes.String,
isColumn: true,
},
]);

View File

@@ -1,5 +1,6 @@
/* eslint-disable no-empty */
import getLocalStorageKey from 'api/browser/localstorage/get';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LOCALSTORAGE } from 'constants/localStorage';
import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants';
import { FontSize } from 'container/OptionsMenu/types';
@@ -50,10 +51,10 @@ const logsLoaders = {
return { columns: [], formatting: undefined } as any;
},
default: async (): Promise<{
columns: BaseAutocompleteData[];
columns: TelemetryFieldKey[];
formatting: FormattingOptions;
}> => ({
columns: defaultLogsSelectedColumns as BaseAutocompleteData[],
columns: defaultLogsSelectedColumns,
formatting: {
maxLines: 2,
format: 'table',

View File

@@ -1,9 +1,9 @@
import setLocalStorageKey from 'api/browser/localstorage/set';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LOCALSTORAGE } from 'constants/localStorage';
import { defaultOptionsQuery } from 'container/OptionsMenu/constants';
import { FontSize, OptionsQuery } from 'container/OptionsMenu/types';
import { Dispatch, SetStateAction } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { FormattingOptions, PreferenceMode, Preferences } from '../types';
@@ -13,10 +13,10 @@ const getLogsUpdaterConfig = (
redirectWithOptionsData: (options: OptionsQuery) => void,
setSavedViewPreferences: Dispatch<SetStateAction<Preferences | null>>,
): {
updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void;
updateColumns: (newColumns: TelemetryFieldKey[], mode: string) => void;
updateFormatting: (newFormatting: FormattingOptions, mode: string) => void;
} => ({
updateColumns: (newColumns: BaseAutocompleteData[], mode: string): void => {
updateColumns: (newColumns: TelemetryFieldKey[], mode: string): void => {
if (mode === PreferenceMode.SAVED_VIEW) {
setSavedViewPreferences((prev) => {
if (!prev) {

View File

@@ -1,5 +1,6 @@
/* eslint-disable no-empty */
import getLocalStorageKey from 'api/browser/localstorage/get';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LOCALSTORAGE } from 'constants/localStorage';
import { defaultTraceSelectedColumns } from 'container/OptionsMenu/constants';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
@@ -33,9 +34,9 @@ const tracesLoaders = {
return { columns: [] };
},
default: async (): Promise<{
columns: BaseAutocompleteData[];
columns: TelemetryFieldKey[];
}> => ({
columns: defaultTraceSelectedColumns as BaseAutocompleteData[],
columns: defaultTraceSelectedColumns,
}),
priority: ['local', 'url', 'default'] as const,
};

View File

@@ -1,9 +1,9 @@
import setLocalStorageKey from 'api/browser/localstorage/set';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LOCALSTORAGE } from 'constants/localStorage';
import { defaultOptionsQuery } from 'container/OptionsMenu/constants';
import { FontSize, OptionsQuery } from 'container/OptionsMenu/types';
import { Dispatch, SetStateAction } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { PreferenceMode, Preferences } from '../types';
@@ -12,10 +12,10 @@ const getTracesUpdaterConfig = (
redirectWithOptionsData: (options: OptionsQuery) => void,
setSavedViewPreferences: Dispatch<SetStateAction<Preferences | null>>,
): {
updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void;
updateColumns: (newColumns: TelemetryFieldKey[], mode: string) => void;
updateFormatting: () => void;
} => ({
updateColumns: (newColumns: BaseAutocompleteData[], mode: string): void => {
updateColumns: (newColumns: TelemetryFieldKey[], mode: string): void => {
// remove the formatting props
if (mode === PreferenceMode.SAVED_VIEW) {
setSavedViewPreferences({

View File

@@ -1,7 +1,7 @@
/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable no-empty */
import { TelemetryFieldKey } from 'api/v5/v5';
import { useEffect, useState } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
import logsLoaderConfig from '../configs/logsLoaderConfig';
@@ -43,14 +43,14 @@ async function preferencesLoader<T>(config: {
// Use the generic loader with specific configs
async function logsPreferencesLoader(): Promise<{
columns: BaseAutocompleteData[];
columns: TelemetryFieldKey[];
formatting: FormattingOptions;
}> {
return preferencesLoader(logsLoaderConfig);
}
async function tracesPreferencesLoader(): Promise<{
columns: BaseAutocompleteData[];
columns: TelemetryFieldKey[];
}> {
return preferencesLoader(tracesLoaderConfig);
}

View File

@@ -1,8 +1,8 @@
import { TelemetryFieldKey } from 'api/v5/v5';
import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants';
import { defaultSelectedColumns as defaultTracesSelectedColumns } from 'container/TracesExplorer/ListView/configs';
import { useGetAllViews } from 'hooks/saveViews/useGetAllViews';
import { useEffect, useState } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceLoader } from '../loader/usePreferenceLoader';
@@ -21,7 +21,7 @@ export function usePreferenceSync({
preferences: Preferences | null;
loading: boolean;
error: Error | null;
updateColumns: (newColumns: BaseAutocompleteData[]) => void;
updateColumns: (newColumns: TelemetryFieldKey[]) => void;
updateFormatting: (newFormatting: FormattingOptions) => void;
} {
const { data: viewsData } = useGetAllViews(dataSource);
@@ -37,7 +37,7 @@ export function usePreferenceSync({
)?.extraData;
const parsedExtraData = JSON.parse(extraData || '{}');
let columns: BaseAutocompleteData[] = [];
let columns: TelemetryFieldKey[] = [];
let formatting: FormattingOptions | undefined;
if (dataSource === DataSource.LOGS) {
columns = parsedExtraData?.selectColumns || defaultLogsSelectedColumns;

View File

@@ -1,6 +1,6 @@
import { TelemetryFieldKey } from 'api/v5/v5';
import { LogViewMode } from 'container/LogsTable';
import { FontSize } from 'container/OptionsMenu/types';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
export enum PreferenceMode {
@@ -15,7 +15,7 @@ export interface PreferenceContextValue {
mode: PreferenceMode;
savedViewId?: string;
dataSource: DataSource;
updateColumns: (newColumns: BaseAutocompleteData[]) => void;
updateColumns: (newColumns: TelemetryFieldKey[]) => void;
updateFormatting: (newFormatting: FormattingOptions) => void;
}
@@ -27,6 +27,6 @@ export interface FormattingOptions {
}
export interface Preferences {
columns: BaseAutocompleteData[];
columns: TelemetryFieldKey[];
formatting?: FormattingOptions;
}

View File

@@ -1,3 +1,4 @@
import { TelemetryFieldKey } from 'api/v5/v5';
import {
defaultOptionsQuery,
URL_OPTIONS,
@@ -5,7 +6,6 @@ import {
import { OptionsQuery } from 'container/OptionsMenu/types';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { Dispatch, SetStateAction } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
import getLogsUpdaterConfig from '../configs/logsUpdaterConfig';
@@ -24,7 +24,7 @@ const getUpdaterConfig = (
): Record<
DataSource,
{
updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void;
updateColumns: (newColumns: TelemetryFieldKey[], mode: string) => void;
updateFormatting: (newFormatting: FormattingOptions, mode: string) => void;
}
> => ({
@@ -53,7 +53,7 @@ export function usePreferenceUpdater({
setReSync: Dispatch<SetStateAction<boolean>>;
setSavedViewPreferences: Dispatch<SetStateAction<Preferences | null>>;
}): {
updateColumns: (newColumns: BaseAutocompleteData[]) => void;
updateColumns: (newColumns: TelemetryFieldKey[]) => void;
updateFormatting: (newFormatting: FormattingOptions) => void;
} {
const {
@@ -66,7 +66,7 @@ export function usePreferenceUpdater({
)[dataSource];
return {
updateColumns: (newColumns: BaseAutocompleteData[]): void => {
updateColumns: (newColumns: TelemetryFieldKey[]): void => {
updater.updateColumns(newColumns, mode);
setReSync(true);
},

View File

@@ -16,7 +16,9 @@ export interface IQueryPair {
key: string;
operator: string;
value?: string;
valueList?: string[];
hasNegation?: boolean;
isMultiValue?: boolean;
position: {
keyStart: number;
keyEnd: number;
@@ -27,6 +29,10 @@ export interface IQueryPair {
negationStart?: number;
negationEnd?: number;
};
valuesPosition?: {
start?: number;
end?: number;
}[];
isComplete: boolean; // true if the pair has all three components
}
@@ -44,7 +50,6 @@ export interface IQueryContext {
isInConjunction?: boolean;
isInParenthesis?: boolean;
isInBracketList?: boolean; // For multi-value operators like IN where values are in brackets
isValueWrappedInQuotes?: boolean;
keyToken?: string;
operatorToken?: string;
valueToken?: string;

View File

@@ -7,7 +7,7 @@ import { Layout } from 'react-grid-layout';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { IField } from '../logs/fields';
import { BaseAutocompleteData } from '../queryBuilder/queryAutocompleteResponse';
import { TelemetryFieldKey } from '../v5/queryRange';
export const VariableQueryTypeArr = ['QUERY', 'TEXTBOX', 'CUSTOM'] as const;
export type TVariableQueryType = typeof VariableQueryTypeArr[number];
@@ -115,7 +115,7 @@ export interface IBaseWidget {
fillSpans?: boolean;
columnUnits?: ColumnUnit;
selectedLogFields: IField[] | null;
selectedTracesFields: BaseAutocompleteData[] | null;
selectedTracesFields: TelemetryFieldKey[] | null;
isLogScale?: boolean;
columnWidths?: Record<string, number>;
legendPosition?: LegendPosition;

View File

@@ -1,3 +1,4 @@
import { TelemetryFieldKey } from 'api/v5/v5';
import { Format } from 'container/NewWidget/RightContainer/types';
import { EQueryType } from 'types/common/dashboard';
import {
@@ -85,7 +86,7 @@ export type IBuilderQuery = {
legend: string;
pageSize?: number;
offset?: number;
selectColumns?: BaseAutocompleteData[];
selectColumns?: BaseAutocompleteData[] | TelemetryFieldKey[];
};
export interface IClickHouseQuery {

View File

@@ -4,10 +4,10 @@ export interface QueryKeyDataSuggestionsProps {
info?: string;
apply?: string;
detail?: string;
fieldContext: string;
fieldDataType: string;
fieldContext?: 'resource' | 'scope' | 'attribute' | 'span';
fieldDataType?: 'string' | 'number' | 'boolean';
name: string;
signal: string;
signal: 'traces' | 'logs' | 'metrics';
}
export interface QueryKeySuggestionsResponseProps {
@@ -21,8 +21,11 @@ export interface QueryKeySuggestionsResponseProps {
}
export interface QueryKeyRequestProps {
signal: string;
name: string;
signal: 'traces' | 'logs' | 'metrics';
searchText: string;
fieldContext?: 'resource' | 'scope' | 'attribute' | 'span';
fieldDataType?: 'string' | 'number' | 'boolean';
metricName?: string;
}
export interface QueryKeyValueSuggestionsProps {

View File

@@ -130,6 +130,9 @@ export interface TelemetryFieldKey {
fieldContext?: FieldContext;
fieldDataType?: FieldDataType;
materialized?: boolean;
isColumn?: boolean;
isJSON?: boolean;
isIndexed?: boolean;
}
export interface Filter {

View File

@@ -13,6 +13,7 @@ import {
isNonValueOperatorToken,
isOperatorToken,
isValueToken,
isWrappedUnderQuotes,
} from './tokenUtils';
// Function to normalize multiple spaces to single spaces when not in quotes
@@ -152,6 +153,32 @@ function determineTokenContext(
};
}
export function getCurrentValueIndexAtCursor(
valuesPosition: {
start?: number;
end?: number;
}[],
cursorIndex: number,
): number | null {
if (!valuesPosition || valuesPosition.length === 0) return null;
// Find the value that contains the cursor index
for (let i = 0; i < valuesPosition.length; i++) {
const start = valuesPosition[i].start;
const end = valuesPosition[i].end;
if (
start !== undefined &&
end !== undefined &&
start <= cursorIndex &&
cursorIndex <= end
) {
return i;
}
}
return null;
}
// Function to determine token context boundaries more precisely
function determineContextBoundaries(
query: string,
@@ -170,32 +197,7 @@ function determineContextBoundaries(
let currentPair: IQueryPair | null = null;
if (queryPairs.length > 0) {
// Look for the rightmost pair whose end position is before or at the cursor
let bestMatch: IQueryPair | null = null;
for (const pair of queryPairs) {
const { position } = pair;
// Find the rightmost position of this pair
const pairEnd = position.valueEnd || position.operatorEnd || position.keyEnd;
// FIXED: Consider cursor position at the end of a token (including the last character)
if (
(pairEnd <= cursorIndex || pairEnd + 1 === cursorIndex) &&
(!bestMatch ||
pairEnd >
(bestMatch.position.valueEnd ||
bestMatch.position.operatorEnd ||
bestMatch.position.keyEnd))
) {
bestMatch = pair;
}
}
// If we found a match, use it
if (bestMatch) {
currentPair = bestMatch;
}
currentPair = getCurrentQueryPair(queryPairs, query, cursorIndex);
}
// Check for bracket context first (could be part of an IN operator's value)
@@ -642,38 +644,7 @@ export function getQueryContextAtCursor(
// Find the current pair without causing a circular dependency
let currentPair: IQueryPair | null = null;
if (queryPairs.length > 0) {
// Look for the rightmost pair whose end position is before or at the cursor
let bestMatch: IQueryPair | null = null;
for (const pair of queryPairs) {
const { position } = pair;
// Find the rightmost position of this pair
const pairEnd =
position.valueEnd || position.operatorEnd || position.keyEnd;
// FIXED: If this pair ends at or before the cursor (including exactly at the end),
// and it's further right than our previous best match
if (
(pairEnd <= adjustedCursorIndex || pairEnd + 1 === adjustedCursorIndex) &&
(!bestMatch ||
pairEnd >
(bestMatch.position.valueEnd ||
bestMatch.position.operatorEnd ||
bestMatch.position.keyEnd))
) {
bestMatch = pair;
}
}
// If we found a match, use it
if (bestMatch) {
currentPair = bestMatch;
}
// If cursor is at the end, use the last pair
else if (adjustedCursorIndex >= input.length) {
currentPair = queryPairs[queryPairs.length - 1];
}
currentPair = getCurrentQueryPair(queryPairs, query, adjustedCursorIndex);
}
// Determine precise context boundaries
@@ -754,8 +725,6 @@ export function getQueryContextAtCursor(
const operatorToken = currentPair?.operator || '';
const valueToken = currentPair?.value || '';
const isValueWrappedInQuotes = isWrappedUnderQuotes(valueToken);
// Determine if we're in a multi-value operator context
const isForMultiValueOperator = isMultiValueOperator(operatorToken);
@@ -782,7 +751,6 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInParenthesis: isInParenthesisBoundary || false,
isInBracketList: isInBracketListBoundary || false,
isValueWrappedInQuotes: isValueWrappedInQuotes,
keyToken: isInKeyBoundary
? keyToken
: isInOperatorBoundary || finalIsInValue
@@ -1237,9 +1205,12 @@ export function extractQueryPairs(query: string): IQueryPair[] {
const queryPairs: IQueryPair[] = [];
let currentPair: Partial<IQueryPair> | null = null;
let iterator = 0;
// Process tokens to build triplets
for (let i = 0; i < allTokens.length; i++) {
const token = allTokens[i];
while (iterator < allTokens.length) {
const token = allTokens[iterator];
iterator += 1;
// Skip EOF and whitespace tokens
if (token.type === FilterQueryLexer.EOF || token.channel !== 0) {
@@ -1257,7 +1228,10 @@ export function extractQueryPairs(query: string): IQueryPair[] {
key: currentPair.key,
operator: currentPair.operator || '',
value: currentPair.value,
valueList: currentPair.valueList || [],
valuesPosition: currentPair.valuesPosition || [],
hasNegation: currentPair.hasNegation || false,
isMultiValue: currentPair.isMultiValue || false,
position: {
keyStart: currentPair.position?.keyStart || 0,
keyEnd: currentPair.position?.keyEnd || 0,
@@ -1310,6 +1284,55 @@ export function extractQueryPairs(query: string): IQueryPair[] {
currentPair.key &&
!currentPair.operator
) {
let multiValueStart: number | undefined;
let multiValueEnd: number | undefined;
if (isMultiValueOperator(token.text)) {
currentPair.isMultiValue = true;
// Iterate from '[' || '(' till ']' || ')' to get all the values
const valueList: string[] = [];
const valuesPosition: { start: number; end: number }[] = [];
if (
[FilterQueryLexer.LPAREN, FilterQueryLexer.LBRACK].includes(
allTokens[iterator].type,
)
) {
multiValueStart = allTokens[iterator].start;
iterator += 1;
const closingToken =
allTokens[iterator].type === FilterQueryLexer.LPAREN
? FilterQueryLexer.RPAREN
: FilterQueryLexer.RBRACK;
while (
allTokens[iterator].type !== closingToken &&
iterator < allTokens.length
) {
if (isValueToken(allTokens[iterator].type)) {
valueList.push(allTokens[iterator].text);
valuesPosition.push({
start: allTokens[iterator].start,
end: allTokens[iterator].stop,
});
}
iterator += 1;
}
if (allTokens[iterator].type === closingToken) {
multiValueEnd = allTokens[iterator].stop;
}
}
currentPair.valuesPosition = valuesPosition;
currentPair.valueList = valueList;
if (multiValueStart && multiValueEnd) {
currentPair.value = query.substring(multiValueStart, multiValueEnd + 1);
}
}
currentPair.operator = token.text;
// Ensure we create a valid position object with all required fields
currentPair.position = {
@@ -1317,8 +1340,8 @@ export function extractQueryPairs(query: string): IQueryPair[] {
keyEnd: currentPair.position?.keyEnd || 0,
operatorStart: token.start,
operatorEnd: token.stop,
valueStart: currentPair.position?.valueStart,
valueEnd: currentPair.position?.valueEnd,
valueStart: multiValueStart || currentPair.position?.valueStart,
valueEnd: multiValueEnd || currentPair.position?.valueEnd,
negationStart: currentPair.position?.negationStart || 0,
negationEnd: currentPair.position?.negationEnd || 0,
};
@@ -1350,7 +1373,10 @@ export function extractQueryPairs(query: string): IQueryPair[] {
key: currentPair.key,
operator: currentPair.operator || '',
value: currentPair.value,
valueList: currentPair.valueList || [],
valuesPosition: currentPair.valuesPosition || [],
hasNegation: currentPair.hasNegation || false,
isMultiValue: currentPair.isMultiValue || false,
position: {
keyStart: currentPair.position?.keyStart || 0,
keyEnd: currentPair.position?.keyEnd || 0,
@@ -1379,7 +1405,10 @@ export function extractQueryPairs(query: string): IQueryPair[] {
key: currentPair.key,
operator: currentPair.operator || '',
value: currentPair.value,
valueList: currentPair.valueList || [],
valuesPosition: currentPair.valuesPosition || [],
hasNegation: currentPair.hasNegation || false,
isMultiValue: currentPair.isMultiValue || false,
position: {
keyStart: currentPair.position?.keyStart || 0,
keyEnd: currentPair.position?.keyEnd || 0,
@@ -1410,18 +1439,17 @@ export function extractQueryPairs(query: string): IQueryPair[] {
* This is useful for getting suggestions based on the current context
* The function finds the rightmost complete pair that ends before or at the cursor position
*
* @param query The query string
* @param queryPairs An array of IQueryPair objects representing the key-operator-value triplets
* @param query The full query string
* @param cursorIndex The position of the cursor in the query
* @returns The query pair at the cursor position, or null if not found
*/
export function getCurrentQueryPair(
queryPairs: IQueryPair[],
query: string,
cursorIndex: number,
): IQueryPair | null {
try {
const queryPairs = extractQueryPairs(query);
// Removed the circular dependency by not calling getQueryContextAtCursor here
// If we have pairs, try to find the one at the cursor position
if (queryPairs.length > 0) {
// Look for the rightmost pair whose end position is before or at the cursor
@@ -1434,9 +1462,13 @@ export function getCurrentQueryPair(
const pairEnd =
position.valueEnd || position.operatorEnd || position.keyEnd;
const pairStart =
position.keyStart || position.operatorStart || position.valueStart || 0;
// If this pair ends at or before the cursor, and it's further right than our previous best match
if (
pairEnd <= cursorIndex &&
pairEnd >= cursorIndex &&
pairStart <= cursorIndex &&
(!bestMatch ||
pairEnd >
(bestMatch.position.valueEnd ||

View File

@@ -60,7 +60,7 @@ export function isMultiValueOperator(operatorToken?: string): boolean {
if (!operatorToken) return false;
const upperOp = operatorToken.toUpperCase();
return upperOp === 'IN' || upperOp === 'NOT IN';
return upperOp === 'IN';
}
export function isFunctionToken(tokenType: number): boolean {

File diff suppressed because one or more lines are too long

View File

@@ -1,44 +0,0 @@
LPAREN=1
RPAREN=2
LBRACK=3
RBRACK=4
COMMA=5
EQUALS=6
NOT_EQUALS=7
NEQ=8
LT=9
LE=10
GT=11
GE=12
LIKE=13
NOT_LIKE=14
ILIKE=15
NOT_ILIKE=16
BETWEEN=17
EXISTS=18
REGEXP=19
CONTAINS=20
IN=21
NOT=22
AND=23
OR=24
HAS=25
HASANY=26
HASALL=27
BOOL=28
NUMBER=29
QUOTED_TEXT=30
KEY=31
WS=32
FREETEXT=33
'('=1
')'=2
'['=3
']'=4
','=5
'!='=7
'<>'=8
'<'=9
'<='=10
'>'=11
'>='=12

File diff suppressed because one or more lines are too long

View File

@@ -1,356 +0,0 @@
// Generated from /Users/abhikumar/Documents/Projects/signoz/grammar/FilterQuery.g4 by ANTLR 4.13.1
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"})
public class FilterQueryLexer extends Lexer {
static { RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
LPAREN=1, RPAREN=2, LBRACK=3, RBRACK=4, COMMA=5, EQUALS=6, NOT_EQUALS=7,
NEQ=8, LT=9, LE=10, GT=11, GE=12, LIKE=13, NOT_LIKE=14, ILIKE=15, NOT_ILIKE=16,
BETWEEN=17, EXISTS=18, REGEXP=19, CONTAINS=20, IN=21, NOT=22, AND=23,
OR=24, HAS=25, HASANY=26, HASALL=27, BOOL=28, NUMBER=29, QUOTED_TEXT=30,
KEY=31, WS=32, FREETEXT=33;
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static String[] modeNames = {
"DEFAULT_MODE"
};
private static String[] makeRuleNames() {
return new String[] {
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "NOT_LIKE", "ILIKE", "NOT_ILIKE",
"BETWEEN", "EXISTS", "REGEXP", "CONTAINS", "IN", "NOT", "AND", "OR",
"HAS", "HASANY", "HASALL", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT",
"EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "FREETEXT"
};
}
public static final String[] ruleNames = makeRuleNames();
private static String[] makeLiteralNames() {
return new String[] {
null, "'('", "')'", "'['", "']'", "','", null, "'!='", "'<>'", "'<'",
"'<='", "'>'", "'>='"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
private static String[] makeSymbolicNames() {
return new String[] {
null, "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "NOT_LIKE", "ILIKE", "NOT_ILIKE",
"BETWEEN", "EXISTS", "REGEXP", "CONTAINS", "IN", "NOT", "AND", "OR",
"HAS", "HASANY", "HASALL", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY", "WS",
"FREETEXT"
};
}
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
/**
* @deprecated Use {@link #VOCABULARY} instead.
*/
@Deprecated
public static final String[] tokenNames;
static {
tokenNames = new String[_SYMBOLIC_NAMES.length];
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = VOCABULARY.getLiteralName(i);
if (tokenNames[i] == null) {
tokenNames[i] = VOCABULARY.getSymbolicName(i);
}
if (tokenNames[i] == null) {
tokenNames[i] = "<INVALID>";
}
}
}
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
public FilterQueryLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
}
@Override
public String getGrammarFileName() { return "FilterQuery.g4"; }
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String[] getChannelNames() { return channelNames; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\u0004\u0000!\u014e\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+
"\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+
"\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+
"\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+
"\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002"+
"\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002"+
"\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002"+
"\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002"+
"\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002"+
"\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002"+
"\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007"+
"!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002%\u0007%\u0001\u0000"+
"\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002\u0001\u0003"+
"\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001\u0005"+
"\u0003\u0005[\b\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0007"+
"\u0001\u0007\u0001\u0007\u0001\b\u0001\b\u0001\t\u0001\t\u0001\t\u0001"+
"\n\u0001\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\f\u0001\f\u0001\f"+
"\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001\r\u0004\rv\b\r\u000b\r"+
"\f\rw\u0001\r\u0001\r\u0001\r\u0001\r\u0001\r\u0001\u000e\u0001\u000e"+
"\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001\u000f"+
"\u0001\u000f\u0001\u000f\u0004\u000f\u0089\b\u000f\u000b\u000f\f\u000f"+
"\u008a\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001"+
"\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001"+
"\u0010\u0001\u0010\u0001\u0010\u0001\u0011\u0001\u0011\u0001\u0011\u0001"+
"\u0011\u0001\u0011\u0001\u0011\u0003\u0011\u00a1\b\u0011\u0001\u0012\u0001"+
"\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001"+
"\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001"+
"\u0013\u0001\u0013\u0003\u0013\u00b2\b\u0013\u0001\u0014\u0001\u0014\u0001"+
"\u0014\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0016\u0001"+
"\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001\u0017\u0001\u0017\u0001"+
"\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0019\u0001\u0019\u0001"+
"\u0019\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001"+
"\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001"+
"\u001b\u0001\u001b\u0001\u001b\u0001\u001b\u0001\u001b\u0001\u001b\u0001"+
"\u001b\u0001\u001b\u0001\u001b\u0003\u001b\u00dd\b\u001b\u0001\u001c\u0001"+
"\u001c\u0001\u001d\u0003\u001d\u00e2\b\u001d\u0001\u001d\u0004\u001d\u00e5"+
"\b\u001d\u000b\u001d\f\u001d\u00e6\u0001\u001d\u0001\u001d\u0005\u001d"+
"\u00eb\b\u001d\n\u001d\f\u001d\u00ee\t\u001d\u0003\u001d\u00f0\b\u001d"+
"\u0001\u001d\u0001\u001d\u0003\u001d\u00f4\b\u001d\u0001\u001d\u0004\u001d"+
"\u00f7\b\u001d\u000b\u001d\f\u001d\u00f8\u0003\u001d\u00fb\b\u001d\u0001"+
"\u001d\u0003\u001d\u00fe\b\u001d\u0001\u001d\u0001\u001d\u0004\u001d\u0102"+
"\b\u001d\u000b\u001d\f\u001d\u0103\u0001\u001d\u0001\u001d\u0003\u001d"+
"\u0108\b\u001d\u0001\u001d\u0004\u001d\u010b\b\u001d\u000b\u001d\f\u001d"+
"\u010c\u0003\u001d\u010f\b\u001d\u0003\u001d\u0111\b\u001d\u0001\u001e"+
"\u0001\u001e\u0001\u001e\u0001\u001e\u0005\u001e\u0117\b\u001e\n\u001e"+
"\f\u001e\u011a\t\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001\u001e"+
"\u0001\u001e\u0005\u001e\u0121\b\u001e\n\u001e\f\u001e\u0124\t\u001e\u0001"+
"\u001e\u0003\u001e\u0127\b\u001e\u0001\u001f\u0001\u001f\u0005\u001f\u012b"+
"\b\u001f\n\u001f\f\u001f\u012e\t\u001f\u0001 \u0001 \u0001 \u0001!\u0001"+
"!\u0001!\u0001!\u0001\"\u0001\"\u0001\"\u0001\"\u0001\"\u0005\"\u013c"+
"\b\"\n\"\f\"\u013f\t\"\u0001#\u0004#\u0142\b#\u000b#\f#\u0143\u0001#\u0001"+
"#\u0001$\u0001$\u0001%\u0004%\u014b\b%\u000b%\f%\u014c\u0000\u0000&\u0001"+
"\u0001\u0003\u0002\u0005\u0003\u0007\u0004\t\u0005\u000b\u0006\r\u0007"+
"\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017\f\u0019\r\u001b\u000e\u001d"+
"\u000f\u001f\u0010!\u0011#\u0012%\u0013\'\u0014)\u0015+\u0016-\u0017/"+
"\u00181\u00193\u001a5\u001b7\u001c9\u0000;\u001d=\u001e?\u0000A\u0000"+
"C\u0000E\u001fG I\u0000K!\u0001\u0000\u001e\u0002\u0000LLll\u0002\u0000"+
"IIii\u0002\u0000KKkk\u0002\u0000EEee\u0002\u0000NNnn\u0002\u0000OOoo\u0002"+
"\u0000TTtt\u0002\u0000\t\t \u0002\u0000BBbb\u0002\u0000WWww\u0002\u0000"+
"XXxx\u0002\u0000SSss\u0002\u0000RRrr\u0002\u0000GGgg\u0002\u0000PPpp\u0002"+
"\u0000CCcc\u0002\u0000AAaa\u0002\u0000DDdd\u0002\u0000HHhh\u0002\u0000"+
"YYyy\u0002\u0000UUuu\u0002\u0000FFff\u0002\u0000++--\u0002\u0000\"\"\\"+
"\\\u0002\u0000\'\'\\\\\u0003\u0000$$AZaz\u0006\u0000$$--0:AZ__az\u0003"+
"\u0000\t\n\r\r \u0001\u000009\b\u0000\t\n\r\r \"\'),,<>[[]]\u0166\u0000"+
"\u0001\u0001\u0000\u0000\u0000\u0000\u0003\u0001\u0000\u0000\u0000\u0000"+
"\u0005\u0001\u0000\u0000\u0000\u0000\u0007\u0001\u0000\u0000\u0000\u0000"+
"\t\u0001\u0000\u0000\u0000\u0000\u000b\u0001\u0000\u0000\u0000\u0000\r"+
"\u0001\u0000\u0000\u0000\u0000\u000f\u0001\u0000\u0000\u0000\u0000\u0011"+
"\u0001\u0000\u0000\u0000\u0000\u0013\u0001\u0000\u0000\u0000\u0000\u0015"+
"\u0001\u0000\u0000\u0000\u0000\u0017\u0001\u0000\u0000\u0000\u0000\u0019"+
"\u0001\u0000\u0000\u0000\u0000\u001b\u0001\u0000\u0000\u0000\u0000\u001d"+
"\u0001\u0000\u0000\u0000\u0000\u001f\u0001\u0000\u0000\u0000\u0000!\u0001"+
"\u0000\u0000\u0000\u0000#\u0001\u0000\u0000\u0000\u0000%\u0001\u0000\u0000"+
"\u0000\u0000\'\u0001\u0000\u0000\u0000\u0000)\u0001\u0000\u0000\u0000"+
"\u0000+\u0001\u0000\u0000\u0000\u0000-\u0001\u0000\u0000\u0000\u0000/"+
"\u0001\u0000\u0000\u0000\u00001\u0001\u0000\u0000\u0000\u00003\u0001\u0000"+
"\u0000\u0000\u00005\u0001\u0000\u0000\u0000\u00007\u0001\u0000\u0000\u0000"+
"\u0000;\u0001\u0000\u0000\u0000\u0000=\u0001\u0000\u0000\u0000\u0000E"+
"\u0001\u0000\u0000\u0000\u0000G\u0001\u0000\u0000\u0000\u0000K\u0001\u0000"+
"\u0000\u0000\u0001M\u0001\u0000\u0000\u0000\u0003O\u0001\u0000\u0000\u0000"+
"\u0005Q\u0001\u0000\u0000\u0000\u0007S\u0001\u0000\u0000\u0000\tU\u0001"+
"\u0000\u0000\u0000\u000bZ\u0001\u0000\u0000\u0000\r\\\u0001\u0000\u0000"+
"\u0000\u000f_\u0001\u0000\u0000\u0000\u0011b\u0001\u0000\u0000\u0000\u0013"+
"d\u0001\u0000\u0000\u0000\u0015g\u0001\u0000\u0000\u0000\u0017i\u0001"+
"\u0000\u0000\u0000\u0019l\u0001\u0000\u0000\u0000\u001bq\u0001\u0000\u0000"+
"\u0000\u001d~\u0001\u0000\u0000\u0000\u001f\u0084\u0001\u0000\u0000\u0000"+
"!\u0092\u0001\u0000\u0000\u0000#\u009a\u0001\u0000\u0000\u0000%\u00a2"+
"\u0001\u0000\u0000\u0000\'\u00a9\u0001\u0000\u0000\u0000)\u00b3\u0001"+
"\u0000\u0000\u0000+\u00b6\u0001\u0000\u0000\u0000-\u00ba\u0001\u0000\u0000"+
"\u0000/\u00be\u0001\u0000\u0000\u00001\u00c1\u0001\u0000\u0000\u00003"+
"\u00c5\u0001\u0000\u0000\u00005\u00cc\u0001\u0000\u0000\u00007\u00dc\u0001"+
"\u0000\u0000\u00009\u00de\u0001\u0000\u0000\u0000;\u0110\u0001\u0000\u0000"+
"\u0000=\u0126\u0001\u0000\u0000\u0000?\u0128\u0001\u0000\u0000\u0000A"+
"\u012f\u0001\u0000\u0000\u0000C\u0132\u0001\u0000\u0000\u0000E\u0136\u0001"+
"\u0000\u0000\u0000G\u0141\u0001\u0000\u0000\u0000I\u0147\u0001\u0000\u0000"+
"\u0000K\u014a\u0001\u0000\u0000\u0000MN\u0005(\u0000\u0000N\u0002\u0001"+
"\u0000\u0000\u0000OP\u0005)\u0000\u0000P\u0004\u0001\u0000\u0000\u0000"+
"QR\u0005[\u0000\u0000R\u0006\u0001\u0000\u0000\u0000ST\u0005]\u0000\u0000"+
"T\b\u0001\u0000\u0000\u0000UV\u0005,\u0000\u0000V\n\u0001\u0000\u0000"+
"\u0000W[\u0005=\u0000\u0000XY\u0005=\u0000\u0000Y[\u0005=\u0000\u0000"+
"ZW\u0001\u0000\u0000\u0000ZX\u0001\u0000\u0000\u0000[\f\u0001\u0000\u0000"+
"\u0000\\]\u0005!\u0000\u0000]^\u0005=\u0000\u0000^\u000e\u0001\u0000\u0000"+
"\u0000_`\u0005<\u0000\u0000`a\u0005>\u0000\u0000a\u0010\u0001\u0000\u0000"+
"\u0000bc\u0005<\u0000\u0000c\u0012\u0001\u0000\u0000\u0000de\u0005<\u0000"+
"\u0000ef\u0005=\u0000\u0000f\u0014\u0001\u0000\u0000\u0000gh\u0005>\u0000"+
"\u0000h\u0016\u0001\u0000\u0000\u0000ij\u0005>\u0000\u0000jk\u0005=\u0000"+
"\u0000k\u0018\u0001\u0000\u0000\u0000lm\u0007\u0000\u0000\u0000mn\u0007"+
"\u0001\u0000\u0000no\u0007\u0002\u0000\u0000op\u0007\u0003\u0000\u0000"+
"p\u001a\u0001\u0000\u0000\u0000qr\u0007\u0004\u0000\u0000rs\u0007\u0005"+
"\u0000\u0000su\u0007\u0006\u0000\u0000tv\u0007\u0007\u0000\u0000ut\u0001"+
"\u0000\u0000\u0000vw\u0001\u0000\u0000\u0000wu\u0001\u0000\u0000\u0000"+
"wx\u0001\u0000\u0000\u0000xy\u0001\u0000\u0000\u0000yz\u0007\u0000\u0000"+
"\u0000z{\u0007\u0001\u0000\u0000{|\u0007\u0002\u0000\u0000|}\u0007\u0003"+
"\u0000\u0000}\u001c\u0001\u0000\u0000\u0000~\u007f\u0007\u0001\u0000\u0000"+
"\u007f\u0080\u0007\u0000\u0000\u0000\u0080\u0081\u0007\u0001\u0000\u0000"+
"\u0081\u0082\u0007\u0002\u0000\u0000\u0082\u0083\u0007\u0003\u0000\u0000"+
"\u0083\u001e\u0001\u0000\u0000\u0000\u0084\u0085\u0007\u0004\u0000\u0000"+
"\u0085\u0086\u0007\u0005\u0000\u0000\u0086\u0088\u0007\u0006\u0000\u0000"+
"\u0087\u0089\u0007\u0007\u0000\u0000\u0088\u0087\u0001\u0000\u0000\u0000"+
"\u0089\u008a\u0001\u0000\u0000\u0000\u008a\u0088\u0001\u0000\u0000\u0000"+
"\u008a\u008b\u0001\u0000\u0000\u0000\u008b\u008c\u0001\u0000\u0000\u0000"+
"\u008c\u008d\u0007\u0001\u0000\u0000\u008d\u008e\u0007\u0000\u0000\u0000"+
"\u008e\u008f\u0007\u0001\u0000\u0000\u008f\u0090\u0007\u0002\u0000\u0000"+
"\u0090\u0091\u0007\u0003\u0000\u0000\u0091 \u0001\u0000\u0000\u0000\u0092"+
"\u0093\u0007\b\u0000\u0000\u0093\u0094\u0007\u0003\u0000\u0000\u0094\u0095"+
"\u0007\u0006\u0000\u0000\u0095\u0096\u0007\t\u0000\u0000\u0096\u0097\u0007"+
"\u0003\u0000\u0000\u0097\u0098\u0007\u0003\u0000\u0000\u0098\u0099\u0007"+
"\u0004\u0000\u0000\u0099\"\u0001\u0000\u0000\u0000\u009a\u009b\u0007\u0003"+
"\u0000\u0000\u009b\u009c\u0007\n\u0000\u0000\u009c\u009d\u0007\u0001\u0000"+
"\u0000\u009d\u009e\u0007\u000b\u0000\u0000\u009e\u00a0\u0007\u0006\u0000"+
"\u0000\u009f\u00a1\u0007\u000b\u0000\u0000\u00a0\u009f\u0001\u0000\u0000"+
"\u0000\u00a0\u00a1\u0001\u0000\u0000\u0000\u00a1$\u0001\u0000\u0000\u0000"+
"\u00a2\u00a3\u0007\f\u0000\u0000\u00a3\u00a4\u0007\u0003\u0000\u0000\u00a4"+
"\u00a5\u0007\r\u0000\u0000\u00a5\u00a6\u0007\u0003\u0000\u0000\u00a6\u00a7"+
"\u0007\n\u0000\u0000\u00a7\u00a8\u0007\u000e\u0000\u0000\u00a8&\u0001"+
"\u0000\u0000\u0000\u00a9\u00aa\u0007\u000f\u0000\u0000\u00aa\u00ab\u0007"+
"\u0005\u0000\u0000\u00ab\u00ac\u0007\u0004\u0000\u0000\u00ac\u00ad\u0007"+
"\u0006\u0000\u0000\u00ad\u00ae\u0007\u0010\u0000\u0000\u00ae\u00af\u0007"+
"\u0001\u0000\u0000\u00af\u00b1\u0007\u0004\u0000\u0000\u00b0\u00b2\u0007"+
"\u000b\u0000\u0000\u00b1\u00b0\u0001\u0000\u0000\u0000\u00b1\u00b2\u0001"+
"\u0000\u0000\u0000\u00b2(\u0001\u0000\u0000\u0000\u00b3\u00b4\u0007\u0001"+
"\u0000\u0000\u00b4\u00b5\u0007\u0004\u0000\u0000\u00b5*\u0001\u0000\u0000"+
"\u0000\u00b6\u00b7\u0007\u0004\u0000\u0000\u00b7\u00b8\u0007\u0005\u0000"+
"\u0000\u00b8\u00b9\u0007\u0006\u0000\u0000\u00b9,\u0001\u0000\u0000\u0000"+
"\u00ba\u00bb\u0007\u0010\u0000\u0000\u00bb\u00bc\u0007\u0004\u0000\u0000"+
"\u00bc\u00bd\u0007\u0011\u0000\u0000\u00bd.\u0001\u0000\u0000\u0000\u00be"+
"\u00bf\u0007\u0005\u0000\u0000\u00bf\u00c0\u0007\f\u0000\u0000\u00c00"+
"\u0001\u0000\u0000\u0000\u00c1\u00c2\u0007\u0012\u0000\u0000\u00c2\u00c3"+
"\u0007\u0010\u0000\u0000\u00c3\u00c4\u0007\u000b\u0000\u0000\u00c42\u0001"+
"\u0000\u0000\u0000\u00c5\u00c6\u0007\u0012\u0000\u0000\u00c6\u00c7\u0007"+
"\u0010\u0000\u0000\u00c7\u00c8\u0007\u000b\u0000\u0000\u00c8\u00c9\u0007"+
"\u0010\u0000\u0000\u00c9\u00ca\u0007\u0004\u0000\u0000\u00ca\u00cb\u0007"+
"\u0013\u0000\u0000\u00cb4\u0001\u0000\u0000\u0000\u00cc\u00cd\u0007\u0012"+
"\u0000\u0000\u00cd\u00ce\u0007\u0010\u0000\u0000\u00ce\u00cf\u0007\u000b"+
"\u0000\u0000\u00cf\u00d0\u0007\u0010\u0000\u0000\u00d0\u00d1\u0007\u0000"+
"\u0000\u0000\u00d1\u00d2\u0007\u0000\u0000\u0000\u00d26\u0001\u0000\u0000"+
"\u0000\u00d3\u00d4\u0007\u0006\u0000\u0000\u00d4\u00d5\u0007\f\u0000\u0000"+
"\u00d5\u00d6\u0007\u0014\u0000\u0000\u00d6\u00dd\u0007\u0003\u0000\u0000"+
"\u00d7\u00d8\u0007\u0015\u0000\u0000\u00d8\u00d9\u0007\u0010\u0000\u0000"+
"\u00d9\u00da\u0007\u0000\u0000\u0000\u00da\u00db\u0007\u000b\u0000\u0000"+
"\u00db\u00dd\u0007\u0003\u0000\u0000\u00dc\u00d3\u0001\u0000\u0000\u0000"+
"\u00dc\u00d7\u0001\u0000\u0000\u0000\u00dd8\u0001\u0000\u0000\u0000\u00de"+
"\u00df\u0007\u0016\u0000\u0000\u00df:\u0001\u0000\u0000\u0000\u00e0\u00e2"+
"\u00039\u001c\u0000\u00e1\u00e0\u0001\u0000\u0000\u0000\u00e1\u00e2\u0001"+
"\u0000\u0000\u0000\u00e2\u00e4\u0001\u0000\u0000\u0000\u00e3\u00e5\u0003"+
"I$\u0000\u00e4\u00e3\u0001\u0000\u0000\u0000\u00e5\u00e6\u0001\u0000\u0000"+
"\u0000\u00e6\u00e4\u0001\u0000\u0000\u0000\u00e6\u00e7\u0001\u0000\u0000"+
"\u0000\u00e7\u00ef\u0001\u0000\u0000\u0000\u00e8\u00ec\u0005.\u0000\u0000"+
"\u00e9\u00eb\u0003I$\u0000\u00ea\u00e9\u0001\u0000\u0000\u0000\u00eb\u00ee"+
"\u0001\u0000\u0000\u0000\u00ec\u00ea\u0001\u0000\u0000\u0000\u00ec\u00ed"+
"\u0001\u0000\u0000\u0000\u00ed\u00f0\u0001\u0000\u0000\u0000\u00ee\u00ec"+
"\u0001\u0000\u0000\u0000\u00ef\u00e8\u0001\u0000\u0000\u0000\u00ef\u00f0"+
"\u0001\u0000\u0000\u0000\u00f0\u00fa\u0001\u0000\u0000\u0000\u00f1\u00f3"+
"\u0007\u0003\u0000\u0000\u00f2\u00f4\u00039\u001c\u0000\u00f3\u00f2\u0001"+
"\u0000\u0000\u0000\u00f3\u00f4\u0001\u0000\u0000\u0000\u00f4\u00f6\u0001"+
"\u0000\u0000\u0000\u00f5\u00f7\u0003I$\u0000\u00f6\u00f5\u0001\u0000\u0000"+
"\u0000\u00f7\u00f8\u0001\u0000\u0000\u0000\u00f8\u00f6\u0001\u0000\u0000"+
"\u0000\u00f8\u00f9\u0001\u0000\u0000\u0000\u00f9\u00fb\u0001\u0000\u0000"+
"\u0000\u00fa\u00f1\u0001\u0000\u0000\u0000\u00fa\u00fb\u0001\u0000\u0000"+
"\u0000\u00fb\u0111\u0001\u0000\u0000\u0000\u00fc\u00fe\u00039\u001c\u0000"+
"\u00fd\u00fc\u0001\u0000\u0000\u0000\u00fd\u00fe\u0001\u0000\u0000\u0000"+
"\u00fe\u00ff\u0001\u0000\u0000\u0000\u00ff\u0101\u0005.\u0000\u0000\u0100"+
"\u0102\u0003I$\u0000\u0101\u0100\u0001\u0000\u0000\u0000\u0102\u0103\u0001"+
"\u0000\u0000\u0000\u0103\u0101\u0001\u0000\u0000\u0000\u0103\u0104\u0001"+
"\u0000\u0000\u0000\u0104\u010e\u0001\u0000\u0000\u0000\u0105\u0107\u0007"+
"\u0003\u0000\u0000\u0106\u0108\u00039\u001c\u0000\u0107\u0106\u0001\u0000"+
"\u0000\u0000\u0107\u0108\u0001\u0000\u0000\u0000\u0108\u010a\u0001\u0000"+
"\u0000\u0000\u0109\u010b\u0003I$\u0000\u010a\u0109\u0001\u0000\u0000\u0000"+
"\u010b\u010c\u0001\u0000\u0000\u0000\u010c\u010a\u0001\u0000\u0000\u0000"+
"\u010c\u010d\u0001\u0000\u0000\u0000\u010d\u010f\u0001\u0000\u0000\u0000"+
"\u010e\u0105\u0001\u0000\u0000\u0000\u010e\u010f\u0001\u0000\u0000\u0000"+
"\u010f\u0111\u0001\u0000\u0000\u0000\u0110\u00e1\u0001\u0000\u0000\u0000"+
"\u0110\u00fd\u0001\u0000\u0000\u0000\u0111<\u0001\u0000\u0000\u0000\u0112"+
"\u0118\u0005\"\u0000\u0000\u0113\u0117\b\u0017\u0000\u0000\u0114\u0115"+
"\u0005\\\u0000\u0000\u0115\u0117\t\u0000\u0000\u0000\u0116\u0113\u0001"+
"\u0000\u0000\u0000\u0116\u0114\u0001\u0000\u0000\u0000\u0117\u011a\u0001"+
"\u0000\u0000\u0000\u0118\u0116\u0001\u0000\u0000\u0000\u0118\u0119\u0001"+
"\u0000\u0000\u0000\u0119\u011b\u0001\u0000\u0000\u0000\u011a\u0118\u0001"+
"\u0000\u0000\u0000\u011b\u0127\u0005\"\u0000\u0000\u011c\u0122\u0005\'"+
"\u0000\u0000\u011d\u0121\b\u0018\u0000\u0000\u011e\u011f\u0005\\\u0000"+
"\u0000\u011f\u0121\t\u0000\u0000\u0000\u0120\u011d\u0001\u0000\u0000\u0000"+
"\u0120\u011e\u0001\u0000\u0000\u0000\u0121\u0124\u0001\u0000\u0000\u0000"+
"\u0122\u0120\u0001\u0000\u0000\u0000\u0122\u0123\u0001\u0000\u0000\u0000"+
"\u0123\u0125\u0001\u0000\u0000\u0000\u0124\u0122\u0001\u0000\u0000\u0000"+
"\u0125\u0127\u0005\'\u0000\u0000\u0126\u0112\u0001\u0000\u0000\u0000\u0126"+
"\u011c\u0001\u0000\u0000\u0000\u0127>\u0001\u0000\u0000\u0000\u0128\u012c"+
"\u0007\u0019\u0000\u0000\u0129\u012b\u0007\u001a\u0000\u0000\u012a\u0129"+
"\u0001\u0000\u0000\u0000\u012b\u012e\u0001\u0000\u0000\u0000\u012c\u012a"+
"\u0001\u0000\u0000\u0000\u012c\u012d\u0001\u0000\u0000\u0000\u012d@\u0001"+
"\u0000\u0000\u0000\u012e\u012c\u0001\u0000\u0000\u0000\u012f\u0130\u0005"+
"[\u0000\u0000\u0130\u0131\u0005]\u0000\u0000\u0131B\u0001\u0000\u0000"+
"\u0000\u0132\u0133\u0005[\u0000\u0000\u0133\u0134\u0005*\u0000\u0000\u0134"+
"\u0135\u0005]\u0000\u0000\u0135D\u0001\u0000\u0000\u0000\u0136\u013d\u0003"+
"?\u001f\u0000\u0137\u0138\u0005.\u0000\u0000\u0138\u013c\u0003?\u001f"+
"\u0000\u0139\u013c\u0003A \u0000\u013a\u013c\u0003C!\u0000\u013b\u0137"+
"\u0001\u0000\u0000\u0000\u013b\u0139\u0001\u0000\u0000\u0000\u013b\u013a"+
"\u0001\u0000\u0000\u0000\u013c\u013f\u0001\u0000\u0000\u0000\u013d\u013b"+
"\u0001\u0000\u0000\u0000\u013d\u013e\u0001\u0000\u0000\u0000\u013eF\u0001"+
"\u0000\u0000\u0000\u013f\u013d\u0001\u0000\u0000\u0000\u0140\u0142\u0007"+
"\u001b\u0000\u0000\u0141\u0140\u0001\u0000\u0000\u0000\u0142\u0143\u0001"+
"\u0000\u0000\u0000\u0143\u0141\u0001\u0000\u0000\u0000\u0143\u0144\u0001"+
"\u0000\u0000\u0000\u0144\u0145\u0001\u0000\u0000\u0000\u0145\u0146\u0006"+
"#\u0000\u0000\u0146H\u0001\u0000\u0000\u0000\u0147\u0148\u0007\u001c\u0000"+
"\u0000\u0148J\u0001\u0000\u0000\u0000\u0149\u014b\b\u001d\u0000\u0000"+
"\u014a\u0149\u0001\u0000\u0000\u0000\u014b\u014c\u0001\u0000\u0000\u0000"+
"\u014c\u014a\u0001\u0000\u0000\u0000\u014c\u014d\u0001\u0000\u0000\u0000"+
"\u014dL\u0001\u0000\u0000\u0000\u001e\u0000Zw\u008a\u00a0\u00b1\u00dc"+
"\u00e1\u00e6\u00ec\u00ef\u00f3\u00f8\u00fa\u00fd\u0103\u0107\u010c\u010e"+
"\u0110\u0116\u0118\u0120\u0122\u0126\u012c\u013b\u013d\u0143\u014c\u0001"+
"\u0006\u0000\u0000";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
}

View File

@@ -1,44 +0,0 @@
LPAREN=1
RPAREN=2
LBRACK=3
RBRACK=4
COMMA=5
EQUALS=6
NOT_EQUALS=7
NEQ=8
LT=9
LE=10
GT=11
GE=12
LIKE=13
NOT_LIKE=14
ILIKE=15
NOT_ILIKE=16
BETWEEN=17
EXISTS=18
REGEXP=19
CONTAINS=20
IN=21
NOT=22
AND=23
OR=24
HAS=25
HASANY=26
HASALL=27
BOOL=28
NUMBER=29
QUOTED_TEXT=30
KEY=31
WS=32
FREETEXT=33
'('=1
')'=2
'['=3
']'=4
','=5
'!='=7
'<>'=8
'<'=9
'<='=10
'>'=11
'>='=12

File diff suppressed because it is too large Load Diff