Skip to content

Commit

Permalink
[Search Bar/Query/Default Syntax] Parser support for "recognizedField…
Browse files Browse the repository at this point in the history
…s" options (#7960)
  • Loading branch information
tsullivan committed Aug 22, 2024
1 parent 98ce259 commit 54ab23a
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/eui/changelogs/upcoming/7960.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Updated `EuiSearchBar`'s optional `box.schema` prop with a new `recognizedFields` configuration. This allows specifying the phrases that will be parsed as field clauses
Original file line number Diff line number Diff line change
Expand Up @@ -1240,4 +1240,76 @@ describe('defaultSyntax', () => {
const printedQuery = defaultSyntax.print(ast);
expect(printedQuery).toBe(query);
});

describe('recognizedFields', () => {
test('parse field clauses from a query only for recognized fields', () => {
const query = 'remote:test type:dataview happiness';
const ast = defaultSyntax.parse(query, {
schema: {
recognizedFields: ['tag', 'type'],
},
});
expect(ast).toBeDefined();
expect(ast.clauses).toHaveLength(3);
expect(ast.getFieldClauses()).toMatchObject([
{ field: 'type', match: 'must', value: 'dataview' },
]);
expect(ast.getTermClauses()).toEqual([
{ match: 'must', type: 'term', value: 'remote:test' },
{ match: 'must', type: 'term', value: 'happiness' },
]);

const printedQuery = defaultSyntax.print(ast);
expect(printedQuery).toBe('remote\\:test type:dataview happiness');
});

test('combined with "is" clause', () => {
const query = 'remote:test is:pending';
const ast = defaultSyntax.parse(query, {
schema: {
recognizedFields: ['tag', 'type'],
},
});
expect(ast).toBeDefined();
expect(ast.clauses).toHaveLength(2);
expect(ast.getFieldClauses()).toHaveLength(0);
expect(ast.getIsClauses()).toEqual([
{ flag: 'pending', match: 'must', type: 'is' },
]);
expect(ast.getTermClauses()).toEqual([
{ match: 'must', type: 'term', value: 'remote:test' },
]);

const printedQuery = defaultSyntax.print(ast);
expect(printedQuery).toBe('remote\\:test is:pending');
});

test('parse complex terms from a query', () => {
const query =
'my-remote-cluster:my-remote-index type:data-view tag:really-nice-content';
const ast = defaultSyntax.parse(query, {
schema: {
recognizedFields: ['tag', 'type'],
},
});
expect(ast).toBeDefined();
expect(ast.clauses).toHaveLength(3);
expect(ast.getFieldClauses()).toMatchObject([
{ field: 'type', match: 'must', value: 'data-view' },
{ field: 'tag', match: 'must', value: 'really-nice-content' },
]);
expect(ast.getTermClauses()).toEqual([
{
match: 'must',
type: 'term',
value: 'my-remote-cluster:my-remote-index',
},
]);

const printedQuery = defaultSyntax.print(ast);
expect(printedQuery).toBe(
'my\\-remote\\-cluster\\:my\\-remote\\-index type:data-view tag:really-nice-content'
);
});
});
});
15 changes: 11 additions & 4 deletions packages/eui/src/components/search_bar/query/default_syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import peg from 'pegjs-inline-precompile'; // eslint-disable-line import/no-unre

const parser = peg`
{
const { AST, Exp, unescapeValue, unescapePhraseValue, resolveFieldValue } = options;
const { AST, Exp, unescapeValue, unescapePhraseValue, resolveFieldValue, recognizedFields } = options;
const hasRecognizedFields = recognizedFields && recognizedFields.length > 0;
const ctx = Object.assign({ error }, options );
}
Expand Down Expand Up @@ -108,8 +109,9 @@ FieldLTEValue
flagName "flag name"
= identifier
// If recognizedFields was given in options, the identifier must match an allowed field
fieldName "field name"
= identifier
= id:identifier &{ return !hasRecognizedFields || recognizedFields.includes(id); } { return id; }
identifier
= identifierChar+ { return unescapeValue(text()); }
Expand Down Expand Up @@ -256,7 +258,11 @@ interface ValueExpression {

export interface ParseOptions {
dateFormat?: any;
schema?: any;
schema?: {
strict?: boolean;
fields?: string[] | Record<string, any>;
recognizedFields?: string[];
};
escapeValue?: (value: any) => string;
}

Expand Down Expand Up @@ -492,7 +498,7 @@ export const defaultSyntax: Syntax = Object.freeze({
parse: (query: string, options: ParseOptions = {}) => {
const dateFormat = options.dateFormat || defaultDateFormat;
const parseDate = dateValueParser(dateFormat);
const schema = options.schema || {};
const { recognizedFields, ...schema } = options.schema || {};
const clauses = parser.parse(query, {
AST,
Exp,
Expand All @@ -502,6 +508,7 @@ export const defaultSyntax: Syntax = Object.freeze({
resolveFieldValue,
validateFlag,
schema: { strict: false, flags: [], fields: {}, ...schema },
recognizedFields,
});
return AST.create(clauses);
},
Expand Down
4 changes: 3 additions & 1 deletion packages/eui/src/components/search_bar/search_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export interface SchemaType {
strict?: boolean;
fields?: any;
flags?: string[];
// Controls which phrases will be parsed as field clauses
recognizedFields?: string[];
}

export type EuiSearchBarOnChangeArgs = ArgsWithQuery | ArgsWithError;
Expand Down Expand Up @@ -123,7 +125,7 @@ const parseQuery = (
props: EuiSearchBarProps
): Query => {
let schema: SchemaType | undefined = undefined;
if (props.box && props.box.schema && typeof props.box.schema === 'object') {
if (props.box?.schema && typeof props.box?.schema === 'object') {
schema = props.box.schema;
}
const dateFormat = props.dateFormat;
Expand Down

0 comments on commit 54ab23a

Please sign in to comment.