Skip to content

Commit

Permalink
[Maps] Split out agg-descriptors (#83294)
Browse files Browse the repository at this point in the history
Splits the AggDescriptor into multiple types, to better distinguish Counts from aggs with fields. Corresponding split in agg-classes.
  • Loading branch information
thomasneirynck authored Nov 16, 2020
1 parent fe33579 commit 32c3676
Show file tree
Hide file tree
Showing 21 changed files with 465 additions and 286 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,28 @@ export type AbstractESSourceDescriptor = AbstractSourceDescriptor & {
applyGlobalTime: boolean;
};

export type AggDescriptor = {
field?: string; // count aggregation does not require field. All other aggregation types do
label?: string;
type AbstractAggDescriptor = {
type: AGG_TYPE;
label?: string;
};

export type CountAggDescriptor = AbstractAggDescriptor & {
type: AGG_TYPE.COUNT;
};

export type FieldedAggDescriptor = AbstractAggDescriptor & {
type:
| AGG_TYPE.UNIQUE_COUNT
| AGG_TYPE.MAX
| AGG_TYPE.MIN
| AGG_TYPE.SUM
| AGG_TYPE.AVG
| AGG_TYPE.TERMS;
field?: string;
};

export type AggDescriptor = CountAggDescriptor | FieldedAggDescriptor;

export type AbstractESAggSourceDescriptor = AbstractESSourceDescriptor & {
metrics: AggDescriptor[];
};
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/maps/common/migrations/join_agg_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function migrateJoinAggKey({
_.get(joinDescriptor, 'right.metrics', []).forEach((aggDescriptor: AggDescriptor) => {
const legacyAggKey = getLegacyAggKey({
aggType: aggDescriptor.type,
aggFieldName: aggDescriptor.field,
aggFieldName: 'field' in aggDescriptor ? aggDescriptor.field : undefined,
indexPatternTitle: _.get(joinDescriptor, 'right.indexPatternTitle', ''),
termFieldName: _.get(joinDescriptor, 'right.term', ''),
});
Expand Down
60 changes: 60 additions & 0 deletions x-pack/plugins/maps/public/classes/fields/agg/agg_field.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { AggField } from './agg_field';
import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants';
import { IESAggSource } from '../../sources/es_agg_source';
import { IIndexPattern } from 'src/plugins/data/public';

const mockIndexPattern = {
title: 'wildIndex',
fields: [
{
name: 'foo*',
},
],
} as IIndexPattern;

const mockEsAggSource = {
getAggKey: (aggType: AGG_TYPE, fieldName: string) => {
return 'agg_key';
},
getAggLabel: (aggType: AGG_TYPE, fieldName: string) => {
return 'agg_label';
},
getIndexPattern: async () => {
return mockIndexPattern;
},
} as IESAggSource;

const defaultParams = {
label: 'my agg field',
source: mockEsAggSource,
origin: FIELD_ORIGIN.SOURCE,
};

describe('supportsFieldMeta', () => {
test('Non-counting aggregations should support field meta', () => {
const avgMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.AVG });
expect(avgMetric.supportsFieldMeta()).toBe(true);
const maxMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.MAX });
expect(maxMetric.supportsFieldMeta()).toBe(true);
const minMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.MIN });
expect(minMetric.supportsFieldMeta()).toBe(true);
const termsMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.TERMS });
expect(termsMetric.supportsFieldMeta()).toBe(true);
});

test('Counting aggregations should not support field meta', () => {
const sumMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.SUM });
expect(sumMetric.supportsFieldMeta()).toBe(false);
const uniqueCountMetric = new AggField({
...defaultParams,
aggType: AGG_TYPE.UNIQUE_COUNT,
});
expect(uniqueCountMetric.supportsFieldMeta()).toBe(false);
});
});
78 changes: 78 additions & 0 deletions x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { IndexPattern } from 'src/plugins/data/public';
import { AGG_TYPE } from '../../../../common/constants';
import { CountAggField } from './count_agg_field';
import { isMetricCountable } from '../../util/is_metric_countable';
import { CountAggFieldParams } from './agg_field_types';
import { addFieldToDSL, getField } from '../../../../common/elasticsearch_util';
import { IField } from '../field';

const TERMS_AGG_SHARD_SIZE = 5;

export interface AggFieldParams extends CountAggFieldParams {
esDocField?: IField;
aggType: AGG_TYPE;
}

