[kql] Remove named args and unused geo functions (#118973)

* [kql] Remove named args and unused geo functions

* Fix tests

* Re-format grammar output
This commit is contained in:
Lukas Olson 2021-12-13 13:44:47 -07:00 committed by GitHub
parent 2c440da1d6
commit 35739880a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 48 additions and 588 deletions

View file

@ -12,7 +12,6 @@
const buildFunctionNode = nodeTypes.function.buildNodeWithArgumentNodes;
const buildLiteralNode = nodeTypes.literal.buildNode;
const buildWildcardNode = nodeTypes.wildcard.buildNode;
const buildNamedArgNode = nodeTypes.namedArg.buildNode;
const { wildcardSymbol } = nodeTypes.wildcard;
}
@ -100,8 +99,7 @@ FieldRangeExpression
suggestionTypes: ['conjunction']
};
}
const range = buildNamedArgNode(operator, value);
return buildFunctionNode('range', [field, range]);
return buildFunctionNode('range', [field, operator, value]);
}
FieldValueExpression

View file

@ -174,12 +174,8 @@ describe('kuery AST API', () => {
test('should support exclusive range operators', () => {
const expected = nodeTypes.function.buildNode('and', [
nodeTypes.function.buildNode('range', 'bytes', {
gt: '1000',
}),
nodeTypes.function.buildNode('range', 'bytes', {
lt: '8000',
}),
nodeTypes.function.buildNode('range', 'bytes', 'gt', '1000'),
nodeTypes.function.buildNode('range', 'bytes', 'lt', '8000'),
]);
const actual = fromKueryExpression('bytes > 1000 and bytes < 8000');
expect(actual).toEqual(expected);
@ -187,12 +183,8 @@ describe('kuery AST API', () => {
test('should support inclusive range operators', () => {
const expected = nodeTypes.function.buildNode('and', [
nodeTypes.function.buildNode('range', 'bytes', {
gte: '1000',
}),
nodeTypes.function.buildNode('range', 'bytes', {
lte: '8000',
}),
nodeTypes.function.buildNode('range', 'bytes', 'gte', '1000'),
nodeTypes.function.buildNode('range', 'bytes', 'lte', '8000'),
]);
const actual = fromKueryExpression('bytes >= 1000 and bytes <= 8000');
expect(actual).toEqual(expected);

View file

@ -1,137 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { get } from 'lodash';
import { nodeTypes } from '../node_types';
import { fields } from '../../filters/stubs';
import { DataViewBase } from '../..';
import * as geoBoundingBox from './geo_bounding_box';
import { JsonObject } from '@kbn/utility-types';
jest.mock('../grammar');
const params = {
bottomRight: {
lat: 50.73,
lon: -135.35,
},
topLeft: {
lat: 73.12,
lon: -174.37,
},
};
describe('kuery functions', () => {
describe('geoBoundingBox', () => {
let indexPattern: DataViewBase;
beforeEach(() => {
indexPattern = {
fields,
title: 'dataView',
};
});
describe('buildNodeParams', () => {
test('should return an "arguments" param', () => {
const result = geoBoundingBox.buildNodeParams('geo', params);
expect(result).toHaveProperty('arguments');
expect(Object.keys(result).length).toBe(1);
});
test('arguments should contain the provided fieldName as a literal', () => {
const result = geoBoundingBox.buildNodeParams('geo', params);
const {
arguments: [fieldName],
} = result;
expect(fieldName).toHaveProperty('type', 'literal');
expect(fieldName).toHaveProperty('value', 'geo');
});
test('arguments should contain the provided params as named arguments with "lat, lon" string values', () => {
const result = geoBoundingBox.buildNodeParams('geo', params);
const {
arguments: [, ...args],
} = result;
args.map((param: any) => {
expect(param).toHaveProperty('type', 'namedArg');
expect(['bottomRight', 'topLeft'].includes(param.name)).toBe(true);
expect(param.value.type).toBe('literal');
const { lat, lon } = get(params, param.name);
expect(param.value.value).toBe(`${lat}, ${lon}`);
});
});
});
describe('toElasticsearchQuery', () => {
test('should return an ES geo_bounding_box query representing the given node', () => {
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
const result = geoBoundingBox.toElasticsearchQuery(node, indexPattern);
expect(result).toHaveProperty('geo_bounding_box');
expect((result.geo_bounding_box as JsonObject).geo).toHaveProperty(
'top_left',
'73.12, -174.37'
);
expect((result.geo_bounding_box as JsonObject).geo).toHaveProperty(
'bottom_right',
'50.73, -135.35'
);
});
test('should return an ES geo_bounding_box query without an index pattern', () => {
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
const result = geoBoundingBox.toElasticsearchQuery(node);
expect(result).toHaveProperty('geo_bounding_box');
expect((result.geo_bounding_box as JsonObject).geo).toHaveProperty(
'top_left',
'73.12, -174.37'
);
expect((result.geo_bounding_box as JsonObject).geo).toHaveProperty(
'bottom_right',
'50.73, -135.35'
);
});
test('should use the ignore_unmapped parameter', () => {
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
const result = geoBoundingBox.toElasticsearchQuery(node, indexPattern);
expect(result.geo_bounding_box!.ignore_unmapped).toBe(true);
});
test('should throw an error for scripted fields', () => {
const node = nodeTypes.function.buildNode('geoBoundingBox', 'script number', params);
expect(() => geoBoundingBox.toElasticsearchQuery(node, indexPattern)).toThrowError(
/Geo bounding box query does not support scripted fields/
);
});
test('should use a provided nested context to create a full field name', () => {
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
const result = geoBoundingBox.toElasticsearchQuery(
node,
indexPattern,
{},
{ nested: { path: 'nestedField' } }
);
expect(result).toHaveProperty('geo_bounding_box');
expect((result.geo_bounding_box as JsonObject)['nestedField.geo']).toBeDefined();
});
});
});
});

View file

@ -1,61 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import _ from 'lodash';
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { nodeTypes } from '../node_types';
import * as ast from '../ast';
import { IndexPatternBase, KueryNode, KueryQueryOptions, LatLon } from '../..';
export function buildNodeParams(fieldName: string, params: any) {
params = _.pick(params, 'topLeft', 'bottomRight');
const fieldNameArg = nodeTypes.literal.buildNode(fieldName);
const args = _.map(params, (value: LatLon, key: string) => {
const latLon = `${value.lat}, ${value.lon}`;
return nodeTypes.namedArg.buildNode(key, latLon);
});
return {
arguments: [fieldNameArg, ...args],
};
}
export function toElasticsearchQuery(
node: KueryNode,
indexPattern?: IndexPatternBase,
config: KueryQueryOptions = {},
context: Record<string, any> = {}
): estypes.QueryDslQueryContainer {
const [fieldNameArg, ...args] = node.arguments;
const fullFieldNameArg = {
...fieldNameArg,
value: context?.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value,
};
const fieldName = nodeTypes.literal.toElasticsearchQuery(fullFieldNameArg) as string;
const fieldList = indexPattern?.fields ?? [];
const field = fieldList.find((fld) => fld.name === fieldName);
const queryParams = args.reduce((acc: any, arg: any) => {
const snakeArgName = _.snakeCase(arg.name);
return {
...acc,
[snakeArgName]: ast.toElasticsearchQuery(arg),
};
}, {});
if (field?.scripted) {
throw new Error(`Geo bounding box query does not support scripted fields`);
}
return {
geo_bounding_box: {
[fieldName]: queryParams,
ignore_unmapped: true,
},
};
}

View file

@ -1,134 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { nodeTypes } from '../node_types';
import { fields } from '../../filters/stubs';
import { DataViewBase } from '../..';
import * as geoPolygon from './geo_polygon';
jest.mock('../grammar');
const points = [
{
lat: 69.77,
lon: -171.56,
},
{
lat: 50.06,
lon: -169.1,
},
{
lat: 69.16,
lon: -125.85,
},
];
describe('kuery functions', () => {
describe('geoPolygon', () => {
let indexPattern: DataViewBase;
beforeEach(() => {
indexPattern = {
fields,
title: 'dataView',
};
});
describe('buildNodeParams', () => {
test('should return an "arguments" param', () => {
const result = geoPolygon.buildNodeParams('geo', points);
expect(result).toHaveProperty('arguments');
expect(Object.keys(result).length).toBe(1);
});
test('arguments should contain the provided fieldName as a literal', () => {
const result = geoPolygon.buildNodeParams('geo', points);
const {
arguments: [fieldName],
} = result;
expect(fieldName).toHaveProperty('type', 'literal');
expect(fieldName).toHaveProperty('value', 'geo');
});
test('arguments should contain the provided points literal "lat, lon" string values', () => {
const result = geoPolygon.buildNodeParams('geo', points);
const {
arguments: [, ...args],
} = result;
args.forEach((param: any, index: number) => {
const expectedPoint = points[index];
const expectedLatLon = `${expectedPoint.lat}, ${expectedPoint.lon}`;
expect(param).toHaveProperty('type', 'literal');
expect(param.value).toBe(expectedLatLon);
});
});
});
describe('toElasticsearchQuery', () => {
test('should return an ES geo_polygon query representing the given node', () => {
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
const result = geoPolygon.toElasticsearchQuery(node, indexPattern);
expect(result).toHaveProperty('geo_polygon');
expect((result.geo_polygon as any).geo).toHaveProperty('points');
(result.geo_polygon as any).geo.points.forEach((point: any, index: number) => {
const expectedLatLon = `${points[index].lat}, ${points[index].lon}`;
expect(point).toBe(expectedLatLon);
});
});
test('should return an ES geo_polygon query without an index pattern', () => {
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
const result = geoPolygon.toElasticsearchQuery(node);
expect(result).toHaveProperty('geo_polygon');
expect((result.geo_polygon as any).geo).toHaveProperty('points');
(result.geo_polygon as any).geo.points.forEach((point: any, index: number) => {
const expectedLatLon = `${points[index].lat}, ${points[index].lon}`;
expect(point).toBe(expectedLatLon);
});
});
test('should use the ignore_unmapped parameter', () => {
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
const result = geoPolygon.toElasticsearchQuery(node, indexPattern);
expect((result.geo_polygon as any).ignore_unmapped).toBe(true);
});
test('should throw an error for scripted fields', () => {
const node = nodeTypes.function.buildNode('geoPolygon', 'script number', points);
expect(() => geoPolygon.toElasticsearchQuery(node, indexPattern)).toThrowError(
/Geo polygon query does not support scripted fields/
);
});
test('should use a provided nested context to create a full field name', () => {
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
const result = geoPolygon.toElasticsearchQuery(
node,
indexPattern,
{},
{ nested: { path: 'nestedField' } }
);
expect(result).toHaveProperty('geo_polygon');
expect((result.geo_polygon as any)['nestedField.geo']).toBeDefined();
});
});
});
});

View file

@ -1,57 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { nodeTypes } from '../node_types';
import * as ast from '../ast';
import { IndexPatternBase, KueryNode, KueryQueryOptions, LatLon } from '../..';
import { LiteralTypeBuildNode } from '../node_types/types';
export function buildNodeParams(fieldName: string, points: LatLon[]) {
const fieldNameArg = nodeTypes.literal.buildNode(fieldName);
const args = points.map((point) => {
const latLon = `${point.lat}, ${point.lon}`;
return nodeTypes.literal.buildNode(latLon);
});
return {
arguments: [fieldNameArg, ...args],
};
}
export function toElasticsearchQuery(
node: KueryNode,
indexPattern?: IndexPatternBase,
config: KueryQueryOptions = {},
context: Record<string, any> = {}
): estypes.QueryDslQueryContainer {
const [fieldNameArg, ...points] = node.arguments;
const fullFieldNameArg = {
...fieldNameArg,
value: context?.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value,
};
const fieldName = nodeTypes.literal.toElasticsearchQuery(fullFieldNameArg) as string;
const fieldList = indexPattern?.fields ?? [];
const field = fieldList.find((fld) => fld.name === fieldName);
const queryParams = {
points: points.map((point: LiteralTypeBuildNode) => {
return ast.toElasticsearchQuery(point, indexPattern, config, context);
}),
};
if (field?.scripted) {
throw new Error(`Geo polygon query does not support scripted fields`);
}
return {
geo_polygon: {
[fieldName]: queryParams,
ignore_unmapped: true,
},
};
}

View file

@ -12,8 +12,6 @@ import * as or from './or';
import * as not from './not';
import * as range from './range';
import * as exists from './exists';
import * as geoBoundingBox from './geo_bounding_box';
import * as geoPolygon from './geo_polygon';
import * as nested from './nested';
export const functions = {
@ -23,7 +21,5 @@ export const functions = {
not,
range,
exists,
geoBoundingBox,
geoPolygon,
nested,
};

View file

@ -6,14 +6,13 @@
* Side Public License, v 1.
*/
import { get } from 'lodash';
import { nodeTypes } from '../node_types';
import { fields } from '../../filters/stubs';
import { DataViewBase } from '../..';
import { RangeFilterParams } from '../../filters';
import * as range from './range';
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
jest.mock('../grammar');
describe('kuery functions', () => {
@ -29,7 +28,7 @@ describe('kuery functions', () => {
describe('buildNodeParams', () => {
test('arguments should contain the provided fieldName as a literal', () => {
const result = range.buildNodeParams('bytes', { gt: 1000, lt: 8000 });
const result = range.buildNodeParams('bytes', 'gt', 1000);
const {
arguments: [fieldName],
} = result;
@ -38,22 +37,14 @@ describe('kuery functions', () => {
expect(fieldName).toHaveProperty('value', 'bytes');
});
test('arguments should contain the provided params as named arguments', () => {
const givenParams: RangeFilterParams = { gt: 1000, lt: 8000, format: 'epoch_millis' };
const result = range.buildNodeParams('bytes', givenParams);
test('arguments should contain the provided value as a literal', () => {
const result = range.buildNodeParams('bytes', 'gt', 1000);
const {
arguments: [, ...params],
arguments: [, , valueArg],
} = result;
expect(Array.isArray(params)).toBeTruthy();
expect(params.length).toBeGreaterThan(1);
params.map((param: any) => {
expect(param).toHaveProperty('type', 'namedArg');
expect(['gt', 'lt', 'format'].includes(param.name)).toBe(true);
expect(param.value.type).toBe('literal');
expect(param.value.value).toBe(get(givenParams, param.name));
});
expect(valueArg).toHaveProperty('type', 'literal');
expect(valueArg).toHaveProperty('value', 1000);
});
});
@ -66,7 +57,6 @@ describe('kuery functions', () => {
range: {
bytes: {
gt: 1000,
lt: 8000,
},
},
},
@ -74,7 +64,7 @@ describe('kuery functions', () => {
minimum_should_match: 1,
},
};
const node = nodeTypes.function.buildNode('range', 'bytes', { gt: 1000, lt: 8000 });
const node = nodeTypes.function.buildNode('range', 'bytes', 'gt', 1000);
const result = range.toElasticsearchQuery(node, indexPattern);
expect(result).toEqual(expected);
@ -88,7 +78,6 @@ describe('kuery functions', () => {
range: {
bytes: {
gt: 1000,
lt: 8000,
},
},
},
@ -97,7 +86,7 @@ describe('kuery functions', () => {
},
};
const node = nodeTypes.function.buildNode('range', 'bytes', { gt: 1000, lt: 8000 });
const node = nodeTypes.function.buildNode('range', 'bytes', 'gt', 1000);
const result = range.toElasticsearchQuery(node);
expect(result).toEqual(expected);
@ -111,7 +100,6 @@ describe('kuery functions', () => {
range: {
bytes: {
gt: 1000,
lt: 8000,
},
},
},
@ -120,14 +108,14 @@ describe('kuery functions', () => {
},
};
const node = nodeTypes.function.buildNode('range', 'byt*', { gt: 1000, lt: 8000 });
const node = nodeTypes.function.buildNode('range', 'byt*', 'gt', 1000);
const result = range.toElasticsearchQuery(node, indexPattern);
expect(result).toEqual(expected);
});
test('should support scripted fields', () => {
const node = nodeTypes.function.buildNode('range', 'script number', { gt: 1000, lt: 8000 });
const node = nodeTypes.function.buildNode('range', 'script number', 'gt', 1000);
const result = range.toElasticsearchQuery(node, indexPattern);
expect((result.bool!.should as estypes.QueryDslQueryContainer[])[0]).toHaveProperty(
@ -143,7 +131,6 @@ describe('kuery functions', () => {
range: {
'@timestamp': {
gt: '2018-01-03T19:04:17',
lt: '2018-04-03T19:04:17',
},
},
},
@ -151,10 +138,12 @@ describe('kuery functions', () => {
minimum_should_match: 1,
},
};
const node = nodeTypes.function.buildNode('range', '@timestamp', {
gt: '2018-01-03T19:04:17',
lt: '2018-04-03T19:04:17',
});
const node = nodeTypes.function.buildNode(
'range',
'@timestamp',
'gt',
'2018-01-03T19:04:17'
);
const result = range.toElasticsearchQuery(node, indexPattern);
expect(result).toEqual(expected);
@ -169,7 +158,6 @@ describe('kuery functions', () => {
range: {
'@timestamp': {
gt: '2018-01-03T19:04:17',
lt: '2018-04-03T19:04:17',
time_zone: 'America/Phoenix',
},
},
@ -178,10 +166,12 @@ describe('kuery functions', () => {
minimum_should_match: 1,
},
};
const node = nodeTypes.function.buildNode('range', '@timestamp', {
gt: '2018-01-03T19:04:17',
lt: '2018-04-03T19:04:17',
});
const node = nodeTypes.function.buildNode(
'range',
'@timestamp',
'gt',
'2018-01-03T19:04:17'
);
const result = range.toElasticsearchQuery(node, indexPattern, config);
expect(result).toEqual(expected);
@ -195,7 +185,6 @@ describe('kuery functions', () => {
range: {
'nestedField.bytes': {
gt: 1000,
lt: 8000,
},
},
},
@ -203,7 +192,7 @@ describe('kuery functions', () => {
minimum_should_match: 1,
},
};
const node = nodeTypes.function.buildNode('range', 'bytes', { gt: 1000, lt: 8000 });
const node = nodeTypes.function.buildNode('range', 'bytes', 'gt', 1000);
const result = range.toElasticsearchQuery(
node,
indexPattern,
@ -224,7 +213,6 @@ describe('kuery functions', () => {
query: {
range: {
'nestedField.nestedChild.doublyNestedChild': {
gt: 1000,
lt: 8000,
},
},
@ -236,10 +224,7 @@ describe('kuery functions', () => {
minimum_should_match: 1,
},
};
const node = nodeTypes.function.buildNode('range', '*doublyNested*', {
gt: 1000,
lt: 8000,
});
const node = nodeTypes.function.buildNode('range', '*doublyNested*', 'lt', 8000);
const result = range.toElasticsearchQuery(node, indexPattern);
expect(result).toEqual(expected);

View file

@ -6,30 +6,24 @@
* Side Public License, v 1.
*/
import { pick, map, mapValues } from 'lodash';
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { nodeTypes } from '../node_types';
import * as ast from '../ast';
import { getRangeScript, RangeFilterParams } from '../../filters';
import { getFields } from './utils/get_fields';
import { getTimeZoneFromSettings, getDataViewFieldSubtypeNested } from '../../utils';
import { getDataViewFieldSubtypeNested, getTimeZoneFromSettings } from '../../utils';
import { getFullFieldNameNode } from './utils/get_full_field_name_node';
import { IndexPatternBase, KueryNode, KueryQueryOptions } from '../..';
import type { IndexPatternBase, KueryNode, KueryQueryOptions } from '../..';
export function buildNodeParams(fieldName: string, params: RangeFilterParams) {
const paramsToMap = pick(params, 'gt', 'lt', 'gte', 'lte', 'format');
const fieldNameArg =
typeof fieldName === 'string'
? ast.fromLiteralExpression(fieldName)
: nodeTypes.literal.buildNode(fieldName);
const args = map(paramsToMap, (value: number | string, key: string) => {
return nodeTypes.namedArg.buildNode(key, value);
});
return {
arguments: [fieldNameArg, ...args],
};
export function buildNodeParams(
fieldName: string,
operator: keyof Pick<RangeFilterParams, 'gt' | 'gte' | 'lt' | 'lte'>,
value: number | string
) {
// Run through the parser instead treating it as a literal because it may contain wildcards
const fieldNameArg = ast.fromLiteralExpression(fieldName);
const valueArg = nodeTypes.literal.buildNode(value);
return { arguments: [fieldNameArg, operator, valueArg] };
}
export function toElasticsearchQuery(
@ -38,17 +32,13 @@ export function toElasticsearchQuery(
config: KueryQueryOptions = {},
context: Record<string, any> = {}
): estypes.QueryDslQueryContainer {
const [fieldNameArg, ...args] = node.arguments;
const [fieldNameArg, operatorArg, valueArg] = node.arguments;
const fullFieldNameArg = getFullFieldNameNode(
fieldNameArg,
indexPattern,
context?.nested ? context.nested.path : undefined
);
const fields = indexPattern ? getFields(fullFieldNameArg, indexPattern) : [];
const namedArgs = extractArguments(args);
const queryParams = mapValues(namedArgs, (arg: KueryNode) => {
return ast.toElasticsearchQuery(arg);
});
// If no fields are found in the index pattern we send through the given field name as-is. We do this to preserve
// the behaviour of lucene on dashboards where there are panels based on different index patterns that have different
@ -81,6 +71,10 @@ export function toElasticsearchQuery(
}
};
const queryParams = {
[operatorArg]: ast.toElasticsearchQuery(valueArg),
};
if (field.scripted) {
return {
script: getRangeScript(field, queryParams),
@ -112,21 +106,3 @@ export function toElasticsearchQuery(
},
};
}
function extractArguments(args: any) {
if ((args.gt && args.gte) || (args.lt && args.lte)) {
throw new Error('range ends cannot be both inclusive and exclusive');
}
const unnamedArgOrder = ['gte', 'lte', 'format'];
return args.reduce((acc: any, arg: any, index: number) => {
if (arg.type === 'namedArg') {
acc[arg.name] = arg.value;
} else {
acc[unnamedArgOrder[index]] = arg;
}
return acc;
}, {});
}

View file

@ -300,8 +300,7 @@ function peg$parse(input, options) {
suggestionTypes: ['conjunction']
};
}
const range = buildNamedArgNode(operator, value);
return buildFunctionNode('range', [field, range]);
return buildFunctionNode('range', [field, operator, value]);
};
var peg$f8 = function(field, partial) {
if (partial.type === 'cursor') {
@ -2190,7 +2189,6 @@ function peg$parse(input, options) {
const buildFunctionNode = nodeTypes.function.buildNodeWithArgumentNodes;
const buildLiteralNode = nodeTypes.literal.buildNode;
const buildWildcardNode = nodeTypes.wildcard.buildNode;
const buildNamedArgNode = nodeTypes.namedArg.buildNode;
const { wildcardSymbol } = nodeTypes.wildcard;

View file

@ -8,7 +8,6 @@
import * as functionType from './function';
import * as literal from './literal';
import * as namedArg from './named_arg';
import * as wildcard from './wildcard';
import { FunctionTypeBuildNode, NodeTypes } from './types';
@ -23,6 +22,5 @@ export const nodeTypes: NodeTypes = {
// @ts-ignore
function: functionType,
literal,
namedArg,
wildcard,
};

View file

@ -1,46 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { nodeTypes } from './index';
import { buildNode, toElasticsearchQuery } from './named_arg';
jest.mock('../grammar');
describe('kuery node types', () => {
describe('named arg', () => {
describe('buildNode', () => {
test('should return a node representing a named argument with the given value', () => {
const result = buildNode('fieldName', 'foo');
expect(result).toHaveProperty('type', 'namedArg');
expect(result).toHaveProperty('name', 'fieldName');
expect(result).toHaveProperty('value');
const literalValue = result.value;
expect(literalValue).toHaveProperty('type', 'literal');
expect(literalValue).toHaveProperty('value', 'foo');
});
test('should support literal nodes as values', () => {
const value = nodeTypes.literal.buildNode('foo');
const result = buildNode('fieldName', value);
expect(result.value).toBe(value);
expect(result.value).toEqual(value);
});
});
describe('toElasticsearchQuery', () => {
test('should return the argument value represented by the given node', () => {
const node = buildNode('fieldName', 'foo');
const result = toElasticsearchQuery(node);
expect(result).toBe('foo');
});
});
});
});

View file

@ -1,27 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import _ from 'lodash';
import { JsonObject } from '@kbn/utility-types';
import * as ast from '../ast';
import { nodeTypes } from '../node_types';
import { NamedArgTypeBuildNode } from './types';
export function buildNode(name: string, value: any): NamedArgTypeBuildNode {
const argumentNode =
_.get(value, 'type') === 'literal' ? value : nodeTypes.literal.buildNode(value);
return {
type: 'namedArg',
name,
value: argumentNode,
};
}
export function toElasticsearchQuery(node: any): JsonObject {
return ast.toElasticsearchQuery(node.value);
}

View file

@ -14,16 +14,7 @@ import { JsonValue } from '@kbn/utility-types';
import { KueryNode, KueryQueryOptions } from '..';
import { IndexPatternBase } from '../..';
export type FunctionName =
| 'is'
| 'and'
| 'or'
| 'not'
| 'range'
| 'exists'
| 'geoBoundingBox'
| 'geoPolygon'
| 'nested';
export type FunctionName = 'is' | 'and' | 'or' | 'not' | 'range' | 'exists' | 'nested';
interface FunctionType {
buildNode: (functionName: FunctionName, ...args: any[]) => FunctionTypeBuildNode;
@ -53,17 +44,6 @@ export interface LiteralTypeBuildNode {
value: null | boolean | number | string;
}
interface NamedArgType {
buildNode: (name: string, value: any) => NamedArgTypeBuildNode;
toElasticsearchQuery: (node: any) => JsonValue;
}
export interface NamedArgTypeBuildNode {
type: 'namedArg';
name: string;
value: any;
}
interface WildcardType {
wildcardSymbol: string;
buildNode: (value: string) => WildcardTypeBuildNode | KueryNode;
@ -81,6 +61,5 @@ export interface WildcardTypeBuildNode {
export interface NodeTypes {
function: FunctionType;
literal: LiteralType;
namedArg: NamedArgType;
wildcard: WildcardType;
}