[data.search.aggs]: Expression functions for bucket agg types (#64772)
* [data.search.aggs]: Expression functions for bucket agg types - ranges agg types + significant terms * new portion of changes * add geo_tile_fn * add geo_hash_fn * Update src/plugins/data/public/search/aggs/buckets/filter_fn.ts Co-authored-by: Luke Elmers <lukeelmers@gmail.com> * Update src/plugins/data/public/search/aggs/buckets/geo_tile_fn.ts Co-authored-by: Luke Elmers <lukeelmers@gmail.com> * Update src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts Co-authored-by: Luke Elmers <lukeelmers@gmail.com> * Update src/plugins/data/public/search/aggs/buckets/ip_range_fn.ts Co-authored-by: Luke Elmers <lukeelmers@gmail.com> * Update src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts Co-authored-by: Luke Elmers <lukeelmers@gmail.com> * Update src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts Co-authored-by: Luke Elmers <lukeelmers@gmail.com> * Update src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts Co-authored-by: Luke Elmers <lukeelmers@gmail.com> * create BaseAggParams * add filters_fn * add histogram / date_histogram expression functions * cleanup * terms - order should be optional * add custom label params Co-authored-by: Luke Elmers <lukeelmers@gmail.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
d77d2e4b77
commit
6349575ec1
|
@ -14,6 +14,7 @@ import { Component } from 'react';
|
|||
import { CoreSetup } from 'src/core/public';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { CoreStart as CoreStart_2 } from 'src/core/public';
|
||||
import { Ensure } from '@kbn/utility-types';
|
||||
import { EuiButtonEmptyProps } from '@elastic/eui';
|
||||
import { EuiComboBoxProps } from '@elastic/eui';
|
||||
import { EuiConfirmModalProps } from '@elastic/eui';
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Assign } from '@kbn/utility-types';
|
||||
import { Assign, Ensure } from '@kbn/utility-types';
|
||||
import { ExpressionAstFunction, ExpressionAstArgument } from 'src/plugins/expressions/public';
|
||||
import { IAggType } from './agg_type';
|
||||
import { writeParams } from './agg_params';
|
||||
|
@ -31,17 +31,22 @@ import { FieldFormatsStart } from '../../field_formats';
|
|||
|
||||
type State = string | number | boolean | null | undefined | SerializableState;
|
||||
|
||||
interface SerializableState {
|
||||
/** @internal **/
|
||||
export interface SerializableState {
|
||||
[key: string]: State | State[];
|
||||
}
|
||||
|
||||
export interface AggConfigSerialized {
|
||||
type: string;
|
||||
enabled?: boolean;
|
||||
id?: string;
|
||||
params?: SerializableState;
|
||||
schema?: string;
|
||||
}
|
||||
/** @internal **/
|
||||
export type AggConfigSerialized = Ensure<
|
||||
{
|
||||
type: string;
|
||||
enabled?: boolean;
|
||||
id?: string;
|
||||
params?: SerializableState;
|
||||
schema?: string;
|
||||
},
|
||||
SerializableState
|
||||
>;
|
||||
|
||||
export interface AggConfigDependencies {
|
||||
fieldFormats: FieldFormatsStart;
|
||||
|
|
|
@ -105,6 +105,29 @@ export const getAggTypes = ({
|
|||
],
|
||||
});
|
||||
|
||||
/** Buckets: **/
|
||||
import { aggFilter } from './buckets/filter_fn';
|
||||
import { aggFilters } from './buckets/filters_fn';
|
||||
import { aggSignificantTerms } from './buckets/significant_terms_fn';
|
||||
import { aggIpRange } from './buckets/ip_range_fn';
|
||||
import { aggDateRange } from './buckets/date_range_fn';
|
||||
import { aggRange } from './buckets/range_fn';
|
||||
import { aggGeoTile } from './buckets/geo_tile_fn';
|
||||
import { aggGeoHash } from './buckets/geo_hash_fn';
|
||||
import { aggHistogram } from './buckets/histogram_fn';
|
||||
import { aggDateHistogram } from './buckets/date_histogram_fn';
|
||||
import { aggTerms } from './buckets/terms_fn';
|
||||
|
||||
export const getAggTypesFunctions = () => [aggTerms];
|
||||
export const getAggTypesFunctions = () => [
|
||||
aggFilter,
|
||||
aggFilters,
|
||||
aggSignificantTerms,
|
||||
aggIpRange,
|
||||
aggDateRange,
|
||||
aggRange,
|
||||
aggGeoTile,
|
||||
aggGeoHash,
|
||||
aggDateHistogram,
|
||||
aggHistogram,
|
||||
aggTerms,
|
||||
];
|
||||
|
|
|
@ -27,7 +27,7 @@ import { BucketAggType, IBucketAggConfig } from './bucket_agg_type';
|
|||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
import { createFilterDateHistogram } from './create_filter/date_histogram';
|
||||
import { intervalOptions } from './_interval_options';
|
||||
import { dateHistogramInterval } from '../../../../common';
|
||||
import { dateHistogramInterval, TimeRange } from '../../../../common';
|
||||
import { writeParams } from '../agg_params';
|
||||
import { isMetricAggType } from '../metrics/metric_agg_type';
|
||||
|
||||
|
@ -35,6 +35,8 @@ import { FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../../../common';
|
|||
import { TimefilterContract } from '../../../query';
|
||||
import { QuerySetup } from '../../../query/query_service';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { BaseAggParams } from '../types';
|
||||
import { ExtendedBounds } from './lib/extended_bounds';
|
||||
|
||||
const detectedTimezone = moment.tz.guess();
|
||||
const tzOffset = moment().format('Z');
|
||||
|
@ -67,6 +69,19 @@ export function isDateHistogramBucketAggConfig(agg: any): agg is IBucketDateHist
|
|||
return Boolean(agg.buckets);
|
||||
}
|
||||
|
||||
export interface AggParamsDateHistogram extends BaseAggParams {
|
||||
field?: string;
|
||||
timeRange?: TimeRange;
|
||||
useNormalizedEsInterval?: boolean;
|
||||
scaleMetricValues?: boolean;
|
||||
interval?: string;
|
||||
time_zone?: string;
|
||||
drop_partials?: boolean;
|
||||
format?: string;
|
||||
min_doc_count?: number;
|
||||
extended_bounds?: ExtendedBounds;
|
||||
}
|
||||
|
||||
export const getDateHistogramBucketAgg = ({
|
||||
uiSettings,
|
||||
query,
|
||||
|
@ -89,6 +104,7 @@ export const getDateHistogramBucketAgg = ({
|
|||
}
|
||||
|
||||
const field = agg.getFieldDisplayName();
|
||||
|
||||
return i18n.translate('data.search.aggs.buckets.dateHistogramLabel', {
|
||||
defaultMessage: '{fieldName} per {intervalDescription}',
|
||||
values: {
|
||||
|
|
|
@ -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 { aggDateHistogram } from './date_histogram_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggDateHistogram', () => {
|
||||
const fn = functionWrapper(aggDateHistogram());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"drop_partials": undefined,
|
||||
"extended_bounds": undefined,
|
||||
"field": undefined,
|
||||
"format": undefined,
|
||||
"interval": undefined,
|
||||
"json": undefined,
|
||||
"min_doc_count": undefined,
|
||||
"scaleMetricValues": undefined,
|
||||
"timeRange": undefined,
|
||||
"time_zone": undefined,
|
||||
"useNormalizedEsInterval": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "date_histogram",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'field',
|
||||
timeRange: JSON.stringify({
|
||||
from: 'from',
|
||||
to: 'to',
|
||||
}),
|
||||
useNormalizedEsInterval: true,
|
||||
scaleMetricValues: true,
|
||||
interval: 'interval',
|
||||
time_zone: 'time_zone',
|
||||
drop_partials: false,
|
||||
format: 'format',
|
||||
min_doc_count: 1,
|
||||
extended_bounds: JSON.stringify({
|
||||
min: 1,
|
||||
max: 2,
|
||||
}),
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"drop_partials": false,
|
||||
"extended_bounds": Object {
|
||||
"max": 2,
|
||||
"min": 1,
|
||||
},
|
||||
"field": "field",
|
||||
"format": "format",
|
||||
"interval": "interval",
|
||||
"json": undefined,
|
||||
"min_doc_count": 1,
|
||||
"scaleMetricValues": true,
|
||||
"timeRange": Object {
|
||||
"from": "from",
|
||||
"to": "to",
|
||||
},
|
||||
"time_zone": "time_zone",
|
||||
"useNormalizedEsInterval": true,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "date_histogram",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
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"`);
|
||||
});
|
||||
});
|
||||
});
|
155
src/plugins/data/public/search/aggs/buckets/date_histogram_fn.ts
Normal file
155
src/plugins/data/public/search/aggs/buckets/date_histogram_fn.ts
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggDateHistogram';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.DATE_HISTOGRAM>;
|
||||
|
||||
type Arguments = Assign<AggArgs, { timeRange?: string; extended_bounds?: string }>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggDateHistogram = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.dateHistogram.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Histogram agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
useNormalizedEsInterval: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.useNormalizedEsInterval.help', {
|
||||
defaultMessage: 'Specifies whether to use useNormalizedEsInterval for this aggregation',
|
||||
}),
|
||||
},
|
||||
time_zone: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.timeZone.help', {
|
||||
defaultMessage: 'Time zone to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
format: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.format.help', {
|
||||
defaultMessage: 'Format to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
scaleMetricValues: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.scaleMetricValues.help', {
|
||||
defaultMessage: 'Specifies whether to use scaleMetricValues for this aggregation',
|
||||
}),
|
||||
},
|
||||
interval: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.interval.help', {
|
||||
defaultMessage: 'Interval to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
timeRange: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.timeRange.help', {
|
||||
defaultMessage: 'Time Range to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
min_doc_count: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.minDocCount.help', {
|
||||
defaultMessage: 'Minimum document count to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
drop_partials: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.dropPartials.help', {
|
||||
defaultMessage: 'Specifies whether to use drop_partials for this aggregation',
|
||||
}),
|
||||
},
|
||||
extended_bounds: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.extendedBounds.help', {
|
||||
defaultMessage:
|
||||
'With extended_bounds setting, you now can "force" the histogram aggregation to start building buckets on a specific min value and also keep on building buckets up to a max value ',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateHistogram.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: BUCKET_TYPES.DATE_HISTOGRAM,
|
||||
params: {
|
||||
...rest,
|
||||
timeRange: getParsedValue(args, 'timeRange'),
|
||||
extended_bounds: getParsedValue(args, 'extended_bounds'),
|
||||
json: getParsedValue(args, 'json'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -29,6 +29,7 @@ import { convertDateRangeToString, DateRangeKey } from './lib/date_range';
|
|||
|
||||
import { KBN_FIELD_TYPES, FieldFormat, TEXT_CONTEXT_TYPE } from '../../../../common';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { BaseAggParams } from '../types';
|
||||
|
||||
const dateRangeTitle = i18n.translate('data.search.aggs.buckets.dateRangeTitle', {
|
||||
defaultMessage: 'Date Range',
|
||||
|
@ -39,6 +40,12 @@ export interface DateRangeBucketAggDependencies {
|
|||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsDateRange extends BaseAggParams {
|
||||
field?: string;
|
||||
ranges?: DateRangeKey[];
|
||||
time_zone?: string;
|
||||
}
|
||||
|
||||
export const getDateRangeBucketAgg = ({
|
||||
uiSettings,
|
||||
getInternalStartServices,
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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 { aggDateRange } from './date_range_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggDateRange', () => {
|
||||
const fn = functionWrapper(aggDateRange());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"field": undefined,
|
||||
"json": undefined,
|
||||
"ranges": undefined,
|
||||
"time_zone": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "date_range",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'date_field',
|
||||
time_zone: 'UTC +3',
|
||||
ranges: JSON.stringify([
|
||||
{ from: 'now-1w/w', to: 'now' },
|
||||
{ from: 1588163532470, to: 1588163532481 },
|
||||
]),
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"field": "date_field",
|
||||
"json": undefined,
|
||||
"ranges": Array [
|
||||
Object {
|
||||
"from": "now-1w/w",
|
||||
"to": "now",
|
||||
},
|
||||
Object {
|
||||
"from": 1588163532470,
|
||||
"to": 1588163532481,
|
||||
},
|
||||
],
|
||||
"time_zone": "UTC +3",
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "date_range",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('correctly parses json string argument', () => {
|
||||
const actual = fn({
|
||||
field: 'date_field',
|
||||
json: '{ "foo": true }',
|
||||
});
|
||||
|
||||
expect(actual.value.params.json).toEqual({ foo: true });
|
||||
|
||||
expect(() => {
|
||||
fn({
|
||||
field: 'date_field',
|
||||
json: '/// intentionally malformed json ///',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`);
|
||||
});
|
||||
});
|
||||
});
|
111
src/plugins/data/public/search/aggs/buckets/date_range_fn.ts
Normal file
111
src/plugins/data/public/search/aggs/buckets/date_range_fn.ts
Normal file
|
@ -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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggDateRange';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.DATE_RANGE>;
|
||||
|
||||
type Arguments = Assign<AggArgs, { ranges?: string }>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggDateRange = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.dateRange.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Date Range agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateRange.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.dateRange.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateRange.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateRange.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
ranges: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateRange.ranges.help', {
|
||||
defaultMessage: 'Serialized ranges to use for this aggregation.',
|
||||
}),
|
||||
},
|
||||
time_zone: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateRange.timeZone.help', {
|
||||
defaultMessage: 'Time zone to use for this aggregation.',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateRange.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.dateRange.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: BUCKET_TYPES.DATE_RANGE,
|
||||
params: {
|
||||
...rest,
|
||||
json: getParsedValue(args, 'json'),
|
||||
ranges: getParsedValue(args, 'ranges'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -21,6 +21,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import { BucketAggType } from './bucket_agg_type';
|
||||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { GeoBoundingBox } from './lib/geo_point';
|
||||
import { BaseAggParams } from '../types';
|
||||
|
||||
const filterTitle = i18n.translate('data.search.aggs.buckets.filterTitle', {
|
||||
defaultMessage: 'Filter',
|
||||
|
@ -30,6 +32,10 @@ export interface FilterBucketAggDependencies {
|
|||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsFilter extends BaseAggParams {
|
||||
geo_bounding_box?: GeoBoundingBox;
|
||||
}
|
||||
|
||||
export const getFilterBucketAgg = ({ getInternalStartServices }: FilterBucketAggDependencies) =>
|
||||
new BucketAggType(
|
||||
{
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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 { aggFilter } from './filter_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggFilter', () => {
|
||||
const fn = functionWrapper(aggFilter());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"geo_bounding_box": undefined,
|
||||
"json": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "filter",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
geo_bounding_box: JSON.stringify({
|
||||
wkt: 'BBOX (-74.1, -71.12, 40.73, 40.01)',
|
||||
}),
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"geo_bounding_box": Object {
|
||||
"wkt": "BBOX (-74.1, -71.12, 40.73, 40.01)",
|
||||
},
|
||||
"json": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "filter",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
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"`);
|
||||
});
|
||||
});
|
||||
});
|
99
src/plugins/data/public/search/aggs/buckets/filter_fn.ts
Normal file
99
src/plugins/data/public/search/aggs/buckets/filter_fn.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggFilter';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.FILTER>;
|
||||
|
||||
type Arguments = Assign<AggArgs, { geo_bounding_box?: string }>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggFilter = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.filter.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Filter agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filter.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.filter.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filter.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
geo_bounding_box: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filter.geoBoundingBox.help', {
|
||||
defaultMessage: 'Filter results based on a point location within a bounding box',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filter.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filter.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: BUCKET_TYPES.FILTER,
|
||||
params: {
|
||||
...rest,
|
||||
json: getParsedValue(args, 'json'),
|
||||
geo_bounding_box: getParsedValue(args, 'geo_bounding_box'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -29,6 +29,7 @@ import { Storage } from '../../../../../../plugins/kibana_utils/public';
|
|||
import { getEsQueryConfig, buildEsQuery, Query } from '../../../../common';
|
||||
import { getQueryLog } from '../../../query';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { BaseAggParams } from '../types';
|
||||
|
||||
const filtersTitle = i18n.translate('data.search.aggs.buckets.filtersTitle', {
|
||||
defaultMessage: 'Filters',
|
||||
|
@ -47,6 +48,13 @@ export interface FiltersBucketAggDependencies {
|
|||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsFilters extends BaseAggParams {
|
||||
filters?: Array<{
|
||||
input: Query;
|
||||
label: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const getFiltersBucketAgg = ({
|
||||
uiSettings,
|
||||
getInternalStartServices,
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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 { aggFilters } from './filters_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggFilters', () => {
|
||||
const fn = functionWrapper(aggFilters());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"filters": undefined,
|
||||
"json": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "filters",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
filters: JSON.stringify([
|
||||
{
|
||||
query: 'query',
|
||||
language: 'lucene',
|
||||
label: 'test',
|
||||
},
|
||||
]),
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"filters": Array [
|
||||
Object {
|
||||
"label": "test",
|
||||
"language": "lucene",
|
||||
"query": "query",
|
||||
},
|
||||
],
|
||||
"json": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "filters",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
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"`);
|
||||
});
|
||||
});
|
||||
});
|
93
src/plugins/data/public/search/aggs/buckets/filters_fn.ts
Normal file
93
src/plugins/data/public/search/aggs/buckets/filters_fn.ts
Normal file
|
@ -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 { i18n } from '@kbn/i18n';
|
||||
import { Assign } from '@kbn/utility-types';
|
||||
import { ExpressionFunctionDefinition } from '../../../../../expressions/public';
|
||||
import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggFilters';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.FILTERS>;
|
||||
|
||||
type Arguments = Assign<AggArgs, { filters?: string }>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggFilters = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.filters.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Filter agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filters.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.filters.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filters.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
filters: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filters.filters.help', {
|
||||
defaultMessage: 'Filters to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.filters.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: (input, args) => {
|
||||
const { id, enabled, schema, ...rest } = args;
|
||||
|
||||
return {
|
||||
type: 'agg_type',
|
||||
value: {
|
||||
id,
|
||||
enabled,
|
||||
schema,
|
||||
type: BUCKET_TYPES.FILTERS,
|
||||
params: {
|
||||
...rest,
|
||||
filters: getParsedValue(args, 'filters'),
|
||||
json: getParsedValue(args, 'json'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -22,6 +22,8 @@ import { BucketAggType, IBucketAggConfig } from './bucket_agg_type';
|
|||
import { KBN_FIELD_TYPES } from '../../../../common';
|
||||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { GeoBoundingBox } from './lib/geo_point';
|
||||
import { BaseAggParams } from '../types';
|
||||
|
||||
const defaultBoundingBox = {
|
||||
top_left: { lat: 1, lon: 1 },
|
||||
|
@ -38,6 +40,15 @@ export interface GeoHashBucketAggDependencies {
|
|||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsGeoHash extends BaseAggParams {
|
||||
field: string;
|
||||
autoPrecision?: boolean;
|
||||
precision?: number;
|
||||
useGeocentroid?: boolean;
|
||||
isFilteredByCollar?: boolean;
|
||||
boundingBox?: GeoBoundingBox;
|
||||
}
|
||||
|
||||
export const getGeoHashBucketAgg = ({ getInternalStartServices }: GeoHashBucketAggDependencies) =>
|
||||
new BucketAggType<IBucketAggConfig>(
|
||||
{
|
||||
|
|
112
src/plugins/data/public/search/aggs/buckets/geo_hash_fn.test.ts
Normal file
112
src/plugins/data/public/search/aggs/buckets/geo_hash_fn.test.ts
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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 { aggGeoHash } from './geo_hash_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggGeoHash', () => {
|
||||
const fn = functionWrapper(aggGeoHash());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'geo_field',
|
||||
});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"autoPrecision": undefined,
|
||||
"boundingBox": undefined,
|
||||
"customLabel": undefined,
|
||||
"field": "geo_field",
|
||||
"isFilteredByCollar": undefined,
|
||||
"json": undefined,
|
||||
"precision": undefined,
|
||||
"useGeocentroid": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "geohash_grid",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'geo_field',
|
||||
autoPrecision: false,
|
||||
precision: 10,
|
||||
useGeocentroid: true,
|
||||
isFilteredByCollar: false,
|
||||
boundingBox: JSON.stringify({
|
||||
top_left: [-74.1, 40.73],
|
||||
bottom_right: [-71.12, 40.01],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"autoPrecision": false,
|
||||
"boundingBox": Object {
|
||||
"bottom_right": Array [
|
||||
-71.12,
|
||||
40.01,
|
||||
],
|
||||
"top_left": Array [
|
||||
-74.1,
|
||||
40.73,
|
||||
],
|
||||
},
|
||||
"customLabel": undefined,
|
||||
"field": "geo_field",
|
||||
"isFilteredByCollar": false,
|
||||
"json": undefined,
|
||||
"precision": 10,
|
||||
"useGeocentroid": true,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "geohash_grid",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('correctly parses json string argument', () => {
|
||||
const actual = fn({
|
||||
field: 'geo_field',
|
||||
json: '{ "foo": true }',
|
||||
});
|
||||
|
||||
expect(actual.value.params.json).toEqual({ foo: true });
|
||||
|
||||
expect(() => {
|
||||
fn({
|
||||
field: 'geo_field',
|
||||
json: '/// intentionally malformed json ///',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`);
|
||||
});
|
||||
});
|
||||
});
|
129
src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts
Normal file
129
src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggGeoHash';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.GEOHASH_GRID>;
|
||||
|
||||
type Arguments = Assign<AggArgs, { boundingBox?: string }>;
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggGeoHash = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.geoHash.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Geo Hash agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
useGeocentroid: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.useGeocentroid.help', {
|
||||
defaultMessage: 'Specifies whether to use geocentroid for this aggregation',
|
||||
}),
|
||||
},
|
||||
autoPrecision: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.autoPrecision.help', {
|
||||
defaultMessage: 'Specifies whether to use auto precision for this aggregation',
|
||||
}),
|
||||
},
|
||||
isFilteredByCollar: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.isFilteredByCollar.help', {
|
||||
defaultMessage: 'Specifies whether to filter by collar',
|
||||
}),
|
||||
},
|
||||
boundingBox: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.boundingBox.help', {
|
||||
defaultMessage: 'Filter results based on a point location within a bounding box',
|
||||
}),
|
||||
},
|
||||
precision: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.precision.help', {
|
||||
defaultMessage: 'Precision to use for this aggregation.',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoHash.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: BUCKET_TYPES.GEOHASH_GRID,
|
||||
params: {
|
||||
...rest,
|
||||
boundingBox: getParsedValue(args, 'boundingBox'),
|
||||
json: getParsedValue(args, 'json'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -25,6 +25,7 @@ import { BUCKET_TYPES } from './bucket_agg_types';
|
|||
import { KBN_FIELD_TYPES } from '../../../../common';
|
||||
import { METRIC_TYPES } from '../metrics/metric_agg_types';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { BaseAggParams } from '../types';
|
||||
|
||||
export interface GeoTitleBucketAggDependencies {
|
||||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
|
@ -34,6 +35,12 @@ const geotileGridTitle = i18n.translate('data.search.aggs.buckets.geotileGridTit
|
|||
defaultMessage: 'Geotile',
|
||||
});
|
||||
|
||||
export interface AggParamsGeoTile extends BaseAggParams {
|
||||
field: string;
|
||||
useGeocentroid?: boolean;
|
||||
precision?: number;
|
||||
}
|
||||
|
||||
export const getGeoTitleBucketAgg = ({ getInternalStartServices }: GeoTitleBucketAggDependencies) =>
|
||||
new BucketAggType(
|
||||
{
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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 { aggGeoTile } from './geo_tile_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggGeoTile', () => {
|
||||
const fn = functionWrapper(aggGeoTile());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'geo_field',
|
||||
});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"field": "geo_field",
|
||||
"json": undefined,
|
||||
"precision": undefined,
|
||||
"useGeocentroid": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "geotile_grid",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'geo_field',
|
||||
useGeocentroid: false,
|
||||
precision: 10,
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"field": "geo_field",
|
||||
"json": undefined,
|
||||
"precision": 10,
|
||||
"useGeocentroid": false,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "geotile_grid",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('correctly parses json string argument', () => {
|
||||
const actual = fn({
|
||||
field: 'geo_field',
|
||||
json: '{ "foo": true }',
|
||||
});
|
||||
|
||||
expect(actual.value.params.json).toEqual({ foo: true });
|
||||
|
||||
expect(() => {
|
||||
fn({
|
||||
field: 'geo_field',
|
||||
json: '/// intentionally malformed json ///',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`);
|
||||
});
|
||||
});
|
||||
});
|
108
src/plugins/data/public/search/aggs/buckets/geo_tile_fn.ts
Normal file
108
src/plugins/data/public/search/aggs/buckets/geo_tile_fn.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggGeoTile';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.GEOTILE_GRID>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, AggArgs, Output>;
|
||||
|
||||
export const aggGeoTile = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.geoTile.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Geo Tile agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoTile.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.geoTile.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoTile.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.geoTile.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
useGeocentroid: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoTile.useGeocentroid.help', {
|
||||
defaultMessage: 'Specifies whether to use geocentroid for this aggregation',
|
||||
}),
|
||||
},
|
||||
precision: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoTile.precision.help', {
|
||||
defaultMessage: 'Precision to use for this aggregation.',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoTile.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.geoTile.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: BUCKET_TYPES.GEOTILE_GRID,
|
||||
params: {
|
||||
...rest,
|
||||
json: getParsedValue(args, 'json'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -26,6 +26,8 @@ import { createFilterHistogram } from './create_filter/histogram';
|
|||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
import { KBN_FIELD_TYPES } from '../../../../common';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { BaseAggParams } from '../types';
|
||||
import { ExtendedBounds } from './lib/extended_bounds';
|
||||
|
||||
export interface AutoBounds {
|
||||
min: number;
|
||||
|
@ -42,6 +44,15 @@ export interface IBucketHistogramAggConfig extends IBucketAggConfig {
|
|||
getAutoBounds: () => AutoBounds;
|
||||
}
|
||||
|
||||
export interface AggParamsHistogram extends BaseAggParams {
|
||||
field: string;
|
||||
interval: string;
|
||||
intervalBase?: number;
|
||||
min_doc_count?: boolean;
|
||||
has_extended_bounds?: boolean;
|
||||
extended_bounds?: ExtendedBounds;
|
||||
}
|
||||
|
||||
export const getHistogramBucketAgg = ({
|
||||
uiSettings,
|
||||
getInternalStartServices,
|
||||
|
|
109
src/plugins/data/public/search/aggs/buckets/histogram_fn.test.ts
Normal file
109
src/plugins/data/public/search/aggs/buckets/histogram_fn.test.ts
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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 { aggHistogram } from './histogram_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggHistogram', () => {
|
||||
const fn = functionWrapper(aggHistogram());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'field',
|
||||
interval: '10',
|
||||
});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"extended_bounds": undefined,
|
||||
"field": "field",
|
||||
"has_extended_bounds": undefined,
|
||||
"interval": "10",
|
||||
"intervalBase": undefined,
|
||||
"json": undefined,
|
||||
"min_doc_count": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "histogram",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'field',
|
||||
interval: '10',
|
||||
intervalBase: 1,
|
||||
min_doc_count: false,
|
||||
has_extended_bounds: false,
|
||||
extended_bounds: JSON.stringify({
|
||||
min: 1,
|
||||
max: 2,
|
||||
}),
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"extended_bounds": Object {
|
||||
"max": 2,
|
||||
"min": 1,
|
||||
},
|
||||
"field": "field",
|
||||
"has_extended_bounds": false,
|
||||
"interval": "10",
|
||||
"intervalBase": 1,
|
||||
"json": undefined,
|
||||
"min_doc_count": false,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "histogram",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('correctly parses json string argument', () => {
|
||||
const actual = fn({
|
||||
field: 'field',
|
||||
interval: '10',
|
||||
json: '{ "foo": true }',
|
||||
});
|
||||
|
||||
expect(actual.value.params.json).toEqual({ foo: true });
|
||||
|
||||
expect(() => {
|
||||
fn({
|
||||
field: 'field',
|
||||
interval: '10',
|
||||
json: '/// intentionally malformed json ///',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`);
|
||||
});
|
||||
});
|
||||
});
|
132
src/plugins/data/public/search/aggs/buckets/histogram_fn.ts
Normal file
132
src/plugins/data/public/search/aggs/buckets/histogram_fn.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggHistogram';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.HISTOGRAM>;
|
||||
|
||||
type Arguments = Assign<AggArgs, { extended_bounds?: string }>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggHistogram = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.histogram.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Histogram agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
interval: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.interval.help', {
|
||||
defaultMessage: 'Interval to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
intervalBase: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.intervalBase.help', {
|
||||
defaultMessage: 'IntervalBase to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
min_doc_count: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.minDocCount.help', {
|
||||
defaultMessage: 'Specifies whether to use min_doc_count for this aggregation',
|
||||
}),
|
||||
},
|
||||
has_extended_bounds: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.hasExtendedBounds.help', {
|
||||
defaultMessage: 'Specifies whether to use has_extended_bounds for this aggregation',
|
||||
}),
|
||||
},
|
||||
extended_bounds: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.extendedBounds.help', {
|
||||
defaultMessage:
|
||||
'With extended_bounds setting, you now can "force" the histogram aggregation to start building buckets on a specific min value and also keep on building buckets up to a max value ',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.histogram.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: BUCKET_TYPES.HISTOGRAM,
|
||||
params: {
|
||||
...rest,
|
||||
extended_bounds: getParsedValue(args, 'extended_bounds'),
|
||||
json: getParsedValue(args, 'json'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -19,11 +19,18 @@
|
|||
|
||||
export * from './_interval_options';
|
||||
export * from './bucket_agg_types';
|
||||
export * from './histogram';
|
||||
export * from './date_histogram';
|
||||
export * from './date_range';
|
||||
export * from './range';
|
||||
export * from './filter';
|
||||
export * from './filters';
|
||||
export * from './geo_tile';
|
||||
export * from './geo_hash';
|
||||
export * from './ip_range';
|
||||
export * from './lib/cidr_mask';
|
||||
export * from './lib/date_range';
|
||||
export * from './lib/ip_range';
|
||||
export * from './migrate_include_exclude_format';
|
||||
export * from './significant_terms';
|
||||
export * from './terms';
|
||||
|
|
|
@ -23,18 +23,38 @@ import { BucketAggType } from './bucket_agg_type';
|
|||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
|
||||
import { createFilterIpRange } from './create_filter/ip_range';
|
||||
import { IpRangeKey, convertIPRangeToString } from './lib/ip_range';
|
||||
import {
|
||||
convertIPRangeToString,
|
||||
IpRangeKey,
|
||||
RangeIpRangeAggKey,
|
||||
CidrMaskIpRangeAggKey,
|
||||
} from './lib/ip_range';
|
||||
import { KBN_FIELD_TYPES, FieldFormat, TEXT_CONTEXT_TYPE } from '../../../../common';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { BaseAggParams } from '../types';
|
||||
|
||||
const ipRangeTitle = i18n.translate('data.search.aggs.buckets.ipRangeTitle', {
|
||||
defaultMessage: 'IPv4 Range',
|
||||
});
|
||||
|
||||
export enum IP_RANGE_TYPES {
|
||||
FROM_TO = 'fromTo',
|
||||
MASK = 'mask',
|
||||
}
|
||||
|
||||
export interface IpRangeBucketAggDependencies {
|
||||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsIpRange extends BaseAggParams {
|
||||
field: string;
|
||||
ipRangeType?: IP_RANGE_TYPES;
|
||||
ranges?: Partial<{
|
||||
[IP_RANGE_TYPES.FROM_TO]: RangeIpRangeAggKey[];
|
||||
[IP_RANGE_TYPES.MASK]: CidrMaskIpRangeAggKey[];
|
||||
}>;
|
||||
}
|
||||
|
||||
export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketAggDependencies) =>
|
||||
new BucketAggType(
|
||||
{
|
||||
|
@ -42,7 +62,7 @@ export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketA
|
|||
title: ipRangeTitle,
|
||||
createFilter: createFilterIpRange,
|
||||
getKey(bucket, key, agg): IpRangeKey {
|
||||
if (agg.params.ipRangeType === 'mask') {
|
||||
if (agg.params.ipRangeType === IP_RANGE_TYPES.MASK) {
|
||||
return { type: 'mask', mask: key };
|
||||
}
|
||||
return { type: 'range', from: bucket.from, to: bucket.to };
|
||||
|
@ -74,7 +94,7 @@ export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketA
|
|||
},
|
||||
{
|
||||
name: 'ipRangeType',
|
||||
default: 'fromTo',
|
||||
default: IP_RANGE_TYPES.FROM_TO,
|
||||
write: noop,
|
||||
},
|
||||
{
|
||||
|
@ -90,7 +110,7 @@ export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketA
|
|||
const ipRangeType = aggConfig.params.ipRangeType;
|
||||
let ranges = aggConfig.params.ranges[ipRangeType];
|
||||
|
||||
if (ipRangeType === 'fromTo') {
|
||||
if (ipRangeType === IP_RANGE_TYPES.FROM_TO) {
|
||||
ranges = map(ranges, (range: any) => omit(range, isNull));
|
||||
}
|
||||
|
||||
|
|
102
src/plugins/data/public/search/aggs/buckets/ip_range_fn.test.ts
Normal file
102
src/plugins/data/public/search/aggs/buckets/ip_range_fn.test.ts
Normal file
|
@ -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 { IP_RANGE_TYPES } from './ip_range';
|
||||
import { aggIpRange } from './ip_range_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggIpRange', () => {
|
||||
const fn = functionWrapper(aggIpRange());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'ip_field',
|
||||
});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"field": "ip_field",
|
||||
"ipRangeType": undefined,
|
||||
"json": undefined,
|
||||
"ranges": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "ip_range",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'ip_field',
|
||||
ipRangeType: IP_RANGE_TYPES.MASK,
|
||||
ranges: JSON.stringify({
|
||||
mask: [{ mask: '10.0.0.0/25' }],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"field": "ip_field",
|
||||
"ipRangeType": "mask",
|
||||
"json": undefined,
|
||||
"ranges": Object {
|
||||
"mask": Array [
|
||||
Object {
|
||||
"mask": "10.0.0.0/25",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "ip_range",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('correctly parses json string argument', () => {
|
||||
const actual = fn({
|
||||
field: 'ip_field',
|
||||
ipRangeType: IP_RANGE_TYPES.MASK,
|
||||
json: '{ "foo": true }',
|
||||
});
|
||||
|
||||
expect(actual.value.params.json).toEqual({ foo: true });
|
||||
|
||||
expect(() => {
|
||||
fn({
|
||||
field: 'ip_field',
|
||||
ipRangeType: IP_RANGE_TYPES.FROM_TO,
|
||||
json: '/// intentionally malformed json ///',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`);
|
||||
});
|
||||
});
|
||||
});
|
114
src/plugins/data/public/search/aggs/buckets/ip_range_fn.ts
Normal file
114
src/plugins/data/public/search/aggs/buckets/ip_range_fn.ts
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggIpRange';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.IP_RANGE>;
|
||||
|
||||
type Arguments = Assign<AggArgs, { ranges?: string; ipRangeType?: string }>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggIpRange = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.ipRange.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Ip Range agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.ipRange.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.ipRange.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.ipRange.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.ipRange.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
ipRangeType: {
|
||||
types: ['string'],
|
||||
options: ['mask', 'fromTo'],
|
||||
help: i18n.translate('data.search.aggs.buckets.ipRange.ipRangeType.help', {
|
||||
defaultMessage:
|
||||
'IP range type to use for this aggregation. Takes one of the following values: mask, fromTo.',
|
||||
}),
|
||||
},
|
||||
ranges: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.ipRange.ranges.help', {
|
||||
defaultMessage: 'Serialized ranges to use for this aggregation.',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.ipRange.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.ipRange.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: BUCKET_TYPES.IP_RANGE,
|
||||
params: {
|
||||
...rest,
|
||||
json: getParsedValue(args, 'json'),
|
||||
ranges: getParsedValue(args, 'ranges'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -18,8 +18,8 @@
|
|||
*/
|
||||
|
||||
export interface DateRangeKey {
|
||||
from: number;
|
||||
to: number;
|
||||
from: number | string;
|
||||
to: number | string;
|
||||
}
|
||||
|
||||
export function convertDateRangeToString({ from, to }: DateRangeKey, format: (val: any) => string) {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export interface ExtendedBounds {
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
86
src/plugins/data/public/search/aggs/buckets/lib/geo_point.ts
Normal file
86
src/plugins/data/public/search/aggs/buckets/lib/geo_point.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
type GeoPoint =
|
||||
| {
|
||||
lat: number;
|
||||
lon: number;
|
||||
}
|
||||
| string
|
||||
| [number, number];
|
||||
|
||||
interface GeoBox {
|
||||
top: number;
|
||||
left: number;
|
||||
bottom: number;
|
||||
right: number;
|
||||
}
|
||||
|
||||
/** GeoBoundingBox Accepted Formats:
|
||||
* Lat Lon As Properties:
|
||||
* "top_left" : {
|
||||
* "lat" : 40.73, "lon" : -74.1
|
||||
* },
|
||||
* "bottom_right" : {
|
||||
* "lat" : 40.01, "lon" : -71.12
|
||||
* }
|
||||
*
|
||||
* Lat Lon As Array:
|
||||
* {
|
||||
* "top_left" : [-74.1, 40.73],
|
||||
* "bottom_right" : [-71.12, 40.01]
|
||||
* }
|
||||
*
|
||||
* Lat Lon As String:
|
||||
* {
|
||||
* "top_left" : "40.73, -74.1",
|
||||
* "bottom_right" : "40.01, -71.12"
|
||||
* }
|
||||
*
|
||||
* Bounding Box as Well-Known Text (WKT):
|
||||
* {
|
||||
* "wkt" : "BBOX (-74.1, -71.12, 40.73, 40.01)"
|
||||
* }
|
||||
*
|
||||
* Geohash:
|
||||
* {
|
||||
* "top_right" : "dr5r9ydj2y73",
|
||||
* "bottom_left" : "drj7teegpus6"
|
||||
* }
|
||||
*
|
||||
* Vertices:
|
||||
* {
|
||||
* "top" : 40.73,
|
||||
* "left" : -74.1,
|
||||
* "bottom" : 40.01,
|
||||
* "right" : -71.12
|
||||
* }
|
||||
*
|
||||
* **/
|
||||
export type GeoBoundingBox =
|
||||
| Partial<{
|
||||
top_left: GeoPoint;
|
||||
top_right: GeoPoint;
|
||||
bottom_right: GeoPoint;
|
||||
bottom_left: GeoPoint;
|
||||
}>
|
||||
| {
|
||||
wkt: string;
|
||||
}
|
||||
| GeoBox;
|
|
@ -17,9 +17,18 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export type IpRangeKey =
|
||||
| { type: 'mask'; mask: string }
|
||||
| { type: 'range'; from: string; to: string };
|
||||
export interface CidrMaskIpRangeAggKey {
|
||||
type: 'mask';
|
||||
mask: string;
|
||||
}
|
||||
|
||||
export interface RangeIpRangeAggKey {
|
||||
type: 'range';
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
export type IpRangeKey = CidrMaskIpRangeAggKey | RangeIpRangeAggKey;
|
||||
|
||||
export const convertIPRangeToString = (range: IpRangeKey, format: (val: any) => string) => {
|
||||
if (range.type === 'mask') {
|
||||
|
|
|
@ -24,6 +24,7 @@ import { RangeKey } from './range_key';
|
|||
import { createFilterRange } from './create_filter/range';
|
||||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { BaseAggParams } from '../types';
|
||||
|
||||
const keyCaches = new WeakMap();
|
||||
const formats = new WeakMap();
|
||||
|
@ -36,6 +37,14 @@ export interface RangeBucketAggDependencies {
|
|||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsRange extends BaseAggParams {
|
||||
field: string;
|
||||
ranges?: Array<{
|
||||
from: number;
|
||||
to: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const getRangeBucketAgg = ({ getInternalStartServices }: RangeBucketAggDependencies) =>
|
||||
new BucketAggType(
|
||||
{
|
||||
|
|
100
src/plugins/data/public/search/aggs/buckets/range_fn.test.ts
Normal file
100
src/plugins/data/public/search/aggs/buckets/range_fn.test.ts
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 { aggRange } from './range_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggRange', () => {
|
||||
const fn = functionWrapper(aggRange());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'number_field',
|
||||
});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"field": "number_field",
|
||||
"json": undefined,
|
||||
"ranges": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "range",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'number_field',
|
||||
ranges: JSON.stringify([
|
||||
{ from: 1, to: 2 },
|
||||
{ from: 5, to: 100 },
|
||||
]),
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"field": "number_field",
|
||||
"json": undefined,
|
||||
"ranges": Array [
|
||||
Object {
|
||||
"from": 1,
|
||||
"to": 2,
|
||||
},
|
||||
Object {
|
||||
"from": 5,
|
||||
"to": 100,
|
||||
},
|
||||
],
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "range",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('correctly parses json string argument', () => {
|
||||
const actual = fn({
|
||||
field: 'number_field',
|
||||
json: '{ "foo": true }',
|
||||
});
|
||||
|
||||
expect(actual.value.params.json).toEqual({ foo: true });
|
||||
|
||||
expect(() => {
|
||||
fn({
|
||||
field: 'number_field',
|
||||
json: '/// intentionally malformed json ///',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`);
|
||||
});
|
||||
});
|
||||
});
|
106
src/plugins/data/public/search/aggs/buckets/range_fn.ts
Normal file
106
src/plugins/data/public/search/aggs/buckets/range_fn.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggRange';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.RANGE>;
|
||||
|
||||
type Arguments = Assign<AggArgs, { ranges?: string }>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggRange = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.range.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Range agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.range.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.range.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.range.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.range.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
ranges: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.range.ranges.help', {
|
||||
defaultMessage: 'Serialized ranges to use for this aggregation.',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.range.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.range.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: BUCKET_TYPES.RANGE,
|
||||
params: {
|
||||
...rest,
|
||||
json: getParsedValue(args, 'json'),
|
||||
ranges: getParsedValue(args, 'ranges'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -24,6 +24,7 @@ import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exc
|
|||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
import { KBN_FIELD_TYPES } from '../../../../common';
|
||||
import { GetInternalStartServicesFn } from '../../../types';
|
||||
import { BaseAggParams } from '../types';
|
||||
|
||||
const significantTermsTitle = i18n.translate('data.search.aggs.buckets.significantTermsTitle', {
|
||||
defaultMessage: 'Significant Terms',
|
||||
|
@ -33,6 +34,13 @@ export interface SignificantTermsBucketAggDependencies {
|
|||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsSignificantTerms extends BaseAggParams {
|
||||
field: string;
|
||||
size?: number;
|
||||
exclude?: string;
|
||||
include?: string;
|
||||
}
|
||||
|
||||
export const getSignificantTermsBucketAgg = ({
|
||||
getInternalStartServices,
|
||||
}: SignificantTermsBucketAggDependencies) =>
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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 { aggSignificantTerms } from './significant_terms_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggSignificantTerms', () => {
|
||||
const fn = functionWrapper(aggSignificantTerms());
|
||||
|
||||
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,
|
||||
"exclude": undefined,
|
||||
"field": "machine.os.keyword",
|
||||
"include": undefined,
|
||||
"json": undefined,
|
||||
"size": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "significant_terms",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
id: '1',
|
||||
enabled: false,
|
||||
schema: 'whatever',
|
||||
field: 'machine.os.keyword',
|
||||
size: 6,
|
||||
include: 'win',
|
||||
exclude: 'ios',
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": false,
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"exclude": "ios",
|
||||
"field": "machine.os.keyword",
|
||||
"include": "win",
|
||||
"json": undefined,
|
||||
"size": 6,
|
||||
},
|
||||
"schema": "whatever",
|
||||
"type": "significant_terms",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
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"`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const fnName = 'aggSignificantTerms';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.SIGNIFICANT_TERMS>;
|
||||
|
||||
type Arguments = AggArgs;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggSignificantTerms = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.significantTerms.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a Significant Terms agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
size: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.size.help', {
|
||||
defaultMessage: 'Max number of buckets to retrieve',
|
||||
}),
|
||||
},
|
||||
exclude: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.exclude.help', {
|
||||
defaultMessage: 'Specific bucket values to exclude from results',
|
||||
}),
|
||||
},
|
||||
include: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.include.help', {
|
||||
defaultMessage: 'Specific bucket values to include in results',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.significantTerms.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: BUCKET_TYPES.SIGNIFICANT_TERMS,
|
||||
params: {
|
||||
...rest,
|
||||
json: getParsedValue(args, 'json'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -26,7 +26,7 @@ import {
|
|||
isStringOrNumberType,
|
||||
migrateIncludeExcludeFormat,
|
||||
} from './migrate_include_exclude_format';
|
||||
import { AggConfigSerialized, IAggConfigs } from '../types';
|
||||
import { AggConfigSerialized, BaseAggParams, IAggConfigs } from '../types';
|
||||
|
||||
import { Adapters } from '../../../../../inspector/public';
|
||||
import { ISearchSource } from '../../search_source';
|
||||
|
@ -63,11 +63,11 @@ export interface TermsBucketAggDependencies {
|
|||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsTerms {
|
||||
export interface AggParamsTerms extends BaseAggParams {
|
||||
field: string;
|
||||
order: 'asc' | 'desc';
|
||||
orderBy: string;
|
||||
orderAgg?: AggConfigSerialized;
|
||||
order?: 'asc' | 'desc';
|
||||
size?: number;
|
||||
missingBucket?: boolean;
|
||||
missingBucketLabel?: string;
|
||||
|
@ -76,7 +76,6 @@ export interface AggParamsTerms {
|
|||
// advanced
|
||||
exclude?: string;
|
||||
include?: string;
|
||||
json?: string;
|
||||
}
|
||||
|
||||
export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDependencies) =>
|
||||
|
|
|
@ -27,7 +27,6 @@ describe('agg_expression_functions', () => {
|
|||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'machine.os.keyword',
|
||||
order: 'asc',
|
||||
orderBy: '1',
|
||||
});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
|
@ -37,18 +36,19 @@ describe('agg_expression_functions', () => {
|
|||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"exclude": undefined,
|
||||
"field": "machine.os.keyword",
|
||||
"include": undefined,
|
||||
"json": undefined,
|
||||
"missingBucket": false,
|
||||
"missingBucketLabel": "Missing",
|
||||
"order": "asc",
|
||||
"missingBucket": undefined,
|
||||
"missingBucketLabel": undefined,
|
||||
"order": undefined,
|
||||
"orderAgg": undefined,
|
||||
"orderBy": "1",
|
||||
"otherBucket": false,
|
||||
"otherBucketLabel": "Other",
|
||||
"size": 5,
|
||||
"otherBucket": undefined,
|
||||
"otherBucketLabel": undefined,
|
||||
"size": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "terms",
|
||||
|
@ -70,6 +70,7 @@ describe('agg_expression_functions', () => {
|
|||
missingBucketLabel: 'missing',
|
||||
otherBucket: true,
|
||||
otherBucketLabel: 'other',
|
||||
include: 'win',
|
||||
exclude: 'ios',
|
||||
});
|
||||
|
||||
|
@ -78,9 +79,10 @@ describe('agg_expression_functions', () => {
|
|||
"enabled": false,
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"exclude": "ios",
|
||||
"field": "machine.os.keyword",
|
||||
"include": undefined,
|
||||
"include": "win",
|
||||
"json": undefined,
|
||||
"missingBucket": true,
|
||||
"missingBucketLabel": "missing",
|
||||
|
@ -107,37 +109,39 @@ describe('agg_expression_functions', () => {
|
|||
|
||||
expect(actual.value.params).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"customLabel": undefined,
|
||||
"exclude": undefined,
|
||||
"field": "machine.os.keyword",
|
||||
"include": undefined,
|
||||
"json": undefined,
|
||||
"missingBucket": false,
|
||||
"missingBucketLabel": "Missing",
|
||||
"missingBucket": undefined,
|
||||
"missingBucketLabel": undefined,
|
||||
"order": "asc",
|
||||
"orderAgg": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"customLabel": undefined,
|
||||
"exclude": undefined,
|
||||
"field": "name",
|
||||
"include": undefined,
|
||||
"json": undefined,
|
||||
"missingBucket": false,
|
||||
"missingBucketLabel": "Missing",
|
||||
"missingBucket": undefined,
|
||||
"missingBucketLabel": undefined,
|
||||
"order": "asc",
|
||||
"orderAgg": undefined,
|
||||
"orderBy": "1",
|
||||
"otherBucket": false,
|
||||
"otherBucketLabel": "Other",
|
||||
"size": 5,
|
||||
"otherBucket": undefined,
|
||||
"otherBucketLabel": undefined,
|
||||
"size": undefined,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "terms",
|
||||
},
|
||||
"orderBy": "1",
|
||||
"otherBucket": false,
|
||||
"otherBucketLabel": "Other",
|
||||
"size": 5,
|
||||
"otherBucket": undefined,
|
||||
"otherBucketLabel": undefined,
|
||||
"size": undefined,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -20,27 +20,25 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { Assign } from '@kbn/utility-types';
|
||||
import { ExpressionFunctionDefinition } from '../../../../../expressions/public';
|
||||
import { AggExpressionType, AggExpressionFunctionArgs } from '../';
|
||||
import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../';
|
||||
import { getParsedValue } from '../utils/get_parsed_value';
|
||||
|
||||
const aggName = 'terms';
|
||||
const fnName = 'aggTerms';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof aggName>;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof BUCKET_TYPES.TERMS>;
|
||||
|
||||
// Since the orderAgg param is an agg nested in a subexpression, we need to
|
||||
// overwrite the param type to expect a value of type AggExpressionType.
|
||||
type Arguments = AggArgs &
|
||||
Assign<
|
||||
AggArgs,
|
||||
{ orderAgg?: AggArgs['orderAgg'] extends undefined ? undefined : AggExpressionType }
|
||||
>;
|
||||
type Arguments = Assign<AggArgs, { orderAgg?: AggExpressionType }>;
|
||||
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggTerms = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.terms.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a terms agg',
|
||||
defaultMessage: 'Generates a serialized agg config for a Terms agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
|
@ -72,7 +70,7 @@ export const aggTerms = (): FunctionDefinition => ({
|
|||
},
|
||||
order: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
options: ['asc', 'desc'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.order.help', {
|
||||
defaultMessage: 'Order in which to return the results: asc or desc',
|
||||
}),
|
||||
|
@ -91,41 +89,30 @@ export const aggTerms = (): FunctionDefinition => ({
|
|||
},
|
||||
size: {
|
||||
types: ['number'],
|
||||
default: 5,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.size.help', {
|
||||
defaultMessage: 'Max number of buckets to retrieve',
|
||||
}),
|
||||
},
|
||||
missingBucket: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.missingBucket.help', {
|
||||
defaultMessage: 'When set to true, groups together any buckets with missing fields',
|
||||
}),
|
||||
},
|
||||
missingBucketLabel: {
|
||||
types: ['string'],
|
||||
default: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel', {
|
||||
defaultMessage: 'Missing',
|
||||
description: `Default label used in charts when documents are missing a field.
|
||||
Visible when you create a chart with a terms aggregation and enable "Show missing values"`,
|
||||
}),
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel.help', {
|
||||
defaultMessage: 'Default label used in charts when documents are missing a field.',
|
||||
}),
|
||||
},
|
||||
otherBucket: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.otherBucket.help', {
|
||||
defaultMessage: 'When set to true, groups together any buckets beyond the allowed size',
|
||||
}),
|
||||
},
|
||||
otherBucketLabel: {
|
||||
types: ['string'],
|
||||
default: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel', {
|
||||
defaultMessage: 'Other',
|
||||
}),
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel.help', {
|
||||
defaultMessage: 'Default label used in charts for documents in the Other bucket',
|
||||
}),
|
||||
|
@ -148,32 +135,27 @@ export const aggTerms = (): FunctionDefinition => ({
|
|||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
customLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.customLabel.help', {
|
||||
defaultMessage: 'Represents a custom label for this aggregation',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: (input, args) => {
|
||||
const { id, enabled, schema, ...rest } = args;
|
||||
|
||||
let json;
|
||||
try {
|
||||
json = args.json ? JSON.parse(args.json) : undefined;
|
||||
} catch (e) {
|
||||
throw new Error('Unable to parse json argument string');
|
||||
}
|
||||
|
||||
// Need to spread this object to work around TS bug:
|
||||
// https://github.com/microsoft/TypeScript/issues/15300#issuecomment-436793742
|
||||
const orderAgg = args.orderAgg?.value ? { ...args.orderAgg.value } : undefined;
|
||||
|
||||
return {
|
||||
type: 'agg_type',
|
||||
value: {
|
||||
id,
|
||||
enabled,
|
||||
schema,
|
||||
type: aggName,
|
||||
type: BUCKET_TYPES.TERMS,
|
||||
params: {
|
||||
...rest,
|
||||
orderAgg,
|
||||
json,
|
||||
orderAgg: args.orderAgg?.value,
|
||||
json: getParsedValue(args, 'json'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -21,11 +21,22 @@ import { IndexPattern } from '../../index_patterns';
|
|||
import {
|
||||
AggConfigSerialized,
|
||||
AggConfigs,
|
||||
AggParamsRange,
|
||||
AggParamsIpRange,
|
||||
AggParamsDateRange,
|
||||
AggParamsFilter,
|
||||
AggParamsFilters,
|
||||
AggParamsSignificantTerms,
|
||||
AggParamsGeoTile,
|
||||
AggParamsGeoHash,
|
||||
AggParamsTerms,
|
||||
AggParamsHistogram,
|
||||
AggParamsDateHistogram,
|
||||
AggTypesRegistrySetup,
|
||||
AggTypesRegistryStart,
|
||||
CreateAggConfigParams,
|
||||
getCalculateAutoTimeExpression,
|
||||
BUCKET_TYPES,
|
||||
} from './';
|
||||
|
||||
export { IAggConfig, AggConfigSerialized } from './agg_config';
|
||||
|
@ -55,6 +66,12 @@ export interface SearchAggsStart {
|
|||
types: AggTypesRegistryStart;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface BaseAggParams {
|
||||
json?: string;
|
||||
customLabel?: string;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface AggExpressionType {
|
||||
type: 'agg_type';
|
||||
|
@ -74,5 +91,15 @@ export type AggExpressionFunctionArgs<
|
|||
* @internal
|
||||
*/
|
||||
export interface AggParamsMapping {
|
||||
terms: AggParamsTerms;
|
||||
[BUCKET_TYPES.RANGE]: AggParamsRange;
|
||||
[BUCKET_TYPES.IP_RANGE]: AggParamsIpRange;
|
||||
[BUCKET_TYPES.DATE_RANGE]: AggParamsDateRange;
|
||||
[BUCKET_TYPES.FILTER]: AggParamsFilter;
|
||||
[BUCKET_TYPES.FILTERS]: AggParamsFilters;
|
||||
[BUCKET_TYPES.SIGNIFICANT_TERMS]: AggParamsSignificantTerms;
|
||||
[BUCKET_TYPES.GEOTILE_GRID]: AggParamsGeoTile;
|
||||
[BUCKET_TYPES.GEOHASH_GRID]: AggParamsGeoHash;
|
||||
[BUCKET_TYPES.HISTOGRAM]: AggParamsHistogram;
|
||||
[BUCKET_TYPES.DATE_HISTOGRAM]: AggParamsDateHistogram;
|
||||
[BUCKET_TYPES.TERMS]: AggParamsTerms;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This method parses a JSON string and constructs the Object or object described by the string.
|
||||
* If the given string is not valid JSON, you will get a syntax error.
|
||||
* @param data { Object } - an object that contains the required for parsing field
|
||||
* @param key { string} - name of the field to be parsed
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const getParsedValue = (data: any, key: string) => {
|
||||
try {
|
||||
return data[key] ? JSON.parse(data[key]) : undefined;
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to parse ${key} argument string`);
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue