Loki: Fix adding of multiple label filters when parser (#52335)

* Loki: Fix adding multiple filters

* Update logic in sorting
This commit is contained in:
Ivana Huckova 2022-07-19 18:08:55 +02:00 committed by GitHub
parent 39d7fdbbbc
commit c087198b1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 14 deletions

View File

@ -27,15 +27,18 @@ export function addLabelToQuery(query: string, key: string, operator: string, va
const streamSelectorPositions = getStreamSelectorPositions(query); const streamSelectorPositions = getStreamSelectorPositions(query);
const parserPositions = getParserPositions(query); const parserPositions = getParserPositions(query);
const labelFilterPositions = getLabelFilterPositions(query);
if (!streamSelectorPositions.length) { if (!streamSelectorPositions.length) {
return query; return query;
} }
const filter = toLabelFilter(key, value, operator); const filter = toLabelFilter(key, value, operator);
if (!parserPositions.length) { // If we have label filters or parser, we want to add new label filter after the last one
return addFilterToStreamSelector(query, streamSelectorPositions, filter); if (labelFilterPositions.length || parserPositions.length) {
const positionToAdd = findLastPosition([...labelFilterPositions, ...parserPositions]);
return addFilterAsLabelFilter(query, [positionToAdd], filter);
} else { } else {
return addFilterAsLabelFilter(query, parserPositions, filter); return addFilterToStreamSelector(query, streamSelectorPositions, filter);
} }
} }
@ -110,6 +113,24 @@ export function getParserPositions(query: string): Position[] {
return positions; return positions;
} }
/**
* Parse the string and get all LabelFilter positions in the query.
* @param query
*/
export function getLabelFilterPositions(query: string): Position[] {
const tree = parser.parse(query);
const positions: Position[] = [];
tree.iterate({
enter: (type, from, to, get): false | void => {
if (type.name === 'LabelFilter') {
positions.push({ from, to });
return false;
}
},
});
return positions;
}
/** /**
* Parse the string and get all Line filter positions in the query. * Parse the string and get all Line filter positions in the query.
* @param query * @param query
@ -172,17 +193,21 @@ function addFilterToStreamSelector(
/** /**
* Add filter as label filter after the parsers * Add filter as label filter after the parsers
* @param query * @param query
* @param parserPositions * @param positionsToAddAfter
* @param filter * @param filter
*/ */
function addFilterAsLabelFilter(query: string, parserPositions: Position[], filter: QueryBuilderLabelFilter): string { function addFilterAsLabelFilter(
query: string,
positionsToAddAfter: Position[],
filter: QueryBuilderLabelFilter
): string {
let newQuery = ''; let newQuery = '';
let prev = 0; let prev = 0;
for (let i = 0; i < parserPositions.length; i++) { for (let i = 0; i < positionsToAddAfter.length; i++) {
// This is basically just doing splice on a string for each matched vector selector. // This is basically just doing splice on a string for each matched vector selector.
const match = parserPositions[i]; const match = positionsToAddAfter[i];
const isLast = i === parserPositions.length - 1; const isLast = i === positionsToAddAfter.length - 1;
const start = query.substring(prev, match.to); const start = query.substring(prev, match.to);
const end = isLast ? query.substring(match.to) : ''; const end = isLast ? query.substring(match.to) : '';
@ -227,3 +252,11 @@ function addParser(query: string, queryPartPositions: Position[], parser: string
function labelExists(labels: QueryBuilderLabelFilter[], filter: QueryBuilderLabelFilter) { function labelExists(labels: QueryBuilderLabelFilter[], filter: QueryBuilderLabelFilter) {
return labels.find((label) => label.label === filter.label && label.value === filter.value); return labels.find((label) => label.label === filter.label && label.value === filter.value);
} }
/**
* Return the last position based on "to" property
* @param positions
*/
function findLastPosition(positions: Position[]): Position {
return positions.reduce((prev, current) => (prev.to > current.to ? prev : current));
}

View File

@ -115,14 +115,14 @@ describe('addLabelToQuery()', () => {
it('should add label filter after parser', () => { it('should add label filter after parser', () => {
expect(addLabelToQuery('{foo="bar"} | logfmt', 'bar', '=', 'baz')).toBe('{foo="bar"} | logfmt | bar=`baz`'); expect(addLabelToQuery('{foo="bar"} | logfmt', 'bar', '=', 'baz')).toBe('{foo="bar"} | logfmt | bar=`baz`');
}); });
it('should add label filter after multiple parsers', () => { it('should add label filter after last parser when multiple parsers', () => {
expect(addLabelToQuery('{foo="bar"} | logfmt | json', 'bar', '=', 'baz')).toBe( expect(addLabelToQuery('{foo="bar"} | logfmt | json', 'bar', '=', 'baz')).toBe(
'{foo="bar"} | logfmt | bar=`baz` | json | bar=`baz`' '{foo="bar"} | logfmt | json | bar=`baz`'
); );
}); });
it('should add label filter after parser when multiple label filters', () => { it('should add label filter after last label filter when multiple label filters', () => {
expect(addLabelToQuery('{foo="bar"} | logfmt | x="y"', 'bar', '=', 'baz')).toBe( expect(addLabelToQuery('{foo="bar"} | logfmt | x="y"', 'bar', '=', 'baz')).toBe(
'{foo="bar"} | logfmt | bar=`baz` | x="y"' '{foo="bar"} | logfmt | x="y" | bar=`baz`'
); );
}); });
it('should add label filter in metric query', () => { it('should add label filter in metric query', () => {
@ -138,7 +138,7 @@ describe('addLabelToQuery()', () => {
'=', '=',
'baz' 'baz'
) )
).toBe('sum by(host) (rate({foo="bar"} | logfmt | bar=`baz` | x="y" | line_format "{{.status}}" [5m]))'); ).toBe('sum by(host) (rate({foo="bar"} | logfmt | x="y" | bar=`baz` | line_format "{{.status}}" [5m]))');
}); });
it('should not add adhoc filter to line_format expressions', () => { it('should not add adhoc filter to line_format expressions', () => {
expect(addLabelToQuery('{foo="bar"} | logfmt | line_format "{{.status}}"', 'bar', '=', 'baz')).toBe( expect(addLabelToQuery('{foo="bar"} | logfmt | line_format "{{.status}}"', 'bar', '=', 'baz')).toBe(

View File

@ -92,7 +92,7 @@ export function OperationList<T extends QueryWithOperations>({
<div className={styles.operationList} ref={provided.innerRef} {...provided.droppableProps}> <div className={styles.operationList} ref={provided.innerRef} {...provided.droppableProps}>
{operations.map((op, index) => ( {operations.map((op, index) => (
<OperationEditor <OperationEditor
key={op.id + index} key={op.id + JSON.stringify(op.params) + index}
queryModeller={queryModeller} queryModeller={queryModeller}
index={index} index={index}
operation={op} operation={op}