Convert ui/agg_types/buckets to TypeScript / Jest (#47730) (#48266)

* Convert `ui/agg_types/buckets` to TypeScript / Jest

* fix eslint issues

* fix CI

* fix JEST test

* fix mocha tests

* Fix PR comments
This commit is contained in:
Alexey Antonov 2019-10-15 23:16:50 +03:00 committed by GitHub
parent baeb7d4d25
commit c3b9013082
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 875 additions and 601 deletions

View file

@ -41,7 +41,7 @@ export function buildPhrasesFilter(
indexPattern: IndexPattern
): PhrasesFilter;
export function buildQueryFilter(query: any, index: string): CustomFilter;
export function buildQueryFilter(query: any, index: string, alias?: string): CustomFilter;
export function buildRangeFilter(
field: Field,

View file

@ -28,7 +28,12 @@ import { GeoPolygonFilter, isGeoPolygonFilter } from './geo_polygon_filter';
import { PhraseFilter, isPhraseFilter, isScriptedPhraseFilter } from './phrase_filter';
import { PhrasesFilter, isPhrasesFilter } from './phrases_filter';
import { QueryStringFilter, isQueryStringFilter } from './query_string_filter';
import { RangeFilter, isRangeFilter, isScriptedRangeFilter } from './range_filter';
import {
RangeFilter,
isRangeFilter,
isScriptedRangeFilter,
RangeFilterParams,
} from './range_filter';
import { MatchAllFilter, isMatchAllFilter } from './match_all_filter';
import { MissingFilter, isMissingFilter } from './missing_filter';
@ -50,6 +55,7 @@ export {
RangeFilter,
isRangeFilter,
isScriptedRangeFilter,
RangeFilterParams,
MatchAllFilter,
isMatchAllFilter,
MissingFilter,

View file

@ -19,23 +19,16 @@
import { get, keys } from 'lodash';
import { Filter, FilterMeta } from './meta_filter';
interface FilterRange {
export interface RangeFilterParams {
from?: number | string;
to?: number | string;
}
interface FilterRangeGt {
gt?: number | string;
lt?: number | string;
}
interface FilterRangeGte {
gte?: number | string;
lte?: number | string;
format?: string;
}
export type RangeFilterParams = FilterRange & FilterRangeGt & FilterRangeGte;
export type RangeFilterMeta = FilterMeta & {
params: RangeFilterParams;
field?: any;

View file

@ -38,7 +38,8 @@ describe('Significant Terms Agg', function () {
$rootScope.agg = {
id: 'test',
params: aggParams,
type: significantTerms
type: significantTerms,
getParam: key => aggParams[key],
};
});
}

View file

@ -39,7 +39,8 @@ describe('Terms Agg', function () {
type: terms,
vis: {
aggs: []
}
},
getParam: key => aggParams[key],
};
$rootScope.metricAggs = metricAggs;
$controller(orderAggController, { $scope: $rootScope });

View file

@ -26,20 +26,23 @@
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { AggType, FieldParamType, BucketAggType } from '.';
import { AggType } from './agg_type';
import { FieldParamType } from './param_types/field';
import { AggGroupNames } from '../vis/editors/default/agg_groups';
// @ts-ignore
import { fieldFormats } from '../registry/field_formats';
import { writeParams } from './agg_params';
import { AggConfigs } from './agg_configs';
import { Schema } from '../vis/editors/default/schemas';
import { ContentType } from '../../../../plugins/data/common';
// @ts-ignore
import { fieldFormats } from '../registry/field_formats';
export interface AggConfigOptions {
id: string;
enabled: boolean;
type: string;
schema: string;
params: any;
id?: string;
schema?: string;
}
const unknownSchema: Schema = {
@ -139,7 +142,10 @@ export class AggConfig {
// setters
this.setType(opts.type);
this.setSchema(opts.schema);
if (opts.schema) {
this.setSchema(opts.schema);
}
// set the params to the values from opts, or just to the defaults
this.setParams(opts.params || {});
@ -253,7 +259,7 @@ export class AggConfig {
* @return {void|Object} - if the config has a dsl representation, it is
* returned, else undefined is returned
*/
toDsl(aggConfigs: AggConfigs) {
toDsl(aggConfigs?: AggConfigs) {
if (this.type.hasNoDsl) return;
const output = this.write(aggConfigs) as any;
@ -327,8 +333,8 @@ export class AggConfig {
}
getKey(bucket: any, key: string) {
if (this.type instanceof BucketAggType) {
return (this.type as BucketAggType).getKey(bucket, key, this);
if (this.type.getKey) {
return this.type.getKey(bucket, key, this);
} else {
return '';
}
@ -336,6 +342,7 @@ export class AggConfig {
getFieldDisplayName() {
const field = this.getField();
return field ? field.displayName || this.fieldName() : '';
}
@ -365,13 +372,16 @@ export class AggConfig {
return this.aggConfigs.timeRange;
}
fieldFormatter(contentType: string, defaultFormat: any) {
fieldFormatter(contentType?: ContentType, defaultFormat?: any) {
const format = this.type && this.type.getFormat(this);
if (format) return format.getConverterFor(contentType);
if (format) {
return format.getConverterFor(contentType);
}
return this.fieldOwnFormatter(contentType, defaultFormat);
}
fieldOwnFormatter(contentType: string, defaultFormat: any) {
fieldOwnFormatter(contentType?: ContentType, defaultFormat?: any) {
const field = this.getField();
let format = field && field.format;
if (!format) format = defaultFormat;

View file

@ -20,17 +20,19 @@
import { constant, noop, identity } from 'lodash';
import { i18n } from '@kbn/i18n';
import { AggParam, initParams } from './agg_params';
// @ts-ignore
import { FieldFormat, fieldFormats } from '../registry/field_formats';
import { AggConfig } from '../vis';
import { AggConfigs } from './agg_configs';
import { SearchSource } from '../courier';
import { Adapters } from '../inspector';
import { BaseParamType } from './param_types/base';
// @ts-ignore
import { FieldFormat, fieldFormats } from '../registry/field_formats';
export interface AggTypeConfig<
TAggConfig extends AggConfig = AggConfig,
TParam extends AggParam = TAggConfig['params'][number]
TParam extends AggParam = AggParam
> {
name: string;
title: string;
@ -50,7 +52,8 @@ export interface AggTypeConfig<
aggConfigs: AggConfigs,
aggConfig: TAggConfig,
searchSource: SearchSource,
inspectorAdapters: Adapters
inspectorAdapters: Adapters,
abortSignal?: AbortSignal
) => Promise<any>;
getFormat?: (agg: TAggConfig) => FieldFormat;
getValue?: (agg: TAggConfig, bucket: any) => any;
@ -62,7 +65,7 @@ const getFormat = (agg: AggConfig) => {
return field ? field.format : fieldFormats.getDefaultInstance('string');
};
export class AggType<TAggConfig extends AggConfig = AggConfig> {
export class AggType<TAggConfig extends AggConfig = AggConfig, TParam extends AggParam = AggParam> {
/**
* the unique, unchanging, name that we have assigned this aggType
*
@ -131,7 +134,7 @@ export class AggType<TAggConfig extends AggConfig = AggConfig> {
* @property params
* @type {AggParams}
*/
params: TAggConfig['params'];
params: TParam[];
/**
* Designed for multi-value metric aggs, this method can return a
* set of AggConfigs that should replace this aggConfig in requests
@ -188,6 +191,8 @@ export class AggType<TAggConfig extends AggConfig = AggConfig> {
getValue: (agg: TAggConfig, bucket: any) => any;
getKey?: (bucket: any, key: any, agg: TAggConfig) => any;
paramByName = (name: string) => {
return this.params.find((p: AggParam) => p.name === name);
};
@ -215,7 +220,7 @@ export class AggType<TAggConfig extends AggConfig = AggConfig> {
}
if (config.params && config.params.length && config.params[0] instanceof BaseParamType) {
this.params = config.params as TAggConfig['params'];
this.params = config.params as TParam[];
} else {
// always append the raw JSON param
const params: any[] = config.params ? [...config.params] : [];

View file

@ -17,13 +17,32 @@
* under the License.
*/
import { AggType, AggTypeConfig } from '../agg_type';
import { AggParamType } from '../param_types/agg';
import { AggConfig } from '../../vis';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
import { AggType, AggTypeConfig } from '../agg_type';
export class BucketAggType extends AggType {
getKey: (bucket: any, key: any, agg: AggConfig) => any;
export type IBucketAggConfig = AggConfig;
constructor(config: AggTypeConfig) {
export interface BucketAggParam extends AggParamType {
scriptable?: boolean;
filterFieldTypes?: KBN_FIELD_TYPES | KBN_FIELD_TYPES[] | '*';
}
export interface BucketAggTypeConfig<TBucketAggConfig extends IBucketAggConfig>
extends AggTypeConfig<TBucketAggConfig, BucketAggParam> {
getKey?: (bucket: any, key: any, agg: AggConfig) => any;
}
const bucketType = 'buckets';
export class BucketAggType<
TBucketAggConfig extends IBucketAggConfig = IBucketAggConfig
> extends AggType<TBucketAggConfig, BucketAggParam> {
getKey: (bucket: any, key: any, agg: IBucketAggConfig) => any;
type = bucketType;
constructor(config: BucketAggTypeConfig<TBucketAggConfig>) {
super(config);
this.getKey =
@ -33,3 +52,7 @@ export class BucketAggType extends AggType {
});
}
}
export function isBucketAggType(aggConfig: any): aggConfig is BucketAggType {
return aggConfig && aggConfig.type === bucketType;
}

View file

@ -17,6 +17,7 @@
* under the License.
*/
import { i18n } from '@kbn/i18n';
import { IBucketAggConfig } from './_bucket_agg_type';
export const intervalOptions = [
{
@ -24,58 +25,58 @@ export const intervalOptions = [
defaultMessage: 'Auto',
}),
val: 'auto',
enabled: function (agg) {
enabled(agg: IBucketAggConfig) {
// not only do we need a time field, but the selected field needs
// to be the time field. (see #3028)
return agg.fieldIsTimeField();
}
},
},
{
display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.millisecondDisplayName', {
defaultMessage: 'Millisecond',
}),
val: 'ms'
val: 'ms',
},
{
display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.secondDisplayName', {
defaultMessage: 'Second',
}),
val: 's'
val: 's',
},
{
display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.minuteDisplayName', {
defaultMessage: 'Minute',
}),
val: 'm'
val: 'm',
},
{
display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.hourlyDisplayName', {
defaultMessage: 'Hourly',
}),
val: 'h'
val: 'h',
},
{
display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.dailyDisplayName', {
defaultMessage: 'Daily',
}),
val: 'd'
val: 'd',
},
{
display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.weeklyDisplayName', {
defaultMessage: 'Weekly',
}),
val: 'w'
val: 'w',
},
{
display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.monthlyDisplayName', {
defaultMessage: 'Monthly',
}),
val: 'M'
val: 'M',
},
{
display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.yearlyDisplayName', {
defaultMessage: 'Yearly',
}),
val: 'y'
}
val: 'y',
},
];

