From 73c32dfe40273a8fa138c719b0e68028391afebb Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 11 May 2021 16:25:52 -0600 Subject: [PATCH 1/9] [Maps] filter dashboard by map extent --- .../public/actions/apply_filter_action.ts | 13 ++- .../elasticsearch_geo_utils.test.js | 10 +- .../elasticsearch_geo_utils.ts | 55 +++++++--- .../maps/public/classes/layers/layer.tsx | 9 ++ .../classes/sources/es_source/es_source.ts | 2 +- .../maps/public/embeddable/map_embeddable.tsx | 101 ++++++++++++++++-- x-pack/plugins/maps/public/plugin.ts | 3 + .../maps/public/selectors/map_selectors.ts | 20 ++++ .../filter_by_map_extent_action.tsx | 79 ++++++++++++++ 9 files changed, 265 insertions(+), 27 deletions(-) create mode 100644 x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx diff --git a/src/plugins/data/public/actions/apply_filter_action.ts b/src/plugins/data/public/actions/apply_filter_action.ts index d4ac72294e257..e0cb6ba4dfca6 100644 --- a/src/plugins/data/public/actions/apply_filter_action.ts +++ b/src/plugins/data/public/actions/apply_filter_action.ts @@ -21,6 +21,9 @@ export interface ApplyGlobalFilterActionContext { // Need to make this unknown to prevent circular dependencies. // Apps using this property will need to cast to `IEmbeddable`. embeddable?: unknown; + // controlledBy is an optional key in filter.meta that identifies the owner of a filter + // Pass controlledBy to cleanup an existing filter(s) owned by embeddable prior to adding new filters + controlledBy?: string; } async function isCompatible(context: ApplyGlobalFilterActionContext) { @@ -42,7 +45,7 @@ export function createFilterAction( }); }, isCompatible, - execute: async ({ filters, timeFieldName }: ApplyGlobalFilterActionContext) => { + execute: async ({ filters, timeFieldName, controlledBy }: ApplyGlobalFilterActionContext) => { if (!filters) { throw new Error('Applying a filter requires a filter'); } @@ -85,6 +88,14 @@ export function createFilterAction( selectedFilters = await filterSelectionPromise; } + if (controlledBy) { + filterManager.getFilters().forEach((filter) => { + if (filter.meta.controlledBy === controlledBy) { + filterManager.removeFilter(filter); + } + }); + } + if (timeFieldName) { const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter( timeFieldName, diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js index 22b8a86158a74..47c7e0cf32cde 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js @@ -396,7 +396,7 @@ describe('createExtentFilter', () => { minLat: 35, minLon: -89, }; - const filter = createExtentFilter(mapExtent, geoFieldName); + const filter = createExtentFilter(mapExtent, [geoFieldName]); expect(filter.geo_bounding_box).toEqual({ location: { top_left: [-89, 39], @@ -412,7 +412,7 @@ describe('createExtentFilter', () => { minLat: -100, minLon: -190, }; - const filter = createExtentFilter(mapExtent, geoFieldName); + const filter = createExtentFilter(mapExtent, [geoFieldName]); expect(filter.geo_bounding_box).toEqual({ location: { top_left: [-180, 89], @@ -428,7 +428,7 @@ describe('createExtentFilter', () => { minLat: 35, minLon: 100, }; - const filter = createExtentFilter(mapExtent, geoFieldName); + const filter = createExtentFilter(mapExtent, [geoFieldName]); const leftLon = filter.geo_bounding_box.location.top_left[0]; const rightLon = filter.geo_bounding_box.location.bottom_right[0]; expect(leftLon).toBeGreaterThan(rightLon); @@ -447,7 +447,7 @@ describe('createExtentFilter', () => { minLat: 35, minLon: -200, }; - const filter = createExtentFilter(mapExtent, geoFieldName); + const filter = createExtentFilter(mapExtent, [geoFieldName]); const leftLon = filter.geo_bounding_box.location.top_left[0]; const rightLon = filter.geo_bounding_box.location.bottom_right[0]; expect(leftLon).toBeGreaterThan(rightLon); @@ -466,7 +466,7 @@ describe('createExtentFilter', () => { minLat: 35, minLon: -191, }; - const filter = createExtentFilter(mapExtent, geoFieldName); + const filter = createExtentFilter(mapExtent, [geoFieldName]); expect(filter.geo_bounding_box).toEqual({ location: { top_left: [-180, 39], diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts index c18a79fa9dcbc..e47afce77f779 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts @@ -349,18 +349,49 @@ export function makeESBbox({ maxLat, maxLon, minLat, minLon }: MapExtent): ESBBo return esBbox; } -export function createExtentFilter(mapExtent: MapExtent, geoFieldName: string): GeoFilter { - return { - geo_bounding_box: { - [geoFieldName]: makeESBbox(mapExtent), - }, - meta: { - alias: null, - disabled: false, - negate: false, - key: geoFieldName, - }, - }; +export function createExtentFilter(mapExtent: MapExtent, geoFieldNames: string[]): GeoFilter { + const esBbox = makeESBbox(mapExtent); + return geoFieldNames.length === 1 + ? { + geo_bounding_box: { + [geoFieldNames[0]]: esBbox, + }, + meta: { + alias: null, + disabled: false, + negate: false, + key: geoFieldNames[0], + }, + } + : { + query: { + bool: { + should: geoFieldNames.map((geoFieldName) => { + return { + bool: { + must: [ + { + exists: { + field: geoFieldName, + }, + }, + { + geo_bounding_box: { + [geoFieldName]: esBbox, + }, + }, + ], + }, + }; + }), + }, + }, + meta: { + alias: null, + disabled: false, + negate: false, + }, + }; } export function createSpatialFilterWithGeometry({ diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 4167ed4775219..04ee91ee1db51 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -42,6 +42,7 @@ import { DataRequestContext } from '../../actions'; import { IStyle } from '../styles/style'; import { getJoinAggKey } from '../../../common/get_agg_key'; import { LICENSED_FEATURES } from '../../licensed_features'; +import { IESSource } from '../sources/es_source'; export interface ILayer { getBounds(dataRequestContext: DataRequestContext): Promise; @@ -100,6 +101,7 @@ export interface ILayer { getLicensedFeatures(): Promise; getCustomIconAndTooltipContent(): CustomIconAndTooltipContent; getDescriptor(): LayerDescriptor; + getGeoFieldNames(): string[]; } export type CustomIconAndTooltipContent = { @@ -506,4 +508,11 @@ export class AbstractLayer implements ILayer { async getLicensedFeatures(): Promise { return []; } + + getGeoFieldNames(): string[] { + const geoFieldNames = []; + + const source = this.getSource(); + return source.isESSource() ? [(source as IESSource).getGeoFieldName()] : []; + } } diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts index 8e31ad7855197..749e3d6058266 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts @@ -213,7 +213,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource typeof searchFilters.geogridPrecision === 'number' ? expandToTileBoundaries(searchFilters.buffer, searchFilters.geogridPrecision) : searchFilters.buffer; - const extentFilter = createExtentFilter(buffer, geoField.name); + const extentFilter = createExtentFilter(buffer, [geoField.name]); allFilters.push(extentFilter); } diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 643199dbf3933..077473b7a6c9a 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; import _ from 'lodash'; import React from 'react'; import { Provider } from 'react-redux'; @@ -27,6 +28,7 @@ import { Query, RefreshInterval, } from '../../../../../src/plugins/data/public'; +import { createExtentFilter } from '../../common/elasticsearch_util'; import { replaceLayerList, setMapSettings, @@ -43,8 +45,11 @@ import { EventHandlers, } from '../reducers/non_serializable_instances'; import { + getGeoFieldNames, getMapCenter, getMapBuffer, + getMapExtent, + getMapReady, getMapZoom, getHiddenLayerIds, getQueryableUniqueIndexPatternIds, @@ -64,7 +69,7 @@ import { getChartsPaletteServiceGetColor, getSearchService, } from '../kibana_services'; -import { LayerDescriptor } from '../../common/descriptor_types'; +import { LayerDescriptor, MapExtent } from '../../common/descriptor_types'; import { MapContainer } from '../connected_components/map_container'; import { SavedMap } from '../routes/map_page'; import { getIndexPatternsFromIds } from '../index_pattern_util'; @@ -96,7 +101,9 @@ export class MapEmbeddable private _savedMap: SavedMap; private _renderTooltipContent?: RenderToolTipContent; private _subscription: Subscription; + private _prevFilterByMapExtent: boolean; private _prevIsRestore: boolean = false; + private _prevMapExtent?: MapExtent; private _prevTimeRange?: TimeRange; private _prevQuery?: Query; private _prevRefreshConfig?: RefreshInterval; @@ -106,6 +113,7 @@ export class MapEmbeddable private _domNode?: HTMLElement; private _unsubscribeFromStore?: Unsubscribe; private _isInitialized = false; + private _controlledBy: string; constructor(config: MapEmbeddableConfig, initialInput: MapEmbeddableInput, parent?: IContainer) { super( @@ -122,6 +130,9 @@ export class MapEmbeddable this._savedMap = new SavedMap({ mapEmbeddableInput: initialInput }); this._initializeSaveMap(); this._subscription = this.getUpdated$().subscribe(() => this.onUpdate()); + this._controlledBy = `mapEmbeddablePanel${this.id}`; + this._prevFilterByMapExtent = + this.input.filterByMapExtent === undefined ? false : this.input.filterByMapExtent; } private async _initializeSaveMap() { @@ -221,10 +232,22 @@ export class MapEmbeddable } onUpdate() { + if ( + this.input.filterByMapExtent !== undefined && + this._prevFilterByMapExtent !== this.input.filterByMapExtent + ) { + this._prevFilterByMapExtent = this.input.filterByMapExtent; + if (this.input.filterByMapExtent) { + this.setMapExtentFilter(); + } else { + this.clearMapExtentFilter(); + } + } + if ( !_.isEqual(this.input.timeRange, this._prevTimeRange) || !_.isEqual(this.input.query, this._prevQuery) || - !esFilters.onlyDisabledFiltersChanged(this.input.filters, this._prevFilters) || + !esFilters.compareFilters(this._getFilters(), this._prevFilters) || this.input.searchSessionId !== this._prevSearchSessionId ) { this._dispatchSetQuery({ @@ -252,17 +275,23 @@ export class MapEmbeddable } } + _getFilters() { + return this.input.filters + ? this.input.filters.filter( + (filter) => !filter.meta.disabled && filter.meta.controlledBy !== this._controlledBy + ) + : []; + } + _dispatchSetQuery({ forceRefresh }: { forceRefresh: boolean }) { + const filters = this._getFilters(); this._prevTimeRange = this.input.timeRange; this._prevQuery = this.input.query; - this._prevFilters = this.input.filters; + this._prevFilters = filters; this._prevSearchSessionId = this.input.searchSessionId; - const enabledFilters = this.input.filters - ? this.input.filters.filter((filter) => !filter.meta.disabled) - : []; this._savedMap.getStore().dispatch( setQuery({ - filters: enabledFilters, + filters, query: this.input.query, timeFilters: this.input.timeRange, forceRefresh, @@ -403,6 +432,56 @@ export class MapEmbeddable } as ActionExecutionContext; }; + setMapExtentFilter() { + const state = this._savedMap.getStore().getState(); + const mapExtent = getMapExtent(state); + const geoFieldNames = getGeoFieldNames(state); + const center = getMapCenter(state); + const zoom = getMapZoom(state); + + if (center === undefined || mapExtent === undefined || geoFieldNames.length === 0) { + return; + } + + this._prevMapExtent = mapExtent; + + const mapExtentFilter = createExtentFilter(mapExtent, geoFieldNames); + mapExtentFilter.meta.controlledBy = this._controlledBy; + mapExtentFilter.meta.alias = i18n.translate('xpack.maps.embeddable.boundsFilterLabel', { + defaultMessage: 'Map bounds at center: {lat}, {lon}, zoom: {zoom}', + values: { + lat: center.lat, + lon: center.lon, + zoom, + }, + }); + + const executeContext = { + ...this.getActionContext(), + filters: [mapExtentFilter], + controlledBy: this._controlledBy, + }; + const action = getUiActions().getAction(ACTION_GLOBAL_APPLY_FILTER); + if (!action) { + throw new Error('Unable to apply map extent filter, could not locate action'); + } + action.execute(executeContext); + } + + clearMapExtentFilter() { + this._prevMapExtent = undefined; + const executeContext = { + ...this.getActionContext(), + filters: [], + controlledBy: this._controlledBy, + }; + const action = getUiActions().getAction(ACTION_GLOBAL_APPLY_FILTER); + if (!action) { + throw new Error('Unable to apply map extent filter, could not locate action'); + } + action.execute(executeContext); + } + destroy() { super.destroy(); this._isActive = false; @@ -426,9 +505,15 @@ export class MapEmbeddable } _handleStoreChanges() { - if (!this._isActive) { + if (!this._isActive || !getMapReady(this._savedMap.getStore().getState())) { return; } + + const mapExtent = getMapExtent(this._savedMap.getStore().getState()); + if (this.input.filterByMapExtent && !_.isEqual(this._prevMapExtent, mapExtent)) { + this.setMapExtentFilter(); + } + const center = getMapCenter(this._savedMap.getStore().getState()); const zoom = getMapZoom(this._savedMap.getStore().getState()); diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index ad8846bd48b60..740112124a251 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -42,8 +42,10 @@ import { createTileMapUrlGenerator, } from './url_generator'; import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action'; +import { filterByMapExtentAction } from './trigger_actions/filter_by_map_extent_action'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import type { EmbeddableSetup, EmbeddableStart } from '../../../../src/plugins/embeddable/public'; +import { CONTEXT_MENU_TRIGGER } from '../../../../src/plugins/embeddable/public'; import { MapsXPackConfig, MapsConfigType } from '../config'; import { getAppTitle } from '../common/i18n_getters'; import { lazyLoadMapModules } from './lazy_load_bundle'; @@ -173,6 +175,7 @@ export class MapsPlugin if (core.application.capabilities.maps.show) { plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); } + plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, filterByMapExtentAction); if (!core.application.capabilities.maps.save) { plugins.visualizations.unRegisterAlias(APP_ID); diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index a818cdd2d00f9..4f3bfbe303cb9 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -401,6 +401,26 @@ export const getQueryableUniqueIndexPatternIds = createSelector( } ); +export const getGeoFieldNames = createSelector( + getLayerList, + getWaitingForMapReadyLayerListRaw, + (layerList, waitingForMapReadyLayerList) => { + const geoFieldNames: string[] = []; + + if (waitingForMapReadyLayerList.length) { + waitingForMapReadyLayerList.forEach((layerDescriptor) => { + const layer = createLayerInstance(layerDescriptor); + geoFieldNames.push(...layer.getGeoFieldNames()); + }); + } else { + layerList.forEach((layer) => { + geoFieldNames.push(...layer.getGeoFieldNames()); + }); + } + return _.uniq(geoFieldNames); + } +); + export const hasDirtyState = createSelector(getLayerListRaw, (layerListRaw) => { return layerListRaw.some((layerDescriptor) => { if (layerDescriptor.__isPreviewLayer) { diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx new file mode 100644 index 0000000000000..d970904731163 --- /dev/null +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { Embeddable, EmbeddableInput } from 'src/plugins/embeddable/public'; +import { Action } from '../../../../src/plugins/ui_actions/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; +import { createAction } from '../../../../../src/plugins/ui_actions/public'; + +export const FILTER_BY_MAP_EXTENT = 'FILTER_BY_MAP_EXTENT'; + +interface FilterByMapExtentInput extends EmbeddableInput { + filterByMapExtent: boolean; +} + +interface FilterByMapExtentActionContext { + embeddable: Embeddable; +} + +export const filterByMapExtentAction = createAction({ + id: FILTER_BY_MAP_EXTENT, + type: FILTER_BY_MAP_EXTENT, + order: 20, + getDisplayName: ({ embeddable }: FilterByMapExtentActionContext) => { + return embeddable.getInput().filterByMapExtent + ? i18n.translate('xpack.map.configureFilterByMapExtentMenuItem.displayName', { + defaultMessage: 'Disable filter by map extent', + }) + : i18n.translate('xpack.map.configureFilterByMapExtentMenuItem.displayName', { + defaultMessage: 'Filter by map extent', + }); + }, + getIconType: () => { + return 'filter'; + }, + isCompatible: async ({ embeddable }: FilterByMapExtentActionContext) => { + return embeddable.type === MAP_SAVED_OBJECT_TYPE; + }, + execute: async ({ embeddable }: FilterByMapExtentActionContext) => { + embeddable.updateInput({ + filterByMapExtent: !embeddable.getInput().filterByMapExtent, + }); + }, +}); + +/* export class FilterByMapExtentAction implements Action { + public readonly type = FILTER_BY_MAP_EXTENT; + public readonly id = FILTER_BY_MAP_EXTENT; + public order = 20; + + public getDisplayName({ embeddable }: FilterByMapExtentActionContext) { + return embeddable.getInput().filterByMapExtent + ? i18n.translate('xpack.map.configureFilterByMapExtentMenuItem.displayName', { + defaultMessage: 'Disable filter by map extent', + }) + : i18n.translate('xpack.map.configureFilterByMapExtentMenuItem.displayName', { + defaultMessage: 'Filter by map extent', + }); + } + + public getIconType() { + return 'filter'; + } + + public async isCompatible({ embeddable }: FilterByMapExtentActionContext) { + return embeddable.type === MAP_SAVED_OBJECT_TYPE; + } + + public async execute({ embeddable }: FilterByMapExtentActionContext) { + embeddable.updateInput({ + filterByMapExtent: !embeddable.getInput().filterByMapExtent, + }); + } +}*/ From 8e3133a087dcc5d6a4a2c9828864fd6f26a19db5 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 11 May 2021 16:33:36 -0600 Subject: [PATCH 2/9] clean up --- .../filter_by_map_extent_action.tsx | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx index d970904731163..84f3857ffc497 100644 --- a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx @@ -47,33 +47,3 @@ export const filterByMapExtentAction = createAction { - public readonly type = FILTER_BY_MAP_EXTENT; - public readonly id = FILTER_BY_MAP_EXTENT; - public order = 20; - - public getDisplayName({ embeddable }: FilterByMapExtentActionContext) { - return embeddable.getInput().filterByMapExtent - ? i18n.translate('xpack.map.configureFilterByMapExtentMenuItem.displayName', { - defaultMessage: 'Disable filter by map extent', - }) - : i18n.translate('xpack.map.configureFilterByMapExtentMenuItem.displayName', { - defaultMessage: 'Filter by map extent', - }); - } - - public getIconType() { - return 'filter'; - } - - public async isCompatible({ embeddable }: FilterByMapExtentActionContext) { - return embeddable.type === MAP_SAVED_OBJECT_TYPE; - } - - public async execute({ embeddable }: FilterByMapExtentActionContext) { - embeddable.updateInput({ - filterByMapExtent: !embeddable.getInput().filterByMapExtent, - }); - } -}*/ From 52d6025809be4a0b5c2014453822f42df2bbb3bb Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 12 May 2021 08:05:36 -0600 Subject: [PATCH 3/9] remove warning from filter pill --- src/plugins/data/common/es_query/filters/meta_filter.ts | 1 + src/plugins/data/public/ui/filter_bar/filter_item.tsx | 5 +++++ x-pack/plugins/maps/public/embeddable/map_embeddable.tsx | 1 + 3 files changed, 7 insertions(+) diff --git a/src/plugins/data/common/es_query/filters/meta_filter.ts b/src/plugins/data/common/es_query/filters/meta_filter.ts index c47dcb245cbf0..87455cf1cb763 100644 --- a/src/plugins/data/common/es_query/filters/meta_filter.ts +++ b/src/plugins/data/common/es_query/filters/meta_filter.ts @@ -31,6 +31,7 @@ export type FilterMeta = { controlledBy?: string; // index and type are optional only because when you create a new filter, there are no defaults index?: string; + isMultiIndex?: boolean; type?: string; key?: string; params?: any; diff --git a/src/plugins/data/public/ui/filter_bar/filter_item.tsx b/src/plugins/data/public/ui/filter_bar/filter_item.tsx index 5ad88e6fdf5be..9e5090f945182 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_item.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_item.tsx @@ -286,6 +286,11 @@ export function FilterItem(props: FilterItemProps) { message: '', status: FILTER_ITEM_OK, }; + + if (filter.meta?.isMultiIndex) { + return label; + } + if (indexPatternExists === false) { label.status = FILTER_ITEM_ERROR; label.title = props.intl.formatMessage({ diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 077473b7a6c9a..1c7c566d3efca 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -446,6 +446,7 @@ export class MapEmbeddable this._prevMapExtent = mapExtent; const mapExtentFilter = createExtentFilter(mapExtent, geoFieldNames); + mapExtentFilter.meta.isMultiIndex = true; mapExtentFilter.meta.controlledBy = this._controlledBy; mapExtentFilter.meta.alias = i18n.translate('xpack.maps.embeddable.boundsFilterLabel', { defaultMessage: 'Map bounds at center: {lat}, {lon}, zoom: {zoom}', From f6d1aa86b2a9bc1b58a04de63d81c1074d28b3f0 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 12 May 2021 08:20:09 -0600 Subject: [PATCH 4/9] tslint --- x-pack/plugins/maps/public/classes/layers/layer.tsx | 2 -- x-pack/plugins/maps/public/embeddable/map_embeddable.tsx | 2 +- x-pack/plugins/maps/public/embeddable/types.ts | 7 ++++--- ...ap_extent_action.tsx => filter_by_map_extent_action.ts} | 2 -- 4 files changed, 5 insertions(+), 8 deletions(-) rename x-pack/plugins/maps/public/trigger_actions/{filter_by_map_extent_action.tsx => filter_by_map_extent_action.ts} (94%) diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 04ee91ee1db51..c911f459a7d1a 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -510,8 +510,6 @@ export class AbstractLayer implements ILayer { } getGeoFieldNames(): string[] { - const geoFieldNames = []; - const source = this.getSource(); return source.isESSource() ? [(source as IESSource).getGeoFieldName()] : []; } diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 1c7c566d3efca..73239c7e3e052 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -107,7 +107,7 @@ export class MapEmbeddable private _prevTimeRange?: TimeRange; private _prevQuery?: Query; private _prevRefreshConfig?: RefreshInterval; - private _prevFilters?: Filter[]; + private _prevFilters: Filter[] = []; private _prevSyncColors?: boolean; private _prevSearchSessionId?: string; private _domNode?: HTMLElement; diff --git a/x-pack/plugins/maps/public/embeddable/types.ts b/x-pack/plugins/maps/public/embeddable/types.ts index 7cd4fa8e1253b..268cdebaf7886 100644 --- a/x-pack/plugins/maps/public/embeddable/types.ts +++ b/x-pack/plugins/maps/public/embeddable/types.ts @@ -35,9 +35,10 @@ interface MapEmbeddableState { } export type MapByValueInput = { attributes: MapSavedObjectAttributes; -} & EmbeddableInput & - MapEmbeddableState; -export type MapByReferenceInput = SavedObjectEmbeddableInput & MapEmbeddableState; +} & EmbeddableInput & { filterByMapExtent: boolean } & MapEmbeddableState; +export type MapByReferenceInput = SavedObjectEmbeddableInput & { + filterByMapExtent: boolean; +} & MapEmbeddableState; export type MapEmbeddableInput = MapByValueInput | MapByReferenceInput; export type MapEmbeddableOutput = EmbeddableOutput & { diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts similarity index 94% rename from x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx rename to x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts index 84f3857ffc497..12e9491233925 100644 --- a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts @@ -6,9 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import React from 'react'; import { Embeddable, EmbeddableInput } from 'src/plugins/embeddable/public'; -import { Action } from '../../../../src/plugins/ui_actions/public'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; import { createAction } from '../../../../../src/plugins/ui_actions/public'; From 1ce2938587565ddbd2e77f5094b113b9a03d0b53 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 12 May 2021 09:26:45 -0600 Subject: [PATCH 5/9] API doc updates, i18n fixes, tslint --- ...lic.applyglobalfilteractioncontext.controlledby.md | 11 +++++++++++ ...gins-data-public.applyglobalfilteractioncontext.md | 1 + .../kibana-plugin-plugins-data-public.esfilters.md | 1 + src/plugins/data/public/public.api.md | 7 +++++-- src/plugins/data/server/server.api.md | 4 ++-- x-pack/plugins/maps/public/embeddable/types.ts | 4 ++-- .../trigger_actions/filter_by_map_extent_action.ts | 4 ++-- 7 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md new file mode 100644 index 0000000000000..d9c47dec9e9d4 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) > [controlledBy](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md) + +## ApplyGlobalFilterActionContext.controlledBy property + +Signature: + +```typescript +controlledBy?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md index 2f844b6844645..01ccd4819d906 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md @@ -14,6 +14,7 @@ export interface ApplyGlobalFilterActionContext | Property | Type | Description | | --- | --- | --- | +| [controlledBy](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md) | string | | | [embeddable](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md) | unknown | | | [filters](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md) | Filter[] | | | [timeFieldName](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index 742b54e19216e..54b5a33ccf682 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -33,6 +33,7 @@ esFilters: { disabled: boolean; controlledBy?: string | undefined; index?: string | undefined; + isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index d4f0ccfe810c6..bb1684c658362 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -492,6 +492,8 @@ export const APPLY_FILTER_TRIGGER = "FILTER_TRIGGER"; // // @public (undocumented) export interface ApplyGlobalFilterActionContext { + // (undocumented) + controlledBy?: string; // (undocumented) embeddable?: unknown; // (undocumented) @@ -763,6 +765,7 @@ export const esFilters: { disabled: boolean; controlledBy?: string | undefined; index?: string | undefined; + isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; @@ -2689,8 +2692,8 @@ export interface WaitUntilNextSessionCompletesOptions { // src/plugins/data/common/es_query/filters/exists_filter.ts:19:3 - (ae-forgotten-export) The symbol "ExistsFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/exists_filter.ts:20:3 - (ae-forgotten-export) The symbol "FilterExistsProperty" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/match_all_filter.ts:17:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:42:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:44:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrase_filter.ts:22:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrases_filter.ts:20:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index e66eaab672e1c..8ef10451398a6 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -1509,8 +1509,8 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // Warnings were encountered during analysis: // -// src/plugins/data/common/es_query/filters/meta_filter.ts:42:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:44:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts diff --git a/x-pack/plugins/maps/public/embeddable/types.ts b/x-pack/plugins/maps/public/embeddable/types.ts index 268cdebaf7886..79a70f3786fe6 100644 --- a/x-pack/plugins/maps/public/embeddable/types.ts +++ b/x-pack/plugins/maps/public/embeddable/types.ts @@ -35,9 +35,9 @@ interface MapEmbeddableState { } export type MapByValueInput = { attributes: MapSavedObjectAttributes; -} & EmbeddableInput & { filterByMapExtent: boolean } & MapEmbeddableState; +} & EmbeddableInput & { filterByMapExtent?: boolean } & MapEmbeddableState; export type MapByReferenceInput = SavedObjectEmbeddableInput & { - filterByMapExtent: boolean; + filterByMapExtent?: boolean; } & MapEmbeddableState; export type MapEmbeddableInput = MapByValueInput | MapByReferenceInput; diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts index 12e9491233925..e587d2f1b5a9b 100644 --- a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts @@ -26,10 +26,10 @@ export const filterByMapExtentAction = createAction { return embeddable.getInput().filterByMapExtent - ? i18n.translate('xpack.map.configureFilterByMapExtentMenuItem.displayName', { + ? i18n.translate('xpack.maps.filterByMapExtentMenuItem.disableDisplayName', { defaultMessage: 'Disable filter by map extent', }) - : i18n.translate('xpack.map.configureFilterByMapExtentMenuItem.displayName', { + : i18n.translate('xpack.maps.filterByMapExtentMenuItem.enableDisplayName', { defaultMessage: 'Filter by map extent', }); }, From 585a49aafb10e46d88de68feea8c3ddeed12ebf1 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 12 May 2021 12:08:56 -0600 Subject: [PATCH 6/9] only show context menu option in edit mode --- .../trigger_actions/filter_by_map_extent_action.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts index e587d2f1b5a9b..5e9cf57fa4293 100644 --- a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts @@ -6,7 +6,11 @@ */ import { i18n } from '@kbn/i18n'; -import { Embeddable, EmbeddableInput } from 'src/plugins/embeddable/public'; +import { + Embeddable, + EmbeddableInput, + ViewMode, +} from '../../../../../src/plugins/embeddable/public'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; import { createAction } from '../../../../../src/plugins/ui_actions/public'; @@ -37,7 +41,9 @@ export const filterByMapExtentAction = createAction { - return embeddable.type === MAP_SAVED_OBJECT_TYPE; + return ( + embeddable.type === MAP_SAVED_OBJECT_TYPE && embeddable.getInput().viewMode === ViewMode.EDIT + ); }, execute: async ({ embeddable }: FilterByMapExtentActionContext) => { embeddable.updateInput({ From a210fef8446dc7872311fb2082968ec794683a89 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 12 May 2021 13:14:17 -0600 Subject: [PATCH 7/9] add functional test --- .../maps/embeddable/filter_by_map_extent.js | 58 +++++++++++++++++++ .../functional/apps/maps/embeddable/index.js | 1 + .../es_archives/maps/kibana/data.json | 50 ++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 x-pack/test/functional/apps/maps/embeddable/filter_by_map_extent.js diff --git a/x-pack/test/functional/apps/maps/embeddable/filter_by_map_extent.js b/x-pack/test/functional/apps/maps/embeddable/filter_by_map_extent.js new file mode 100644 index 0000000000000..efe02d2d85156 --- /dev/null +++ b/x-pack/test/functional/apps/maps/embeddable/filter_by_map_extent.js @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export default function ({ getPageObjects, getService }) { + const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'lens', 'maps']); + + const testSubjects = getService('testSubjects'); + const dashboardPanelActions = getService('dashboardPanelActions'); + const security = getService('security'); + + describe('filter by map extent', () => { + before(async () => { + await security.testUser.setRoles( + ['test_logstash_reader', 'global_maps_all', 'global_dashboard_all'], + false + ); + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.gotoDashboardEditMode('filter by map extent dashboard'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.waitForRenderComplete(); + }); + + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should not filter dashboard by map extent before "filter by map extent" is enabled', async () => { + await PageObjects.lens.assertMetric('Count of records', '6'); + }); + + it('should filter dashboard by map extent when "filter by map extent" is enabled', async () => { + const mapPanelHeader = await dashboardPanelActions.getPanelHeading('document example'); + await dashboardPanelActions.openContextMenuMorePanel(mapPanelHeader); + await await testSubjects.click('embeddablePanelAction-FILTER_BY_MAP_EXTENT'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + await PageObjects.lens.assertMetric('Count of records', '1'); + }); + + it('should filter dashboard by new map extent when map is moved', async () => { + await PageObjects.maps.setView(32.95539, -93.93054, 5); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.assertMetric('Count of records', '2'); + }); + + it('should remove map extent filter dashboard when "filter by map extent" is disabled', async () => { + const mapPanelHeader = await dashboardPanelActions.getPanelHeading('document example'); + await dashboardPanelActions.openContextMenuMorePanel(mapPanelHeader); + await await testSubjects.click('embeddablePanelAction-FILTER_BY_MAP_EXTENT'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.assertMetric('Count of records', '6'); + }); + }); +} diff --git a/x-pack/test/functional/apps/maps/embeddable/index.js b/x-pack/test/functional/apps/maps/embeddable/index.js index 552f830e2a379..da5d4b8945da7 100644 --- a/x-pack/test/functional/apps/maps/embeddable/index.js +++ b/x-pack/test/functional/apps/maps/embeddable/index.js @@ -13,5 +13,6 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./embeddable_library')); loadTestFile(require.resolve('./embeddable_state')); loadTestFile(require.resolve('./tooltip_filter_actions')); + loadTestFile(require.resolve('./filter_by_map_extent')); }); } diff --git a/x-pack/test/functional/es_archives/maps/kibana/data.json b/x-pack/test/functional/es_archives/maps/kibana/data.json index 631efb58f9c7b..4a879c20f19ab 100644 --- a/x-pack/test/functional/es_archives/maps/kibana/data.json +++ b/x-pack/test/functional/es_archives/maps/kibana/data.json @@ -1149,6 +1149,56 @@ } } +{ + "type": "doc", + "value": { + "id": "dashboard:42f6f040-b34f-11eb-8c95-dd19591c63df", + "index": ".kibana", + "source": { + "dashboard": { + "title" : "filter by map extent dashboard", + "hits" : 0, + "description" : "", + "panelsJSON" : "[{\"version\":\"8.0.0\",\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":0,\"w\":29,\"h\":21,\"i\":\"24ade730-afe4-42b6-919a-c4e0a98c94f2\"},\"panelIndex\":\"24ade730-afe4-42b6-919a-c4e0a98c94f2\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":38.64679,\"lon\":-120.96481,\"zoom\":7.06},\"mapBuffer\":{\"minLon\":-125.44180499999999,\"minLat\":36.364824999999996,\"maxLon\":-116.603825,\"maxLat\":40.943405},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_24ade730-afe4-42b6-919a-c4e0a98c94f2\"},{\"version\":\"8.0.0\",\"type\":\"lens\",\"gridData\":{\"x\":29,\"y\":0,\"w\":10,\"h\":21,\"i\":\"44eb3c47-f6ad-4da8-993b-13c10997d585\"},\"panelIndex\":\"44eb3c47-f6ad-4da8-993b-13c10997d585\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsMetric\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"3cda3519-055a-4b9c-8759-caa28388298c\":{\"columns\":{\"26acba84-22ca-4625-b2ac-5309945e9b30\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"}},\"columnOrder\":[\"26acba84-22ca-4625-b2ac-5309945e9b30\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"3cda3519-055a-4b9c-8759-caa28388298c\",\"accessor\":\"26acba84-22ca-4625-b2ac-5309945e9b30\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"name\":\"indexpattern-datasource-layer-3cda3519-055a-4b9c-8759-caa28388298c\"}]},\"enhancements\":{},\"hidePanelTitles\":false},\"title\":\"Count panel\"}]", + "optionsJSON" : "{\"hidePanelTitles\":false,\"useMargins\":true}", + "version" : 1, + "timeRestore" : true, + "timeTo" : "2015-09-20T01:00:00.000Z", + "timeFrom" : "2015-09-20T00:00:00.000Z", + "refreshInterval" : { + "pause" : true, + "value" : 1000 + }, + "kibanaSavedObjectMeta" : { + "searchSourceJSON" : "{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}" + } + }, + "type" : "dashboard", + "references" : [ + { + "name" : "24ade730-afe4-42b6-919a-c4e0a98c94f2:panel_24ade730-afe4-42b6-919a-c4e0a98c94f2", + "type" : "map", + "id" : "d2e73f40-e14a-11e8-a35a-370a8516603a" + }, + { + "type" : "index-pattern", + "id" : "c698b940-e149-11e8-a35a-370a8516603a", + "name" : "44eb3c47-f6ad-4da8-993b-13c10997d585:indexpattern-datasource-current-indexpattern" + }, + { + "type" : "index-pattern", + "id" : "c698b940-e149-11e8-a35a-370a8516603a", + "name" : "44eb3c47-f6ad-4da8-993b-13c10997d585:indexpattern-datasource-layer-3cda3519-055a-4b9c-8759-caa28388298c" + } + ], + "migrationVersion" : { + "dashboard" : "7.11.0" + }, + "updated_at" : "2021-05-12T18:24:17.228Z" + } + } +} + { "type": "doc", "value": { From 3bafeb5a0d1f7df5917fdcc61890f1ae51776418 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 24 May 2021 11:39:55 -0600 Subject: [PATCH 8/9] review feedback --- src/plugins/data/public/actions/apply_filter_action.ts | 1 + .../maps/public/trigger_actions/filter_by_map_extent_action.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/public/actions/apply_filter_action.ts b/src/plugins/data/public/actions/apply_filter_action.ts index e0cb6ba4dfca6..43445d9448f2c 100644 --- a/src/plugins/data/public/actions/apply_filter_action.ts +++ b/src/plugins/data/public/actions/apply_filter_action.ts @@ -88,6 +88,7 @@ export function createFilterAction( selectedFilters = await filterSelectionPromise; } + // remove existing filters for control prior to adding new filtes for control if (controlledBy) { filterManager.getFilters().forEach((filter) => { if (filter.meta.controlledBy === controlledBy) { diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts index 5e9cf57fa4293..7706704cdd63d 100644 --- a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.ts @@ -34,7 +34,7 @@ export const filterByMapExtentAction = createAction { From fe8cbb889176d1ddaba5bef0ff0561c4415e0db5 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 25 May 2021 11:02:46 -0600 Subject: [PATCH 9/9] do not use search session when filtering by map bounds --- .../maps/public/embeddable/map_embeddable.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 73239c7e3e052..65fdbca328542 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -248,7 +248,7 @@ export class MapEmbeddable !_.isEqual(this.input.timeRange, this._prevTimeRange) || !_.isEqual(this.input.query, this._prevQuery) || !esFilters.compareFilters(this._getFilters(), this._prevFilters) || - this.input.searchSessionId !== this._prevSearchSessionId + this._getSearchSessionId() !== this._prevSearchSessionId ) { this._dispatchSetQuery({ forceRefresh: false, @@ -263,7 +263,7 @@ export class MapEmbeddable this._dispatchSetChartsPaletteServiceGetColor(this.input.syncColors); } - const isRestore = getIsRestore(this.input.searchSessionId); + const isRestore = getIsRestore(this._getSearchSessionId()); if (isRestore !== this._prevIsRestore) { this._prevIsRestore = isRestore; this._savedMap.getStore().dispatch( @@ -283,20 +283,30 @@ export class MapEmbeddable : []; } + _getSearchSessionId() { + // New search session id causes all layers from elasticsearch to refetch data. + // Dashboard provides a new search session id anytime filters change. + // Thus, filtering embeddable container by map extent causes a new search session id any time the map is moved. + // Disabling search session when filtering embeddable container by map extent. + // The use case for search sessions (restoring results because of slow responses) does not match the use case of + // filtering by map extent (rapid responses as users explore their map). + return this.input.filterByMapExtent ? undefined : this.input.searchSessionId; + } + _dispatchSetQuery({ forceRefresh }: { forceRefresh: boolean }) { const filters = this._getFilters(); this._prevTimeRange = this.input.timeRange; this._prevQuery = this.input.query; this._prevFilters = filters; - this._prevSearchSessionId = this.input.searchSessionId; + this._prevSearchSessionId = this._getSearchSessionId(); this._savedMap.getStore().dispatch( setQuery({ filters, query: this.input.query, timeFilters: this.input.timeRange, forceRefresh, - searchSessionId: this.input.searchSessionId, - searchSessionMapBuffer: getIsRestore(this.input.searchSessionId) + searchSessionId: this._getSearchSessionId(), + searchSessionMapBuffer: getIsRestore(this._getSearchSessionId()) ? this.input.mapBuffer : undefined, })