Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v10.1.x] Loki: Fix filters not being added with multiple expressions and parsers #75172

Merged
merged 1 commit into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/app/plugins/datasource/loki/modifyQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('addLabelToQuery()', () => {
${'{foo="bar"} | logfmt'} | ${'query with parser with escaped value and regex operator'} | ${'bar'} | ${'~='} | ${'\\"baz\\"'} | ${'{foo="bar"} | logfmt | bar~=`"baz"`'}
${'{foo="bar"} | logfmt'} | ${'query with parser, > operator and number value'} | ${'bar'} | ${'>'} | ${'5'} | ${'{foo="bar"} | logfmt | bar>5'}
${'{foo="bar"} | logfmt'} | ${'query with parser, < operator and non-number value'} | ${'bar'} | ${'<'} | ${'5KiB'} | ${'{foo="bar"} | logfmt | bar<`5KiB`'}
${'sum(rate({x="y"} | logfmt [5m])) + sum(rate({x="z"} | logfmt [5m]))'} | ${'metric query with non empty selectors and parsers'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum(rate({x="y"} | logfmt | bar=`baz` [5m])) + sum(rate({x="z"} | logfmt | bar=`baz` [5m]))'}
`(
'should add label to query: $query, description: $description',
({ query, description, label, operator, value, expectedResult }) => {
Expand Down
54 changes: 39 additions & 15 deletions public/app/plugins/datasource/loki/modifyQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import {
UnwrapExpr,
String,
PipelineStage,
Expr,
} from '@grafana/lezer-logql';

import { QueryBuilderLabelFilter } from '../prometheus/querybuilder/shared/types';

import { unescapeLabelValue } from './languageUtils';
import { getNodePositionsFromQuery } from './queryUtils';
import { lokiQueryModeller as modeller } from './querybuilder/LokiQueryModeller';
import { buildVisualQueryFromString, handleQuotes } from './querybuilder/parsing';

Expand Down Expand Up @@ -168,8 +170,20 @@ export function addLabelToQuery(query: string, key: string, operator: string, va
// If we have non-empty stream selector and parser/label filter, we want to add a new label filter after the last one.
// If some of the stream selectors don't have matchers, we want to add new matcher to the all stream selectors.
if (everyStreamSelectorHasMatcher && (labelFilterPositions.length || parserPositions.length)) {
const positionToAdd = findLastPosition([...labelFilterPositions, ...parserPositions]);
return addFilterAsLabelFilter(query, [positionToAdd], filter);
// in case we are not adding the label to stream selectors we need to find the last position to add in each expression
const subExpressions = findLeaves(getNodePositionsFromQuery(query, [Expr]));
const parserFilterPositions = [...parserPositions, ...labelFilterPositions];

// find last position for each subexpression
const lastPositionsPerExpression = subExpressions.map((subExpression) => {
return findLastPosition(
parserFilterPositions.filter((p) => {
return subExpression.contains(p);
})
);
});

return addFilterAsLabelFilter(query, lastPositionsPerExpression, filter);
} else {
return addFilterToStreamSelector(query, streamSelectorPositions, filter);
}
Expand Down Expand Up @@ -255,9 +269,9 @@ export function getStreamSelectorPositions(query: string): NodePosition[] {
const tree = parser.parse(query);
const positions: NodePosition[] = [];
tree.iterate({
enter: ({ type, from, to }): false | void => {
enter: ({ type, node }): false | void => {
if (type.id === Selector) {
positions.push(new NodePosition(from, to, type));
positions.push(NodePosition.fromNode(node));
return false;
}
},
Expand All @@ -271,7 +285,7 @@ function getMatcherInStreamPositions(query: string): NodePosition[] {
tree.iterate({
enter: ({ node }): false | void => {
if (node.type.id === Selector) {
positions.push(...getAllPositionsInNodeByType(query, node, Matcher));
positions.push(...getAllPositionsInNodeByType(node, Matcher));
}
},
});
Expand All @@ -286,9 +300,9 @@ export function getParserPositions(query: string): NodePosition[] {
const tree = parser.parse(query);
const positions: NodePosition[] = [];
tree.iterate({
enter: ({ type, from, to }): false | void => {
enter: ({ type, node }): false | void => {
if (type.id === LabelParser || type.id === JsonExpressionParser) {
positions.push(new NodePosition(from, to, type));
positions.push(NodePosition.fromNode(node));
return false;
}
},
Expand All @@ -304,9 +318,9 @@ export function getLabelFilterPositions(query: string): NodePosition[] {
const tree = parser.parse(query);
const positions: NodePosition[] = [];
tree.iterate({
enter: ({ type, from, to }): false | void => {
enter: ({ type, node }): false | void => {
if (type.id === LabelFilter) {
positions.push(new NodePosition(from, to, type));
positions.push(NodePosition.fromNode(node));
return false;
}
},
Expand All @@ -322,9 +336,9 @@ function getLineFiltersPositions(query: string): NodePosition[] {
const tree = parser.parse(query);
const positions: NodePosition[] = [];
tree.iterate({
enter: ({ type, from, to }): false | void => {
enter: ({ type, node }): false | void => {
if (type.id === LineFilters) {
positions.push(new NodePosition(from, to, type));
positions.push(NodePosition.fromNode(node));
return false;
}
},
Expand All @@ -340,9 +354,9 @@ function getLogQueryPositions(query: string): NodePosition[] {
const tree = parser.parse(query);
const positions: NodePosition[] = [];
tree.iterate({
enter: ({ type, from, to, node }): false | void => {
enter: ({ type, node }): false | void => {
if (type.id === LogExpr) {
positions.push(new NodePosition(from, to, type));
positions.push(NodePosition.fromNode(node));
return false;
}

Expand Down Expand Up @@ -549,7 +563,7 @@ export function findLastPosition(positions: NodePosition[]): NodePosition {
return positions.reduce((prev, current) => (prev.to > current.to ? prev : current));
}

function getAllPositionsInNodeByType(query: string, node: SyntaxNode, type: number): NodePosition[] {
function getAllPositionsInNodeByType(node: SyntaxNode, type: number): NodePosition[] {
if (node.type.id === type) {
return [NodePosition.fromNode(node)];
}
Expand All @@ -558,9 +572,19 @@ function getAllPositionsInNodeByType(query: string, node: SyntaxNode, type: numb
let pos = 0;
let child = node.childAfter(pos);
while (child) {
positions.push(...getAllPositionsInNodeByType(query, child, type));
positions.push(...getAllPositionsInNodeByType(child, type));
pos = child.to;
child = node.childAfter(pos);
}
return positions;
}

/**
* Gets all leaves of the nodes given. Leaves are nodes that don't contain any other nodes.
*
* @param {NodePosition[]} nodes
* @return
*/
function findLeaves(nodes: NodePosition[]): NodePosition[] {
return nodes.filter((node) => nodes.every((n) => node.contains(n) === false || node === n));
}
Loading