mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Share global filters & queries with coordinate maps vis. (#30595)
This commit is contained in:
parent
3cc064f6d0
commit
09a6c9dcd0
6 changed files with 151 additions and 41 deletions
|
@ -18,11 +18,9 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { GeohashLayer } from './geohash_layer';
|
||||
import { BaseMapsVisualizationProvider } from './base_maps_visualization';
|
||||
import { TileMapTooltipFormatterProvider } from './editors/_tooltip_formatter';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
||||
export function CoordinateMapsVisualizationProvider(Notifier, Private) {
|
||||
const BaseMapsVisualization = Private(BaseMapsVisualizationProvider);
|
||||
|
@ -37,7 +35,6 @@ export function CoordinateMapsVisualizationProvider(Notifier, Private) {
|
|||
this._notify = new Notifier({ location: 'Coordinate Map' });
|
||||
}
|
||||
|
||||
|
||||
async _makeKibanaMap() {
|
||||
|
||||
await super._makeKibanaMap();
|
||||
|
@ -161,7 +158,6 @@ export function CoordinateMapsVisualizationProvider(Notifier, Private) {
|
|||
}
|
||||
|
||||
_getGeohashOptions() {
|
||||
|
||||
const newParams = this._getMapsParams();
|
||||
const metricAgg = this._getMetricAgg();
|
||||
const boundTooltipFormatter = tooltipFormatter.bind(null, this.vis.getAggConfig(), metricAgg);
|
||||
|
@ -172,7 +168,7 @@ export function CoordinateMapsVisualizationProvider(Notifier, Private) {
|
|||
tooltipFormatter: this._geoJsonFeatureCollectionAndMeta ? boundTooltipFormatter : null,
|
||||
mapType: newParams.mapType,
|
||||
isFilteredByCollar: this._isFilteredByCollar(),
|
||||
fetchBounds: this.getGeohashBounds.bind(this),
|
||||
fetchBounds: () => this.vis.API.getGeohashBounds(), // TODO: Remove this (elastic/kibana#30593)
|
||||
colorRamp: newParams.colorSchema,
|
||||
heatmap: {
|
||||
heatClusterSize: newParams.heatClusterSize
|
||||
|
@ -196,47 +192,12 @@ export function CoordinateMapsVisualizationProvider(Notifier, Private) {
|
|||
this.vis.updateState();
|
||||
}
|
||||
|
||||
async getGeohashBounds() {
|
||||
const agg = this._getGeoHashAgg();
|
||||
if (agg) {
|
||||
const searchSource = this.vis.searchSource.createChild();
|
||||
searchSource.setField('size', 0);
|
||||
searchSource.setField('aggs', () => {
|
||||
const geoBoundsAgg = this.vis.getAggConfig().createAggConfig({
|
||||
type: 'geo_bounds',
|
||||
enabled: true,
|
||||
params: {
|
||||
field: agg.getField()
|
||||
},
|
||||
schema: 'metric',
|
||||
}, { addToAggConfigs: false });
|
||||
return {
|
||||
'1': geoBoundsAgg.toDsl()
|
||||
};
|
||||
});
|
||||
let esResp;
|
||||
try {
|
||||
esResp = await searchSource.fetch();
|
||||
} catch(error) {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('tileMap.coordinateMapsVisualization.unableToGetBoundErrorTitle', {
|
||||
defaultMessage: 'Unable to get bounds',
|
||||
}),
|
||||
text: `${error.message}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
return _.get(esResp, 'aggregations.1.bounds');
|
||||
}
|
||||
}
|
||||
|
||||
_getGeoHashAgg() {
|
||||
return this.vis.getAggConfig().find((agg) => {
|
||||
return _.get(agg, 'type.dslName') === 'geohash_grid';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
_getMetricAgg() {
|
||||
return this.vis.getAggConfig().find((agg) => {
|
||||
return agg.type.type === 'metrics';
|
||||
|
|
|
@ -44,6 +44,7 @@ import {
|
|||
VisualizeLoaderParams,
|
||||
VisualizeUpdateParams,
|
||||
} from './types';
|
||||
import { queryGeohashBounds } from './utils';
|
||||
|
||||
interface EmbeddedVisualizeHandlerParams extends VisualizeLoaderParams {
|
||||
Private: IPrivate;
|
||||
|
@ -157,6 +158,16 @@ export class EmbeddedVisualizeHandler {
|
|||
timefilter.on('autoRefreshFetch', this.reload);
|
||||
}
|
||||
|
||||
// This is a hack to give maps visualizations access to data in the
|
||||
// globalState, since they can no longer access it via searchSource.
|
||||
// TODO: Remove this as a part of elastic/kibana#30593
|
||||
this.vis.API.getGeohashBounds = () => {
|
||||
return queryGeohashBounds(this.vis, {
|
||||
filters: this.dataLoaderParams.filters,
|
||||
query: this.dataLoaderParams.query,
|
||||
});
|
||||
};
|
||||
|
||||
this.dataLoader = EmbeddedVisualizeHandler.__ENABLE_PIPELINE_DATA_LOADER__
|
||||
? new PipelineDataLoader(vis)
|
||||
: new VisualizeDataLoader(vis, Private);
|
||||
|
|
20
src/legacy/ui/public/visualize/loader/utils/index.ts
Normal file
20
src/legacy/ui/public/visualize/loader/utils/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export { queryGeohashBounds } from './query_geohash_bounds';
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { get } from 'lodash';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
||||
import { AggConfig } from 'ui/vis';
|
||||
import { Vis } from '../../../vis';
|
||||
import { Filters, Query } from '../types';
|
||||
|
||||
interface QueryGeohashBoundsParams {
|
||||
filters?: Filters;
|
||||
query?: Query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinate map visualization needs to be able to query for the latest geohash
|
||||
* bounds when a user clicks the "fit to data" map icon, which requires knowing
|
||||
* about global filters & queries. This logic has been extracted here so we can
|
||||
* keep `searchSource` out of the vis, but ultimately we need to design a
|
||||
* long-term solution for situations like this.
|
||||
*
|
||||
* TODO: Remove this as a part of elastic/kibana#30593
|
||||
*/
|
||||
export async function queryGeohashBounds(vis: Vis, params: QueryGeohashBoundsParams) {
|
||||
const agg = vis.getAggConfig().find((a: AggConfig) => {
|
||||
return get(a, 'type.dslName') === 'geohash_grid';
|
||||
});
|
||||
|
||||
if (agg) {
|
||||
const searchSource = vis.searchSource.createChild();
|
||||
searchSource.setField('size', 0);
|
||||
searchSource.setField('aggs', () => {
|
||||
const geoBoundsAgg = vis.getAggConfig().createAggConfig(
|
||||
{
|
||||
type: 'geo_bounds',
|
||||
enabled: true,
|
||||
params: {
|
||||
field: agg.getField(),
|
||||
},
|
||||
schema: 'metric',
|
||||
},
|
||||
{
|
||||
addToAggConfigs: false,
|
||||
}
|
||||
);
|
||||
return {
|
||||
'1': geoBoundsAgg.toDsl(),
|
||||
};
|
||||
});
|
||||
|
||||
const { filters, query } = params;
|
||||
if (filters) {
|
||||
searchSource.setField('filter', () => {
|
||||
const activeFilters = [...filters];
|
||||
const indexPattern = agg.getIndexPattern();
|
||||
const useTimeFilter = !!indexPattern.timeFieldName;
|
||||
if (useTimeFilter) {
|
||||
activeFilters.push(vis.API.timeFilter.createFilter(indexPattern));
|
||||
}
|
||||
return activeFilters;
|
||||
});
|
||||
}
|
||||
if (query) {
|
||||
searchSource.setField('query', query);
|
||||
}
|
||||
|
||||
try {
|
||||
const esResp = await searchSource.fetch();
|
||||
return get(esResp, 'aggregations.1.bounds');
|
||||
} catch (error) {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('common.ui.visualize.queryGeohashBounds.unableToGetBoundErrorTitle', {
|
||||
defaultMessage: 'Unable to get bounds',
|
||||
}),
|
||||
text: `${error.message}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
const retry = getService('retry');
|
||||
const inspector = getService('inspector');
|
||||
const find = getService('find');
|
||||
const filterBar = getService('filterBar');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'settings']);
|
||||
|
@ -165,6 +166,26 @@ export default function ({ getService, getPageObjects }) {
|
|||
compareTableData(data, expectedPrecision2DataTable);
|
||||
});
|
||||
|
||||
it('Fit data bounds works with pinned filter data', async () => {
|
||||
const expectedPrecision2DataTable = [
|
||||
['-', 'f05', '1', { lat: 45, lon: -85 }],
|
||||
['-', 'dpr', '1', { lat: 40, lon: -79 }],
|
||||
['-', '9qh', '1', { lat: 33, lon: -118 }],
|
||||
];
|
||||
|
||||
await filterBar.addFilter('bytes', 'is between', '19980', '19990');
|
||||
await filterBar.toggleFilterPinned('bytes');
|
||||
await PageObjects.visualize.zoomAllTheWayOut();
|
||||
await PageObjects.visualize.clickMapFitDataBounds();
|
||||
|
||||
await inspector.open();
|
||||
const data = await inspector.getTableData();
|
||||
await inspector.close();
|
||||
|
||||
await filterBar.removeAllFilters();
|
||||
compareTableData(data, expectedPrecision2DataTable);
|
||||
});
|
||||
|
||||
it('Newly saved visualization retains map bounds', async () => {
|
||||
const vizName1 = 'Visualization TileMap';
|
||||
|
||||
|
|
|
@ -2412,7 +2412,6 @@
|
|||
"tagCloud.visParams.showLabelToggleLabel": "显示标签",
|
||||
"tagCloud.visParams.textScaleLabel": "文本比例",
|
||||
"tileMap.baseMapsVisualization.childShouldImplementMethodErrorMessage": "子函数应实现此方法以响应数据更新",
|
||||
"tileMap.coordinateMapsVisualization.unableToGetBoundErrorTitle": "无法获取边界",
|
||||
"tileMap.geohashLayer.mapTitle": "{mapType} 地图类型无法识别",
|
||||
"tileMap.tooltipFormatter.latitudeLabel": "纬度",
|
||||
"tileMap.tooltipFormatter.longitudeLabel": "经度",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue