diff --git a/x-pack/plugins/maps/public/components/distance_filter_form.tsx b/x-pack/plugins/maps/public/components/distance_filter_form.tsx index 14ae6b11b85c8..c0bb0d8366f71 100644 --- a/x-pack/plugins/maps/public/components/distance_filter_form.tsx +++ b/x-pack/plugins/maps/public/components/distance_filter_form.tsx @@ -16,7 +16,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public'; -import { MultiIndexGeoFieldSelect } from './multi_index_geo_field_select'; +import { GeoFieldSelect } from './geo_field_select'; import { GeoFieldWithIndex } from './geo_field_with_index'; import { ActionSelect } from './action_select'; import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../src/plugins/data/public'; @@ -95,7 +95,7 @@ export class DistanceFilterForm extends Component { /> - void; + selectedField: GeoFieldWithIndex | undefined; +} + +export function GeoFieldSelect({ fields, onChange, selectedField }: Props) { + function onFieldSelect(selectedOptionId: string) { + const { geoFieldType, geoFieldName } = splitOptionId(selectedOptionId); + + const newSelectedField = fields.find((field) => { + return field.geoFieldType === geoFieldType && field.geoFieldName === geoFieldName; + }); + onChange(newSelectedField); + } + + const options = fields.map((geoField: GeoFieldWithIndex) => { + return { + inputDisplay: ( + + + {geoField.geoFieldType} + +
+ {geoField.geoFieldName} +
+ ), + value: createOptionId(geoField), + }; + }); + + return ( + + + + ); +} diff --git a/x-pack/plugins/maps/public/components/geometry_filter_form.js b/x-pack/plugins/maps/public/components/geometry_filter_form.js index ab9964d49d0ef..83102bfd712b4 100644 --- a/x-pack/plugins/maps/public/components/geometry_filter_form.js +++ b/x-pack/plugins/maps/public/components/geometry_filter_form.js @@ -20,7 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../../common/constants'; import { getEsSpatialRelationLabel } from '../../common/i18n_getters'; -import { MultiIndexGeoFieldSelect } from './multi_index_geo_field_select'; +import { GeoFieldSelect } from './geo_field_select'; import { ActionSelect } from './action_select'; import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../src/plugins/data/public'; @@ -137,7 +137,7 @@ export class GeometryFilterForm extends Component { /> - ; mapInitError: string | null | undefined; refreshConfig: MapRefreshConfig; renderTooltipContent?: RenderToolTipContent; @@ -66,7 +66,10 @@ interface State { export class MapContainer extends Component { private _isMounted: boolean = false; private _isInitalLoadRenderTimerStarted: boolean = false; - private _prevIndexPatternIds: string[] = []; + private _prevIndexPatternIdsAndFieldNames: Array<{ + indexPatternId: string; + fieldName: string; + }> = []; private _refreshTimerId: number | null = null; private _prevIsPaused: boolean | null = null; private _prevInterval: number | null = null; @@ -91,7 +94,7 @@ export class MapContainer extends Component { } if (!!this.props.addFilters) { - this._loadGeoFields(this.props.indexPatternIds); + this._loadGeoFields(this.props.indexPatternIdsAndFieldNames); } } @@ -116,37 +119,44 @@ export class MapContainer extends Component { } }; - _loadGeoFields = async (nextIndexPatternIds: string[]) => { - if (_.isEqual(nextIndexPatternIds, this._prevIndexPatternIds)) { + _loadGeoFields = async ( + nextIndexPatternIdsAndFieldNames: Array<{ indexPatternId: string; fieldName: string }> + ) => { + if (_.isEqual(nextIndexPatternIdsAndFieldNames, this._prevIndexPatternIdsAndFieldNames)) { // all ready loaded index pattern ids return; } - this._prevIndexPatternIds = nextIndexPatternIds; + this._prevIndexPatternIdsAndFieldNames = nextIndexPatternIdsAndFieldNames; - const geoFields: GeoFieldWithIndex[] = []; - const indexPatterns = await getIndexPatternsFromIds(nextIndexPatternIds); - indexPatterns.forEach((indexPattern) => { - indexPattern.fields.forEach((field) => { - if ( - indexPattern.id && - !indexPatternsUtils.isNestedField(field) && - (field.type === ES_GEO_FIELD_TYPE.GEO_POINT || field.type === ES_GEO_FIELD_TYPE.GEO_SHAPE) - ) { - geoFields.push({ - geoFieldName: field.name, - geoFieldType: field.type, - indexPatternTitle: indexPattern.title, - indexPatternId: indexPattern.id, - }); - } - }); + let geoFields: GeoFieldWithIndex[] = []; + const queryableFields = await getFieldsFromIds(nextIndexPatternIdsAndFieldNames); + queryableFields.forEach(({ indexPattern, field }) => { + if ( + indexPattern.id && + !indexPatternsUtils.isNestedField(field) && + (field.type === ES_GEO_FIELD_TYPE.GEO_POINT || field.type === ES_GEO_FIELD_TYPE.GEO_SHAPE) + ) { + // TODO - the indexPatterns are somewhat silly to include in the various + // filter tools because ultimately the filter that is created simply uses + // the field name and applies to all indexes. Furthermore, it clutters + // the user interface because the same name is repeated mutliple times if + // but the final effect is the same regardless of what you pick. To keep the + // changes small, no refactoring has been done to the GeoFieldWithIndex type + // we simply set the indexPattern fields to null + geoFields.push({ + geoFieldName: field.name, + geoFieldType: field.type, + indexPatternTitle: null, + indexPatternId: null, + }); + } }); + geoFields = _.uniqWith(geoFields, _.isEqual); if (!this._isMounted) { return; } - this.setState({ geoFields }); }; diff --git a/x-pack/plugins/maps/public/index_pattern_util.ts b/x-pack/plugins/maps/public/index_pattern_util.ts index f7894085b15ac..4ed84763474c0 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.ts +++ b/x-pack/plugins/maps/public/index_pattern_util.ts @@ -29,6 +29,32 @@ export function getGeoTileAggNotSupportedReason(field: IFieldType): string | nul return null; } +export async function getFieldFromIndexByName( + indexPatternId: string, + fieldName: string +) { + const indexPattern = await getIndexPatternService().get(indexPatternId); + const field = indexPattern.fields.getByName(fieldName) + return {indexPattern, field}; +} + +export async function getFieldsFromIds( + indexPatternIdsWithFields +) { + const promises: IndexPattern[] = []; + indexPatternIdsWithFields.forEach(async ({indexPatternId, fieldName}) => { + try { + // @ts-ignore + promises.push(getFieldFromIndexByName(indexPatternId, fieldName)); + } catch (error) { + // Unable to load index pattern, better to not throw error so map can render + // Error will be surfaced by layer since it too will be unable to locate the index pattern + return null; + } + }); + return await Promise.all(promises); +} + export async function getIndexPatternsFromIds( indexPatternIds: string[] = [] ): Promise { diff --git a/x-pack/plugins/maps/public/routes/map_page/map_app/index.ts b/x-pack/plugins/maps/public/routes/map_page/map_app/index.ts index 62e645198ba30..968d9afc34369 100644 --- a/x-pack/plugins/maps/public/routes/map_page/map_app/index.ts +++ b/x-pack/plugins/maps/public/routes/map_page/map_app/index.ts @@ -14,7 +14,7 @@ import { getFlyoutDisplay, getIsFullScreen } from '../../../selectors/ui_selecto import { getFilters, getQuery, - getQueryableUniqueIndexPatternIds, + getQueryableUniqueIndexPatternIdsAndFieldNames, getRefreshConfig, getTimeFilters, hasDirtyState, @@ -31,7 +31,7 @@ function mapStateToProps(state: MapStoreState) { isOpenSettingsDisabled: getFlyoutDisplay(state) !== FLYOUT_STATE.NONE, isSaveDisabled: hasDirtyState(state), inspectorAdapters: getInspectorAdapters(state), - nextIndexPatternIds: getQueryableUniqueIndexPatternIds(state), + nextIndexPatternIdsAndFieldNames: getQueryableUniqueIndexPatternIdsAndFieldNames(state), flyoutDisplay: getFlyoutDisplay(state), refreshConfig: getRefreshConfig(state), filters: getFilters(state), diff --git a/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx b/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx index 53c84e4351cc0..32dc36abaa337 100644 --- a/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx +++ b/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx @@ -38,7 +38,7 @@ import { QueryState, } from '../../../../../../../src/plugins/data/public'; import { MapContainer } from '../../../connected_components/map_container'; -import { getIndexPatternsFromIds } from '../../../index_pattern_util'; +import { getIndexPatternsFromIds, getQueryableUniqueIndexPatternIdsAndFieldNames } from '../../../index_pattern_util'; import { getTopNavConfig } from '../top_nav_config'; import { MapRefreshConfig, MapQuery } from '../../../../common/descriptor_types'; import { goToSpecifiedPath } from '../../../render_app'; @@ -64,7 +64,7 @@ interface Props { enableFullScreen: () => void; openMapSettings: () => void; inspectorAdapters: Adapters; - nextIndexPatternIds: string[]; + nextIndexPatternIdsAndFieldNames: {indexPatternId:string, fieldName:string}[]; dispatchSetQuery: ({ forceRefresh, filters, @@ -95,7 +95,7 @@ export class MapApp extends React.Component { _globalSyncChangeMonitorSubscription: Subscription | null = null; _appSyncUnsubscribe: (() => void) | null = null; _appStateManager = new AppStateManager(); - _prevIndexPatternIds: string[] | null = null; + _prevIndexPatternIdsAndFieldNames: {indexPatternId:string, fieldName:string}[] | null = null; _isMounted: boolean = false; constructor(props: Props) { @@ -132,7 +132,7 @@ export class MapApp extends React.Component { } componentDidUpdate() { - this._updateIndexPatterns(); + this._updateIndexPatternsAndFieldNames(); } componentWillUnmount() { @@ -167,16 +167,17 @@ export class MapApp extends React.Component { this._onQueryChange({ time: globalState.time }); }; - async _updateIndexPatterns() { - const { nextIndexPatternIds } = this.props; + async _updateIndexPatternsAndFieldNames() { + const { nextIndexPatternIdsAndFieldNames } = this.props; - if (_.isEqual(nextIndexPatternIds, this._prevIndexPatternIds)) { + if (_.isEqual(nextIndexPatternIdsAndFieldNames, this._prevIndexPatternIdsAndFieldNames)) { return; } - this._prevIndexPatternIds = nextIndexPatternIds; + this._prevIndexPatternIdsAndFieldNames = nextIndexPatternIdsAndFieldNames; - const indexPatterns = await getIndexPatternsFromIds(nextIndexPatternIds); + const indexPatternIds = _.map(nextIndexPatternIdsAndFieldNames, 'indexPatternId'); + const indexPatterns = await getIndexPatternsFromIds(indexPatternIds); if (this._isMounted) { this.setState({ indexPatterns }); } diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index d89a5cb76416b..d78a41e75e550 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -23,6 +23,7 @@ import { TiledVectorLayer } from '../classes/layers/tiled_vector_layer/tiled_vec import { DatashaderLayer } from '../datashader/datashader_layer'; import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/util'; import { InnerJoin } from '../classes/joins/inner_join'; +import { IESSource } from '../classes/sources/es_source'; import { getSourceByType } from '../classes/sources/source_registry'; import { GeojsonFileSource } from '../classes/sources/geojson_file_source'; import { @@ -395,6 +396,39 @@ export const getQueryableUniqueIndexPatternIds = createSelector( } ); +// Get list of unique index patterns, excluding index patterns from layers that disable applyGlobalQuery +export const getQueryableUniqueIndexPatternIdsAndFieldNames = createSelector( + getLayerList, + getWaitingForMapReadyLayerListRaw, + (layerList, waitingForMapReadyLayerList) => { + const indexPatternIdsAndFieldNames: Array<{ indexPatternId: string; fieldName: string }> = []; + + if (waitingForMapReadyLayerList.length) { + waitingForMapReadyLayerList.forEach((layerDescriptor) => { + const layer = createLayerInstance(layerDescriptor); + const indexPatternIds = layer.getQueryableIndexPatternIds(); + if (layer.getSource().isESSource()) { + const fieldName = (layer.getSource() as IESSource).getGeoFieldName(); + indexPatternIds.forEach((indexPatternId) => { + indexPatternIdsAndFieldNames.push({ indexPatternId, fieldName }); + }); + } + }); + } else { + layerList.forEach((layer) => { + const indexPatternIds = layer.getQueryableIndexPatternIds(); + if (layer.getSource().isESSource()) { + const fieldName = (layer.getSource() as IESSource).getGeoFieldName(); + indexPatternIds.forEach((indexPatternId) => { + indexPatternIdsAndFieldNames.push({ indexPatternId, fieldName }); + }); + } + }); + } + return _.uniq(indexPatternIdsAndFieldNames); + } +); + export const hasDirtyState = createSelector(getLayerListRaw, (layerListRaw) => { return layerListRaw.some((layerDescriptor) => { if (layerDescriptor.__isPreviewLayer) {