View file

@ -59,7 +59,9 @@ const getAggResultBuckets = (aggConfigs, response, aggWithOtherBucket, key) => {
const aggKey = _.keys(responseAgg)[aggId];
const aggConfig = _.find(aggConfigs.aggs, agg => agg.id === aggKey);
const bucket = _.find(agg.buckets, (bucket, bucketObjKey) => {
const bucketKey = aggConfig.getKey(bucket, Number.isInteger(bucketObjKey) ? null : bucketObjKey).toString();
const bucketKey = aggConfig
.getKey(bucket, Number.isInteger(bucketObjKey) ? null : bucketObjKey)
.toString();
return bucketKey === keyParts[i];
});
if (bucket) {
@ -93,7 +95,7 @@ const getAggConfigResultMissingBuckets = (responseAggs, aggId) => {
_.each(agg.buckets, bucket => {
resultBuckets = [
...resultBuckets,
...getAggConfigResultMissingBuckets(bucket, aggId, missingKey)
...getAggConfigResultMissingBuckets(bucket, aggId, missingKey),
];
});
}
@ -109,14 +111,11 @@ const getAggConfigResultMissingBuckets = (responseAggs, aggId) => {
* @param otherAgg: AggConfig of the aggregation with other bucket
*/
const getOtherAggTerms = (requestAgg, key, otherAgg) => {
return requestAgg['other-filter'].filters.filters[key].bool.must_not.filter(filter =>
filter.match_phrase && filter.match_phrase[otherAgg.params.field.name]
).map(filter =>
filter.match_phrase[otherAgg.params.field.name].query
);
return requestAgg['other-filter'].filters.filters[key].bool.must_not
.filter(filter => filter.match_phrase && filter.match_phrase[otherAgg.params.field.name])
.map(filter => filter.match_phrase[otherAgg.params.field.name].query);
};
export const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) => {
const bucketAggs = aggConfigs.aggs.filter(agg => agg.type.type === AggGroupNames.Buckets);
const index = bucketAggs.findIndex(agg => agg.id === aggWithOtherBucket.id);
@ -124,15 +123,18 @@ export const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) =>
const indexPattern = aggWithOtherBucket.params.field.indexPattern;
// create filters aggregation
const filterAgg = aggConfigs.createAggConfig({
type: 'filters',
id: 'other',
params: {
filters: [],
const filterAgg = aggConfigs.createAggConfig(
{
type: 'filters',
id: 'other',
params: {
filters: [],
},
},
}, {
addToAggConfigs: false,
});
{
addToAggConfigs: false,
}
);
// nest all the child aggregations of aggWithOtherBucket
const resultAgg = {
@ -156,16 +158,33 @@ export const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) =>
const currentAgg = bucketAggs[aggIndex];
if (aggIndex < index) {
_.each(agg.buckets, (bucket, bucketObjKey) => {
const bucketKey = currentAgg.getKey(bucket, Number.isInteger(bucketObjKey) ? null : bucketObjKey);
const bucketKey = currentAgg.getKey(
bucket,
Number.isInteger(bucketObjKey) ? null : bucketObjKey
);
const filter = _.cloneDeep(bucket.filters) || currentAgg.createFilter(bucketKey);
const newFilters = _.flatten([...filters, filter]);
walkBucketTree(newAggIndex, bucket, newAgg.id, newFilters, `${key}-${bucketKey.toString()}`);
walkBucketTree(
newAggIndex,
bucket,
newAgg.id,
newFilters,
`${key}-${bucketKey.toString()}`
);
});
return;
}
if (!aggWithOtherBucket.params.missingBucket || agg.buckets.some(bucket => bucket.key === '__missing__')) {
filters.push(buildExistsFilter(aggWithOtherBucket.params.field, aggWithOtherBucket.params.field.indexPattern));
if (
!aggWithOtherBucket.params.missingBucket ||
agg.buckets.some(bucket => bucket.key === '__missing__')
) {
filters.push(
buildExistsFilter(
aggWithOtherBucket.params.field,
aggWithOtherBucket.params.field.indexPattern
)
);
}
// create not filters for all the buckets
@ -177,7 +196,7 @@ export const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) =>
});
resultAgg.filters.filters[key] = {
bool: buildQueryFromFilters(filters, indexPattern)
bool: buildQueryFromFilters(filters, indexPattern),
};
};
walkBucketTree(0, response.aggregations, bucketAggs[0].id, [], '');
@ -189,26 +208,43 @@ export const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) =>
return () => {
return {
'other-filter': resultAgg
'other-filter': resultAgg,
};
};
};
export const mergeOtherBucketAggResponse = (aggsConfig, response, otherResponse, otherAgg, requestAgg) => {
export const mergeOtherBucketAggResponse = (
aggsConfig,
response,
otherResponse,
otherAgg,
requestAgg
) => {
const updatedResponse = _.cloneDeep(response);
_.each(otherResponse.aggregations['other-filter'].buckets, (bucket, key) => {
if (!bucket.doc_count) return;
const bucketKey = key.replace(/^-/, '');
const aggResultBuckets = getAggResultBuckets(aggsConfig, updatedResponse.aggregations, otherAgg, bucketKey);
const aggResultBuckets = getAggResultBuckets(
aggsConfig,
updatedResponse.aggregations,
otherAgg,
bucketKey
);
const requestFilterTerms = getOtherAggTerms(requestAgg, key, otherAgg);
const phraseFilter = buildPhrasesFilter(otherAgg.params.field, requestFilterTerms, otherAgg.params.field.indexPattern);
const phraseFilter = buildPhrasesFilter(
otherAgg.params.field,
requestFilterTerms,
otherAgg.params.field.indexPattern
);
phraseFilter.meta.negate = true;
bucket.filters = [ phraseFilter ];
bucket.filters = [phraseFilter];
bucket.key = '__other__';
if (aggResultBuckets.some(bucket => bucket.key === '__missing__')) {
bucket.filters.push(buildExistsFilter(otherAgg.params.field, otherAgg.params.field.indexPattern));
bucket.filters.push(
buildExistsFilter(otherAgg.params.field, otherAgg.params.field.indexPattern)
);
}
aggResultBuckets.push(bucket);
});

View file

@ -17,7 +17,15 @@
* under the License.
*/
import { AggConfig } from 'ui/agg_types/agg_config';
export function isStringType(type: AggConfig): boolean;
export function isType(type: string): (agg: AggConfig) => boolean;
export enum BUCKET_TYPES {
FILTER = 'filter',
HISTOGRAM = 'histogram',
IP_RANGE = 'ip_range',
DATE_RANGE = 'date_range',
RANGE = 'range',
TERMS = 'terms',
SIGNIFICANT_TERMS = 'significant_terms',
GEOHASH_GRID = 'geohash_grid',
GEOTILE_GRID = 'geotile_grid',
DATE_HISTOGRAM = 'date_histogram',
}

View file

@ -19,14 +19,19 @@
import moment from 'moment';
import { buildRangeFilter } from '@kbn/es-query';
import { IBucketDateHistogramAggConfig } from '../date_histogram';
export function createFilterDateHistogram(agg, key) {
export const createFilterDateHistogram = (agg: IBucketDateHistogramAggConfig, key: string) => {
const start = moment(key);
const interval = agg.buckets.getInterval();
return buildRangeFilter(agg.params.field, {
gte: start.toISOString(),
lt: start.add(interval).toISOString(),
format: 'strict_date_optional_time'
}, agg.getIndexPattern());
}
return buildRangeFilter(
agg.params.field,
{
gte: start.toISOString(),
lt: start.add(interval).toISOString(),
format: 'strict_date_optional_time',
},
agg.getIndexPattern()
);
};

View file

@ -17,19 +17,20 @@
* under the License.
*/
import chrome from '../../../chrome';
import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query';
import { npSetup } from 'ui/new_platform';
import { IBucketAggConfig } from '../_bucket_agg_type';
// @ts-ignore
import { dateRange } from '../../../utils/date_range';
import { buildRangeFilter } from '@kbn/es-query';
const config = chrome.getUiSettingsClient();
export const createFilterDateRange = (agg: IBucketAggConfig, rangeString: string) => {
const range = dateRange.parse(rangeString, npSetup.core.uiSettings.get('dateFormat'));
export function createFilterDateRange(agg, key) {
const range = dateRange.parse(key, config.get('dateFormat'));
const filter = {};
const filter: RangeFilterParams = {};
if (range.from) filter.gte = range.from.toISOString();
if (range.to) filter.lt = range.to.toISOString();
if (range.to && range.from) filter.format = 'strict_date_optional_time';
return buildRangeFilter(agg.params.field, filter, agg.getIndexPattern());
}
};

View file

@ -17,15 +17,17 @@
* under the License.
*/
import { get } from 'lodash';
import { buildQueryFilter } from '@kbn/es-query';
import _ from 'lodash';
import { IBucketAggConfig } from '../_bucket_agg_type';
export function createFilterFilters(aggConfig, key) {
export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) => {
// have the aggConfig write agg dsl params
const dslFilters = _.get(aggConfig.toDsl(), 'filters.filters');
const dslFilters: any = get(aggConfig.toDsl(), 'filters.filters');
const filter = dslFilters[key];
const indexPattern = aggConfig.getIndexPattern();
if (filter) {
return buildQueryFilter(filter.query, aggConfig.getIndexPattern().id, key);
if (filter && indexPattern && indexPattern.id) {
return buildQueryFilter(filter.query, indexPattern.id, key);
}
}
};

View file

@ -17,15 +17,17 @@
* under the License.
*/
import { buildRangeFilter } from '@kbn/es-query';
import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query';
import { IBucketAggConfig } from '../_bucket_agg_type';
export function createFilterHistogram(aggConfig, key) {
export const createFilterHistogram = (aggConfig: IBucketAggConfig, key: string) => {
const value = parseInt(key, 10);
const params: RangeFilterParams = { gte: value, lt: value + aggConfig.params.interval };
return buildRangeFilter(
aggConfig.params.field,
{ gte: value, lt: value + aggConfig.params.interval },
params,
aggConfig.getIndexPattern(),
aggConfig.fieldFormatter()(key)
);
}
};

View file

@ -17,20 +17,27 @@
* under the License.
*/
import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query';
import { CidrMask } from '../../../utils/cidr_mask';
import { buildRangeFilter } from '@kbn/es-query';
import { IBucketAggConfig } from '../_bucket_agg_type';
export const createFilterIpRange = (aggConfig: IBucketAggConfig, key: string) => {
let range: RangeFilterParams;
export function createFilterIpRange(aggConfig, key) {
let range;
if (aggConfig.params.ipRangeType === 'mask') {
range = new CidrMask(key).getRange();
} else {
const [from, to] = key.split(/\s+to\s+/);
range = {
from: from === '-Infinity' ? -Infinity : from,
to: to === 'Infinity' ? Infinity : to
to: to === 'Infinity' ? Infinity : to,
};
}
return buildRangeFilter(aggConfig.params.field, { gte: range.from, lte: range.to }, aggConfig.getIndexPattern());
}
return buildRangeFilter(
aggConfig.params.field,
{ gte: range.from, lte: range.to },
aggConfig.getIndexPattern()
);
};

View file

@ -18,12 +18,13 @@
*/
import { buildRangeFilter } from '@kbn/es-query';
import { IBucketAggConfig } from '../_bucket_agg_type';
export function createFilterRange(aggConfig, key) {
export const createFilterRange = (aggConfig: IBucketAggConfig, params: any) => {
return buildRangeFilter(
aggConfig.params.field,
key,
params,
aggConfig.getIndexPattern(),
aggConfig.fieldFormatter()(key)
aggConfig.fieldFormatter()(params)
);
}
};

View file

@ -17,9 +17,10 @@
* under the License.
*/
import { buildPhraseFilter, buildPhrasesFilter, buildExistsFilter } from '@kbn/es-query';
import { Filter, buildPhraseFilter, buildPhrasesFilter, buildExistsFilter } from '@kbn/es-query';
import { IBucketAggConfig } from '../_bucket_agg_type';
export function createFilterTerms(aggConfig, key, params) {
export const createFilterTerms = (aggConfig: IBucketAggConfig, key: string, params: any) => {
const field = aggConfig.params.field;
const indexPattern = field.indexPattern;
@ -29,9 +30,9 @@ export function createFilterTerms(aggConfig, key, params) {
const phraseFilter = buildPhrasesFilter(field, terms, indexPattern);
phraseFilter.meta.negate = true;
const filters = [phraseFilter];
const filters: Filter[] = [phraseFilter];
if (terms.some(term => term === '__missing__')) {
if (terms.some((term: string) => term === '__missing__')) {
filters.push(buildExistsFilter(field, indexPattern));
}
@ -42,4 +43,4 @@ export function createFilterTerms(aggConfig, key, params) {
return existsFilter;
}
return buildPhraseFilter(field, key, indexPattern);
}
};

View file

@ -18,10 +18,11 @@
*/
import _ from 'lodash';
import chrome from '../../chrome';
import moment from 'moment-timezone';
import { BucketAggType } from './_bucket_agg_type';
import { TimeBuckets } from '../../time_buckets';
import { i18n } from '@kbn/i18n';
import { BUCKET_TYPES } from 'ui/agg_types/buckets/bucket_agg_types';
import chrome from '../../chrome';
import { BucketAggParam, BucketAggType, IBucketAggConfig } from './_bucket_agg_type';
import { createFilterDateHistogram } from './create_filter/date_histogram';
import { intervalOptions } from './_interval_options';
import { TimeIntervalParamEditor } from '../../vis/editors/default/controls/time_interval';
@ -29,51 +30,72 @@ import { timefilter } from '../../timefilter';
import { DropPartialsParamEditor } from '../../vis/editors/default/controls/drop_partials';
import { ScaleMetricsParamEditor } from '../../vis/editors/default/controls/scale_metrics';
import { dateHistogramInterval } from '../../../../core_plugins/data/public';
import { i18n } from '@kbn/i18n';
import { writeParams } from '../agg_params';
import { AggConfigs } from '../agg_configs';
import { AggConfig } from '../agg_config';
import { isMetricAggType } from '../metrics/metric_agg_type';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
// @ts-ignore
import { TimeBuckets } from '../../time_buckets';
const config = chrome.getUiSettingsClient();
const detectedTimezone = moment.tz.guess();
const tzOffset = moment().format('Z');
function getInterval(agg) {
return _.get(agg, ['params', 'interval']);
}
const getInterval = (agg: IBucketAggConfig): string => _.get(agg, ['params', 'interval']);
export function setBounds(agg, force) {
export const setBounds = (agg: IBucketDateHistogramAggConfig, force?: boolean) => {
if (agg.buckets._alreadySet && !force) return;
agg.buckets._alreadySet = true;
const bounds = agg.params.timeRange ? timefilter.calculateBounds(agg.params.timeRange) : null;
agg.buckets.setBounds(agg.fieldIsTimeField() && bounds);
};
// will be replaced by src/legacy/ui/public/time_buckets/time_buckets.js
interface TimeBuckets {
_alreadySet?: boolean;
setBounds: Function;
getScaledDateFormatter: Function;
setInterval: Function;
getInterval: Function;
}
export interface IBucketDateHistogramAggConfig extends IBucketAggConfig {
buckets: TimeBuckets;
}
export const dateHistogramBucketAgg = new BucketAggType({
name: 'date_histogram',
export function isDateHistogramBucketAggConfig(agg: any): agg is IBucketDateHistogramAggConfig {
return Boolean(agg.buckets);
}
export const dateHistogramBucketAgg = new BucketAggType<IBucketDateHistogramAggConfig>({
name: BUCKET_TYPES.DATE_HISTOGRAM,
title: i18n.translate('common.ui.aggTypes.buckets.dateHistogramTitle', {
defaultMessage: 'Date Histogram',
}),
ordered: {
date: true
date: true,
},
makeLabel: function (agg) {
const output = writeParams(this.params, agg);
makeLabel(agg) {
const output: Record<string, any> = writeParams(this.params as BucketAggParam[], agg);
const field = agg.getFieldDisplayName();
return i18n.translate('common.ui.aggTypes.buckets.dateHistogramLabel', {
defaultMessage: '{fieldName} per {intervalDescription}',
values: {
fieldName: field,
intervalDescription: output.metricScaleText || output.bucketInterval.description,
}
},
});
},
createFilter: createFilterDateHistogram,
decorateAggConfig: function () {
let buckets;
decorateAggConfig() {
let buckets: any;
return {
buckets: {
configurable: true,
get: function () {
get() {
if (buckets) return buckets;
buckets = new TimeBuckets();
@ -81,28 +103,28 @@ export const dateHistogramBucketAgg = new BucketAggType({
setBounds(this);
return buckets;
}
}
},
},
};
},
getFormat: function (agg) {
getFormat(agg) {
return agg.buckets.getScaledDateFormatter();
},
params: [
{
name: 'field',
type: 'field',
filterFieldTypes: 'date',
default: function (agg) {
filterFieldTypes: KBN_FIELD_TYPES.DATE,
default(agg: IBucketDateHistogramAggConfig) {
return agg.getIndexPattern().timeFieldName;
},
onChange: function (agg) {
onChange(agg: IBucketDateHistogramAggConfig) {
if (_.get(agg, 'params.interval') === 'auto' && !agg.fieldIsTimeField()) {
delete agg.params.interval;
}
setBounds(agg, true);
}
},
},
{
name: 'timeRange',
@ -124,7 +146,7 @@ export const dateHistogramBucketAgg = new BucketAggType({
{
name: 'interval',
editorComponent: TimeIntervalParamEditor,
deserialize: function (state, agg) {
deserialize(state: any, agg: IBucketDateHistogramAggConfig) {
// For upgrading from 7.0.x to 7.1.x - intervals are now stored as key of options or custom value
if (state === 'custom') {
return _.get(agg, 'params.customInterval');
@ -141,10 +163,10 @@ export const dateHistogramBucketAgg = new BucketAggType({
},
default: 'auto',
options: intervalOptions,
modifyAggConfigOnSearchRequestStart: function (agg) {
modifyAggConfigOnSearchRequestStart(agg: IBucketDateHistogramAggConfig) {
setBounds(agg, true);
},
write: function (agg, output, aggs) {
write(agg: IBucketDateHistogramAggConfig, output: Record<string, any>, aggs: AggConfigs) {
setBounds(agg, true);
agg.buckets.setInterval(getInterval(agg));
const { useNormalizedEsInterval, scaleMetricValues } = agg.params;
@ -166,31 +188,41 @@ export const dateHistogramBucketAgg = new BucketAggType({
const scaleMetrics = scaleMetricValues && interval.scaled && interval.scale < 1;
if (scaleMetrics && aggs) {
const metrics = aggs.aggs.filter(agg => agg.type && agg.type.type === 'metrics');
const all = _.every(metrics, function (agg) {
return agg.type.isScalable();
const metrics = aggs.aggs.filter(a => isMetricAggType(a.type));
const all = _.every(metrics, (a: AggConfig) => {
const { type } = a;
if (isMetricAggType(type)) {
return type.isScalable();
}
});
if (all) {
output.metricScale = interval.scale;
output.metricScaleText = interval.preScaled.description;
}
}
}
},
},
} as BucketAggParam,
{
name: 'time_zone',
default: undefined,
// We don't ever want this parameter to be serialized out (when saving or to URLs)
// since we do all the logic handling it "on the fly" in the `write` method, to prevent
// time_zones being persisted into saved_objects
serialize: () => undefined,
write: (agg, output) => {
serialize: _.noop,
write(agg: IBucketDateHistogramAggConfig, output: Record<string, any>) {
// If a time_zone has been set explicitly always prefer this.
let tz = agg.params.time_zone;
if (!tz && agg.params.field) {
// If a field has been configured check the index pattern's typeMeta if a date_histogram on that
// field requires a specific time_zone
tz = _.get(agg.getIndexPattern(), ['typeMeta', 'aggs', 'date_histogram', agg.params.field.name, 'time_zone']);
tz = _.get(agg.getIndexPattern(), [
'typeMeta',
'aggs',
'date_histogram',
agg.params.field.name,
'time_zone',
]);
}
if (!tz) {
// If the index pattern typeMeta data, didn't had a time zone assigned for the selected field use the configured tz
@ -199,7 +231,7 @@ export const dateHistogramBucketAgg = new BucketAggType({
}
output.params.time_zone = tz;
},
},
} as BucketAggParam,
{
name: 'drop_partials',
default: false,
@ -211,29 +243,27 @@ export const dateHistogramBucketAgg = new BucketAggType({
},
},
{
name: 'format'
name: 'format',
},
{
name: 'min_doc_count',
default: 1
default: 1,
},
{
name: 'extended_bounds',
default: {},
write: function (agg, output) {
write(agg: IBucketDateHistogramAggConfig, output: Record<string, any>) {
const val = agg.params.extended_bounds;
if (val.min != null || val.max != null) {
output.params.extended_bounds = {
min: moment(val.min).valueOf(),
max: moment(val.max).valueOf()
max: moment(val.max).valueOf(),
};
return;
}
}
}
]
},
} as BucketAggParam,
],
});

View file

@ -1,80 +0,0 @@
/*
* 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 { get } from 'lodash';
import chrome from '../../chrome';
import moment from 'moment-timezone';
import { dateRange } from '../../utils/date_range';
import { BucketAggType } from './_bucket_agg_type';
import { createFilterDateRange } from './create_filter/date_range';
import { fieldFormats } from '../../registry/field_formats';
import { DateRangesParamEditor } from '../../vis/editors/default/controls/date_ranges';
import { i18n } from '@kbn/i18n';
const config = chrome.getUiSettingsClient();
const detectedTimezone = moment.tz.guess();
const tzOffset = moment().format('Z');
export const dateRangeBucketAgg = new BucketAggType({
name: 'date_range',
title: i18n.translate('common.ui.aggTypes.buckets.dateRangeTitle', {
defaultMessage: 'Date Range',
}),
createFilter: createFilterDateRange,
getKey: function (bucket, key, agg) {
const formatter = agg.fieldOwnFormatter('text', fieldFormats.getDefaultInstance('date'));
return dateRange.toString(bucket, formatter);
},
getFormat: function () {
return fieldFormats.getDefaultInstance('string');
},
makeLabel: function (aggConfig) {
return aggConfig.getFieldDisplayName() + ' date ranges';
},
params: [{
name: 'field',
type: 'field',
filterFieldTypes: 'date',
default: function (agg) {
return agg.getIndexPattern().timeFieldName;
}
}, {
name: 'ranges',
default: [{
from: 'now-1w/w',
to: 'now'
}],
editorComponent: DateRangesParamEditor,
}, {
name: 'time_zone',
default: undefined,
// Implimentation method is the same as that of date_histogram
serialize: () => undefined,
write: (agg, output) => {
let tz = agg.params.time_zone;
if (!tz && agg.params.field) {
tz = get(agg.getIndexPattern(), ['typeMeta', 'aggs', 'date_range', agg.params.field.name, 'time_zone']);
}
if (!tz) {
const isDefaultTimezone = config.isDefault('dateFormat:tz');
tz = isDefaultTimezone ? detectedTimezone || tzOffset : config.get('dateFormat:tz');
}
output.params.time_zone = tz;
}
}]
});

View 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 { get } from 'lodash';
import moment from 'moment-timezone';
import { i18n } from '@kbn/i18n';
import { npStart } from 'ui/new_platform';
import { BUCKET_TYPES } from './bucket_agg_types';
import { BucketAggType } from './_bucket_agg_type';
import { createFilterDateRange } from './create_filter/date_range';
import { AggConfig } from '../agg_config';
import { DateRangesParamEditor } from '../../vis/editors/default/controls/date_ranges';
// @ts-ignore
import { fieldFormats } from '../../registry/field_formats';
// @ts-ignore
import { dateRange } from '../../utils/date_range';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
const dateRangeTitle = i18n.translate('common.ui.aggTypes.buckets.dateRangeTitle', {
defaultMessage: 'Date Range',
});
export const dateRangeBucketAgg = new BucketAggType({
name: BUCKET_TYPES.DATE_RANGE,
title: dateRangeTitle,
createFilter: createFilterDateRange,
getKey(bucket, key, agg) {
const formatter = agg.fieldOwnFormatter('text', fieldFormats.getDefaultInstance('date'));
return dateRange.toString(bucket, formatter);
},
getFormat() {
return fieldFormats.getDefaultInstance('string');
},
makeLabel(aggConfig) {
return aggConfig.getFieldDisplayName() + ' date ranges';
},
params: [
{
name: 'field',
type: 'field',
filterFieldTypes: KBN_FIELD_TYPES.DATE,
default(agg: AggConfig) {
return agg.getIndexPattern().timeFieldName;
},
},
{
name: 'ranges',
default: [
{
from: 'now-1w/w',
to: 'now',
},
],
editorComponent: DateRangesParamEditor,
},
{
name: 'time_zone',
default: undefined,
// Implimentation method is the same as that of date_histogram
serialize: () => undefined,
write: (agg: AggConfig, output: Record<string, any>) => {
const field = agg.getParam('field');
let tz = agg.getParam('time_zone');
if (!tz && field) {
tz = get(agg.getIndexPattern(), [
'typeMeta',
'aggs',
'date_range',
field.name,
'time_zone',
]);
}
if (!tz) {
const config = npStart.core.uiSettings;
const detectedTimezone = moment.tz.guess();
const tzOffset = moment().format('Z');
const isDefaultTimezone = config.isDefault('dateFormat:tz');
tz = isDefaultTimezone ? detectedTimezone || tzOffset : config.get('dateFormat:tz');
}
output.params.time_zone = tz;
},
},
],
});

View file

@ -16,18 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
import { BucketAggType } from './_bucket_agg_type';
import { i18n } from '@kbn/i18n';
import { BucketAggType } from './_bucket_agg_type';
import { BUCKET_TYPES } from './bucket_agg_types';
const filterTitle = i18n.translate('common.ui.aggTypes.buckets.filterTitle', {
defaultMessage: 'Filter',
});
export const filterBucketAgg = new BucketAggType({
name: 'filter',
title: i18n.translate('common.ui.aggTypes.buckets.filterTitle', {
defaultMessage: 'Filter',
}),
name: BUCKET_TYPES.FILTER,
title: filterTitle,
params: [
{
name: 'geo_bounding_box'
}
]
name: 'geo_bounding_box',
},
],
});

View file

@ -20,69 +20,79 @@
import _ from 'lodash';
import angular from 'angular';
import { BucketAggType } from './_bucket_agg_type';
import { createFilterFilters } from './create_filter/filters';
import { FiltersParamEditor } from '../../vis/editors/default/controls/filters';
import { i18n } from '@kbn/i18n';
import { Storage } from 'ui/storage';
import chrome from 'ui/chrome';
import { buildEsQuery } from '@kbn/es-query';
import { FiltersParamEditor, FilterValue } from '../../vis/editors/default/controls/filters';
import { createFilterFilters } from './create_filter/filters';
import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type';
import { setup as data } from '../../../../core_plugins/data/public/legacy';
const { getQueryLog } = data.query.helpers;
const config = chrome.getUiSettingsClient();
const storage = new Storage(window.localStorage);
const filtersTitle = i18n.translate('common.ui.aggTypes.buckets.filtersTitle', {
defaultMessage: 'Filters',
description:
'The name of an aggregation, that allows to specify multiple individual filters to group data by.',
});
export const filtersBucketAgg = new BucketAggType({
name: 'filters',
title: i18n.translate('common.ui.aggTypes.buckets.filtersTitle', {
defaultMessage: 'Filters',
description: 'The name of an aggregation, that allows to specify multiple individual filters to group data by.'
}),
title: filtersTitle,
createFilter: createFilterFilters,
customLabels: false,
params: [
{
name: 'filters',
editorComponent: FiltersParamEditor,
default: [ { input: { query: '', language: config.get('search:queryLanguage') }, label: '' } ],
write: function (aggConfig, output) {
const inFilters = aggConfig.params.filters;
default: [{ input: { query: '', language: config.get('search:queryLanguage') }, label: '' }],
write(aggConfig: IBucketAggConfig, output: Record<string, any>) {
const inFilters: FilterValue[] = aggConfig.params.filters;
if (!_.size(inFilters)) return;
inFilters.forEach((filter) => {
inFilters.forEach(filter => {
const persistedLog = getQueryLog(config, storage, 'filtersAgg', filter.input.language);
persistedLog.add(filter.input.query);
});
const outFilters = _.transform(inFilters, function (filters, filter) {
const input = _.cloneDeep(filter.input);
const outFilters = _.transform(
inFilters,
function(filters, filter) {
const input = _.cloneDeep(filter.input);
if (!input) {
console.log('malformed filter agg params, missing "input" query'); // eslint-disable-line no-console
return;
}
if (!input) {
console.log('malformed filter agg params, missing "input" query'); // eslint-disable-line no-console
return;
}
const query = buildEsQuery(aggConfig.getIndexPattern(), [input], [], config);
const query = buildEsQuery(aggConfig.getIndexPattern(), [input], [], config);
if (!query) {
console.log('malformed filter agg params, missing "query" on input'); // eslint-disable-line no-console
return;
}
if (!query) {
console.log('malformed filter agg params, missing "query" on input'); // eslint-disable-line no-console
return;
}
const matchAllLabel = filter.input.query === '' ? '*' : '';
const label = filter.label
|| matchAllLabel
|| (typeof filter.input.query === 'string' ? filter.input.query : angular.toJson(filter.input.query));
filters[label] = { query };
}, {});
const matchAllLabel = filter.input.query === '' ? '*' : '';
const label =
filter.label ||
matchAllLabel ||
(typeof filter.input.query === 'string'
? filter.input.query
: angular.toJson(filter.input.query));
filters[label] = { query };
},
{}
);
if (!_.size(outFilters)) return;
const params = output.params || (output.params = {});
params.filters = outFilters;
}
}
]
},
},
],
});

View file

@ -17,16 +17,21 @@
* under the License.
*/
import { i18n } from '@kbn/i18n';
import chrome from '../../chrome';
import { BucketAggType } from './_bucket_agg_type';
import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type';
import { AutoPrecisionParamEditor } from '../../vis/editors/default/controls/auto_precision';
import { UseGeocentroidParamEditor } from '../../vis/editors/default/controls/use_geocentroid';
import { IsFilteredByCollarParamEditor } from '../../vis/editors/default/controls/is_filtered_by_collar';
import { PrecisionParamEditor } from '../../vis/editors/default/controls/precision';
import { geohashColumns } from '../../utils/decode_geo_hash';
import { geoContains, scaleBounds } from '../../utils/geo_utils';
import { i18n } from '@kbn/i18n';
import { AggGroupNames } from '../../vis/editors/default/agg_groups';
import { AggConfig } from '../agg_config';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
// @ts-ignore
import { geoContains, scaleBounds } from '../../utils/geo_utils';
import { BUCKET_TYPES } from './bucket_agg_types';
const config = chrome.getUiSettingsClient();
@ -36,14 +41,15 @@ const maxPrecision = parseInt(config.get('visualization:tileMap:maxPrecision'),
* Map Leaflet zoom levels to geohash precision levels.
* The size of a geohash column-width on the map should be at least `minGeohashPixels` pixels wide.
*/
const zoomPrecision = {};
const zoomPrecision: any = {};
const minGeohashPixels = 16;
for (let zoom = 0; zoom <= 21; zoom += 1) {
const worldPixels = 256 * Math.pow(2, zoom);
zoomPrecision[zoom] = 1;
for (let precision = 2; precision <= maxPrecision; precision += 1) {
const columns = geohashColumns(precision);
if ((worldPixels / columns) >= minGeohashPixels) {
if (worldPixels / columns >= minGeohashPixels) {
zoomPrecision[zoom] = precision;
} else {
break;
@ -51,11 +57,10 @@ for (let zoom = 0; zoom <= 21; zoom += 1) {
}
}
function getPrecision(precision) {
function getPrecision(val: string) {
let precision = parseInt(val, 10);
precision = parseInt(precision, 10);
if (isNaN(precision)) {
if (Number.isNaN(precision)) {
precision = defaultPrecision;
}
@ -66,20 +71,29 @@ function getPrecision(precision) {
return precision;
}
function isOutsideCollar(bounds, collar) {
return bounds && collar && !geoContains(collar, bounds);
const isOutsideCollar = (bounds: unknown, collar: MapCollar) =>
bounds && collar && !geoContains(collar, bounds);
const geohashGridTitle = i18n.translate('common.ui.aggTypes.buckets.geohashGridTitle', {
defaultMessage: 'Geohash',
});
interface MapCollar {
zoom: unknown;
}
export const geoHashBucketAgg = new BucketAggType({
name: 'geohash_grid',
title: i18n.translate('common.ui.aggTypes.buckets.geohashGridTitle', {
defaultMessage: 'Geohash',
}),
export interface IBucketGeoHashGridAggConfig extends IBucketAggConfig {
lastMapCollar: MapCollar;
}
export const geoHashBucketAgg = new BucketAggType<IBucketGeoHashGridAggConfig>({
name: BUCKET_TYPES.GEOHASH_GRID,
title: geohashGridTitle,
params: [
{
name: 'field',
type: 'field',
filterFieldTypes: 'geo_point'
filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT,
},
{
name: 'autoPrecision',
@ -92,12 +106,13 @@ export const geoHashBucketAgg = new BucketAggType({
editorComponent: PrecisionParamEditor,
default: defaultPrecision,
deserialize: getPrecision,
write: function (aggConfig, output) {
write(aggConfig, output) {
const currZoom = aggConfig.params.mapZoom;
const autoPrecisionVal = zoomPrecision[currZoom];
output.params.precision = aggConfig.params.autoPrecision ?
autoPrecisionVal : getPrecision(aggConfig.params.precision);
}
output.params.precision = aggConfig.params.autoPrecision
? autoPrecisionVal
: getPrecision(aggConfig.params.precision);
},
},
{
name: 'useGeocentroid',
@ -125,17 +140,21 @@ export const geoHashBucketAgg = new BucketAggType({
name: 'mapBounds',
default: null,
write: () => {},
}
},
],
getRequestAggs: function (agg) {
const aggs = [];
getRequestAggs(agg) {
const aggs: AggConfig[] = [];
const params = agg.params;
if (params.isFilteredByCollar && agg.getField()) {
const { mapBounds, mapZoom } = params;
if (mapBounds) {
let mapCollar;
if (!agg.lastMapCollar || agg.lastMapCollar.zoom !== mapZoom || isOutsideCollar(mapBounds, agg.lastMapCollar)) {
if (
!agg.lastMapCollar ||
agg.lastMapCollar.zoom !== mapZoom ||
isOutsideCollar(mapBounds, agg.lastMapCollar)
) {
mapCollar = scaleBounds(mapBounds);
mapCollar.zoom = mapZoom;
agg.lastMapCollar = mapCollar;
@ -146,35 +165,45 @@ export const geoHashBucketAgg = new BucketAggType({
ignore_unmapped: true,
[agg.getField().name]: {
top_left: mapCollar.top_left,
bottom_right: mapCollar.bottom_right
}
};
aggs.push(agg.aggConfigs.createAggConfig({
type: 'filter',
id: 'filter_agg',
enabled: true,
params: {
geo_bounding_box: boundingBox
bottom_right: mapCollar.bottom_right,
},
schema: {
group: AggGroupNames.Buckets
}
}, { addToAggConfigs: false }));
};
aggs.push(
agg.aggConfigs.createAggConfig(
{
type: 'filter',
id: 'filter_agg',
enabled: true,
params: {
geo_bounding_box: boundingBox,
},
schema: {
group: AggGroupNames.Buckets,
},
} as any,
{ addToAggConfigs: false }
)
);
}
}
aggs.push(agg);
if (params.useGeocentroid) {
aggs.push(agg.aggConfigs.createAggConfig({
type: 'geo_centroid',
enabled: true,
params: {
field: agg.getField()
}
}, { addToAggConfigs: false }));
aggs.push(
agg.aggConfigs.createAggConfig(
{
type: 'geo_centroid',
enabled: true,
params: {
field: agg.getField(),
},
},
{ addToAggConfigs: false }
)
);
}
return aggs;
}
return aggs as IBucketGeoHashGridAggConfig[];
},
});

View file

@ -17,47 +17,56 @@
* under the License.
*/
import _ from 'lodash';
import { BucketAggType } from './_bucket_agg_type';
import { i18n } from '@kbn/i18n';
import { noop } from 'lodash';
import { METRIC_TYPES } from 'ui/agg_types/metrics/metric_agg_types';
import { AggConfigOptions } from 'ui/agg_types/agg_config';
import { BucketAggType } from './_bucket_agg_type';
import { BUCKET_TYPES } from './bucket_agg_types';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
const geotileGridTitle = i18n.translate('common.ui.aggTypes.buckets.geotileGridTitle', {
defaultMessage: 'Geotile',
});
export const geoTileBucketAgg = new BucketAggType({
name: 'geotile_grid',
title: i18n.translate('common.ui.aggTypes.buckets.geotileGridTitle', {
defaultMessage: 'Geotile',
}),
name: BUCKET_TYPES.GEOTILE_GRID,
title: geotileGridTitle,
params: [
{
name: 'field',
type: 'field',
filterFieldTypes: 'geo_point'
filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT,
},
{
name: 'useGeocentroid',
default: true,
write: _.noop
write: noop,
},
{
name: 'precision',
default: 0,
}
},
],
getRequestAggs: function (agg) {
getRequestAggs(agg) {
const aggs = [];
const params = agg.params;
const useGeocentroid = agg.getParam('useGeocentroid');
aggs.push(agg);
if (params.useGeocentroid) {
aggs.push(agg.aggConfigs.createAggConfig({
type: 'geo_centroid',
if (useGeocentroid) {
const aggConfig: AggConfigOptions = {
type: METRIC_TYPES.GEO_CENTROID,
enabled: true,
params: {
field: agg.getField()
}
}, { addToAggConfigs: false }));
field: agg.getField(),
},
};
aggs.push(agg.aggConfigs.createAggConfig(aggConfig, { addToAggConfigs: false }));
}
return aggs;
}
},
});

View file

@ -18,51 +18,64 @@
*/
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
import chrome from '../../chrome';
import { BucketAggType } from './_bucket_agg_type';
import { BucketAggType, IBucketAggConfig, BucketAggParam } from './_bucket_agg_type';
import { createFilterHistogram } from './create_filter/histogram';
import { NumberIntervalParamEditor } from '../../vis/editors/default/controls/number_interval';
import { MinDocCountParamEditor } from '../../vis/editors/default/controls/min_doc_count';
import { HasExtendedBoundsParamEditor } from '../../vis/editors/default/controls/has_extended_bounds';
import { ExtendedBoundsParamEditor } from '../../vis/editors/default/controls/extended_bounds';
import { i18n } from '@kbn/i18n';
import { AggConfig } from '../agg_config';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
import { BUCKET_TYPES } from './bucket_agg_types';
interface AutoBounds {
min: number;
max: number;
}
export interface IBucketHistogramAggConfig extends IBucketAggConfig {
setAutoBounds: (bounds: AutoBounds) => void;
getAutoBounds: () => AutoBounds;
}
const config = chrome.getUiSettingsClient();
export const histogramBucketAgg = new BucketAggType({
name: 'histogram',
export const histogramBucketAgg = new BucketAggType<IBucketHistogramAggConfig>({
name: BUCKET_TYPES.HISTOGRAM,
title: i18n.translate('common.ui.aggTypes.buckets.histogramTitle', {
defaultMessage: 'Histogram',
}),
ordered: {},
makeLabel: function (aggConfig) {
makeLabel(aggConfig) {
return aggConfig.getFieldDisplayName();
},
createFilter: createFilterHistogram,
decorateAggConfig: function () {
let autoBounds;
decorateAggConfig() {
let autoBounds: AutoBounds;
return {
setAutoBounds: {
configurable: true,
value(newValue) {
value(newValue: AutoBounds) {
autoBounds = newValue;
}
},
},
getAutoBounds: {
configurable: true,
value() {
return autoBounds;
}
}
},
},
};
},
params: [
{
name: 'field',
type: 'field',
filterFieldTypes: 'number'
filterFieldTypes: KBN_FIELD_TYPES.NUMBER,
},
{
/*
@ -76,7 +89,11 @@ export const histogramBucketAgg = new BucketAggType({
{
name: 'interval',
editorComponent: NumberIntervalParamEditor,
modifyAggConfigOnSearchRequestStart(aggConfig, searchSource, searchRequest) {
modifyAggConfigOnSearchRequestStart(
aggConfig: IBucketHistogramAggConfig,
searchSource: any,
searchRequest: any
) {
const field = aggConfig.getField();
const aggBody = field.scripted
? { script: { source: field.script, lang: field.lang } }
@ -87,31 +104,35 @@ export const histogramBucketAgg = new BucketAggType({
.setField('size', 0)
.setField('aggs', {
maxAgg: {
max: aggBody
max: aggBody,
},
minAgg: {
min: aggBody
}
min: aggBody,
},
});
searchRequest.whenAborted(() => childSearchSource.cancelQueued());
return childSearchSource.fetch()
.then((resp) => {
return childSearchSource
.fetch()
.then((resp: any) => {
aggConfig.setAutoBounds({
min: _.get(resp, 'aggregations.minAgg.value'),
max: _.get(resp, 'aggregations.maxAgg.value')
max: _.get(resp, 'aggregations.maxAgg.value'),
});
})
.catch(e => {
.catch((e: Error) => {
if (e.name === 'AbortError') return;
toastNotifications.addWarning(i18n.translate('common.ui.aggTypes.histogram.missingMaxMinValuesWarning', {
// eslint-disable-next-line max-len
defaultMessage: 'Unable to retrieve max and min values to auto-scale histogram buckets. This may lead to poor visualization performance.'
}));
toastNotifications.addWarning(
i18n.translate('common.ui.aggTypes.histogram.missingMaxMinValuesWarning', {
// eslint-disable-next-line max-len
defaultMessage:
'Unable to retrieve max and min values to auto-scale histogram buckets. This may lead to poor visualization performance.',
})
);
});
},
write: function (aggConfig, output) {
write(aggConfig: IBucketHistogramAggConfig, output: Record<string, any>) {
let interval = parseFloat(aggConfig.params.interval);
if (interval <= 0) {
interval = 1;
@ -146,29 +167,26 @@ export const histogramBucketAgg = new BucketAggType({
}
output.params.interval = interval;
}
},
},
} as BucketAggParam,
{
name: 'min_doc_count',
default: false,
editorComponent: MinDocCountParamEditor,
write: function (aggConfig, output) {
write(aggConfig: AggConfig, output: Record<string, any>) {
if (aggConfig.params.min_doc_count) {
output.params.min_doc_count = 0;
} else {
output.params.min_doc_count = 1;
}
}
},
},
{
name: 'has_extended_bounds',
default: false,
editorComponent: HasExtendedBoundsParamEditor,
write: () => {},
},
{
name: 'extended_bounds',
default: {
@ -176,16 +194,14 @@ export const histogramBucketAgg = new BucketAggType({
max: '',
},
editorComponent: ExtendedBoundsParamEditor,
write: function (aggConfig, output) {
write(aggConfig: AggConfig, output: Record<string, any>) {
const { min, max } = aggConfig.params.extended_bounds;
if (aggConfig.params.has_extended_bounds &&
(min || min === 0) &&
(max || max === 0)) {
if (aggConfig.params.has_extended_bounds && (min || min === 0) && (max || max === 0)) {
output.params.extended_bounds = { min, max };
}
},
shouldShow: aggConfig => aggConfig.params.has_extended_bounds
}
]
shouldShow: (aggConfig: AggConfig) => aggConfig.params.has_extended_bounds,
},
],
});

View file

@ -0,0 +1,29 @@
/*
* 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 React, { ComponentType } from 'react';
import { AggParamEditorProps } from 'ui/vis/editors/default';
export const wrapWithInlineComp = <T extends unknown>(
WrapComponent: ComponentType<AggParamEditorProps<T>>
) => (props: AggParamEditorProps<T>) => (
<div className={`visEditorAggParam--half visEditorAggParam--half-${props.aggParam.name}`}>
<WrapComponent {...props} />
</div>
);

View file

@ -17,68 +17,72 @@
* under the License.
*/
import _ from 'lodash';
import { BucketAggType } from './_bucket_agg_type';
import { createFilterIpRange } from './create_filter/ip_range';
import { get, noop, map, omit, isNull } from 'lodash';
import { i18n } from '@kbn/i18n';
import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type';
import { IpRangeTypeParamEditor } from '../../vis/editors/default/controls/ip_range_type';
import { IpRangesParamEditor } from '../../vis/editors/default/controls/ip_ranges';
import { i18n } from '@kbn/i18n';
import { BUCKET_TYPES } from './bucket_agg_types';
// @ts-ignore
import { createFilterIpRange } from './create_filter/ip_range';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
const ipRangeTitle = i18n.translate('common.ui.aggTypes.buckets.ipRangeTitle', {
defaultMessage: 'IPv4 Range',
});
export const ipRangeBucketAgg = new BucketAggType({
name: 'ip_range',
title: i18n.translate('common.ui.aggTypes.buckets.ipRangeTitle', {
defaultMessage: 'IPv4 Range',
}),
name: BUCKET_TYPES.IP_RANGE,
title: ipRangeTitle,
createFilter: createFilterIpRange,
getKey: function (bucket, key) {
getKey(bucket, key) {
if (key) return key;
const from = _.get(bucket, 'from', '-Infinity');
const to = _.get(bucket, 'to', 'Infinity');
const from = get(bucket, 'from', '-Infinity');
const to = get(bucket, 'to', 'Infinity');
return `${from} to ${to}`;
},
makeLabel: function (aggConfig) {
makeLabel(aggConfig) {
return i18n.translate('common.ui.aggTypes.buckets.ipRangeLabel', {
defaultMessage: '{fieldName} IP ranges',
values: {
fieldName: aggConfig.getFieldDisplayName()
}
fieldName: aggConfig.getFieldDisplayName(),
},
});
},
params: [
{
name: 'field',
type: 'field',
filterFieldTypes: 'ip'
}, {
filterFieldTypes: KBN_FIELD_TYPES.IP,
},
{
name: 'ipRangeType',
editorComponent: IpRangeTypeParamEditor,
default: 'fromTo',
write: _.noop
}, {
write: noop,
},
{
name: 'ranges',
default: {
fromTo: [
{ from: '0.0.0.0', to: '127.255.255.255' },
{ from: '128.0.0.0', to: '191.255.255.255' }
{ from: '128.0.0.0', to: '191.255.255.255' },
],
mask: [
{ mask: '0.0.0.0/1' },
{ mask: '128.0.0.0/2' }
]
mask: [{ mask: '0.0.0.0/1' }, { mask: '128.0.0.0/2' }],
},
editorComponent: IpRangesParamEditor,
write: function (aggConfig, output) {
write(aggConfig: IBucketAggConfig, output: Record<string, any>) {
const ipRangeType = aggConfig.params.ipRangeType;
let ranges = aggConfig.params.ranges[ipRangeType];
if (ipRangeType === 'fromTo') {
ranges = _.map(ranges, (range) => {
return _.omit(range, _.isNull);
});
ranges = map(ranges, (range: any) => omit(range, isNull));
}
output.params.ranges = ranges;
}
}
]
},
},
],
});

View file

@ -18,35 +18,32 @@
*/
import { isString, isObject } from 'lodash';
import { AggConfig } from 'ui/agg_types';
import { IBucketAggConfig, BucketAggType, BucketAggParam } from './_bucket_agg_type';
function isType(type) {
return function (agg) {
export const isType = (type: string) => {
return (agg: IBucketAggConfig): boolean => {
const field = agg.params.field;
return field && field.type === type;
};
}
};
const isStringType = isType('string');
export const isStringType = isType('string');
const migrateIncludeExcludeFormat = {
serialize: function (value, agg) {
export const migrateIncludeExcludeFormat = {
serialize(this: BucketAggParam, value: any, agg: AggConfig) {
if (this.shouldShow && !this.shouldShow(agg)) return;
if (!value || isString(value)) return value;
else return value.pattern;
},
write: function (aggConfig, output) {
const value = aggConfig.params[this.name];
write(this: BucketAggType<IBucketAggConfig>, aggConfig: AggConfig, output: Record<string, any>) {
const value = aggConfig.getParam(this.name);
if (isObject(value)) {
output.params[this.name] = value.pattern;
} else if (value && isStringType(aggConfig)) {
output.params[this.name] = value;
}
}
};
export {
isType,
isStringType,
migrateIncludeExcludeFormat
},
};

View file

@ -17,32 +17,38 @@
* under the License.
*/
import React from 'react';
import { i18n } from '@kbn/i18n';
import { IBucketAggConfig } from './_bucket_agg_type';
import { BucketAggType } from './_bucket_agg_type';
import { createFilterRange } from './create_filter/range';
import { FieldFormat } from '../../../../../plugins/data/common/field_formats';
import { RangeKey } from './range_key';
import { RangesParamEditor } from '../../vis/editors/default/controls/ranges';
import { i18n } from '@kbn/i18n';
import { RangesEditor } from './range_editor';
// @ts-ignore
import { createFilterRange } from './create_filter/range';
import { BUCKET_TYPES } from './bucket_agg_types';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
const keyCaches = new WeakMap();
const formats = new WeakMap();
const rangeTitle = i18n.translate('common.ui.aggTypes.buckets.rangeTitle', {
defaultMessage: 'Range',
});
export const rangeBucketAgg = new BucketAggType({
name: 'range',
title: i18n.translate('common.ui.aggTypes.buckets.rangeTitle', {
defaultMessage: 'Range',
}),
name: BUCKET_TYPES.RANGE,
title: rangeTitle,
createFilter: createFilterRange,
makeLabel: function (aggConfig) {
makeLabel(aggConfig) {
return i18n.translate('common.ui.aggTypes.buckets.rangesLabel', {
defaultMessage: '{fieldName} ranges',
values: {
fieldName: aggConfig.getFieldDisplayName()
}
fieldName: aggConfig.getFieldDisplayName(),
},
});
},
getKey: function (bucket, key, agg) {
getKey(bucket, key, agg) {
let keys = keyCaches.get(agg);
if (!keys) {
@ -60,11 +66,11 @@ export const rangeBucketAgg = new BucketAggType({
return key;
},
getFormat: function (agg) {
let format = formats.get(agg);
if (format) return format;
getFormat(agg) {
let aggFormat = formats.get(agg);
if (aggFormat) return aggFormat;
const RangeFormat = FieldFormat.from(function (range) {
const RangeFormat = FieldFormat.from((range: any) => {
const format = agg.fieldOwnFormatter();
const gte = '\u2265';
const lt = '\u003c';
@ -79,28 +85,25 @@ export const rangeBucketAgg = new BucketAggType({
});
});
format = new RangeFormat();
aggFormat = new RangeFormat();
formats.set(agg, format);
return format;
formats.set(agg, aggFormat);
return aggFormat;
},
params: [
{
name: 'field',
type: 'field',
filterFieldTypes: ['number']
filterFieldTypes: [KBN_FIELD_TYPES.NUMBER],
},
{
name: 'ranges',
default: [
{ from: 0, to: 1000 },
{ from: 1000, to: 2000 }
],
editorComponent: ({ value, setValue }) => <RangesParamEditor value={value} setValue={setValue}/>,
write: function (aggConfig, output) {
default: [{ from: 0, to: 1000 }, { from: 1000, to: 2000 }],
editorComponent: RangesEditor,
write(aggConfig: IBucketAggConfig, output: Record<string, any>) {
output.params.ranges = aggConfig.params.ranges;
output.params.keyed = true;
}
}
]
},
},
],
});

View file

@ -18,10 +18,9 @@
*/
import React from 'react';
import { AggParamEditorProps } from 'ui/vis/editors/default';
import { RangesParamEditor } from '../../vis/editors/default/controls/ranges';
const wrapWithInlineComp = Component => props => (
<div className={`visEditorAggParam--half visEditorAggParam--half-${props.aggParam.name}`}>
<Component {...props}/>
</div>);
export { wrapWithInlineComp };
export const RangesEditor = (props: AggParamEditorProps<any>) => (
<RangesParamEditor value={props.value} setValue={props.setValue} />
);

View file

@ -19,16 +19,19 @@
const id = Symbol('id');
class RangeKey {
constructor(bucket) {
export class RangeKey {
[id]: string;
gte: string | number;
lt: string | number;
constructor(bucket: any) {
this.gte = bucket.from == null ? -Infinity : bucket.from;
this.lt = bucket.to == null ? +Infinity : bucket.to;
this[id] = RangeKey.idBucket(bucket);
}
static idBucket(bucket) {
static idBucket(bucket: any) {
return `from:${bucket.from},to:${bucket.to}`;
}
@ -36,5 +39,3 @@ class RangeKey {
return this[id];
}
}
export { RangeKey };

View file

@ -19,22 +19,26 @@
import { i18n } from '@kbn/i18n';
import { SizeParamEditor } from '../../vis/editors/default/controls/size';
import { BucketAggType } from './_bucket_agg_type';
import { BucketAggType, BucketAggParam } from './_bucket_agg_type';
import { createFilterTerms } from './create_filter/terms';
import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format';
import { BUCKET_TYPES } from './bucket_agg_types';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
const significantTermsTitle = i18n.translate('common.ui.aggTypes.buckets.significantTermsTitle', {
defaultMessage: 'Significant Terms',
});
export const significantTermsBucketAgg = new BucketAggType({
name: 'significant_terms',
title: i18n.translate('common.ui.aggTypes.buckets.significantTermsTitle', {
defaultMessage: 'Significant Terms',
}),
makeLabel: function (aggConfig) {
name: BUCKET_TYPES.SIGNIFICANT_TERMS,
title: significantTermsTitle,
makeLabel(aggConfig) {
return i18n.translate('common.ui.aggTypes.buckets.significantTermsLabel', {
defaultMessage: 'Top {size} unusual terms in {fieldName}',
values: {
size: aggConfig.params.size,
fieldName: aggConfig.getFieldDisplayName(),
}
},
});
},
createFilter: createFilterTerms,
@ -43,7 +47,7 @@ export const significantTermsBucketAgg = new BucketAggType({
name: 'field',
type: 'field',
scriptable: false,
filterFieldTypes: 'string'
filterFieldTypes: KBN_FIELD_TYPES.STRING,
},
{
name: 'size',
@ -53,22 +57,22 @@ export const significantTermsBucketAgg = new BucketAggType({
{
name: 'exclude',
displayName: i18n.translate('common.ui.aggTypes.buckets.significantTerms.excludeLabel', {
defaultMessage: 'Exclude'
defaultMessage: 'Exclude',
}),
type: 'string',
advanced: true,
shouldShow: isStringType,
...migrateIncludeExcludeFormat
},
...migrateIncludeExcludeFormat,
} as BucketAggParam,
{
name: 'include',
displayName: i18n.translate('common.ui.aggTypes.buckets.significantTerms.includeLabel', {
defaultMessage: 'Include'
defaultMessage: 'Include',
}),
type: 'string',
advanced: true,
shouldShow: isStringType,
...migrateIncludeExcludeFormat
}
]
...migrateIncludeExcludeFormat,
} as BucketAggParam,
],
});

View file

@ -18,13 +18,20 @@
*/
import chrome from 'ui/chrome';
import { noop } from 'lodash';
import { SearchSource } from 'ui/courier';
import { i18n } from '@kbn/i18n';
import { BucketAggType } from './_bucket_agg_type';
import { Schemas } from '../../vis/editors/default/schemas';
import { getRequestInspectorStats, getResponseInspectorStats } from '../../courier/utils/courier_inspector_utils';
import { BucketAggType, BucketAggParam } from './_bucket_agg_type';
import { BUCKET_TYPES } from './bucket_agg_types';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
import { AggConfigOptions } from '../agg_config';
import { IBucketAggConfig } from './_bucket_agg_type';
import {
getRequestInspectorStats,
getResponseInspectorStats,
} from '../../courier/utils/courier_inspector_utils';
import { createFilterTerms } from './create_filter/terms';
import { wrapWithInlineComp } from './_inline_comp_wrapper';
import { buildOtherBucketAgg, mergeOtherBucketAggResponse, updateMissingBucket } from './_terms_other_bucket_helper';
import { wrapWithInlineComp } from './inline_comp_wrapper';
import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format';
import { OrderAggParamEditor } from '../../vis/editors/default/controls/order_agg';
import { OrderParamEditor } from '../../vis/editors/default/controls/order';
@ -32,31 +39,47 @@ import { OrderByParamEditor, aggFilter } from '../../vis/editors/default/control
import { SizeParamEditor } from '../../vis/editors/default/controls/size';
import { MissingBucketParamEditor } from '../../vis/editors/default/controls/missing_bucket';
import { OtherBucketParamEditor } from '../../vis/editors/default/controls/other_bucket';
import { ContentType } from '../../../../../plugins/data/common';
import { AggConfigs } from '../agg_configs';
const orderAggSchema = (new Schemas([
import { Adapters } from '../../../../../plugins/inspector/public';
// @ts-ignore
import { Schemas } from '../../vis/editors/default/schemas';
import {
buildOtherBucketAgg,
mergeOtherBucketAggResponse,
updateMissingBucket,
// @ts-ignore
} from './_terms_other_bucket_helper';
const [orderAggSchema] = new Schemas([
{
group: 'none',
name: 'orderAgg',
// This string is never visible to the user so it doesn't need to be translated
title: 'Order Agg',
hideCustomLabel: true,
aggFilter: aggFilter
}
])).all[0];
aggFilter,
},
]).all;
const termsTitle = i18n.translate('common.ui.aggTypes.buckets.termsTitle', {
defaultMessage: 'Terms',
});
export const termsBucketAgg = new BucketAggType({
name: 'terms',
title: i18n.translate('common.ui.aggTypes.buckets.termsTitle', {
defaultMessage: 'Terms',
}),
makeLabel: function (agg) {
name: BUCKET_TYPES.TERMS,
title: termsTitle,
makeLabel(agg) {
const params = agg.params;
return agg.getFieldDisplayName() + ': ' + params.order.text;
},
getFormat: function (bucket) {
getFormat(bucket) {
return {
getConverterFor: (type) => {
return (val) => {
getConverterFor: (type: ContentType) => {
return (val: any) => {
if (val === '__other__') {
return bucket.params.otherBucketLabel;
}
@ -71,11 +94,18 @@ export const termsBucketAgg = new BucketAggType({
const converter = bucket.params.field.format.getConverterFor(type);
return converter(val, undefined, undefined, parsedUrl);
};
}
},
};
},
createFilter: createFilterTerms,
postFlightRequest: async (resp, aggConfigs, aggConfig, searchSource, inspectorAdapters, abortSignal) => {
postFlightRequest: async (
resp: any,
aggConfigs: AggConfigs,
aggConfig: IBucketAggConfig,
searchSource: SearchSource,
inspectorAdapters: Adapters,
abortSignal?: AbortSignal
) => {
if (!resp.aggregations) return resp;
const nestedSearchSource = searchSource.createChild();
if (aggConfig.params.otherBucket) {
@ -88,23 +118,24 @@ export const termsBucketAgg = new BucketAggType({
nestedSearchSource.setField('aggs', filterAgg);
const request = inspectorAdapters.requests.start(
i18n.translate('common.ui.aggTypes.buckets.terms.otherBucketTitle', { defaultMessage: 'Other bucket' }),
i18n.translate('common.ui.aggTypes.buckets.terms.otherBucketTitle', {
defaultMessage: 'Other bucket',
}),
{
description: i18n.translate('common.ui.aggTypes.buckets.terms.otherBucketDescription', {
defaultMessage: 'This request counts the number of documents that fall ' +
'outside the criterion of the data buckets.'
defaultMessage:
'This request counts the number of documents that fall ' +
'outside the criterion of the data buckets.',
}),
}
);
nestedSearchSource.getSearchRequestBody().then(body => {
nestedSearchSource.getSearchRequestBody().then((body: string) => {
request.json(body);
});
request.stats(getRequestInspectorStats(nestedSearchSource));
const response = await nestedSearchSource.fetch();
request
.stats(getResponseInspectorStats(nestedSearchSource, response))
.ok({ json: response });
request.stats(getResponseInspectorStats(nestedSearchSource, response)).ok({ json: response });
resp = mergeOtherBucketAggResponse(aggConfigs, resp, response, aggConfig, filterAgg());
}
if (aggConfig.params.missingBucket) {
@ -116,28 +147,34 @@ export const termsBucketAgg = new BucketAggType({
{
name: 'field',
type: 'field',
filterFieldTypes: ['number', 'boolean', 'date', 'ip', 'string']
filterFieldTypes: [
KBN_FIELD_TYPES.NUMBER,
KBN_FIELD_TYPES.BOOLEAN,
KBN_FIELD_TYPES.DATE,
KBN_FIELD_TYPES.IP,
KBN_FIELD_TYPES.STRING,
],
},
{
name: 'orderBy',
editorComponent: OrderByParamEditor,
write: () => {} // prevent default write, it's handled by orderAgg
write: noop, // prevent default write, it's handled by orderAgg
},
{
name: 'orderAgg',
type: 'agg',
default: null,
editorComponent: OrderAggParamEditor,
makeAgg: function (termsAgg, state) {
makeAgg(termsAgg: IBucketAggConfig, state: AggConfigOptions) {
state = state || {};
state.schema = orderAggSchema;
const orderAgg = termsAgg.aggConfigs.createAggConfig(state, { addToAggConfigs: false });
orderAgg.id = termsAgg.id + '-orderAgg';
return orderAgg;
},
write: function (agg, output, aggs) {
write(agg: IBucketAggConfig, output: Record<string, any>, aggs: AggConfigs) {
const dir = agg.params.order.value;
const order = output.params.order = {};
const order: Record<string, any> = (output.params.order = {});
let orderAgg = agg.params.orderAgg || aggs.getResponseAggById(agg.params.orderBy);
@ -145,7 +182,8 @@ export const termsBucketAgg = new BucketAggType({
// thus causing issues with filtering. This probably causes other issues since float might not
// be able to contain the number on the elasticsearch side
if (output.params.script) {
output.params.value_type = agg.getField().type === 'number' ? 'float' : agg.getField().type;
output.params.value_type =
agg.getField().type === 'number' ? 'float' : agg.getField().type;
}
if (agg.params.missingBucket && agg.params.field.type === 'string') {
@ -169,7 +207,7 @@ export const termsBucketAgg = new BucketAggType({
output.subAggs = (output.subAggs || []).concat(orderAgg);
order[orderAggId] = dir;
}
},
},
{
name: 'order',
@ -181,27 +219,27 @@ export const termsBucketAgg = new BucketAggType({
text: i18n.translate('common.ui.aggTypes.buckets.terms.orderDescendingTitle', {
defaultMessage: 'Descending',
}),
value: 'desc'
value: 'desc',
},
{
text: i18n.translate('common.ui.aggTypes.buckets.terms.orderAscendingTitle', {
defaultMessage: 'Ascending',
}),
value: 'asc'
}
value: 'asc',
},
],
write: () => {} // prevent default write, it's handled by orderAgg
write: noop, // prevent default write, it's handled by orderAgg
},
{
name: 'size',
editorComponent: wrapWithInlineComp(SizeParamEditor),
default: 5
default: 5,
},
{
name: 'otherBucket',
default: false,
editorComponent: OtherBucketParamEditor,
write: () => {},
write: noop,
},
{
name: 'otherBucketLabel',
@ -212,14 +250,14 @@ export const termsBucketAgg = new BucketAggType({
displayName: i18n.translate('common.ui.aggTypes.otherBucket.labelForOtherBucketLabel', {
defaultMessage: 'Label for other bucket',
}),
shouldShow: agg => agg.params.otherBucket,
write: () => {},
},
shouldShow: (agg: IBucketAggConfig) => agg.getParam('otherBucket'),
write: noop,
} as BucketAggParam,
{
name: 'missingBucket',
default: false,
editorComponent: MissingBucketParamEditor,
write: () => {},
write: noop,
},
{
name: 'missingBucketLabel',
@ -232,24 +270,28 @@ export const termsBucketAgg = new BucketAggType({
displayName: i18n.translate('common.ui.aggTypes.otherBucket.labelForMissingValuesLabel', {
defaultMessage: 'Label for missing values',
}),
shouldShow: agg => agg.params.missingBucket,
write: () => {},
shouldShow: (agg: IBucketAggConfig) => agg.getParam('missingBucket'),
write: noop,
},
{
name: 'exclude',
displayName: i18n.translate('common.ui.aggTypes.buckets.terms.excludeLabel', { defaultMessage: 'Exclude' }),
displayName: i18n.translate('common.ui.aggTypes.buckets.terms.excludeLabel', {
defaultMessage: 'Exclude',
}),
type: 'string',
advanced: true,
shouldShow: isStringType,
...migrateIncludeExcludeFormat
...migrateIncludeExcludeFormat,
},
{
name: 'include',
displayName: i18n.translate('common.ui.aggTypes.buckets.terms.includeLabel', { defaultMessage: 'Include' }),
displayName: i18n.translate('common.ui.aggTypes.buckets.terms.includeLabel', {
defaultMessage: 'Include',
}),
type: 'string',
advanced: true,
shouldShow: isStringType,
...migrateIncludeExcludeFormat
...migrateIncludeExcludeFormat,
},
]
] as BucketAggParam[],
});

View file

@ -17,73 +17,41 @@
* under the License.
*/
// @ts-ignore
import { AggType } from './agg_type';
// @ts-ignore
import { countMetricAgg } from './metrics/count';
// @ts-ignore
import { avgMetricAgg } from './metrics/avg';
// @ts-ignore
import { sumMetricAgg } from './metrics/sum';
// @ts-ignore
import { medianMetricAgg } from './metrics/median';
// @ts-ignore
import { minMetricAgg } from './metrics/min';
// @ts-ignore
import { maxMetricAgg } from './metrics/max';
// @ts-ignore
import { topHitMetricAgg } from './metrics/top_hit';
// @ts-ignore
import { stdDeviationMetricAgg } from './metrics/std_deviation';
// @ts-ignore
import { cardinalityMetricAgg } from './metrics/cardinality';
// @ts-ignore
import { percentilesMetricAgg } from './metrics/percentiles';
// @ts-ignore
import { geoBoundsMetricAgg } from './metrics/geo_bounds';
// @ts-ignore
import { geoCentroidMetricAgg } from './metrics/geo_centroid';
// @ts-ignore
import { percentileRanksMetricAgg } from './metrics/percentile_ranks';
// @ts-ignore
import { derivativeMetricAgg } from './metrics/derivative';
// @ts-ignore
import { cumulativeSumMetricAgg } from './metrics/cumulative_sum';
// @ts-ignore
import { movingAvgMetricAgg } from './metrics/moving_avg';
// @ts-ignore
import { serialDiffMetricAgg } from './metrics/serial_diff';
// @ts-ignore
import { dateHistogramBucketAgg, setBounds } from './buckets/date_histogram';
// @ts-ignore
import { histogramBucketAgg } from './buckets/histogram';
// @ts-ignore
import { rangeBucketAgg } from './buckets/range';
// @ts-ignore
import { dateRangeBucketAgg } from './buckets/date_range';
// @ts-ignore
import { ipRangeBucketAgg } from './buckets/ip_range';
// @ts-ignore
import { termsBucketAgg } from './buckets/terms';
// @ts-ignore
import { filterBucketAgg } from './buckets/filter';
// @ts-ignore
import { filtersBucketAgg } from './buckets/filters';
// @ts-ignore
import { significantTermsBucketAgg } from './buckets/significant_terms';
// @ts-ignore
import { geoHashBucketAgg } from './buckets/geo_hash';
// @ts-ignore
import { geoTileBucketAgg } from './buckets/geo_tile';
// @ts-ignore
import { bucketSumMetricAgg } from './metrics/bucket_sum';
// @ts-ignore
import { bucketAvgMetricAgg } from './metrics/bucket_avg';
// @ts-ignore
import { bucketMinMetricAgg } from './metrics/bucket_min';
// @ts-ignore
import { bucketMaxMetricAgg } from './metrics/bucket_max';
export { AggType } from './agg_type';
export const aggTypes = {
metrics: [
countMetricAgg,
@ -123,13 +91,9 @@ export const aggTypes = {
],
};
aggTypes.metrics.forEach(aggType => (aggType.type = 'metrics'));
aggTypes.buckets.forEach(aggType => (aggType.type = 'buckets'));
export { AggParam } from './agg_params';
export { AggType } from './agg_type';
export { AggConfig } from './agg_config';
export { AggConfigs } from './agg_configs';
export { FieldParamType } from './param_types';
export { BucketAggType } from './buckets/_bucket_agg_type';
export { setBounds };

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { i18n } from '@kbn/i18n';
import { MetricAggType } from './metric_agg_type';
import { IMetricAggConfig, MetricAggType } from './metric_agg_type';
import { METRIC_TYPES } from './metric_agg_types';
// @ts-ignore
@ -49,7 +49,7 @@ export const medianMetricAgg = new MetricAggType({
default: [50],
},
{
write(agg, output) {
write(agg: IMetricAggConfig, output: Record<string, any>) {
output.params.keyed = false;
},
},

View file

@ -27,12 +27,10 @@ import { METRIC_TYPES } from './metric_agg_types';
import { fieldFormats } from '../../registry/field_formats';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
export interface IMetricAggConfig extends AggConfig {
params: MetricAggParam[];
}
export type IMetricAggConfig = AggConfig;
export interface MetricAggTypeConfig<TMetricAggConfig extends IMetricAggConfig>
extends AggTypeConfig<TMetricAggConfig> {
extends AggTypeConfig<TMetricAggConfig, MetricAggParam> {
isScalable?: () => boolean;
subtype?: string;
}
@ -42,11 +40,16 @@ export interface MetricAggParam extends AggParamType {
onlyAggregatable?: boolean;
}
export class MetricAggType<TMetricAggConfig extends IMetricAggConfig> extends AggType<
TMetricAggConfig
> {
const metricType = 'metrics';
export class MetricAggType<
TMetricAggConfig extends IMetricAggConfig = IMetricAggConfig
> extends AggType<TMetricAggConfig, MetricAggParam> {
subtype: string;
isScalable: () => boolean;
type = metricType;
getKey = () => {};
constructor(config: MetricAggTypeConfig<TMetricAggConfig>) {
super(config);
@ -81,3 +84,7 @@ export class MetricAggType<TMetricAggConfig extends IMetricAggConfig> extends Ag
this.isScalable = config.isScalable || (() => false);
}
}
export function isMetricAggType(aggConfig: any): aggConfig is MetricAggType {
return aggConfig && aggConfig.type === metricType;
}

View file

@ -19,7 +19,7 @@
import { i18n } from '@kbn/i18n';
import { PercentileRanksEditor } from '../../vis/editors/default/controls/percentile_ranks';
import { MetricAggType } from './metric_agg_type';
import { IMetricAggConfig, MetricAggType } from './metric_agg_type';
import { getResponseAggConfigClass, IResponseAggConfig } from './get_response_agg_config_class';
import { getPercentileValue } from './percentiles_get_value';
@ -69,7 +69,7 @@ export const percentileRanksMetricAgg = new MetricAggType<IPercentileRanksAggCon
default: [],
},
{
write(agg, output) {
write(agg: IMetricAggConfig, output: Record<string, any>) {
output.params.keyed = false;
},
},

View file

@ -19,8 +19,9 @@
import { i18n } from '@kbn/i18n';
import { MetricAggType } from './metric_agg_type';
import { IMetricAggConfig, MetricAggType } from './metric_agg_type';
import { METRIC_TYPES } from './metric_agg_types';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
import { getResponseAggConfigClass, IResponseAggConfig } from './get_response_agg_config_class';
import { getPercentileValue } from './percentiles_get_value';
@ -28,7 +29,6 @@ import { PercentilesEditor } from '../../vis/editors/default/controls/percentile
// @ts-ignore
import { ordinalSuffix } from '../../utils/ordinal_suffix';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
type IPercentileAggConfig = IResponseAggConfig;
@ -67,7 +67,7 @@ export const percentilesMetricAgg = new MetricAggType<IPercentileAggConfig>({
default: [1, 5, 25, 50, 75, 95, 99],
},
{
write(agg, output) {
write(agg: IMetricAggConfig, output: Record<string, any>) {
output.params.keyed = false;
},
},

View file

@ -30,7 +30,7 @@ import { METRIC_TYPES } from './metric_agg_types';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
// @ts-ignore
import { wrapWithInlineComp } from '../buckets/_inline_comp_wrapper';
import { wrapWithInlineComp } from '../buckets/inline_comp_wrapper';
const isNumericFieldSelected = (agg: IMetricAggConfig) => {
const field = agg.getParam('field');
@ -46,7 +46,7 @@ aggTypeFieldFilters.addFilter((field, aggConfig: IMetricAggConfig) => {
return true;
}
return field.type === 'number';
return field.type === KBN_FIELD_TYPES.NUMBER;
});
export const topHitMetricAgg = new MetricAggType({
@ -82,7 +82,7 @@ export const topHitMetricAgg = new MetricAggType({
editorComponent: TopFieldParamEditor,
onlyAggregatable: false,
filterFieldTypes: '*',
write(agg, output) {
write(agg: IMetricAggConfig, output: Record<string, any>) {
const field = agg.getParam('field');
output.params = {};
@ -196,7 +196,7 @@ export const topHitMetricAgg = new MetricAggType({
value: 'asc',
},
],
write(agg, output) {
write(agg: IMetricAggConfig, output: Record<string, any>) {
const sortField = agg.params.sortField;
const sortOrder = agg.params.sortOrder;

View file

@ -56,8 +56,8 @@ export class BaseParamType implements AggParam {
*/
modifyAggConfigOnSearchRequestStart: (
aggconfig: AggConfig,
searchSource: SearchSource,
searchRequest: any
searchSource?: SearchSource,
searchRequest?: any
) => void;
constructor(config: Record<string, any>) {

View file

@ -29,7 +29,7 @@ import { AggParamEditorProps } from '..';
const generateId = htmlIdGenerator();
const config = chrome.getUiSettingsClient();
interface FilterValue {
export interface FilterValue {
input: Query;
label: string;
id: string;

View file

@ -34,6 +34,7 @@ jest.mock('ui/new_platform');
jest.mock('ui/agg_types/buckets/date_histogram', () => ({
setBounds: () => {},
dateHistogramBucketAgg: () => {},
isDateHistogramBucketAggConfig: () => true,
}));
describe('visualize loader pipeline helpers: build pipeline', () => {

View file

@ -22,6 +22,7 @@ import { cloneDeep, get } from 'lodash';
import { setBounds } from 'ui/agg_types';
import { SearchSource } from 'ui/courier';
import { AggConfig, Vis, VisParams, VisState } from 'ui/vis';
import { isDateHistogramBucketAggConfig } from 'ui/agg_types/buckets/date_histogram';
import moment from 'moment';
import { SerializedFieldFormat } from 'src/plugins/expressions/common/expressions/types/common';
import { createFormat } from './utilities';
@ -76,7 +77,7 @@ const vislibCharts: string[] = [
export const getSchemas = (vis: Vis, timeRange?: any): Schemas => {
const createSchemaConfig = (accessor: number, agg: AggConfig): SchemaConfig => {
if (agg.type.name === 'date_histogram') {
if (isDateHistogramBucketAggConfig(agg)) {
agg.params.timeRange = timeRange;
setBounds(agg, true);
}

View file

@ -37,7 +37,8 @@
],
"monitoring/common/*": [
"x-pack/monitoring/common/*"
]
],
"plugins/*": ["src/legacy/core_plugins/*/public/"]
},
"types": [
"node",