Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] Typescript es-query functions (#57473) #58695

Merged
merged 2 commits into from
Feb 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any> = {},
context: Record<string, any> = {}
) {
const children = node.arguments || [];

return {
bool: {
filter: children.map(child => {
filter: children.map((child: KueryNode) => {
return ast.toElasticsearchQuery(child, indexPattern, config, context);
}),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any> = {},
context: Record<string, any> = {}
) {
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand All @@ -34,23 +35,30 @@ export function buildNodeParams(fieldName, params) {
};
}

export function toElasticsearchQuery(node, indexPattern, config, context = {}) {
export function toElasticsearchQuery(
node: KueryNode,
indexPattern?: IIndexPattern,
config: Record<string, any> = {},
context: Record<string, any> = {}
) {
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,
[snakeArgName]: ast.toElasticsearchQuery(arg),
};
}, {});

if (field && field.scripted) {
if (field?.scripted) {
throw new Error(`Geo bounding box query does not support scripted fields`);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
Expand All @@ -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<string, any> = {},
context: Record<string, any> = {}
) {
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`);
}

Expand Down
9 changes: 2 additions & 7 deletions src/plugins/data/common/es_query/kuery/functions/is.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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 "*"', () => {
Expand Down Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
Expand All @@ -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<string, any> = {},
context: Record<string, any> = {}
) {
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;
Expand Down Expand Up @@ -85,35 +91,36 @@ 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;

if (isMatchAllQuery) {
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',
},
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -187,7 +194,7 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con

return {
bool: {
should: queries,
should: queries || [],
minimum_should_match: 1,
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,25 @@

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 {
arguments: [pathNode, child],
};
}

export function toElasticsearchQuery(node, indexPattern, config, context = {}) {
export function toElasticsearchQuery(
node: KueryNode,
indexPattern?: IIndexPattern,
config: Record<string, any> = {},
context: Record<string, any> = {}
) {
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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any> = {},
context: Record<string, any> = {}
) {
const [argument] = node.arguments;

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any> = {},
context: Record<string, any> = {}
) {
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,
Expand Down
Loading