export class AggField extends CountAggField {
private readonly _esDocField?: IField;
private readonly _aggType: AGG_TYPE;

constructor(params: AggFieldParams) {
super(params);
this._esDocField = params.esDocField;
this._aggType = params.aggType;
}

isValid(): boolean {
return !!this._esDocField;
}

supportsFieldMeta(): boolean {
// count and sum aggregations are not within field bounds so they do not support field meta.
return !isMetricCountable(this._getAggType());
}

canValueBeFormatted(): boolean {
return this._getAggType() !== AGG_TYPE.UNIQUE_COUNT;
}

_getAggType(): AGG_TYPE {
return this._aggType;
}

getValueAggDsl(indexPattern: IndexPattern): unknown {
const field = getField(indexPattern, this.getRootName());
const aggType = this._getAggType();
const aggBody = aggType === AGG_TYPE.TERMS ? { size: 1, shard_size: TERMS_AGG_SHARD_SIZE } : {};
return {
[aggType]: addFieldToDSL(aggBody, field),
};
}

getRootName(): string {
return this._esDocField ? this._esDocField.getName() : '';
}

async getDataType(): Promise<string> {
return this._getAggType() === AGG_TYPE.TERMS ? 'string' : 'number';
}

getBucketCount(): number {
// terms aggregation increases the overall number of buckets per split bucket
return this._getAggType() === AGG_TYPE.TERMS ? TERMS_AGG_SHARD_SIZE : 0;
}

async getOrdinalFieldMetaRequest(): Promise<unknown> {
return this._esDocField ? await this._esDocField.getOrdinalFieldMetaRequest() : null;
}

async getCategoricalFieldMetaRequest(size: number): Promise<unknown> {
return this._esDocField ? await this._esDocField.getCategoricalFieldMetaRequest(size) : null;
}
}
22 changes: 22 additions & 0 deletions x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { IField } from '../field';
import { IndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns/index_patterns';
import { IESAggSource } from '../../sources/es_agg_source';
import { FIELD_ORIGIN } from '../../../../common/constants';

export interface IESAggField extends IField {
getValueAggDsl(indexPattern: IndexPattern): unknown | null;
getBucketCount(): number;
}

export interface CountAggFieldParams {
label?: string;
source: IESAggSource;
origin: FIELD_ORIGIN;
canReadFromGeoJson?: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { CountAggField } from './count_agg_field';
import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants';
import { IESAggSource } from '../../sources/es_agg_source';
import { IIndexPattern } from 'src/plugins/data/public';

const mockIndexPattern = {
title: 'wildIndex',
fields: [
{
name: 'foo*',
},
],
} as IIndexPattern;

const mockEsAggSource = {
getAggKey: (aggType: AGG_TYPE, fieldName: string) => {
return 'agg_key';
},
getAggLabel: (aggType: AGG_TYPE, fieldName: string) => {
return 'agg_label';
},
getIndexPattern: async () => {
return mockIndexPattern;
},
} as IESAggSource;

const defaultParams = {
label: 'my agg field',
source: mockEsAggSource,
aggType: AGG_TYPE.COUNT,
origin: FIELD_ORIGIN.SOURCE,
};

describe('supportsFieldMeta', () => {
test('Counting aggregations should not support field meta', () => {
const countMetric = new CountAggField({ ...defaultParams });
expect(countMetric.supportsFieldMeta()).toBe(false);
});
});
100 changes: 100 additions & 0 deletions x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { IndexPattern } from 'src/plugins/data/public';
import { IESAggSource } from '../../sources/es_agg_source';
import { IVectorSource } from '../../sources/vector_source';
import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants';
import { ITooltipProperty, TooltipProperty } from '../../tooltips/tooltip_property';
import { ESAggTooltipProperty } from '../../tooltips/es_agg_tooltip_property';
import { IESAggField, CountAggFieldParams } from './agg_field_types';

// Agg without field. Essentially a count-aggregation.
export class CountAggField implements IESAggField {
private readonly _source: IESAggSource;
private readonly _origin: FIELD_ORIGIN;
private readonly _label?: string;
private readonly _canReadFromGeoJson: boolean;

constructor({ label, source, origin, canReadFromGeoJson = true }: CountAggFieldParams) {
this._source = source;
this._origin = origin;
this._label = label;
this._canReadFromGeoJson = canReadFromGeoJson;
}

_getAggType(): AGG_TYPE {
return AGG_TYPE.COUNT;
}

getSource(): IVectorSource {
return this._source;
}

getOrigin(): FIELD_ORIGIN {
return this._origin;
}

getName(): string {
return this._source.getAggKey(this._getAggType(), this.getRootName());
}

getRootName(): string {
return '';
}

async getLabel(): Promise<string> {
return this._label
? this._label
: this._source.getAggLabel(this._getAggType(), this.getRootName());
}

isValid(): boolean {
return true;
}

async getDataType(): Promise<string> {
return 'number';
}

async createTooltipProperty(value: string | string[] | undefined): Promise<ITooltipProperty> {
const indexPattern = await this._source.getIndexPattern();
const tooltipProperty = new TooltipProperty(this.getName(), await this.getLabel(), value);
return new ESAggTooltipProperty(tooltipProperty, indexPattern, this, this._getAggType());
}

getValueAggDsl(indexPattern: IndexPattern): unknown | null {
return null;
}

supportsFieldMeta(): boolean {
return false;
}

getBucketCount() {
return 0;
}

canValueBeFormatted(): boolean {
return false;
}

async getOrdinalFieldMetaRequest(): Promise<unknown> {
return null;
}

async getCategoricalFieldMetaRequest(size: number): Promise<unknown> {
return null;
}

supportsAutoDomain(): boolean {
return this._canReadFromGeoJson ? true : this.supportsFieldMeta();
}

canReadFromGeoJson(): boolean {
return this._canReadFromGeoJson;
}
}
Loading

0 comments on commit 32c3676

Please sign in to comment.