From 2ad90df09d3e3a8b644dde3b35624c576ace1817 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Wed, 6 Dec 2023 07:55:12 +0100 Subject: [PATCH] Add exact phrase search syntax Use double quote sign to search for queries with whitespaces Signed-off-by: Kyrylo Shmidt --- .../TracePageHeader/TracePageSearchBar.tsx | 4 ++++ .../jaeger-ui/src/utils/filter-spans.test.js | 22 +++++++++++++++++- packages/jaeger-ui/src/utils/filter-spans.tsx | 23 ++++++++++--------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageSearchBar.tsx b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageSearchBar.tsx index f64f6aa354..6b2a3eebad 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageSearchBar.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageSearchBar.tsx @@ -65,6 +65,10 @@ export function TracePageSearchBarFn(props: TracePageSearchBarProps & { forwarde span ID, and key-value pairs in tags and logs. The spans that match any of the search terms will be highlighted.

+

+ For exact phrase search surround the query in double quotes like{' '} + "The quick brown fox" +

When matching key-value pairs, the substring search is applied separately against the key, the value, and the concatenated "key=value" string. The latter allows searching diff --git a/packages/jaeger-ui/src/utils/filter-spans.test.js b/packages/jaeger-ui/src/utils/filter-spans.test.js index 15ca64036d..428e699fb3 100644 --- a/packages/jaeger-ui/src/utils/filter-spans.test.js +++ b/packages/jaeger-ui/src/utils/filter-spans.test.js @@ -31,6 +31,10 @@ describe('filterSpans', () => { key: 'processTagKey1', value: 'processTagValue1', }, + { + key: 'processTagKey3', + value: 'processTagValue3', + }, ], }, tags: [ @@ -42,6 +46,10 @@ describe('filterSpans', () => { key: 'tagKey1', value: 'tagValue1', }, + { + key: 'tagKey3', + value: 'tagValue3', + }, ], logs: [ { @@ -75,6 +83,10 @@ describe('filterSpans', () => { key: 'processTagKey1', value: 'processTagValue2', }, + { + key: 'processTagKey3', + value: 'processTag Value3', + }, ], }, tags: [ @@ -86,6 +98,10 @@ describe('filterSpans', () => { key: 'tagKey1', value: 'tagValue2', }, + { + key: 'tagKey3', + value: 'tag Value3', + }, ], logs: [ { @@ -158,6 +174,7 @@ describe('filterSpans', () => { expect(filterSpans('tagValue1', spans)).toEqual(new Set([spanID0, spanID2])); expect(filterSpans('tagValue0', spans)).toEqual(new Set([spanID0])); expect(filterSpans('tagValue2', spans)).toEqual(new Set([spanID2])); + expect(filterSpans('"tag Value3"', spans)).toEqual(new Set([spanID2])); }); it("should return spans whose tags' kv.key=kv.value match a filter", () => { @@ -169,6 +186,7 @@ describe('filterSpans', () => { it("should exclude span whose tags' kv.value or kv.key match a filter if the key matches an excludeKey", () => { expect(filterSpans('tagValue1 -tagKey2', spans)).toEqual(new Set([spanID0])); expect(filterSpans('tagValue1 -tagKey1', spans)).toEqual(new Set([spanID2])); + expect(filterSpans('"tag Value3" -tagKey3', spans)).toEqual(new Set()); }); it('should return spans whose logs have a field whose kv.key match a filter', () => { @@ -209,9 +227,10 @@ describe('filterSpans', () => { expect(filterSpans('processTagValue1', spans)).toEqual(new Set([spanID0, spanID2])); expect(filterSpans('processTagValue0', spans)).toEqual(new Set([spanID0])); expect(filterSpans('processTagValue2', spans)).toEqual(new Set([spanID2])); + expect(filterSpans('"processTag Value3"', spans)).toEqual(new Set([spanID2])); }); - it("should return spans whose process.processTags' kv.keykv.value match a filter", () => { + it("should return spans whose process.processTags' kv.key=kv.value match a filter", () => { expect(filterSpans('processTagKey1=processTagValue1', spans)).toEqual(new Set([spanID0])); expect(filterSpans('processTagKey0=processTagValue0', spans)).toEqual(new Set([spanID0])); expect(filterSpans('processTagKey2=processTagValue1', spans)).toEqual(new Set([spanID2])); @@ -220,6 +239,7 @@ describe('filterSpans', () => { it("should exclude span whose process.processTags' kv.value or kv.key match a filter if the key matches an excludeKey", () => { expect(filterSpans('processTagValue1 -processTagKey2', spans)).toEqual(new Set([spanID0])); expect(filterSpans('processTagValue1 -processTagKey1', spans)).toEqual(new Set([spanID2])); + expect(filterSpans('"processTag Value3" -processTagKey3', spans)).toEqual(new Set()); }); // This test may false positive if other tests are failing diff --git a/packages/jaeger-ui/src/utils/filter-spans.tsx b/packages/jaeger-ui/src/utils/filter-spans.tsx index da43138e99..ea09ecd329 100644 --- a/packages/jaeger-ui/src/utils/filter-spans.tsx +++ b/packages/jaeger-ui/src/utils/filter-spans.tsx @@ -26,17 +26,18 @@ export default function filterSpans(textFilter: string, spans: Span[] | TNil) { // values with keys that include text in any one of the excludeKeys will be ignored const excludeKeys: string[] = []; - // split textFilter by whitespace, remove empty strings, and extract includeFilters and excludeKeys - textFilter - .split(/\s+/) - .filter(Boolean) - .forEach(w => { - if (w[0] === '-') { - excludeKeys.push(w.substr(1).toLowerCase()); - } else { - includeFilters.push(w.toLowerCase()); - } - }); + // split textFilter by whitespace, but not that in double quotes, remove empty strings, and extract includeFilters and excludeKeys + const regex = /[^\s"]+|"([^"]*)"/g; + const match = textFilter.match(regex); + const results = match ? match.map(e => e.replace(/"(.*)"/, '$1')) : []; + + results.filter(Boolean).forEach(w => { + if (w[0] === '-') { + excludeKeys.push(w.substr(1).toLowerCase()); + } else { + includeFilters.push(w.toLowerCase()); + } + }); const isTextInFilters = (filters: Array, text: string) => filters.some(filter => text.toLowerCase().includes(filter));