diff --git a/public/app/plugins/datasource/loki/modifyQuery.test.ts b/public/app/plugins/datasource/loki/modifyQuery.test.ts index f3c953816f3..90f6e8d876b 100644 --- a/public/app/plugins/datasource/loki/modifyQuery.test.ts +++ b/public/app/plugins/datasource/loki/modifyQuery.test.ts @@ -98,6 +98,18 @@ describe('addLabelToQuery()', () => { '{foo="bar"} | logfmt | forcedLabel=`value`' ); }); + + it('should add label as labelFilter to multiple places if label is StructuredMetadata', () => { + expect( + addLabelToQuery( + 'rate({foo="bar"} [$__auto]) / rate({foo="bar"} [$__auto])', + 'forcedLabel', + '=', + 'value', + LabelType.StructuredMetadata + ) + ).toEqual('rate({foo="bar"} | forcedLabel=`value` [$__auto]) / rate({foo="bar"} | forcedLabel=`value` [$__auto])'); + }); }); describe('addParserToQuery', () => { diff --git a/public/app/plugins/datasource/loki/modifyQuery.ts b/public/app/plugins/datasource/loki/modifyQuery.ts index b18887b4c13..130c29c0a68 100644 --- a/public/app/plugins/datasource/loki/modifyQuery.ts +++ b/public/app/plugins/datasource/loki/modifyQuery.ts @@ -171,8 +171,13 @@ export function addLabelToQuery( const filter = toLabelFilter(key, value, operator); if (labelType === LabelType.Parsed || labelType === LabelType.StructuredMetadata) { - const positionToAdd = findLastPosition([...streamSelectorPositions, ...labelFilterPositions, ...parserPositions]); - return addFilterAsLabelFilter(query, [positionToAdd], filter); + const lastPositionsPerExpression = getLastPositionPerExpression(query, [ + ...streamSelectorPositions, + ...labelFilterPositions, + ...parserPositions, + ]); + + return addFilterAsLabelFilter(query, lastPositionsPerExpression, filter); } else if (labelType === LabelType.Indexed) { return addFilterToStreamSelector(query, streamSelectorPositions, filter); } else { @@ -183,23 +188,31 @@ export function addLabelToQuery( } else { // If `labelType` is not set, it indicates a potential metric query (`labelType` is present only in log queries that came from a Loki instance supporting the `categorize-labels` API). In case we are not adding the label to stream selectors we need to find the last position to add in each expression. // E.g. in `sum(rate({foo="bar"} | logfmt [$__auto])) / sum(rate({foo="baz"} | logfmt [$__auto]))` we need to add the label at two places. - 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); - }) - ); - }); + const lastPositionsPerExpression = getLastPositionPerExpression(query, [ + ...parserPositions, + ...labelFilterPositions, + ]); return addFilterAsLabelFilter(query, lastPositionsPerExpression, filter); } } } +function getLastPositionPerExpression(query: string, positions: NodePosition[]): NodePosition[] { + const subExpressions = findLeaves(getNodePositionsFromQuery(query, [Expr])); + const subPositions = [...positions]; + + // find last position for each subexpression + const lastPositionsPerExpression = subExpressions.map((subExpression) => { + return findLastPosition( + subPositions.filter((p) => { + return subExpression.contains(p); + }) + ); + }); + return lastPositionsPerExpression; +} + /** * Adds parser to existing query. Useful for query modification for hints. * It uses LogQL parser to find instances of stream selectors or line filters and adds parser after them.