Skip to content

Commit

Permalink
add tests for fields and keywords
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Sebastian <paulstn@amazon.com>
  • Loading branch information
paulstn committed Jul 23, 2024
1 parent 684e27d commit c9206b8
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 30 deletions.
22 changes: 9 additions & 13 deletions src/plugins/data/public/antlr/dql/.generated/DQLParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,6 @@ export class DQLParser extends antlr.Parser {
{
this.state = 70;
this.match(DQLParser.LPAREN);
{
this.state = 72;
this.errorHandler.sync(this);
_la = this.tokenStream.LA(1);
Expand All @@ -379,7 +378,6 @@ export class DQLParser extends antlr.Parser {
}
}

}
this.state = 74;
this.groupContent();
this.state = 82;
Expand All @@ -397,7 +395,6 @@ export class DQLParser extends antlr.Parser {
this.errorHandler.reportMatch(this);
this.consume();
}
{
this.state = 77;
this.errorHandler.sync(this);
_la = this.tokenStream.LA(1);
Expand All @@ -408,7 +405,6 @@ export class DQLParser extends antlr.Parser {
}
}

}
this.state = 79;
this.groupContent();
}
Expand Down Expand Up @@ -855,6 +851,15 @@ export class GroupExpressionContext extends antlr.ParserRuleContext {
public RPAREN(): antlr.TerminalNode {
return this.getToken(DQLParser.RPAREN, 0)!;

Check warning on line 852 in src/plugins/data/public/antlr/dql/.generated/DQLParser.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data/public/antlr/dql/.generated/DQLParser.ts#L852

Added line #L852 was not covered by tests
}
public NOT(): antlr.TerminalNode[];
public NOT(i: number): antlr.TerminalNode | null;
public NOT(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] {
if (i === undefined) {
return this.getTokens(DQLParser.NOT);

Check warning on line 858 in src/plugins/data/public/antlr/dql/.generated/DQLParser.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data/public/antlr/dql/.generated/DQLParser.ts#L858

Added line #L858 was not covered by tests
} else {
return this.getToken(DQLParser.NOT, i);

Check warning on line 860 in src/plugins/data/public/antlr/dql/.generated/DQLParser.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data/public/antlr/dql/.generated/DQLParser.ts#L860

Added line #L860 was not covered by tests
}
}
public OR(): antlr.TerminalNode[];
public OR(i: number): antlr.TerminalNode | null;
public OR(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] {
Expand All @@ -873,15 +878,6 @@ export class GroupExpressionContext extends antlr.ParserRuleContext {
return this.getToken(DQLParser.AND, i);

Check warning on line 878 in src/plugins/data/public/antlr/dql/.generated/DQLParser.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data/public/antlr/dql/.generated/DQLParser.ts#L878

Added line #L878 was not covered by tests
}
}
public NOT(): antlr.TerminalNode[];
public NOT(i: number): antlr.TerminalNode | null;
public NOT(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] {
if (i === undefined) {
return this.getTokens(DQLParser.NOT);
} else {
return this.getToken(DQLParser.NOT, i);
}
}
public override get ruleIndex(): number {
return DQLParser.RULE_groupExpression;

Check warning on line 882 in src/plugins/data/public/antlr/dql/.generated/DQLParser.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data/public/antlr/dql/.generated/DQLParser.ts#L882

Added line #L882 was not covered by tests
}
Expand Down
281 changes: 281 additions & 0 deletions src/plugins/data/public/antlr/dql/code_completion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { monaco } from '@osd/monaco';
import { getSuggestions } from './code_completion';
import { IIndexPattern } from '../..';

const testingIndex = ({
title: 'opensearch_dashboards_sample_data_flights',
fields: [
{
count: 0,
name: 'Carrier',
displayName: 'Carrier',
type: 'string',
esTypes: ['keyword'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
subType: undefined,
},
{
count: 2,
name: 'DestCityName',
displayName: 'DestCityName',
type: 'string',
esTypes: ['keyword'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
subType: undefined,
},
{
count: 0,
name: 'DestCountry',
displayName: 'DestCountry',
type: 'string',
esTypes: ['keyword'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
subType: undefined,
},
{
count: 0,
name: 'DestWeather',
displayName: 'DestWeather',
type: 'string',
esTypes: ['keyword'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
subType: undefined,
},
{
count: 0,
name: 'DistanceMiles',
displayName: 'DistanceMiles',
type: 'number',
esTypes: ['float'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
subType: undefined,
},
{
count: 0,
name: 'FlightDelay',
displayName: 'FlightDelay',
type: 'boolean',
esTypes: ['boolean'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
subType: undefined,
},
{
count: 0,
name: 'FlightNum',
displayName: 'FlightNum',
type: 'string',
esTypes: ['keyword'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
subType: undefined,
},
{
count: 0,
name: 'OriginWeather',
displayName: 'OriginWeather',
type: 'string',
esTypes: ['keyword'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
subType: undefined,
},
{
count: 0,
name: '_id',
displayName: '_id',
type: 'string',
esTypes: ['_id'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: false,
subType: undefined,
},
{
count: 0,
name: '_index',
displayName: '_index',
type: 'string',
esTypes: ['_index'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: false,
subType: undefined,
},
{
count: 0,
name: '_score',
displayName: '_score',
type: 'number',
scripted: false,
searchable: false,
aggregatable: false,
readFromDocValues: false,
subType: undefined,
},
{
count: 0,
name: '_source',
displayName: '_source',
type: '_source',
esTypes: ['_source'],
scripted: false,
searchable: false,
aggregatable: false,
readFromDocValues: false,
subType: undefined,
},
{
count: 0,
name: '_type',
displayName: '_type',
type: 'string',
esTypes: ['_type'],
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: false,
subType: undefined,
},
],
} as unknown) as IIndexPattern;

const booleanOperatorSuggestions = [
{ text: 'or', type: 'keyword' },
{ text: 'and', type: 'keyword' },
];

const notOperatorSuggestion = { text: 'not', type: 'keyword' };

const fieldNameSuggestions = [
{ text: 'Carrier', type: 'field' },
{ text: 'DestCityName', type: 'field' },
{ text: 'DestCountry', type: 'field' },
{ text: 'DestWeather', type: 'field' },
{ text: 'DistanceMiles', type: 'field' },
{ text: 'FlightDelay', type: 'field' },
{ text: 'FlightNum', type: 'field' },
{ text: 'OriginWeather', type: 'field' },
{ text: '_id', type: 'field' },
{ text: '_index', type: 'field' },
{ text: '_score', type: 'field' },
{ text: '_source', type: 'field' },
{ text: '_type', type: 'field' },
];

const fieldNameWithNotSuggestions = fieldNameSuggestions.concat(notOperatorSuggestion);

const getSuggestionsAtPos = async (query: string, endPos: number) => {
return await getSuggestions({
query,
indexPatterns: [testingIndex],
position: new monaco.Position(1, endPos),
language: '', // not relevant
selectionEnd: 0, // not relevant
selectionStart: 0, // not relevant
});
};

const getSuggestionAtEnd = async (query: string) => {
return await getSuggestionsAtPos(query, query.length + 1);
};

describe('Test Boolean Operators', () => {
it('should suggest AND and OR after expression', async () => {
expect(await getSuggestionAtEnd('field: value ')).toStrictEqual(booleanOperatorSuggestions);
});

it('should suggest NOT initially', async () => {
expect(await getSuggestionAtEnd('')).toContainEqual(notOperatorSuggestion);
});

it('should suggest NOT after expression', async () => {
expect(await getSuggestionAtEnd('field: value and ')).toContainEqual(notOperatorSuggestion);
});

it('should not suggest NOT twice', async () => {
expect(await getSuggestionAtEnd('not ')).not.toContainEqual(notOperatorSuggestion);
});

it('should suggest after multiple token search', async () => {
expect(await getSuggestionAtEnd('field: one two three ')).toStrictEqual(
booleanOperatorSuggestions
);
});

it('should suggest after phrase value', async () => {
expect(await getSuggestionAtEnd('field: "value" ')).toStrictEqual(booleanOperatorSuggestions);
});

it('should suggest after number', async () => {
expect(await getSuggestionAtEnd('field: 123 ')).toStrictEqual(booleanOperatorSuggestions);
});

it('should not suggest after incomplete quote', async () => {
expect(await getSuggestionAtEnd('field: "value ')).not.toStrictEqual(
booleanOperatorSuggestions
);
});
});

describe('Test Boolean Operators within groups', () => {
it('should suggest AND and OR', async () => {
expect(await getSuggestionAtEnd('field: (value ')).toStrictEqual(booleanOperatorSuggestions);
});

it('should suggest NOT after expression', async () => {
expect(await getSuggestionAtEnd('field: (value and ')).toContainEqual(notOperatorSuggestion);
});

it('should suggest operator within nested group', async () => {
expect(await getSuggestionAtEnd('field: ("one" and ("two" ')).toStrictEqual(
booleanOperatorSuggestions
);
});
});

describe('Test field suggestions', () => {
it('basic field suggestion', async () => {
expect(await getSuggestionAtEnd('')).toStrictEqual(fieldNameWithNotSuggestions);
});

it('field suggestion after one term', async () => {
expect(await getSuggestionAtEnd('field: value and ')).toStrictEqual(
fieldNameWithNotSuggestions
);
});

it('field suggestion within group', async () => {
expect(await getSuggestionAtEnd('field: value and (one: "two" or ')).toStrictEqual(
fieldNameWithNotSuggestions
);
});
});
17 changes: 9 additions & 8 deletions src/plugins/data/public/antlr/dql/code_completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const findCursorIndex = (

const findFieldSuggestions = (indexPattern: IndexPattern) => {
const fieldNames: string[] = indexPattern.fields
.getAll()
.filter((idxField: IndexPatternField) => !idxField.subType) // filter removed .keyword fields
.map((idxField: { displayName: string }) => {
return idxField.displayName;
Expand Down Expand Up @@ -72,11 +71,11 @@ const findValueSuggestions = async (index: IndexPattern, field: string, value: s
// check to see if last field is within index and if it can suggest values, first check
// if .keyword appended field exists because that has values
const matchedField =
index.fields.getAll().find((idxField: IndexPatternField) => {
index.fields.find((idxField: IndexPatternField) => {
// check to see if the field matches another field with .keyword appended
if (idxField.displayName === `${field}.keyword`) return idxField;
}) ||
index.fields.getAll().find((idxField: IndexPatternField) => {
index.fields.find((idxField: IndexPatternField) => {
// if the display name matches, return
if (idxField.displayName === field) return idxField;
});
Expand Down Expand Up @@ -162,11 +161,13 @@ export const getSuggestions = async ({
const { field: lastField = '', value: lastValue = '' } = visitor.visit(tree) ?? {};
if (!!lastField && candidates.tokens.has(DQLParser.PHRASE)) {
const values = await findValueSuggestions(currentIndexPattern, lastField, lastValue ?? '');
completions.push(
...values.map((val: any) => {
return { text: val, type: 'value' };
})
);
if (!!values) {
completions.push(

Check warning on line 165 in src/plugins/data/public/antlr/dql/code_completion.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data/public/antlr/dql/code_completion.ts#L165

Added line #L165 was not covered by tests
...values?.map((val: any) => {
return { text: val, type: 'value' };

Check warning on line 167 in src/plugins/data/public/antlr/dql/code_completion.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data/public/antlr/dql/code_completion.ts#L167

Added line #L167 was not covered by tests
})
);
}
}

// suggest other candidates, mainly keywords
Expand Down
Loading

0 comments on commit c9206b8

Please sign in to comment.