diff --git a/packages/kbn-es-query/grammar/grammar.peggy b/packages/kbn-es-query/grammar/grammar.peggy index 8f5ef17340aa9..86074764852d6 100644 --- a/packages/kbn-es-query/grammar/grammar.peggy +++ b/packages/kbn-es-query/grammar/grammar.peggy @@ -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 diff --git a/packages/kbn-es-query/src/kuery/ast/ast.test.ts b/packages/kbn-es-query/src/kuery/ast/ast.test.ts index 0e4366d6e21d1..fe18eb3881ecc 100644 --- a/packages/kbn-es-query/src/kuery/ast/ast.test.ts +++ b/packages/kbn-es-query/src/kuery/ast/ast.test.ts @@ -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); diff --git a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts deleted file mode 100644 index 7580765d59282..0000000000000 --- a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts +++ /dev/null @@ -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(); - }); - }); - }); -}); diff --git a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts deleted file mode 100644 index 1808b7a2c0106..0000000000000 --- a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts +++ /dev/null @@ -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 = {} -): 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, - }, - }; -} diff --git a/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts b/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts deleted file mode 100644 index 5a96f6f3cb03d..0000000000000 --- a/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts +++ /dev/null @@ -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(); - }); - }); - }); -}); diff --git a/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts b/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts deleted file mode 100644 index 0cc95f8012a42..0000000000000 --- a/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts +++ /dev/null @@ -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 = {} -): 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, - }, - }; -} diff --git a/packages/kbn-es-query/src/kuery/functions/index.ts b/packages/kbn-es-query/src/kuery/functions/index.ts index e48382991b5d7..8e1b44517d3a1 100644 --- a/packages/kbn-es-query/src/kuery/functions/index.ts +++ b/packages/kbn-es-query/src/kuery/functions/index.ts @@ -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, }; diff --git a/packages/kbn-es-query/src/kuery/functions/range.test.ts b/packages/kbn-es-query/src/kuery/functions/range.test.ts index 2a97a74ac385d..575246724d6b2 100644 --- a/packages/kbn-es-query/src/kuery/functions/range.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/range.test.ts @@ -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); diff --git a/packages/kbn-es-query/src/kuery/functions/range.ts b/packages/kbn-es-query/src/kuery/functions/range.ts index e016feb908bc7..6ebda78aec8a7 100644 --- a/packages/kbn-es-query/src/kuery/functions/range.ts +++ b/packages/kbn-es-query/src/kuery/functions/range.ts @@ -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, + 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 = {} ): 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; - }, {}); -} diff --git a/packages/kbn-es-query/src/kuery/grammar/__mocks__/grammar.js b/packages/kbn-es-query/src/kuery/grammar/__mocks__/grammar.js index 89c13bb2b05c2..14f08e4a080c2 100644 --- a/packages/kbn-es-query/src/kuery/grammar/__mocks__/grammar.js +++ b/packages/kbn-es-query/src/kuery/grammar/__mocks__/grammar.js @@ -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; diff --git a/packages/kbn-es-query/src/kuery/node_types/index.ts b/packages/kbn-es-query/src/kuery/node_types/index.ts index cc538bdd1541c..899ab632718bf 100644 --- a/packages/kbn-es-query/src/kuery/node_types/index.ts +++ b/packages/kbn-es-query/src/kuery/node_types/index.ts @@ -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, }; diff --git a/packages/kbn-es-query/src/kuery/node_types/named_arg.test.ts b/packages/kbn-es-query/src/kuery/node_types/named_arg.test.ts deleted file mode 100644 index fa944660288d5..0000000000000 --- a/packages/kbn-es-query/src/kuery/node_types/named_arg.test.ts +++ /dev/null @@ -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'); - }); - }); - }); -}); diff --git a/packages/kbn-es-query/src/kuery/node_types/named_arg.ts b/packages/kbn-es-query/src/kuery/node_types/named_arg.ts deleted file mode 100644 index 1892a4885b705..0000000000000 --- a/packages/kbn-es-query/src/kuery/node_types/named_arg.ts +++ /dev/null @@ -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); -} diff --git a/packages/kbn-es-query/src/kuery/node_types/types.ts b/packages/kbn-es-query/src/kuery/node_types/types.ts index 8ee83971c833b..6df292b9929fd 100644 --- a/packages/kbn-es-query/src/kuery/node_types/types.ts +++ b/packages/kbn-es-query/src/kuery/node_types/types.ts @@ -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; }