Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[7.x] [Maps] Support categorical styling for numbers and dates (#57908) #59684

Merged
merged 1 commit into from
Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x-pack/legacy/plugins/maps/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export const COLOR_MAP_TYPE = {
export const COLOR_PALETTE_MAX_SIZE = 10;

export const CATEGORICAL_DATA_TYPES = ['string', 'ip', 'boolean'];
export const ORDINAL_DATA_TYPES = ['number', 'date'];

export const SYMBOLIZE_AS_TYPES = {
CIRCLE: 'circle',
Expand Down
8 changes: 4 additions & 4 deletions x-pack/legacy/plugins/maps/common/descriptor_types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ export type AggDescriptor = {
type: AGG_TYPE;
};

export type AbstractESAggDescriptor = AbstractESSourceDescriptor & {
export type AbstractESAggSourceDescriptor = AbstractESSourceDescriptor & {
metrics: AggDescriptor[];
};

export type ESGeoGridSourceDescriptor = AbstractESAggDescriptor & {
export type ESGeoGridSourceDescriptor = AbstractESAggSourceDescriptor & {
requestType?: RENDER_AS;
resolution?: GRID_RESOLUTION;
};
Expand All @@ -54,12 +54,12 @@ export type ESSearchSourceDescriptor = AbstractESSourceDescriptor & {
topHitsSize?: number;
};

export type ESPewPewSourceDescriptor = AbstractESAggDescriptor & {
export type ESPewPewSourceDescriptor = AbstractESAggSourceDescriptor & {
sourceGeoField: string;
destGeoField: string;
};

export type ESTermSourceDescriptor = AbstractESAggDescriptor & {
export type ESTermSourceDescriptor = AbstractESAggSourceDescriptor & {
indexPatternTitle: string;
term: string; // term field name
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,6 @@ export class ESDocField extends AbstractField {

async getCategoricalFieldMetaRequest() {
const field = await this._getField();
if (field.type !== 'string') {
//UX does not support categorical styling for number/date fields
return null;
}

const topTerms = {
size: COLOR_PALETTE_MAX_SIZE - 1, //need additional color for the "other"-value
};
Expand Down
12 changes: 0 additions & 12 deletions x-pack/legacy/plugins/maps/public/layers/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,18 +332,6 @@ export class AbstractLayer {
return [];
}

async getDateFields() {
return [];
}

async getNumberFields() {
return [];
}

async getCategoricalFields() {
return [];
}

async getFields() {
return [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@
import { IESSource } from './es_source';
import { AbstractESSource } from './es_source';
import { AGG_TYPE } from '../../../common/constants';
import { IESAggField } from '../fields/es_agg_field';
import { AbstractESAggSourceDescriptor } from '../../../common/descriptor_types';

export interface IESAggSource extends IESSource {
getAggKey(aggType: AGG_TYPE, fieldName: string): string;
getAggLabel(aggType: AGG_TYPE, fieldName: string): string;
getMetricFields(): IESAggField[];
}

export class AbstractESAggSource extends AbstractESSource implements IESAggSource {
constructor(sourceDescriptor: AbstractESAggSourceDescriptor, inspectorAdapters: object);

getAggKey(aggType: AGG_TYPE, fieldName: string): string;
getAggLabel(aggType: AGG_TYPE, fieldName: string): string;
getMetricFields(): IESAggField[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export class AbstractESAggSource extends AbstractESSource {
}
}

async getFields() {
return this.getMetricFields();
}

getValueAggsDsl(indexPattern) {
const valueAggsDsl = {};
this.getMetricFields().forEach(esAggMetric => {
Expand All @@ -89,10 +93,6 @@ export class AbstractESAggSource extends AbstractESSource {
return valueAggsDsl;
}

async getNumberFields() {
return this.getMetricFields();
}

async filterAndFormatPropertiesToHtmlForMetricFields(properties) {
const metricFields = this.getMetricFields();
const tooltipPropertiesPromises = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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 { AbstractESAggSource } from './es_agg_source';
import { IField } from '../fields/field';
import { IESAggField } from '../fields/es_agg_field';
import _ from 'lodash';
import { AGG_TYPE } from '../../../common/constants';
import { AggDescriptor } from '../../../common/descriptor_types';

jest.mock('ui/new_platform');

const sumFieldName = 'myFieldGettingSummed';
const metricExamples = [
{
type: AGG_TYPE.SUM,
field: sumFieldName,
label: 'my custom label',
},
{
// metric config is invalid beause field is missing
type: AGG_TYPE.MAX,
},
{
// metric config is valid because "count" metric does not need to provide field
type: AGG_TYPE.COUNT,
label: '', // should ignore empty label fields
},
];

class TestESAggSource extends AbstractESAggSource {
constructor(metrics: AggDescriptor[]) {
super({ type: 'test', id: 'foobar', indexPatternId: 'foobarid', metrics }, []);
}
}

describe('getMetricFields', () => {
it('should add default "count" metric when no metrics are provided', async () => {
const source = new TestESAggSource([]);
const metrics = source.getMetricFields();
expect(metrics.length).toBe(1);

expect(metrics[0].getName()).toEqual('doc_count');
expect(await metrics[0].getLabel()).toEqual('count');
});

it('should remove incomplete metric configurations', async () => {
const source = new TestESAggSource(metricExamples);
const metrics = source.getMetricFields();
expect(metrics.length).toBe(2);

expect(metrics[0].getRootName()).toEqual(sumFieldName);
expect(metrics[0].getName()).toEqual('sum_of_myFieldGettingSummed');
expect(await metrics[0].getLabel()).toEqual('my custom label');

expect(metrics[1].getName()).toEqual('doc_count');
expect(await metrics[1].getLabel()).toEqual('count');
});

it('getMetrics should be identical to getFields', async () => {
const source = new TestESAggSource(metricExamples);
const metrics = source.getMetricFields();
const fields = await source.getFields();

const getFieldMeta = async (field: IField) => {
const esAggField = field as IESAggField; // this ensures we can downcast correctly.
return {
name: esAggField.getName(),
label: await esAggField.getLabel(),
esDoc: esAggField.getRootName(),
};
};

const metricsFieldMeta = await Promise.all(metrics.map(getFieldMeta));
const fieldsFieldMeta = await Promise.all(fields.map(getFieldMeta));

expect(_.isEqual(metricsFieldMeta, fieldsFieldMeta)).toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

import { AbstractESAggSource } from '../es_agg_source';
import { ESGeoGridSourceDescriptor } from '../../../../common/descriptor_types';
import { GRID_RESOLUTION } from '../../../../common/constants';

export class ESGeoGridSource extends AbstractESAggSource {
constructor(sourceDescriptor: ESGeoGridSourceDescriptor, inspectorAdapters: unknown);
getGridResolution(): GRID_RESOLUTION;
getGeoGridPrecision(zoom: number): number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_sty
import { StaticStyleProperty } from '../../styles/vector/properties/static_style_property';
import { DataRequestAbortError } from '../../util/data_request';

const MAX_GEOTILE_LEVEL = 29;
export const MAX_GEOTILE_LEVEL = 29;

export class ESGeoGridSource extends AbstractESAggSource {
static type = ES_GEO_GRID;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.
*/
jest.mock('../../../kibana_services', () => {});
jest.mock('ui/new_platform');

import { ESGeoGridSource } from './es_geo_grid_source';
import { ES_GEO_GRID, GRID_RESOLUTION, RENDER_AS } from '../../../../common/constants';

describe('ESGeoGridSource', () => {
const geogridSource = new ESGeoGridSource(
{
id: 'foobar',
indexPatternId: 'fooIp',
geoField: 'bar',
metrics: [],
resolution: GRID_RESOLUTION.COARSE,
type: ES_GEO_GRID,
requestType: RENDER_AS.HEATMAP,
},
{}
);

describe('getGridResolution', () => {
it('should echo gridResoltuion', () => {
expect(geogridSource.getGridResolution()).toBe(GRID_RESOLUTION.COARSE);
});
});

describe('getGeoGridPrecision', () => {
it('should clamp geo-grid derived zoom to max geotile level supported by ES', () => {
expect(geogridSource.getGeoGridPrecision(29)).toBe(29);
});

it('should use heuristic to derive precision', () => {
expect(geogridSource.getGeoGridPrecision(10)).toBe(12);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
ES_GEO_FIELD_TYPE,
DEFAULT_MAX_BUCKETS_LIMIT,
SORT_ORDER,
CATEGORICAL_DATA_TYPES,
} from '../../../../common/constants';
import { i18n } from '@kbn/i18n';
import { getDataSourceLabel } from '../../../../common/i18n_getters';
Expand Down Expand Up @@ -135,49 +134,6 @@ export class ESSearchSource extends AbstractESSource {
);
}

async getNumberFields() {
try {
const indexPattern = await this.getIndexPattern();
return indexPattern.fields.getByType('number').map(field => {
return this.createField({ fieldName: field.name });
});
} catch (error) {
return [];
}
}

async getDateFields() {
try {
const indexPattern = await this.getIndexPattern();
return indexPattern.fields.getByType('date').map(field => {
return this.createField({ fieldName: field.name });
});
} catch (error) {
return [];
}
}

async getCategoricalFields() {
try {
const indexPattern = await this.getIndexPattern();

const aggFields = [];
CATEGORICAL_DATA_TYPES.forEach(dataType => {
indexPattern.fields.getByType(dataType).forEach(field => {
if (field.aggregatable) {
aggFields.push(field);
}
});
});
return aggFields.map(field => {
return this.createField({ fieldName: field.name });
});
} catch (error) {
//error surfaces in the LayerTOC UI
return [];
}
}

async getFields() {
try {
const indexPattern = await this.getIndexPattern();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,27 @@ const metricExamples = [
];

describe('getMetricFields', () => {
it('should add default "count" metric when no metrics are provided', async () => {
it('should override name and label of count metric', async () => {
const source = new ESTermSource({
indexPatternTitle: indexPatternTitle,
term: termFieldName,
});
const metrics = source.getMetricFields();
expect(metrics.length).toBe(1);

expect(metrics[0].getAggType()).toEqual('count');
expect(metrics[0].getName()).toEqual('__kbnjoin__count_groupby_myIndex.myTermField');
expect(await metrics[0].getLabel()).toEqual('Count of myIndex');
});

it('should remove incomplete metric configurations', async () => {
it('should override name and label of sum metric', async () => {
const source = new ESTermSource({
indexPatternTitle: indexPatternTitle,
term: termFieldName,
metrics: metricExamples,
});
const metrics = source.getMetricFields();
expect(metrics.length).toBe(2);

expect(metrics[0].getAggType()).toEqual('sum');
expect(metrics[0].getRootName()).toEqual(sumFieldName);
expect(metrics[0].getName()).toEqual(
'__kbnjoin__sum_of_myFieldGettingSummed_groupby_myIndex.myTermField'
);
expect(await metrics[0].getLabel()).toEqual('my custom label');

expect(metrics[1].getAggType()).toEqual('count');
expect(metrics[1].getName()).toEqual('__kbnjoin__count_groupby_myIndex.myTermField');
expect(await metrics[1].getLabel()).toEqual('Count of myIndex');
});
Expand Down
12 changes: 0 additions & 12 deletions x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,7 @@ export class AbstractVectorSource extends AbstractSource {
return null;
}

async getDateFields() {
return [];
}

async getNumberFields() {
return [];
}

async getFields() {
return [...(await this.getDateFields()), ...(await this.getNumberFields())];
}

async getCategoricalFields() {
return [];
}

Expand Down
Loading