[Global Search, Saved Objects Management] Use new parse option to specify recognized fields (#190464)

This PR adds a new option to the `SchemaType` interface for parsing a
query from a search in EuiSearchBar. This new field controls how
EuiSearchBar text is parsed into a Query object. It enables better
accuracy in how search terms are parsed when they include a `:`
character.

## Release note
Fixed an issue when using search bars with a term that includes a colon
`:` character.

## Summary

Closes https://github.com/elastic/kibana/issues/184496
Depends on https://github.com/elastic/eui/pull/7960

**GLOBAL SEARCH: BEFORE**

![akshfgkalsfh-before](https://github.com/user-attachments/assets/22377a3e-394e-43fb-83db-ae2477ce22d7)

**GLOBAL SEARCH: AFTER**

![akshfgkalsfh-after](https://github.com/user-attachments/assets/406d56eb-c946-493b-94f3-abb0380611f3)

**SAVED OBJECTS MANAGEMENT: BEFORE**

![okjoyofjiuh-before](https://github.com/user-attachments/assets/c1c56572-31aa-41df-b0c5-3eef421a06c5)

**SAVED OBJECTS MANAGEMENT: AFTER**

![okjoyofjiuh-after](https://github.com/user-attachments/assets/9e19bbcf-72e7-43d5-a9e7-3c5805632a38)

**SAVED OBJECTS FINDER: BEFORE**

![lfdgnhklfd-before](https://github.com/user-attachments/assets/b826987d-8af6-4c20-93b0-0d0bb76d9501)

**SAVED OBJECTS FINDER: AFTER**

![lfdgnhklfd-after](https://github.com/user-attachments/assets/e8e007b9-91f7-4209-bfd5-ce43a2cbc894)

## Checklist
 - [x] Ensure that filtering using `type:` and `tags:` still works
This commit is contained in:
Tim Sullivan 2024-09-19 09:02:45 -07:00 committed by GitHub
parent 44adb7353d
commit 460ca2a83f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 29 additions and 31 deletions

View file

@ -349,6 +349,9 @@ export class SavedObjectFinderUi extends React.Component<
box: {
incremental: true,
'data-test-subj': 'savedObjectFinderSearchInput',
schema: {
recognizedFields: ['type', 'tag'],
},
},
filters: this.props.showFilter
? [

View file

@ -6,6 +6,12 @@ exports[`Table prevents hidden saved objects from being deleted 1`] = `
box={
Object {
"data-test-subj": "savedObjectSearchBar",
"schema": Object {
"recognizedFields": Array [
"type",
"tag",
],
},
}
}
filters={
@ -234,6 +240,12 @@ exports[`Table should render normally 1`] = `
box={
Object {
"data-test-subj": "savedObjectSearchBar",
"schema": Object {
"recognizedFields": Array [
"type",
"tag",
],
},
}
}
filters={

View file

@ -392,7 +392,12 @@ export class Table extends PureComponent<TableProps, TableState> {
<Fragment>
{activeActionContents}
<EuiSearchBar
box={{ 'data-test-subj': 'savedObjectSearchBar' }}
box={{
'data-test-subj': 'savedObjectSearchBar',
schema: {
recognizedFields: ['type', 'tag'],
},
}}
filters={filters as any}
onChange={this.onChange}
defaultQuery={this.props.initialQuery}

View file

@ -17,9 +17,7 @@ describe('parseSearchParams', () => {
const searchParams = parseSearchParams('tag:((()^invalid');
expect(searchParams).toEqual({
term: 'tag:((()^invalid',
filters: {
unknowns: {},
},
filters: {},
});
});
@ -33,7 +31,6 @@ describe('parseSearchParams', () => {
expect(searchParams.filters).toEqual({
tags: undefined,
types: undefined,
unknowns: {},
});
});
@ -44,20 +41,16 @@ describe('parseSearchParams', () => {
filters: {
tags: ['foo', 'dolly'],
types: ['bar'],
unknowns: {},
},
});
});
it('handles unknowns field clauses', () => {
it('considers unknown field clauses to be part of the raw search term', () => {
const searchParams = parseSearchParams('tag:foo unknown:bar hello');
expect(searchParams).toEqual({
term: 'hello',
term: 'unknown:bar hello',
filters: {
tags: ['foo'],
unknowns: {
unknown: ['bar'],
},
},
});
});
@ -69,7 +62,6 @@ describe('parseSearchParams', () => {
filters: {
tags: ['foo', 'bar'],
types: ['dash', 'board'],
unknowns: {},
},
});
});
@ -81,7 +73,6 @@ describe('parseSearchParams', () => {
filters: {
tags: ['42', 'true'],
types: ['69', 'false'],
unknowns: {},
},
});
});

View file

@ -17,32 +17,24 @@ const aliasMap = {
};
export const parseSearchParams = (term: string): ParsedSearchParams => {
const recognizedFields = knownFilters.concat(...Object.values(aliasMap));
let query: Query;
try {
query = Query.parse(term);
query = Query.parse(term, {
schema: { recognizedFields },
});
} catch (e) {
// if the query fails to parse, we just perform the search against the raw search term.
return {
term,
filters: {
unknowns: {},
},
filters: {},
};
}
const searchTerm = getSearchTerm(query);
const filterValues = applyAliases(getFieldValueMap(query), aliasMap);
const unknownFilters = [...filterValues.entries()]
.filter(([key]) => !knownFilters.includes(key))
.reduce((unknowns, [key, value]) => {
return {
...unknowns,
[key]: value,
};
}, {} as Record<string, FilterValues>);
const tags = filterValues.get('tag');
const types = filterValues.get('type');
@ -51,7 +43,6 @@ export const parseSearchParams = (term: string): ParsedSearchParams => {
filters: {
tags: tags ? valuesToString(tags) : undefined,
types: types ? valuesToString(types) : undefined,
unknowns: unknownFilters,
},
};
};

View file

@ -27,9 +27,5 @@ export interface ParsedSearchParams {
* Aggregation of `type` and `types` field clauses
*/
types?: FilterValues<string>;
/**
* All unknown field clauses
*/
unknowns: Record<string, FilterValues>;
};
}