From 3e491f88fa6da3d0000e21d4aa2d4c6b79cbe940 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Thu, 27 Feb 2020 11:06:28 +0000 Subject: [PATCH] Typescript es-query functions (#57473) (#58695) * TS Functions * Rename index.ts * tsing * JSON Value * ts ignore * TS adjustments * context?.nested?.path * Code review * Import LiteralTypeBuildNode * Fix jest tests * revert test * TS fix IFieldType * Remvoe unnecessary casting * range types Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../kuery/functions/{and.js => and.ts} | 12 ++++-- .../kuery/functions/{exists.js => exists.ts} | 16 ++++--- ...eo_bounding_box.js => geo_bounding_box.ts} | 24 +++++++---- .../kuery/functions/geo_polygon.test.ts | 4 +- .../{geo_polygon.js => geo_polygon.ts} | 23 ++++++---- .../kuery/functions/{index.js => index.ts} | 0 .../es_query/kuery/functions/is.test.ts | 9 +--- .../es_query/kuery/functions/{is.js => is.ts} | 29 ++++++++----- .../kuery/functions/{nested.js => nested.ts} | 13 ++++-- .../kuery/functions/{not.js => not.ts} | 10 ++++- .../es_query/kuery/functions/{or.js => or.ts} | 12 ++++-- .../kuery/functions/{range.js => range.ts} | 42 ++++++++++++------- .../kuery/functions/utils/get_fields.test.ts | 10 ++--- .../utils/{get_fields.js => get_fields.ts} | 11 +++-- .../utils/get_full_field_name_node.test.ts | 4 +- ...me_node.js => get_full_field_name_node.ts} | 15 +++++-- .../es_query/kuery/node_types/function.ts | 11 ++--- .../common/es_query/kuery/node_types/index.ts | 2 + 18 files changed, 159 insertions(+), 88 deletions(-) rename src/plugins/data/common/es_query/kuery/functions/{and.js => and.ts} (76%) rename src/plugins/data/common/es_query/kuery/functions/{exists.js => exists.ts} (70%) rename src/plugins/data/common/es_query/kuery/functions/{geo_bounding_box.js => geo_bounding_box.ts} (70%) rename src/plugins/data/common/es_query/kuery/functions/{geo_polygon.js => geo_polygon.ts} (69%) rename src/plugins/data/common/es_query/kuery/functions/{index.js => index.ts} (100%) rename src/plugins/data/common/es_query/kuery/functions/{is.js => is.ts} (86%) rename src/plugins/data/common/es_query/kuery/functions/{nested.js => nested.ts} (78%) rename src/plugins/data/common/es_query/kuery/functions/{not.js => not.ts} (79%) rename src/plugins/data/common/es_query/kuery/functions/{or.js => or.ts} (76%) rename src/plugins/data/common/es_query/kuery/functions/{range.js => range.ts} (74%) rename src/plugins/data/common/es_query/kuery/functions/utils/{get_fields.js => get_fields.ts} (69%) rename src/plugins/data/common/es_query/kuery/functions/utils/{get_full_field_name_node.js => get_full_field_name_node.ts} (82%) diff --git a/src/plugins/data/common/es_query/kuery/functions/and.js b/src/plugins/data/common/es_query/kuery/functions/and.ts similarity index 76% rename from src/plugins/data/common/es_query/kuery/functions/and.js rename to src/plugins/data/common/es_query/kuery/functions/and.ts index 336b1a6ab5c21..bcfc4e7f2a168 100644 --- a/src/plugins/data/common/es_query/kuery/functions/and.js +++ b/src/plugins/data/common/es_query/kuery/functions/and.ts @@ -18,19 +18,25 @@ */ import * as ast from '../ast'; +import { IIndexPattern, KueryNode } from '../../..'; -export function buildNodeParams(children) { +export function buildNodeParams(children: KueryNode[]) { return { arguments: children, }; } -export function toElasticsearchQuery(node, indexPattern, config, context) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const children = node.arguments || []; return { bool: { - filter: children.map(child => { + filter: children.map((child: KueryNode) => { return ast.toElasticsearchQuery(child, indexPattern, config, context); }), }, diff --git a/src/plugins/data/common/es_query/kuery/functions/exists.js b/src/plugins/data/common/es_query/kuery/functions/exists.ts similarity index 70% rename from src/plugins/data/common/es_query/kuery/functions/exists.js rename to src/plugins/data/common/es_query/kuery/functions/exists.ts index 44f484f37a940..eb6829fca58d9 100644 --- a/src/plugins/data/common/es_query/kuery/functions/exists.js +++ b/src/plugins/data/common/es_query/kuery/functions/exists.ts @@ -19,25 +19,31 @@ import { get } from 'lodash'; import * as literal from '../node_types/literal'; +import { IIndexPattern, KueryNode, IFieldType } from '../../..'; -export function buildNodeParams(fieldName) { +export function buildNodeParams(fieldName: string) { return { arguments: [literal.buildNode(fieldName)], }; } -export function toElasticsearchQuery(node, indexPattern = null, config, context = {}) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const { arguments: [fieldNameArg], } = node; const fullFieldNameArg = { ...fieldNameArg, - value: context.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value, + value: context?.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value, }; const fieldName = literal.toElasticsearchQuery(fullFieldNameArg); - const field = get(indexPattern, 'fields', []).find(field => field.name === fieldName); + const field = get(indexPattern, 'fields', []).find((fld: IFieldType) => fld.name === fieldName); - if (field && field.scripted) { + if (field && (field as IFieldType).scripted) { throw new Error(`Exists query does not support scripted fields`); } return { diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.js b/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts similarity index 70% rename from src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.js rename to src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts index c0699d4064824..d61b16f8dcd85 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.js +++ b/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts @@ -20,11 +20,12 @@ import _ from 'lodash'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; +import { IIndexPattern, KueryNode, IFieldType, LatLon } from '../../..'; -export function buildNodeParams(fieldName, params) { +export function buildNodeParams(fieldName: string, params: any) { params = _.pick(params, 'topLeft', 'bottomRight'); const fieldNameArg = nodeTypes.literal.buildNode(fieldName); - const args = _.map(params, (value, key) => { + const args = _.map(params, (value: LatLon, key: string) => { const latLon = `${value.lat}, ${value.lon}`; return nodeTypes.namedArg.buildNode(key, latLon); }); @@ -34,15 +35,22 @@ export function buildNodeParams(fieldName, params) { }; } -export function toElasticsearchQuery(node, indexPattern, config, context = {}) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const [fieldNameArg, ...args] = node.arguments; const fullFieldNameArg = { ...fieldNameArg, - value: context.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value, + value: context?.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value, }; - const fieldName = nodeTypes.literal.toElasticsearchQuery(fullFieldNameArg); - const field = _.get(indexPattern, 'fields', []).find(field => field.name === fieldName); - const queryParams = args.reduce((acc, arg) => { + const fieldName = nodeTypes.literal.toElasticsearchQuery(fullFieldNameArg) as string; + const fieldList: IFieldType[] = indexPattern?.fields ?? []; + const field = fieldList.find((fld: IFieldType) => fld.name === fieldName); + + const queryParams = args.reduce((acc: any, arg: any) => { const snakeArgName = _.snakeCase(arg.name); return { ...acc, @@ -50,7 +58,7 @@ export function toElasticsearchQuery(node, indexPattern, config, context = {}) { }; }, {}); - if (field && field.scripted) { + if (field?.scripted) { throw new Error(`Geo bounding box query does not support scripted fields`); } diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.test.ts b/src/plugins/data/common/es_query/kuery/functions/geo_polygon.test.ts index 84500cb4ade7e..6653920f96559 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.test.ts +++ b/src/plugins/data/common/es_query/kuery/functions/geo_polygon.test.ts @@ -91,7 +91,7 @@ describe('kuery functions', () => { expect(result).toHaveProperty('geo_polygon'); expect(result.geo_polygon.geo).toHaveProperty('points'); - result.geo_polygon.geo.points.forEach((point: any, index: number) => { + (result.geo_polygon.geo as any).points.forEach((point: any, index: number) => { const expectedLatLon = `${points[index].lat}, ${points[index].lon}`; expect(point).toBe(expectedLatLon); @@ -105,7 +105,7 @@ describe('kuery functions', () => { expect(result).toHaveProperty('geo_polygon'); expect(result.geo_polygon.geo).toHaveProperty('points'); - result.geo_polygon.geo.points.forEach((point: any, index: number) => { + (result.geo_polygon.geo as any).points.forEach((point: any, index: number) => { const expectedLatLon = `${points[index].lat}, ${points[index].lon}`; expect(point).toBe(expectedLatLon); diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.js b/src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts similarity index 69% rename from src/plugins/data/common/es_query/kuery/functions/geo_polygon.js rename to src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts index 0934fde7d1e6a..f382e5668bb9d 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.js +++ b/src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts @@ -17,11 +17,12 @@ * under the License. */ -import { get } from 'lodash'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; +import { IIndexPattern, KueryNode, IFieldType, LatLon } from '../../..'; +import { LiteralTypeBuildNode } from '../node_types/types'; -export function buildNodeParams(fieldName, points) { +export function buildNodeParams(fieldName: string, points: LatLon[]) { const fieldNameArg = nodeTypes.literal.buildNode(fieldName); const args = points.map(point => { const latLon = `${point.lat}, ${point.lon}`; @@ -33,21 +34,27 @@ export function buildNodeParams(fieldName, points) { }; } -export function toElasticsearchQuery(node, indexPattern, config = {}, context = {}) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const [fieldNameArg, ...points] = node.arguments; const fullFieldNameArg = { ...fieldNameArg, - value: context.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value, + value: context?.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value, }; - const fieldName = nodeTypes.literal.toElasticsearchQuery(fullFieldNameArg); - const field = get(indexPattern, 'fields', []).find(field => field.name === fieldName); + const fieldName = nodeTypes.literal.toElasticsearchQuery(fullFieldNameArg) as string; + const fieldList: IFieldType[] = indexPattern?.fields ?? []; + const field = fieldList.find((fld: IFieldType) => fld.name === fieldName); const queryParams = { - points: points.map(point => { + points: points.map((point: LiteralTypeBuildNode) => { return ast.toElasticsearchQuery(point, indexPattern, config, context); }), }; - if (field && field.scripted) { + if (field?.scripted) { throw new Error(`Geo polygon query does not support scripted fields`); } diff --git a/src/plugins/data/common/es_query/kuery/functions/index.js b/src/plugins/data/common/es_query/kuery/functions/index.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/functions/index.js rename to src/plugins/data/common/es_query/kuery/functions/index.ts diff --git a/src/plugins/data/common/es_query/kuery/functions/is.test.ts b/src/plugins/data/common/es_query/kuery/functions/is.test.ts index df147bad54a34..5053af948fd86 100644 --- a/src/plugins/data/common/es_query/kuery/functions/is.test.ts +++ b/src/plugins/data/common/es_query/kuery/functions/is.test.ts @@ -35,11 +35,6 @@ describe('kuery functions', () => { }); describe('buildNodeParams', () => { - test('fieldName and value should be required arguments', () => { - expect(() => is.buildNodeParams()).toThrowError(/fieldName is a required argument/); - expect(() => is.buildNodeParams('foo')).toThrowError(/value is a required argument/); - }); - test('arguments should contain the provided fieldName and value as literals', () => { const { arguments: [fieldName, value], @@ -117,7 +112,7 @@ describe('kuery functions', () => { const result = is.toElasticsearchQuery(node, indexPattern); expect(result).toHaveProperty('bool'); - expect(result.bool.should.length).toBe(indexPattern.fields.length); + expect(result.bool!.should!.length).toBe(indexPattern.fields.length); }); test('should return an ES exists query when value is "*"', () => { @@ -196,7 +191,7 @@ describe('kuery functions', () => { const node = nodeTypes.function.buildNode('is', 'script string', 'foo'); const result = is.toElasticsearchQuery(node, indexPattern); - expect(result.bool.should[0]).toHaveProperty('script'); + expect(result.bool!.should![0]).toHaveProperty('script'); }); test('should support date fields without a dateFormat provided', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/is.js b/src/plugins/data/common/es_query/kuery/functions/is.ts similarity index 86% rename from src/plugins/data/common/es_query/kuery/functions/is.js rename to src/plugins/data/common/es_query/kuery/functions/is.ts index 4d1435adec143..89aec6e55e81b 100644 --- a/src/plugins/data/common/es_query/kuery/functions/is.js +++ b/src/plugins/data/common/es_query/kuery/functions/is.ts @@ -22,13 +22,14 @@ import { getPhraseScript } from '../../filters'; import { getFields } from './utils/get_fields'; import { getTimeZoneFromSettings } from '../../utils'; import { getFullFieldNameNode } from './utils/get_full_field_name_node'; +import { IIndexPattern, KueryNode, IFieldType } from '../../..'; import * as ast from '../ast'; import * as literal from '../node_types/literal'; import * as wildcard from '../node_types/wildcard'; -export function buildNodeParams(fieldName, value, isPhrase = false) { +export function buildNodeParams(fieldName: string, value: any, isPhrase: boolean = false) { if (isUndefined(fieldName)) { throw new Error('fieldName is a required argument'); } @@ -47,14 +48,19 @@ export function buildNodeParams(fieldName, value, isPhrase = false) { }; } -export function toElasticsearchQuery(node, indexPattern = null, config = {}, context = {}) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const { arguments: [fieldNameArg, valueArg, isPhraseArg], } = node; const fullFieldNameArg = getFullFieldNameNode( fieldNameArg, indexPattern, - context.nested ? context.nested.path : undefined + context?.nested ? context.nested.path : undefined ); const fieldName = ast.toElasticsearchQuery(fullFieldNameArg); const value = !isUndefined(valueArg) ? ast.toElasticsearchQuery(valueArg) : valueArg; @@ -85,14 +91,15 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con // keep things familiar for now. if (fields && fields.length === 0) { fields.push({ - name: ast.toElasticsearchQuery(fullFieldNameArg), + name: (ast.toElasticsearchQuery(fullFieldNameArg) as unknown) as string, scripted: false, + type: '', }); } const isExistsQuery = valueArg.type === 'wildcard' && value === '*'; const isAllFieldsQuery = - (fullFieldNameArg.type === 'wildcard' && fieldName === '*') || + (fullFieldNameArg.type === 'wildcard' && ((fieldName as unknown) as string) === '*') || (fields && indexPattern && fields.length === indexPattern.fields.length); const isMatchAllQuery = isExistsQuery && isAllFieldsQuery; @@ -100,20 +107,20 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con return { match_all: {} }; } - const queries = fields.reduce((accumulator, field) => { - const wrapWithNestedQuery = query => { + const queries = fields!.reduce((accumulator: any, field: IFieldType) => { + const wrapWithNestedQuery = (query: any) => { // Wildcards can easily include nested and non-nested fields. There isn't a good way to let // users handle this themselves so we automatically add nested queries in this scenario. if ( !(fullFieldNameArg.type === 'wildcard') || !get(field, 'subType.nested') || - context.nested + context?.nested ) { return query; } else { return { nested: { - path: field.subType.nested.path, + path: field.subType!.nested!.path, query, score_mode: 'none', }, @@ -158,7 +165,7 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con dateFormatTZ can have the value of 'Browser', in which case we guess the timezone using moment.tz.guess. */ const timeZoneParam = config.dateFormatTZ - ? { time_zone: getTimeZoneFromSettings(config.dateFormatTZ) } + ? { time_zone: getTimeZoneFromSettings(config!.dateFormatTZ) } : {}; return [ ...accumulator, @@ -187,7 +194,7 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con return { bool: { - should: queries, + should: queries || [], minimum_should_match: 1, }, }; diff --git a/src/plugins/data/common/es_query/kuery/functions/nested.js b/src/plugins/data/common/es_query/kuery/functions/nested.ts similarity index 78% rename from src/plugins/data/common/es_query/kuery/functions/nested.js rename to src/plugins/data/common/es_query/kuery/functions/nested.ts index f9ce496636e73..4111700e5c4ce 100644 --- a/src/plugins/data/common/es_query/kuery/functions/nested.js +++ b/src/plugins/data/common/es_query/kuery/functions/nested.ts @@ -19,8 +19,9 @@ import * as ast from '../ast'; import * as literal from '../node_types/literal'; +import { IIndexPattern, KueryNode } from '../../..'; -export function buildNodeParams(path, child) { +export function buildNodeParams(path: any, child: any) { const pathNode = typeof path === 'string' ? ast.fromLiteralExpression(path) : literal.buildNode(path); return { @@ -28,11 +29,15 @@ export function buildNodeParams(path, child) { }; } -export function toElasticsearchQuery(node, indexPattern, config, context = {}) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const [path, child] = node.arguments; const stringPath = ast.toElasticsearchQuery(path); - const fullPath = - context.nested && context.nested.path ? `${context.nested.path}.${stringPath}` : stringPath; + const fullPath = context?.nested?.path ? `${context.nested.path}.${stringPath}` : stringPath; return { nested: { diff --git a/src/plugins/data/common/es_query/kuery/functions/not.js b/src/plugins/data/common/es_query/kuery/functions/not.ts similarity index 79% rename from src/plugins/data/common/es_query/kuery/functions/not.js rename to src/plugins/data/common/es_query/kuery/functions/not.ts index 4cc40b220d31e..87276c1b0653c 100644 --- a/src/plugins/data/common/es_query/kuery/functions/not.js +++ b/src/plugins/data/common/es_query/kuery/functions/not.ts @@ -18,14 +18,20 @@ */ import * as ast from '../ast'; +import { IIndexPattern, KueryNode } from '../../..'; -export function buildNodeParams(child) { +export function buildNodeParams(child: KueryNode) { return { arguments: [child], }; } -export function toElasticsearchQuery(node, indexPattern, config, context) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const [argument] = node.arguments; return { diff --git a/src/plugins/data/common/es_query/kuery/functions/or.js b/src/plugins/data/common/es_query/kuery/functions/or.ts similarity index 76% rename from src/plugins/data/common/es_query/kuery/functions/or.js rename to src/plugins/data/common/es_query/kuery/functions/or.ts index 3f138a978d46f..8a8b479c47ea2 100644 --- a/src/plugins/data/common/es_query/kuery/functions/or.js +++ b/src/plugins/data/common/es_query/kuery/functions/or.ts @@ -18,19 +18,25 @@ */ import * as ast from '../ast'; +import { IIndexPattern, KueryNode } from '../../..'; -export function buildNodeParams(children) { +export function buildNodeParams(children: KueryNode[]) { return { arguments: children, }; } -export function toElasticsearchQuery(node, indexPattern, config, context) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const children = node.arguments || []; return { bool: { - should: children.map(child => { + should: children.map((child: KueryNode) => { return ast.toElasticsearchQuery(child, indexPattern, config, context); }), minimum_should_match: 1, diff --git a/src/plugins/data/common/es_query/kuery/functions/range.js b/src/plugins/data/common/es_query/kuery/functions/range.ts similarity index 74% rename from src/plugins/data/common/es_query/kuery/functions/range.js rename to src/plugins/data/common/es_query/kuery/functions/range.ts index 31577d8004d4e..feffaa3ec7dda 100644 --- a/src/plugins/data/common/es_query/kuery/functions/range.js +++ b/src/plugins/data/common/es_query/kuery/functions/range.ts @@ -20,18 +20,20 @@ import _ from 'lodash'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; -import { getRangeScript } from '../../filters'; +import { getRangeScript, RangeFilterParams } from '../../filters'; import { getFields } from './utils/get_fields'; import { getTimeZoneFromSettings } from '../../utils'; import { getFullFieldNameNode } from './utils/get_full_field_name_node'; +import { IIndexPattern, KueryNode, IFieldType } from '../../..'; -export function buildNodeParams(fieldName, params) { - params = _.pick(params, 'gt', 'lt', 'gte', 'lte', 'format'); +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(params, (value, key) => { + + const args = _.map(paramsToMap, (value: number | string, key: string) => { return nodeTypes.namedArg.buildNode(key, value); }); @@ -40,16 +42,23 @@ export function buildNodeParams(fieldName, params) { }; } -export function toElasticsearchQuery(node, indexPattern = null, config = {}, context = {}) { +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: IIndexPattern, + config: Record = {}, + context: Record = {} +) { const [fieldNameArg, ...args] = node.arguments; const fullFieldNameArg = getFullFieldNameNode( fieldNameArg, indexPattern, - context.nested ? context.nested.path : undefined + context?.nested ? context.nested.path : undefined ); const fields = indexPattern ? getFields(fullFieldNameArg, indexPattern) : []; const namedArgs = extractArguments(args); - const queryParams = _.mapValues(namedArgs, ast.toElasticsearchQuery); + 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 @@ -58,25 +67,26 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con // keep things familiar for now. if (fields && fields.length === 0) { fields.push({ - name: ast.toElasticsearchQuery(fullFieldNameArg), + name: (ast.toElasticsearchQuery(fullFieldNameArg) as unknown) as string, scripted: false, + type: '', }); } - const queries = fields.map(field => { - const wrapWithNestedQuery = query => { + const queries = fields!.map((field: IFieldType) => { + const wrapWithNestedQuery = (query: any) => { // Wildcards can easily include nested and non-nested fields. There isn't a good way to let // users handle this themselves so we automatically add nested queries in this scenario. if ( - !fullFieldNameArg.type === 'wildcard' || + !(fullFieldNameArg.type === 'wildcard') || !_.get(field, 'subType.nested') || - context.nested + context!.nested ) { return query; } else { return { nested: { - path: field.subType.nested.path, + path: field.subType!.nested!.path, query, score_mode: 'none', }, @@ -90,7 +100,7 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con }; } else if (field.type === 'date') { const timeZoneParam = config.dateFormatTZ - ? { time_zone: getTimeZoneFromSettings(config.dateFormatTZ) } + ? { time_zone: getTimeZoneFromSettings(config!.dateFormatTZ) } : {}; return wrapWithNestedQuery({ range: { @@ -116,14 +126,14 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con }; } -function extractArguments(args) { +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, arg, index) => { + return args.reduce((acc: any, arg: any, index: number) => { if (arg.type === 'namedArg') { acc[arg.name] = arg.value; } else { diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.test.ts b/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.test.ts index d48f0943082c9..b40d170ff024f 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.test.ts +++ b/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.test.ts @@ -48,7 +48,7 @@ describe('getFields', () => { expect(results).toHaveLength(1); expect(Array.isArray(results)).toBeTruthy(); - expect(results[0].name).toBe('extension'); + expect(results![0].name).toBe('extension'); }); test('should not match a wildcard in a literal node', () => { @@ -59,14 +59,14 @@ describe('getFields', () => { name: 'foo*', }, ], - }; + } as IIndexPattern; const fieldNameNode = nodeTypes.literal.buildNode('foo*'); const results = getFields(fieldNameNode, indexPatternWithWildField); expect(results).toHaveLength(1); expect(Array.isArray(results)).toBeTruthy(); - expect(results[0].name).toBe('foo*'); + expect(results![0].name).toBe('foo*'); const actual = getFields(nodeTypes.literal.buildNode('fo*'), indexPatternWithWildField); expect(actual).toEqual([]); @@ -87,8 +87,8 @@ describe('getFields', () => { expect(Array.isArray(results)).toBeTruthy(); expect(results).toHaveLength(2); - expect(results.find((field: IFieldType) => field.name === 'machine.os')).toBeDefined(); - expect(results.find((field: IFieldType) => field.name === 'machine.os.raw')).toBeDefined(); + expect(results!.find((field: IFieldType) => field.name === 'machine.os')).toBeDefined(); + expect(results!.find((field: IFieldType) => field.name === 'machine.os.raw')).toBeDefined(); }); }); }); diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.js b/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts similarity index 69% rename from src/plugins/data/common/es_query/kuery/functions/utils/get_fields.js rename to src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts index 5a0f6deef1b3a..0e314ec778af8 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.js +++ b/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts @@ -19,17 +19,20 @@ import * as literal from '../../node_types/literal'; import * as wildcard from '../../node_types/wildcard'; +import { KueryNode, IIndexPattern } from '../../../..'; +import { LiteralTypeBuildNode } from '../../node_types/types'; -export function getFields(node, indexPattern) { +export function getFields(node: KueryNode, indexPattern?: IIndexPattern) { + if (!indexPattern) return []; if (node.type === 'literal') { - const fieldName = literal.toElasticsearchQuery(node); - const field = indexPattern.fields.find(field => field.name === fieldName); + const fieldName = literal.toElasticsearchQuery(node as LiteralTypeBuildNode); + const field = indexPattern.fields.find(fld => fld.name === fieldName); if (!field) { return []; } return [field]; } else if (node.type === 'wildcard') { - const fields = indexPattern.fields.filter(field => wildcard.test(node, field.name)); + const fields = indexPattern.fields.filter(fld => wildcard.test(node, fld.name)); return fields; } } diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.test.ts b/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.test.ts index e138e22b76ad3..200c5b51e4d27 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.test.ts +++ b/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.test.ts @@ -79,9 +79,9 @@ describe('getFullFieldNameNode', function() { test('should skip error checking if no index pattern is passed in', () => { const nameNode = nodeTypes.literal.buildNode('os'); - expect(() => getFullFieldNameNode(nameNode, null, 'machine')).not.toThrowError(); + expect(() => getFullFieldNameNode(nameNode, undefined, 'machine')).not.toThrowError(); - const result = getFullFieldNameNode(nameNode, null, 'machine'); + const result = getFullFieldNameNode(nameNode, undefined, 'machine'); expect(result).toEqual(nodeTypes.literal.buildNode('machine.os')); }); }); diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.js b/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts similarity index 82% rename from src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.js rename to src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts index d01d0935a9dc6..5a12c7b273a04 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.js +++ b/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts @@ -18,8 +18,13 @@ */ import { getFields } from './get_fields'; +import { IIndexPattern, IFieldType, KueryNode } from '../../../..'; -export function getFullFieldNameNode(rootNameNode, indexPattern, nestedPath) { +export function getFullFieldNameNode( + rootNameNode: any, + indexPattern?: IIndexPattern, + nestedPath?: string +): KueryNode { const fullFieldNameNode = { ...rootNameNode, value: nestedPath ? `${nestedPath}.${rootNameNode.value}` : rootNameNode.value, @@ -33,7 +38,7 @@ export function getFullFieldNameNode(rootNameNode, indexPattern, nestedPath) { } const fields = getFields(fullFieldNameNode, indexPattern); - const errors = fields.reduce((acc, field) => { + const errors = fields!.reduce((acc: any, field: IFieldType) => { const nestedPathFromField = field.subType && field.subType.nested ? field.subType.nested.path : undefined; @@ -54,7 +59,11 @@ export function getFullFieldNameNode(rootNameNode, indexPattern, nestedPath) { if (nestedPathFromField !== nestedPath) { return [ ...acc, - `Nested field ${field.name} is being queried with the incorrect nested path. The correct path is ${field.subType.nested.path}.`, + `Nested field ${ + field.name + } is being queried with the incorrect nested path. The correct path is ${ + field.subType!.nested!.path + }.`, ]; } diff --git a/src/plugins/data/common/es_query/kuery/node_types/function.ts b/src/plugins/data/common/es_query/kuery/node_types/function.ts index 3137177fbfcc0..1fb3b2cec0a1c 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/function.ts +++ b/src/plugins/data/common/es_query/kuery/node_types/function.ts @@ -20,9 +20,8 @@ import _ from 'lodash'; // @ts-ignore import { functions } from '../functions'; -import { IIndexPattern } from '../../..'; +import { IIndexPattern, KueryNode } from '../../..'; import { FunctionName, FunctionTypeBuildNode } from './types'; -import { JsonValue } from '../../../../../kibana_utils/public'; export function buildNode(functionName: FunctionName, ...args: any[]) { const kueryFunction = functions[functionName]; @@ -33,6 +32,8 @@ export function buildNode(functionName: FunctionName, ...args: any[]) { return { type: 'function', function: functionName, + // This requires better typing of the different typings and their return types. + // @ts-ignore ...kueryFunction.buildNodeParams(...args), }; } @@ -54,11 +55,11 @@ export function buildNodeWithArgumentNodes( } export function toElasticsearchQuery( - node: any, + node: KueryNode, indexPattern?: IIndexPattern, config?: Record, context?: Record -): JsonValue { - const kueryFunction = functions[node.function]; +) { + const kueryFunction = functions[node.function as FunctionName]; return kueryFunction.toElasticsearchQuery(node, indexPattern, config, context); } diff --git a/src/plugins/data/common/es_query/kuery/node_types/index.ts b/src/plugins/data/common/es_query/kuery/node_types/index.ts index 6023952b634f2..22e73e791df9a 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/index.ts +++ b/src/plugins/data/common/es_query/kuery/node_types/index.ts @@ -26,6 +26,8 @@ import { NodeTypes } from './types'; export { NodeTypes }; export const nodeTypes: NodeTypes = { + // This requires better typing of the different typings and their return types. + // @ts-ignore function: functionType, literal, namedArg,