From 2ccc397fe778058530f1af56b793e820a7efedba Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Tue, 5 May 2020 21:59:58 +0300 Subject: [PATCH] [data.search.aggs]: Expression functions for metric agg types (#64914) --- .../data/public/search/aggs/agg_types.ts | 44 ++++++ .../data/public/search/aggs/metrics/avg.ts | 5 + .../public/search/aggs/metrics/avg_fn.test.ts | 64 +++++++++ .../data/public/search/aggs/metrics/avg_fn.ts | 95 +++++++++++++ .../public/search/aggs/metrics/bucket_avg.ts | 6 + .../search/aggs/metrics/bucket_avg_fn.test.ts | 78 +++++++++++ .../search/aggs/metrics/bucket_avg_fn.ts | 107 ++++++++++++++ .../public/search/aggs/metrics/bucket_max.ts | 6 + .../search/aggs/metrics/bucket_max_fn.test.ts | 78 +++++++++++ .../search/aggs/metrics/bucket_max_fn.ts | 107 ++++++++++++++ .../public/search/aggs/metrics/bucket_min.ts | 6 + .../search/aggs/metrics/bucket_min_fn.test.ts | 78 +++++++++++ .../search/aggs/metrics/bucket_min_fn.ts | 107 ++++++++++++++ .../public/search/aggs/metrics/bucket_sum.ts | 6 + .../search/aggs/metrics/bucket_sum_fn.test.ts | 78 +++++++++++ .../search/aggs/metrics/bucket_sum_fn.ts | 107 ++++++++++++++ .../public/search/aggs/metrics/cardinality.ts | 5 + .../aggs/metrics/cardinality_fn.test.ts | 64 +++++++++ .../search/aggs/metrics/cardinality_fn.ts | 95 +++++++++++++ .../search/aggs/metrics/count_fn.test.ts | 59 ++++++++ .../public/search/aggs/metrics/count_fn.ts | 88 ++++++++++++ .../search/aggs/metrics/cumulative_sum.ts | 7 + .../aggs/metrics/cumulative_sum_fn.test.ts | 120 ++++++++++++++++ .../search/aggs/metrics/cumulative_sum_fn.ts | 111 +++++++++++++++ .../public/search/aggs/metrics/derivative.ts | 7 + .../search/aggs/metrics/derivative_fn.test.ts | 120 ++++++++++++++++ .../search/aggs/metrics/derivative_fn.ts | 111 +++++++++++++++ .../public/search/aggs/metrics/geo_bounds.ts | 5 + .../search/aggs/metrics/geo_bounds_fn.test.ts | 64 +++++++++ .../search/aggs/metrics/geo_bounds_fn.ts | 95 +++++++++++++ .../search/aggs/metrics/geo_centroid.ts | 5 + .../aggs/metrics/geo_centroid_fn.test.ts | 64 +++++++++ .../search/aggs/metrics/geo_centroid_fn.ts | 95 +++++++++++++ .../data/public/search/aggs/metrics/index.ts | 20 +++ .../data/public/search/aggs/metrics/max.ts | 5 + .../public/search/aggs/metrics/max_fn.test.ts | 64 +++++++++ .../data/public/search/aggs/metrics/max_fn.ts | 95 +++++++++++++ .../data/public/search/aggs/metrics/median.ts | 5 + .../search/aggs/metrics/median_fn.test.ts | 64 +++++++++ .../public/search/aggs/metrics/median_fn.ts | 95 +++++++++++++ .../data/public/search/aggs/metrics/min.ts | 5 + .../public/search/aggs/metrics/min_fn.test.ts | 64 +++++++++ .../data/public/search/aggs/metrics/min_fn.ts | 95 +++++++++++++ .../public/search/aggs/metrics/moving_avg.ts | 9 ++ .../search/aggs/metrics/moving_avg_fn.test.ts | 130 ++++++++++++++++++ .../search/aggs/metrics/moving_avg_fn.ts | 124 +++++++++++++++++ .../search/aggs/metrics/percentile_ranks.ts | 6 + .../aggs/metrics/percentile_ranks_fn.test.ts | 93 +++++++++++++ .../aggs/metrics/percentile_ranks_fn.ts | 102 ++++++++++++++ .../public/search/aggs/metrics/percentiles.ts | 6 + .../aggs/metrics/percentiles_fn.test.ts | 93 +++++++++++++ .../search/aggs/metrics/percentiles_fn.ts | 102 ++++++++++++++ .../public/search/aggs/metrics/serial_diff.ts | 7 + .../aggs/metrics/serial_diff_fn.test.ts | 120 ++++++++++++++++ .../search/aggs/metrics/serial_diff_fn.ts | 111 +++++++++++++++ .../search/aggs/metrics/std_deviation.ts | 5 + .../aggs/metrics/std_deviation_fn.test.ts | 64 +++++++++ .../search/aggs/metrics/std_deviation_fn.ts | 95 +++++++++++++ .../data/public/search/aggs/metrics/sum.ts | 5 + .../public/search/aggs/metrics/sum_fn.test.ts | 64 +++++++++ .../data/public/search/aggs/metrics/sum_fn.ts | 95 +++++++++++++ .../public/search/aggs/metrics/top_hit.ts | 9 ++ .../search/aggs/metrics/top_hit_fn.test.ts | 102 ++++++++++++++ .../public/search/aggs/metrics/top_hit_fn.ts | 122 ++++++++++++++++ src/plugins/data/public/search/aggs/types.ts | 42 ++++++ 65 files changed, 4105 insertions(+) create mode 100644 src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/avg_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/count_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/count_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/derivative_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/max_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/max_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/median_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/median_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/min_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/min_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/sum_fn.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts diff --git a/src/plugins/data/public/search/aggs/agg_types.ts b/src/plugins/data/public/search/aggs/agg_types.ts index 7c7d7609cc82f..2af29d3600246 100644 --- a/src/plugins/data/public/search/aggs/agg_types.ts +++ b/src/plugins/data/public/search/aggs/agg_types.ts @@ -118,7 +118,51 @@ import { aggHistogram } from './buckets/histogram_fn'; import { aggDateHistogram } from './buckets/date_histogram_fn'; import { aggTerms } from './buckets/terms_fn'; +/** Metrics: **/ +import { aggAvg } from './metrics/avg_fn'; +import { aggBucketAvg } from './metrics/bucket_avg_fn'; +import { aggBucketMax } from './metrics/bucket_max_fn'; +import { aggBucketMin } from './metrics/bucket_min_fn'; +import { aggBucketSum } from './metrics/bucket_sum_fn'; +import { aggCardinality } from './metrics/cardinality_fn'; +import { aggCount } from './metrics/count_fn'; +import { aggCumulativeSum } from './metrics/cumulative_sum_fn'; +import { aggDerivative } from './metrics/derivative_fn'; +import { aggGeoBounds } from './metrics/geo_bounds_fn'; +import { aggGeoCentroid } from './metrics/geo_centroid_fn'; +import { aggMax } from './metrics/max_fn'; +import { aggMedian } from './metrics/median_fn'; +import { aggMin } from './metrics/min_fn'; +import { aggMovingAvg } from './metrics/moving_avg_fn'; +import { aggPercentileRanks } from './metrics/percentile_ranks_fn'; +import { aggPercentiles } from './metrics/percentiles_fn'; +import { aggSerialDiff } from './metrics/serial_diff_fn'; +import { aggStdDeviation } from './metrics/std_deviation_fn'; +import { aggSum } from './metrics/sum_fn'; +import { aggTopHit } from './metrics/top_hit_fn'; + export const getAggTypesFunctions = () => [ + aggAvg, + aggBucketAvg, + aggBucketMax, + aggBucketMin, + aggBucketSum, + aggCardinality, + aggCount, + aggCumulativeSum, + aggDerivative, + aggGeoBounds, + aggGeoCentroid, + aggMax, + aggMedian, + aggMin, + aggMovingAvg, + aggPercentileRanks, + aggPercentiles, + aggSerialDiff, + aggStdDeviation, + aggSum, + aggTopHit, aggFilter, aggFilters, aggSignificantTerms, diff --git a/src/plugins/data/public/search/aggs/metrics/avg.ts b/src/plugins/data/public/search/aggs/metrics/avg.ts index d53ce8d3fc489..96be3e849a3e8 100644 --- a/src/plugins/data/public/search/aggs/metrics/avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/avg.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const averageTitle = i18n.translate('data.search.aggs.metrics.averageTitle', { defaultMessage: 'Average', }); +export interface AggParamsAvg extends BaseAggParams { + field: string; +} + export interface AvgMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts new file mode 100644 index 0000000000000..0e2ee00df49dd --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggAvg } from './avg_fn'; + +describe('agg_expression_functions', () => { + describe('aggAvg', () => { + const fn = functionWrapper(aggAvg()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "avg", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/avg_fn.ts b/src/plugins/data/public/search/aggs/metrics/avg_fn.ts new file mode 100644 index 0000000000000..c370623b2752a --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/avg_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggAvg'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggAvg = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.avg.help', { + defaultMessage: 'Generates a serialized agg config for a Avg agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.avg.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.avg.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.avg.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.avg.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.avg.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.avg.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.AVG, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts b/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts index 2c32ebc671539..ded17eebf465b 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts @@ -23,8 +23,14 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsBucketAvg extends BaseAggParams { + customMetric?: AggConfigSerialized; + customBucket?: AggConfigSerialized; +} + export interface BucketAvgMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts new file mode 100644 index 0000000000000..7e08bc9954510 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggBucketAvg } from './bucket_avg_fn'; + +describe('agg_expression_functions', () => { + describe('aggBucketAvg', () => { + const fn = functionWrapper(aggBucketAvg()); + + test('handles customMetric and customBucket as a subexpression', () => { + const actual = fn({ + customMetric: fn({}), + customBucket: fn({}), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "customBucket": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "avg_bucket", + }, + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "avg_bucket", + }, + "json": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts b/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts new file mode 100644 index 0000000000000..56643a2df54bd --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts @@ -0,0 +1,107 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggBucketAvg'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign< + AggArgs, + { customBucket?: AggExpressionType; customMetric?: AggExpressionType } +>; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggBucketAvg = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.bucket_avg.help', { + defaultMessage: 'Generates a serialized agg config for a Avg Bucket agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.bucket_avg.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + customBucket: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.customBucket.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.customMetric.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_avg.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.AVG_BUCKET, + params: { + ...rest, + customBucket: args.customBucket?.value, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max.ts b/src/plugins/data/public/search/aggs/metrics/bucket_max.ts index 1e57a2dd8e38e..dde328008b88a 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_max.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_max.ts @@ -22,8 +22,14 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsBucketMax extends BaseAggParams { + customMetric?: AggConfigSerialized; + customBucket?: AggConfigSerialized; +} + export interface BucketMaxMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts new file mode 100644 index 0000000000000..b789bdf51ebd5 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggBucketMax } from './bucket_max_fn'; + +describe('agg_expression_functions', () => { + describe('aggBucketMax', () => { + const fn = functionWrapper(aggBucketMax()); + + test('handles customMetric and customBucket as a subexpression', () => { + const actual = fn({ + customMetric: fn({}), + customBucket: fn({}), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "customBucket": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "max_bucket", + }, + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "max_bucket", + }, + "json": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts b/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts new file mode 100644 index 0000000000000..896e9cf839605 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts @@ -0,0 +1,107 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggBucketMax'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign< + AggArgs, + { customBucket?: AggExpressionType; customMetric?: AggExpressionType } +>; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggBucketMax = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.bucket_max.help', { + defaultMessage: 'Generates a serialized agg config for a Max Bucket agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.bucket_max.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + customBucket: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.customBucket.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.customMetric.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_max.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MAX_BUCKET, + params: { + ...rest, + customBucket: args.customBucket?.value, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min.ts b/src/plugins/data/public/search/aggs/metrics/bucket_min.ts index 0484af23a7141..9949524ce6110 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_min.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_min.ts @@ -22,8 +22,14 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsBucketMin extends BaseAggParams { + customMetric?: AggConfigSerialized; + customBucket?: AggConfigSerialized; +} + export interface BucketMinMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts new file mode 100644 index 0000000000000..6ebc83417813b --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggBucketMin } from './bucket_min_fn'; + +describe('agg_expression_functions', () => { + describe('aggBucketMin', () => { + const fn = functionWrapper(aggBucketMin()); + + test('handles customMetric and customBucket as a subexpression', () => { + const actual = fn({ + customMetric: fn({}), + customBucket: fn({}), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "customBucket": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "min_bucket", + }, + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "min_bucket", + }, + "json": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts b/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts new file mode 100644 index 0000000000000..2ae3d9211227a --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts @@ -0,0 +1,107 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggBucketMin'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign< + AggArgs, + { customBucket?: AggExpressionType; customMetric?: AggExpressionType } +>; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggBucketMin = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.bucket_min.help', { + defaultMessage: 'Generates a serialized agg config for a Min Bucket agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.bucket_min.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + customBucket: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.customBucket.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.customMetric.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_min.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MIN_BUCKET, + params: { + ...rest, + customBucket: args.customBucket?.value, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts b/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts index 0a4d29a18a980..e69ae5798c6e1 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts @@ -22,8 +22,14 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsBucketSum extends BaseAggParams { + customMetric?: AggConfigSerialized; + customBucket?: AggConfigSerialized; +} + export interface BucketSumMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts new file mode 100644 index 0000000000000..71549f41b1d15 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggBucketSum } from './bucket_sum_fn'; + +describe('agg_expression_functions', () => { + describe('aggBucketSum', () => { + const fn = functionWrapper(aggBucketSum()); + + test('handles customMetric and customBucket as a subexpression', () => { + const actual = fn({ + customMetric: fn({}), + customBucket: fn({}), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "customBucket": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "sum_bucket", + }, + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customBucket": undefined, + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "sum_bucket", + }, + "json": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts b/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts new file mode 100644 index 0000000000000..eceb11a90f293 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts @@ -0,0 +1,107 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggBucketSum'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign< + AggArgs, + { customBucket?: AggExpressionType; customMetric?: AggExpressionType } +>; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggBucketSum = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.bucket_sum.help', { + defaultMessage: 'Generates a serialized agg config for a Sum Bucket agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.bucket_sum.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + customBucket: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.customBucket.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.customMetric.help', { + defaultMessage: 'Agg config to use for building sibling pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.bucket_sum.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.SUM_BUCKET, + params: { + ...rest, + customBucket: args.customBucket?.value, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality.ts b/src/plugins/data/public/search/aggs/metrics/cardinality.ts index 10b6b5aff1abd..af594195fe027 100644 --- a/src/plugins/data/public/search/aggs/metrics/cardinality.ts +++ b/src/plugins/data/public/search/aggs/metrics/cardinality.ts @@ -22,11 +22,16 @@ import { MetricAggType, IMetricAggConfig } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const uniqueCountTitle = i18n.translate('data.search.aggs.metrics.uniqueCountTitle', { defaultMessage: 'Unique Count', }); +export interface AggParamsCardinality extends BaseAggParams { + field: string; +} + export interface CardinalityMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts new file mode 100644 index 0000000000000..4008819018ee5 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggCardinality } from './cardinality_fn'; + +describe('agg_expression_functions', () => { + describe('aggCardinality', () => { + const fn = functionWrapper(aggCardinality()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "cardinality", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts b/src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts new file mode 100644 index 0000000000000..f30429993638f --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggCardinality'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggCardinality = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.cardinality.help', { + defaultMessage: 'Generates a serialized agg config for a Cardinality agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cardinality.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.cardinality.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cardinality.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.cardinality.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cardinality.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cardinality.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.CARDINALITY, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/count_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/count_fn.test.ts new file mode 100644 index 0000000000000..846feb9296fca --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/count_fn.test.ts @@ -0,0 +1,59 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggCount } from './count_fn'; + +describe('agg_expression_functions', () => { + describe('aggCount', () => { + const fn = functionWrapper(aggCount()); + + test('correctly creates agg type', () => { + const actual = fn({}); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "json": undefined, + }, + "schema": undefined, + "type": "count", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/count_fn.ts b/src/plugins/data/public/search/aggs/metrics/count_fn.ts new file mode 100644 index 0000000000000..f4c7e8e854230 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/count_fn.ts @@ -0,0 +1,88 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggCount'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggCount = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.count.help', { + defaultMessage: 'Generates a serialized agg config for a Count agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.count.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.count.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.count.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.count.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.count.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.COUNT, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts b/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts index 8ca922e144a1f..67e907239799a 100644 --- a/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts @@ -22,8 +22,15 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsCumulativeSum extends BaseAggParams { + buckets_path: string; + customMetric?: AggConfigSerialized; + metricAgg?: string; +} + export interface CumulativeSumMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts new file mode 100644 index 0000000000000..3cf53e3da153e --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts @@ -0,0 +1,120 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggCumulativeSum } from './cumulative_sum_fn'; + +describe('agg_expression_functions', () => { + describe('aggCumulativeSum', () => { + const fn = functionWrapper(aggCumulativeSum()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "cumulative_sum", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + metricAgg: 'sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": "sum", + }, + "schema": undefined, + "type": "cumulative_sum", + }, + } + `); + }); + + test('handles customMetric as a subexpression', () => { + const actual = fn({ + customMetric: fn({ buckets_path: 'the_sum' }), + buckets_path: 'the_sum', + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "cumulative_sum", + }, + "json": undefined, + "metricAgg": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + buckets_path: 'the_sum', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + buckets_path: 'the_sum', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts b/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts new file mode 100644 index 0000000000000..950df03b10134 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts @@ -0,0 +1,111 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggCumulativeSum'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggCumulativeSum = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.cumulative_sum.help', { + defaultMessage: 'Generates a serialized agg config for a Cumulative Sum agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + metricAgg: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.metricAgg.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.customMetric.help', { + defaultMessage: 'Agg config to use for building parent pipeline aggregations', + }), + }, + buckets_path: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.buckets_path.help', { + defaultMessage: 'Path to the metric of interest', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.cumulative_sum.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.CUMULATIVE_SUM, + params: { + ...rest, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/derivative.ts b/src/plugins/data/public/search/aggs/metrics/derivative.ts index 5752a72c846aa..edb907ca4ed41 100644 --- a/src/plugins/data/public/search/aggs/metrics/derivative.ts +++ b/src/plugins/data/public/search/aggs/metrics/derivative.ts @@ -22,8 +22,15 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsDerivative extends BaseAggParams { + buckets_path: string; + customMetric?: AggConfigSerialized; + metricAgg?: string; +} + export interface DerivativeMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts new file mode 100644 index 0000000000000..79ea7292104ee --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts @@ -0,0 +1,120 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggDerivative } from './derivative_fn'; + +describe('agg_expression_functions', () => { + describe('aggDerivative', () => { + const fn = functionWrapper(aggDerivative()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "derivative", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + metricAgg: 'sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": "sum", + }, + "schema": undefined, + "type": "derivative", + }, + } + `); + }); + + test('handles customMetric as a subexpression', () => { + const actual = fn({ + customMetric: fn({ buckets_path: 'the_sum' }), + buckets_path: 'the_sum', + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "derivative", + }, + "json": undefined, + "metricAgg": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + buckets_path: 'the_sum', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + buckets_path: 'the_sum', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/derivative_fn.ts b/src/plugins/data/public/search/aggs/metrics/derivative_fn.ts new file mode 100644 index 0000000000000..90b88b4de2712 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/derivative_fn.ts @@ -0,0 +1,111 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggDerivative'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggDerivative = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.derivative.help', { + defaultMessage: 'Generates a serialized agg config for a Derivative agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.derivative.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + metricAgg: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.metricAgg.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.derivative.customMetric.help', { + defaultMessage: 'Agg config to use for building parent pipeline aggregations', + }), + }, + buckets_path: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.derivative.buckets_path.help', { + defaultMessage: 'Path to the metric of interest', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.derivative.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.DERIVATIVE, + params: { + ...rest, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts b/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts index 00927ebba56bf..864e97ca8dfe7 100644 --- a/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts +++ b/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsGeoBounds extends BaseAggParams { + field: string; +} export interface GeoBoundsMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts new file mode 100644 index 0000000000000..96bd31916784a --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggGeoBounds } from './geo_bounds_fn'; + +describe('agg_expression_functions', () => { + describe('aggGeoBounds', () => { + const fn = functionWrapper(aggGeoBounds()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "geo_bounds", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts b/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts new file mode 100644 index 0000000000000..8ba71a098fc70 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggGeoBounds'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggGeoBounds = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.geo_bounds.help', { + defaultMessage: 'Generates a serialized agg config for a Geo Bounds agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_bounds.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.geo_bounds.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_bounds.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.geo_bounds.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_bounds.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_bounds.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.GEO_BOUNDS, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts b/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts index a4b084f794a5d..2bbb6b2de8d87 100644 --- a/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts +++ b/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsGeoCentroid extends BaseAggParams { + field: string; +} export interface GeoCentroidMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts new file mode 100644 index 0000000000000..bf9a4548bafbf --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggGeoCentroid } from './geo_centroid_fn'; + +describe('agg_expression_functions', () => { + describe('aggGeoCentroid', () => { + const fn = functionWrapper(aggGeoCentroid()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "geo_centroid", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts b/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts new file mode 100644 index 0000000000000..464f9b535cd8b --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggGeoCentroid'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggGeoCentroid = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.geo_centroid.help', { + defaultMessage: 'Generates a serialized agg config for a Geo Centroid agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_centroid.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.geo_centroid.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_centroid.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.geo_centroid.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_centroid.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.geo_centroid.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.GEO_CENTROID, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/index.ts b/src/plugins/data/public/search/aggs/metrics/index.ts index eb93e99427f65..ef7de68b05de9 100644 --- a/src/plugins/data/public/search/aggs/metrics/index.ts +++ b/src/plugins/data/public/search/aggs/metrics/index.ts @@ -21,3 +21,23 @@ export * from './metric_agg_type'; export * from './metric_agg_types'; export * from './lib/parent_pipeline_agg_helper'; export * from './lib/sibling_pipeline_agg_helper'; +export { AggParamsAvg } from './avg'; +export { AggParamsCardinality } from './cardinality'; +export { AggParamsGeoBounds } from './geo_bounds'; +export { AggParamsGeoCentroid } from './geo_centroid'; +export { AggParamsMax } from './max'; +export { AggParamsMedian } from './median'; +export { AggParamsMin } from './min'; +export { AggParamsStdDeviation } from './std_deviation'; +export { AggParamsSum } from './sum'; +export { AggParamsBucketAvg } from './bucket_avg'; +export { AggParamsBucketMax } from './bucket_max'; +export { AggParamsBucketMin } from './bucket_min'; +export { AggParamsBucketSum } from './bucket_sum'; +export { AggParamsCumulativeSum } from './cumulative_sum'; +export { AggParamsDerivative } from './derivative'; +export { AggParamsMovingAvg } from './moving_avg'; +export { AggParamsPercentileRanks } from './percentile_ranks'; +export { AggParamsPercentiles } from './percentiles'; +export { AggParamsSerialDiff } from './serial_diff'; +export { AggParamsTopHit } from './top_hit'; diff --git a/src/plugins/data/public/search/aggs/metrics/max.ts b/src/plugins/data/public/search/aggs/metrics/max.ts index 88e8b485cb73f..49cbfba5a269d 100644 --- a/src/plugins/data/public/search/aggs/metrics/max.ts +++ b/src/plugins/data/public/search/aggs/metrics/max.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const maxTitle = i18n.translate('data.search.aggs.metrics.maxTitle', { defaultMessage: 'Max', }); +export interface AggParamsMax extends BaseAggParams { + field: string; +} + export interface MaxMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/max_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/max_fn.test.ts new file mode 100644 index 0000000000000..156b51ca54af5 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/max_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggMax } from './max_fn'; + +describe('agg_expression_functions', () => { + describe('aggMax', () => { + const fn = functionWrapper(aggMax()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "max", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/max_fn.ts b/src/plugins/data/public/search/aggs/metrics/max_fn.ts new file mode 100644 index 0000000000000..1d68c8919fca8 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/max_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggMax'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggMax = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.max.help', { + defaultMessage: 'Generates a serialized agg config for a Max agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.max.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.max.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.max.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.max.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.max.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.max.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MAX, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/median.ts b/src/plugins/data/public/search/aggs/metrics/median.ts index a398f017602b0..725fdcb2400d1 100644 --- a/src/plugins/data/public/search/aggs/metrics/median.ts +++ b/src/plugins/data/public/search/aggs/metrics/median.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const medianTitle = i18n.translate('data.search.aggs.metrics.medianTitle', { defaultMessage: 'Median', }); +export interface AggParamsMedian extends BaseAggParams { + field: string; +} + export interface MedianMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/median_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/median_fn.test.ts new file mode 100644 index 0000000000000..69200c35426c8 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/median_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggMedian } from './median_fn'; + +describe('agg_expression_functions', () => { + describe('aggMedian', () => { + const fn = functionWrapper(aggMedian()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "median", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/median_fn.ts b/src/plugins/data/public/search/aggs/metrics/median_fn.ts new file mode 100644 index 0000000000000..2e8e89992136e --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/median_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggMedian'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggMedian = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.median.help', { + defaultMessage: 'Generates a serialized agg config for a Median agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.median.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.median.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.median.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.median.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.median.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.median.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MEDIAN, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/min.ts b/src/plugins/data/public/search/aggs/metrics/min.ts index aae16f357186c..0f52aa8a4f788 100644 --- a/src/plugins/data/public/search/aggs/metrics/min.ts +++ b/src/plugins/data/public/search/aggs/metrics/min.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const minTitle = i18n.translate('data.search.aggs.metrics.minTitle', { defaultMessage: 'Min', }); +export interface AggParamsMin extends BaseAggParams { + field: string; +} + export interface MinMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/min_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/min_fn.test.ts new file mode 100644 index 0000000000000..ef32d086e41f7 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/min_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggMin } from './min_fn'; + +describe('agg_expression_functions', () => { + describe('aggMin', () => { + const fn = functionWrapper(aggMin()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "min", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/min_fn.ts b/src/plugins/data/public/search/aggs/metrics/min_fn.ts new file mode 100644 index 0000000000000..b51da46a137b0 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/min_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggMin'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggMin = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.min.help', { + defaultMessage: 'Generates a serialized agg config for a Min agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.min.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.min.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.min.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.min.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.min.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.min.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MIN, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg.ts b/src/plugins/data/public/search/aggs/metrics/moving_avg.ts index 94b9b1d8cd487..38a824629d304 100644 --- a/src/plugins/data/public/search/aggs/metrics/moving_avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/moving_avg.ts @@ -22,8 +22,17 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsMovingAvg extends BaseAggParams { + buckets_path: string; + window?: number; + script?: string; + customMetric?: AggConfigSerialized; + metricAgg?: string; +} + export interface MovingAvgMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts new file mode 100644 index 0000000000000..d6c0e6b2cbd6e --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts @@ -0,0 +1,130 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggMovingAvg } from './moving_avg_fn'; + +describe('agg_expression_functions', () => { + describe('aggMovingAvg', () => { + const fn = functionWrapper(aggMovingAvg()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + "script": undefined, + "window": undefined, + }, + "schema": undefined, + "type": "moving_avg", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + metricAgg: 'sum', + window: 10, + script: 'test', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": "sum", + "script": "test", + "window": 10, + }, + "schema": undefined, + "type": "moving_avg", + }, + } + `); + }); + + test('handles customMetric as a subexpression', () => { + const actual = fn({ + customMetric: fn({ buckets_path: 'the_sum' }), + buckets_path: 'the_sum', + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + "script": undefined, + "window": undefined, + }, + "schema": undefined, + "type": "moving_avg", + }, + "json": undefined, + "metricAgg": undefined, + "script": undefined, + "window": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + buckets_path: 'the_sum', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + buckets_path: 'the_sum', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts b/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts new file mode 100644 index 0000000000000..54a3fa176385b --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts @@ -0,0 +1,124 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggMovingAvg'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggMovingAvg = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.moving_avg.help', { + defaultMessage: 'Generates a serialized agg config for a Moving Average agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.moving_avg.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + metricAgg: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.metricAgg.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.customMetric.help', { + defaultMessage: 'Agg config to use for building parent pipeline aggregations', + }), + }, + window: { + types: ['number'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.window.help', { + defaultMessage: 'The size of window to "slide" across the histogram.', + }), + }, + buckets_path: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.derivative.buckets_path.help', { + defaultMessage: 'Path to the metric of interest', + }), + }, + script: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.script.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.moving_avg.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.MOVING_FN, + params: { + ...rest, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts index 0d79665ff9c4e..c8383f6bcc3d9 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts @@ -24,6 +24,12 @@ import { getPercentileValue } from './percentiles_get_value'; import { METRIC_TYPES } from './metric_agg_types'; import { FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsPercentileRanks extends BaseAggParams { + field: string; + values?: number[]; +} // required by the values editor export type IPercentileRanksAggConfig = IResponseAggConfig; diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts new file mode 100644 index 0000000000000..e3ce91bafd40a --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts @@ -0,0 +1,93 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggPercentileRanks } from './percentile_ranks_fn'; + +describe('agg_expression_functions', () => { + describe('aggPercentileRanks', () => { + const fn = functionWrapper(aggPercentileRanks()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "values": undefined, + }, + "schema": undefined, + "type": "percentile_ranks", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + values: [1, 2, 3], + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "values": Array [ + 1, + 2, + 3, + ], + }, + "schema": undefined, + "type": "percentile_ranks", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts new file mode 100644 index 0000000000000..851e938f28c1c --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts @@ -0,0 +1,102 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggPercentileRanks'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggPercentileRanks = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.percentile_ranks.help', { + defaultMessage: 'Generates a serialized agg config for a Percentile Ranks agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + values: { + types: ['number'], + multi: true, + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.values.help', { + defaultMessage: 'Range of percentiles ranks', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentile_ranks.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.PERCENTILE_RANKS, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.ts b/src/plugins/data/public/search/aggs/metrics/percentiles.ts index 040a52588dd94..ad3c19cfaffcc 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentiles.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentiles.ts @@ -25,6 +25,12 @@ import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_respons import { getPercentileValue } from './percentiles_get_value'; import { ordinalSuffix } from './lib/ordinal_suffix'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsPercentiles extends BaseAggParams { + field: string; + percents?: number[]; +} export type IPercentileAggConfig = IResponseAggConfig; diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts new file mode 100644 index 0000000000000..2074cc1d89527 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts @@ -0,0 +1,93 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggPercentiles } from './percentiles_fn'; + +describe('agg_expression_functions', () => { + describe('aggPercentiles', () => { + const fn = functionWrapper(aggPercentiles()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "percents": undefined, + }, + "schema": undefined, + "type": "percentiles", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + percents: [1, 2, 3], + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "percents": Array [ + 1, + 2, + 3, + ], + }, + "schema": undefined, + "type": "percentiles", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts b/src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts new file mode 100644 index 0000000000000..b799be07925fa --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts @@ -0,0 +1,102 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggPercentiles'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggPercentiles = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.percentiles.help', { + defaultMessage: 'Generates a serialized agg config for a Percentiles agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentiles.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.percentiles.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentiles.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.percentiles.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + percents: { + types: ['number'], + multi: true, + help: i18n.translate('data.search.aggs.metrics.percentiles.percents.help', { + defaultMessage: 'Range of percentiles ranks', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentiles.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.percentiles.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.PERCENTILES, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff.ts b/src/plugins/data/public/search/aggs/metrics/serial_diff.ts index 2b1498560f862..fe112a50ad3c1 100644 --- a/src/plugins/data/public/search/aggs/metrics/serial_diff.ts +++ b/src/plugins/data/public/search/aggs/metrics/serial_diff.ts @@ -22,8 +22,15 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { AggConfigSerialized, BaseAggParams } from '../types'; import { GetInternalStartServicesFn } from '../../../types'; +export interface AggParamsSerialDiff extends BaseAggParams { + buckets_path: string; + customMetric?: AggConfigSerialized; + metricAgg?: string; +} + export interface SerialDiffMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts new file mode 100644 index 0000000000000..1bb859ad4bad8 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts @@ -0,0 +1,120 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggSerialDiff } from './serial_diff_fn'; + +describe('agg_expression_functions', () => { + describe('aggSerialDiff', () => { + const fn = functionWrapper(aggSerialDiff()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "serial_diff", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + buckets_path: 'the_sum', + metricAgg: 'sum', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": "sum", + }, + "schema": undefined, + "type": "serial_diff", + }, + } + `); + }); + + test('handles customMetric as a subexpression', () => { + const actual = fn({ + customMetric: fn({ buckets_path: 'the_sum' }), + buckets_path: 'the_sum', + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": Object { + "enabled": true, + "id": undefined, + "params": Object { + "buckets_path": "the_sum", + "customLabel": undefined, + "customMetric": undefined, + "json": undefined, + "metricAgg": undefined, + }, + "schema": undefined, + "type": "serial_diff", + }, + "json": undefined, + "metricAgg": undefined, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + json: '{ "foo": true }', + buckets_path: 'the_sum', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + json: '/// intentionally malformed json ///', + buckets_path: 'the_sum', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts b/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts new file mode 100644 index 0000000000000..9ba313aff7386 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts @@ -0,0 +1,111 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggSerialDiff'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Arguments = Assign; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggSerialDiff = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.serial_diff.help', { + defaultMessage: 'Generates a serialized agg config for a Serial Differencing agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.serial_diff.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + metricAgg: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.metricAgg.help', { + defaultMessage: + 'Id for finding agg config to use for building parent pipeline aggregations', + }), + }, + customMetric: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.customMetric.help', { + defaultMessage: 'Agg config to use for building parent pipeline aggregations', + }), + }, + buckets_path: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.serial_diff.buckets_path.help', { + defaultMessage: 'Path to the metric of interest', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.serial_diff.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.SERIAL_DIFF, + params: { + ...rest, + customMetric: args.customMetric?.value, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation.ts index e972132542ceb..1733d5476f667 100644 --- a/src/plugins/data/public/search/aggs/metrics/std_deviation.ts +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation.ts @@ -24,6 +24,11 @@ import { METRIC_TYPES } from './metric_agg_types'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsStdDeviation extends BaseAggParams { + field: string; +} interface ValProp { valProp: string[]; diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts new file mode 100644 index 0000000000000..bfa6aa7cc4122 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggStdDeviation } from './std_deviation_fn'; + +describe('agg_expression_functions', () => { + describe('aggStdDeviation', () => { + const fn = functionWrapper(aggStdDeviation()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "std_dev", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts new file mode 100644 index 0000000000000..70623e2e48041 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggStdDeviation'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggStdDeviation = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.std_deviation.help', { + defaultMessage: 'Generates a serialized agg config for a Standard Deviation agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.std_deviation.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.std_deviation.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.std_deviation.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.std_deviation.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.std_deviation.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.std_deviation.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.STD_DEV, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/sum.ts b/src/plugins/data/public/search/aggs/metrics/sum.ts index 545c6d6a4939e..70fc379f2d5f1 100644 --- a/src/plugins/data/public/search/aggs/metrics/sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/sum.ts @@ -22,11 +22,16 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; const sumTitle = i18n.translate('data.search.aggs.metrics.sumTitle', { defaultMessage: 'Sum', }); +export interface AggParamsSum extends BaseAggParams { + field: string; +} + export interface SumMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } diff --git a/src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts new file mode 100644 index 0000000000000..6e57632ba84cc --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggSum } from './sum_fn'; + +describe('agg_expression_functions', () => { + describe('aggSum', () => { + const fn = functionWrapper(aggSum()); + + test('required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + }, + "schema": undefined, + "type": "sum", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/sum_fn.ts b/src/plugins/data/public/search/aggs/metrics/sum_fn.ts new file mode 100644 index 0000000000000..a277aef02693f --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/sum_fn.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggSum'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggSum = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.sum.help', { + defaultMessage: 'Generates a serialized agg config for a Sum agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.sum.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.sum.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.sum.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.sum.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.sum.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.sum.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.SUM, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.ts b/src/plugins/data/public/search/aggs/metrics/top_hit.ts index 15da2b485aee7..df7a76f151c07 100644 --- a/src/plugins/data/public/search/aggs/metrics/top_hit.ts +++ b/src/plugins/data/public/search/aggs/metrics/top_hit.ts @@ -23,6 +23,15 @@ import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { GetInternalStartServicesFn } from '../../../types'; +import { BaseAggParams } from '../types'; + +export interface AggParamsTopHit extends BaseAggParams { + field: string; + aggregate: 'min' | 'max' | 'sum' | 'average' | 'concat'; + sortField?: string; + size?: number; + sortOrder?: 'desc' | 'asc'; +} export interface TopHitMetricAggDependencies { getInternalStartServices: GetInternalStartServicesFn; diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts b/src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts new file mode 100644 index 0000000000000..d0e9788f85025 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts @@ -0,0 +1,102 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggTopHit } from './top_hit_fn'; + +describe('agg_expression_functions', () => { + describe('aggTopHit', () => { + const fn = functionWrapper(aggTopHit()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + aggregate: 'min', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "aggregate": "min", + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "size": undefined, + "sortField": undefined, + "sortOrder": undefined, + }, + "schema": undefined, + "type": "top_hits", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + id: '1', + enabled: false, + schema: 'whatever', + field: 'machine.os.keyword', + sortOrder: 'asc', + size: 6, + aggregate: 'min', + sortField: '_score', + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": false, + "id": "1", + "params": Object { + "aggregate": "min", + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "size": 6, + "sortField": "_score", + "sortOrder": "asc", + }, + "schema": "whatever", + "type": "top_hits", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + aggregate: 'min', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + aggregate: 'min', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts b/src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts new file mode 100644 index 0000000000000..adfd22b540e06 --- /dev/null +++ b/src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts @@ -0,0 +1,122 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; + +const fnName = 'aggTopHit'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggTopHit = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.metrics.top_hit.help', { + defaultMessage: 'Generates a serialized agg config for a Top Hit agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.top_hit.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.top_hit.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + aggregate: { + types: ['string'], + required: true, + options: ['min', 'max', 'sum', 'average', 'concat'], + help: i18n.translate('data.search.aggs.metrics.top_hit.aggregate.help', { + defaultMessage: 'Aggregate type', + }), + }, + size: { + types: ['number'], + help: i18n.translate('data.search.aggs.metrics.top_hit.size.help', { + defaultMessage: 'Max number of buckets to retrieve', + }), + }, + sortOrder: { + types: ['string'], + options: ['desc', 'asc'], + help: i18n.translate('data.search.aggs.metrics.top_hit.sortOrder.help', { + defaultMessage: 'Order in which to return the results: asc or desc', + }), + }, + sortField: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.sortField.help', { + defaultMessage: 'Field to order results by', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.top_hit.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.TOP_HITS, + params: { + ...rest, + json: getParsedValue(args, 'json'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/types.ts b/src/plugins/data/public/search/aggs/types.ts index 8ad264f59cc27..a784bfaada4c7 100644 --- a/src/plugins/data/public/search/aggs/types.ts +++ b/src/plugins/data/public/search/aggs/types.ts @@ -30,12 +30,33 @@ import { AggParamsGeoTile, AggParamsGeoHash, AggParamsTerms, + AggParamsAvg, + AggParamsCardinality, + AggParamsGeoBounds, + AggParamsGeoCentroid, + AggParamsMax, + AggParamsMedian, + AggParamsMin, + AggParamsStdDeviation, + AggParamsSum, + AggParamsBucketAvg, + AggParamsBucketMax, + AggParamsBucketMin, + AggParamsBucketSum, + AggParamsCumulativeSum, + AggParamsDerivative, + AggParamsMovingAvg, + AggParamsPercentileRanks, + AggParamsPercentiles, + AggParamsSerialDiff, + AggParamsTopHit, AggParamsHistogram, AggParamsDateHistogram, AggTypesRegistrySetup, AggTypesRegistryStart, CreateAggConfigParams, getCalculateAutoTimeExpression, + METRIC_TYPES, BUCKET_TYPES, } from './'; @@ -102,4 +123,25 @@ export interface AggParamsMapping { [BUCKET_TYPES.HISTOGRAM]: AggParamsHistogram; [BUCKET_TYPES.DATE_HISTOGRAM]: AggParamsDateHistogram; [BUCKET_TYPES.TERMS]: AggParamsTerms; + [METRIC_TYPES.AVG]: AggParamsAvg; + [METRIC_TYPES.CARDINALITY]: AggParamsCardinality; + [METRIC_TYPES.COUNT]: BaseAggParams; + [METRIC_TYPES.GEO_BOUNDS]: AggParamsGeoBounds; + [METRIC_TYPES.GEO_CENTROID]: AggParamsGeoCentroid; + [METRIC_TYPES.MAX]: AggParamsMax; + [METRIC_TYPES.MEDIAN]: AggParamsMedian; + [METRIC_TYPES.MIN]: AggParamsMin; + [METRIC_TYPES.STD_DEV]: AggParamsStdDeviation; + [METRIC_TYPES.SUM]: AggParamsSum; + [METRIC_TYPES.AVG_BUCKET]: AggParamsBucketAvg; + [METRIC_TYPES.MAX_BUCKET]: AggParamsBucketMax; + [METRIC_TYPES.MIN_BUCKET]: AggParamsBucketMin; + [METRIC_TYPES.SUM_BUCKET]: AggParamsBucketSum; + [METRIC_TYPES.CUMULATIVE_SUM]: AggParamsCumulativeSum; + [METRIC_TYPES.DERIVATIVE]: AggParamsDerivative; + [METRIC_TYPES.MOVING_FN]: AggParamsMovingAvg; + [METRIC_TYPES.PERCENTILE_RANKS]: AggParamsPercentileRanks; + [METRIC_TYPES.PERCENTILES]: AggParamsPercentiles; + [METRIC_TYPES.SERIAL_DIFF]: AggParamsSerialDiff; + [METRIC_TYPES.TOP_HITS]: AggParamsTopHit; }