mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Feature: Case insensitive Loki search (#15948)
* Case insensitive Loki search * Make Loki case insensitivity work with highlighting Signed-off-by: Steven Sheehy <ssheehy@firescope.com>
This commit is contained in:
parent
c8b2102500
commit
0bc314a47b
@ -1,4 +1,4 @@
|
||||
import { findMatchesInText } from './text';
|
||||
import { findMatchesInText, parseFlags } from './text';
|
||||
|
||||
describe('findMatchesInText()', () => {
|
||||
it('gets no matches for when search and or line are empty', () => {
|
||||
@ -32,4 +32,37 @@ describe('findMatchesInText()', () => {
|
||||
expect(findMatchesInText('foo foo bar', '(')).toEqual([]);
|
||||
expect(findMatchesInText('foo foo bar', '(foo|')).toEqual([]);
|
||||
});
|
||||
|
||||
test('should parse and use flags', () => {
|
||||
expect(findMatchesInText(' foo FOO bar ', '(?i)foo')).toEqual([
|
||||
{ length: 3, start: 1, text: 'foo', end: 4 },
|
||||
{ length: 3, start: 5, text: 'FOO', end: 8 },
|
||||
]);
|
||||
expect(findMatchesInText(' foo FOO bar ', '(?i)(?-i)foo')).toEqual([{ length: 3, start: 1, text: 'foo', end: 4 }]);
|
||||
expect(findMatchesInText('FOO\nfoobar\nbar', '(?ims)^foo.')).toEqual([
|
||||
{ length: 4, start: 0, text: 'FOO\n', end: 4 },
|
||||
{ length: 4, start: 4, text: 'foob', end: 8 },
|
||||
]);
|
||||
expect(findMatchesInText('FOO\nfoobar\nbar', '(?ims)(?-smi)^foo.')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseFlags()', () => {
|
||||
it('when no flags or text', () => {
|
||||
expect(parseFlags('')).toEqual({ cleaned: '', flags: 'g' });
|
||||
expect(parseFlags('(?is)')).toEqual({ cleaned: '', flags: 'gis' });
|
||||
expect(parseFlags('foo')).toEqual({ cleaned: 'foo', flags: 'g' });
|
||||
});
|
||||
|
||||
it('when flags present', () => {
|
||||
expect(parseFlags('(?i)foo')).toEqual({ cleaned: 'foo', flags: 'gi' });
|
||||
expect(parseFlags('(?ims)foo')).toEqual({ cleaned: 'foo', flags: 'gims' });
|
||||
});
|
||||
|
||||
it('when flags cancel each other', () => {
|
||||
expect(parseFlags('(?i)(?-i)foo')).toEqual({ cleaned: 'foo', flags: 'g' });
|
||||
expect(parseFlags('(?i-i)foo')).toEqual({ cleaned: 'foo', flags: 'g' });
|
||||
expect(parseFlags('(?is)(?-ims)foo')).toEqual({ cleaned: 'foo', flags: 'g' });
|
||||
expect(parseFlags('(?i)(?-i)(?i)foo')).toEqual({ cleaned: 'foo', flags: 'gi' });
|
||||
});
|
||||
});
|
||||
|
@ -22,10 +22,10 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[]
|
||||
return [];
|
||||
}
|
||||
const matches = [];
|
||||
const cleaned = cleanNeedle(needle);
|
||||
const { cleaned, flags } = parseFlags(cleanNeedle(needle));
|
||||
let regexp: RegExp;
|
||||
try {
|
||||
regexp = new RegExp(`(?:${cleaned})`, 'g');
|
||||
regexp = new RegExp(`(?:${cleaned})`, flags);
|
||||
} catch (error) {
|
||||
return matches;
|
||||
}
|
||||
@ -44,6 +44,35 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[]
|
||||
return matches;
|
||||
}
|
||||
|
||||
const CLEAR_FLAG = '-';
|
||||
const FLAGS_REGEXP = /\(\?([ims-]+)\)/g;
|
||||
|
||||
/**
|
||||
* Converts any mode modifers in the text to the Javascript equivalent flag
|
||||
*/
|
||||
export function parseFlags(text: string): { cleaned: string; flags: string } {
|
||||
const flags: Set<string> = new Set(['g']);
|
||||
|
||||
const cleaned = text.replace(FLAGS_REGEXP, (str, group) => {
|
||||
const clearAll = group.startsWith(CLEAR_FLAG);
|
||||
|
||||
for (let i = 0; i < group.length; ++i) {
|
||||
const flag = group.charAt(i);
|
||||
if (clearAll || group.charAt(i - 1) === CLEAR_FLAG) {
|
||||
flags.delete(flag);
|
||||
} else if (flag !== CLEAR_FLAG) {
|
||||
flags.add(flag);
|
||||
}
|
||||
}
|
||||
return ''; // Remove flag from text
|
||||
});
|
||||
|
||||
return {
|
||||
cleaned: cleaned,
|
||||
flags: Array.from(flags).join(''),
|
||||
};
|
||||
}
|
||||
|
||||
const XSSWL = Object.keys(xss.whiteList).reduce((acc, element) => {
|
||||
acc[element] = xss.whiteList[element].concat(['class', 'style']);
|
||||
return acc;
|
||||
|
@ -11,7 +11,7 @@ describe('parseQuery', () => {
|
||||
it('returns regexp for strings without query', () => {
|
||||
expect(parseQuery('test')).toEqual({
|
||||
query: '',
|
||||
regexp: 'test',
|
||||
regexp: '(?i)test',
|
||||
});
|
||||
});
|
||||
|
||||
@ -25,14 +25,14 @@ describe('parseQuery', () => {
|
||||
it('returns query for strings with query and search string', () => {
|
||||
expect(parseQuery('x {foo="bar"}')).toEqual({
|
||||
query: '{foo="bar"}',
|
||||
regexp: 'x',
|
||||
regexp: '(?i)x',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns query for strings with query and regexp', () => {
|
||||
expect(parseQuery('{foo="bar"} x|y')).toEqual({
|
||||
query: '{foo="bar"}',
|
||||
regexp: 'x|y',
|
||||
regexp: '(?i)x|y',
|
||||
});
|
||||
});
|
||||
|
||||
@ -46,11 +46,11 @@ describe('parseQuery', () => {
|
||||
it('returns query and regexp with quantifiers', () => {
|
||||
expect(parseQuery('{foo="bar"} \\.java:[0-9]{1,5}')).toEqual({
|
||||
query: '{foo="bar"}',
|
||||
regexp: '\\.java:[0-9]{1,5}',
|
||||
regexp: '(?i)\\.java:[0-9]{1,5}',
|
||||
});
|
||||
expect(parseQuery('\\.java:[0-9]{1,5} {foo="bar"}')).toEqual({
|
||||
query: '{foo="bar"}',
|
||||
regexp: '\\.java:[0-9]{1,5}',
|
||||
regexp: '(?i)\\.java:[0-9]{1,5}',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
const selectorRegexp = /(?:^|\s){[^{]*}/g;
|
||||
const caseInsensitive = '(?i)'; // Golang mode modifier for Loki, doesn't work in JavaScript
|
||||
export function parseQuery(input: string) {
|
||||
input = input || '';
|
||||
const match = input.match(selectorRegexp);
|
||||
@ -10,6 +11,9 @@ export function parseQuery(input: string) {
|
||||
regexp = input.replace(selectorRegexp, '').trim();
|
||||
}
|
||||
|
||||
if (regexp) {
|
||||
regexp = caseInsensitive + regexp;
|
||||
}
|
||||
return { query, regexp };
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user