diff --git a/frontend/src/components/QueryBuilderV2/CodeMirrorWhereClause/CodeMirrorWhereClause.tsx b/frontend/src/components/QueryBuilderV2/CodeMirrorWhereClause/CodeMirrorWhereClause.tsx
index da0d7ba550b1..88638c7be1d5 100644
--- a/frontend/src/components/QueryBuilderV2/CodeMirrorWhereClause/CodeMirrorWhereClause.tsx
+++ b/frontend/src/components/QueryBuilderV2/CodeMirrorWhereClause/CodeMirrorWhereClause.tsx
@@ -118,8 +118,10 @@ function CodeMirrorWhereClause(): JSX.Element {
// signal: 'traces',
// });
- const generateOptions = (data: any): any[] => {
- const options = Object.values(data.keys).flatMap((items: any) =>
+ console.log('loading', isLoading);
+
+ const generateOptions = (data: any): any[] =>
+ Object.values(data.keys).flatMap((items: any) =>
items.map(({ name, fieldDataType, fieldContext }: any) => ({
label: name,
type: fieldDataType === 'string' ? 'keyword' : fieldDataType,
@@ -128,23 +130,14 @@ function CodeMirrorWhereClause(): JSX.Element {
})),
);
- console.log('options', options);
-
- return options;
- };
-
useEffect(() => {
if (queryKeySuggestions) {
- console.log('queryKeySuggestions', queryKeySuggestions);
-
const options = generateOptions(queryKeySuggestions.data.data);
setKeySuggestions(options);
}
}, [queryKeySuggestions]);
- console.log('keySuggestions', keySuggestions);
-
const handleUpdate = (viewUpdate: { view: EditorView }): void => {
const selection = viewUpdate.view.state.selection.main;
const pos = selection.head;
@@ -164,13 +157,6 @@ function CodeMirrorWhereClause(): JSX.Element {
}
};
- console.log({
- cursorPos,
- queryContext,
- validation,
- isLoading,
- });
-
const handleQueryChange = useCallback(async (newQuery: string) => {
setIsLoading(true);
setQuery(newQuery);
@@ -261,20 +247,18 @@ function CodeMirrorWhereClause(): JSX.Element {
} else if (queryContext.isInOperator) {
options = queryOperatorSuggestions;
} else if (queryContext.isInValue) {
+ console.log('is In Value', queryContext.currentToken);
// refetchQueryKeyValuesSuggestions();
- // Fetch values based on the key
- const key = queryContext.currentToken;
- // refetchQueryKeyValuesSuggestions({ key }).then((response) => {
- // if (response && response.data && Array.isArray(response.data.values)) {
- // options = response.data.values.map((value: string) => ({
- // label: value,
- // type: 'value',
- // }));
- // }
- // });
+ // Fetch values based on the key - use the keyToken if available
+ const key = queryContext.keyToken || queryContext.currentToken;
- console.log('key', key, queryContext, query);
+ console.log('key', key);
+
+ // const response = refetchQueryKeyValuesSuggestions({
+ // key: 'status',
+ // signal: 'traces',
+ // });
options = [
{ label: 'error', type: 'value' },
@@ -410,6 +394,40 @@ function CodeMirrorWhereClause(): JSX.Element {
{renderContextBadge()}
+
+ {/* Display the key-operator-value triplet when available */}
+ {queryContext.keyToken && (
+
+
+ Key:
+
+
+ {queryContext.keyToken}
+
+
+ )}
+
+ {queryContext.operatorToken && (
+
+
+ Operator:
+
+
+ {queryContext.operatorToken}
+
+
+ )}
+
+ {queryContext.valueToken && (
+
+
+ Value:
+
+
+ {queryContext.valueToken}
+
+
+ )}
diff --git a/frontend/src/types/antlrQueryTypes.ts b/frontend/src/types/antlrQueryTypes.ts
index cf35a6565945..1ecfc30a9b5b 100644
--- a/frontend/src/types/antlrQueryTypes.ts
+++ b/frontend/src/types/antlrQueryTypes.ts
@@ -24,6 +24,9 @@ export interface IQueryContext {
isInFunction: boolean;
isInConjunction?: boolean;
isInParenthesis?: boolean;
+ keyToken?: string;
+ operatorToken?: string;
+ valueToken?: string;
}
export interface IDetailedError {
diff --git a/frontend/src/utils/antlrQueryUtils.ts b/frontend/src/utils/antlrQueryUtils.ts
index 0948001c44d1..722ae30a0e97 100644
--- a/frontend/src/utils/antlrQueryUtils.ts
+++ b/frontend/src/utils/antlrQueryUtils.ts
@@ -162,6 +162,103 @@ export const validateQuery = (query: string): IValidationResult => {
}
};
+// Helper function to find key-operator-value triplets in token stream
+function findKeyOperatorValueTriplet(
+ allTokens: IToken[],
+ currentToken: IToken,
+ isInKey: boolean,
+ isInOperator: boolean,
+ isInValue: boolean,
+): { keyToken?: string; operatorToken?: string; valueToken?: string } {
+ // Find current token index in allTokens
+ let currentTokenIndex = -1;
+ for (let i = 0; i < allTokens.length; i++) {
+ if (
+ allTokens[i].start === currentToken.start &&
+ allTokens[i].stop === currentToken.stop &&
+ allTokens[i].type === currentToken.type
+ ) {
+ currentTokenIndex = i;
+ break;
+ }
+ }
+
+ if (currentTokenIndex === -1) return {};
+
+ // Initialize result with empty object
+ const result: {
+ keyToken?: string;
+ operatorToken?: string;
+ valueToken?: string;
+ } = {};
+
+ if (isInKey) {
+ // When in key context, we only know the key
+ result.keyToken = currentToken.text;
+ } else if (isInOperator) {
+ // When in operator context, we know the operator and can find the preceding key
+ result.operatorToken = currentToken.text;
+
+ // Look backward for key
+ for (let i = currentTokenIndex - 1; i >= 0; i--) {
+ const token = allTokens[i];
+ // Skip whitespace and other hidden channel tokens
+ if (token.channel !== 0) continue;
+
+ if (token.type === FilterQueryLexer.KEY) {
+ result.keyToken = token.text;
+ break;
+ }
+ }
+ } else if (isInValue) {
+ // When in value context, we know the value and can find the preceding operator and key
+ result.valueToken = currentToken.text;
+
+ let foundOperator = false;
+
+ // Look backward for operator and key
+ for (let i = currentTokenIndex - 1; i >= 0; i--) {
+ const token = allTokens[i];
+ // Skip whitespace and other hidden channel tokens
+ if (token.channel !== 0) continue;
+
+ // If we haven't found an operator yet, check for operator
+ if (
+ !foundOperator &&
+ [
+ FilterQueryLexer.EQUALS,
+ FilterQueryLexer.NOT_EQUALS,
+ FilterQueryLexer.NEQ,
+ FilterQueryLexer.LT,
+ FilterQueryLexer.LE,
+ FilterQueryLexer.GT,
+ FilterQueryLexer.GE,
+ FilterQueryLexer.LIKE,
+ FilterQueryLexer.NOT_LIKE,
+ FilterQueryLexer.ILIKE,
+ FilterQueryLexer.NOT_ILIKE,
+ FilterQueryLexer.BETWEEN,
+ FilterQueryLexer.EXISTS,
+ FilterQueryLexer.REGEXP,
+ FilterQueryLexer.CONTAINS,
+ FilterQueryLexer.IN,
+ FilterQueryLexer.NOT,
+ ].includes(token.type)
+ ) {
+ result.operatorToken = token.text;
+ foundOperator = true;
+ }
+ // If we already found an operator and this is a key, record it
+ else if (foundOperator && token.type === FilterQueryLexer.KEY) {
+ result.keyToken = token.text;
+ break; // We found our triplet
+ }
+ }
+ }
+
+ return result;
+}
+
export function getQueryContextAtCursor(
query: string,
cursorIndex: number,
@@ -233,11 +330,6 @@ export function getQueryContextAtCursor(
}
}
- console.log('exactToken', exactToken);
- console.log('previousToken', previousToken);
- console.log('nextToken', nextToken);
- console.log('query', query);
-
// Determine the context based on cursor position and surrounding tokens
let currentToken: IToken | null = null;
@@ -337,7 +429,14 @@ export function getQueryContextAtCursor(
FilterQueryLexer.HASNONE,
].includes(currentToken.type);
- console.log('currentToken', currentToken);
+ // Get the context-related tokens (key, operator, value)
+ const relationTokens = findKeyOperatorValueTriplet(
+ allTokens,
+ currentToken,
+ isInKey,
+ isInOperator,
+ isInValue,
+ );
// Handle transitions based on spaces
// When a user adds a space after a token, change the context accordingly
@@ -362,6 +461,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -379,6 +479,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -396,6 +497,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: true,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -413,6 +515,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
}
@@ -437,6 +540,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -460,6 +564,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -483,6 +588,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -525,6 +631,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -550,6 +657,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -572,6 +680,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
@@ -588,6 +697,7 @@ export function getQueryContextAtCursor(
isInFunction: false,
isInConjunction: true,
isInParenthesis: false,
+ ...relationTokens, // Include related tokens
};
}
}
@@ -605,6 +715,7 @@ export function getQueryContextAtCursor(
isInFunction,
isInConjunction,
// isInParenthesis,
+ ...relationTokens, // Include related tokens
};
} catch (error) {
console.error('Error in getQueryContextAtCursor:', error);