From 16f8b7ce6aa7dfd1193efb158c214cdbff1b265e Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 3 Oct 2019 16:49:33 -0600 Subject: [PATCH] [Maps] use pre-indexed shapes in shape filters when shape is stored in elasticsearch (#47171) * [Maps] use pre-indexed shapes in shape filters when shape is stored in elasticsearch * add _index to properties so it can be used in pre-indexed context * update TooltipControl snapshot --- .../public/components/geometry_filter_form.js | 1 + .../map/feature_geometry_filter_form.js | 41 ++++++++++++++++++- .../map/feature_tooltip.js | 10 +++++ .../__snapshots__/tool_control.test.js.snap | 1 + .../map/mb/tooltip_control/tooltip_control.js | 17 ++++++++ .../maps/public/elasticsearch_geo_utils.js | 16 ++++++-- .../es_search_source/es_search_source.js | 12 +++++- .../maps/public/layers/sources/source.js | 5 +++ 8 files changed, 97 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js b/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js index 10410f4d79e5b5..767237ecdf38dc 100644 --- a/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js +++ b/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js @@ -197,6 +197,7 @@ export class GeometryFilterForm extends Component { fill onClick={this._onSubmit} isDisabled={!this.state.geometryLabel || !this.state.geoFieldTag} + isLoading={this.props.isLoading} > {this.props.buttonLabel} diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/feature_geometry_filter_form.js b/x-pack/legacy/plugins/maps/public/connected_components/map/feature_geometry_filter_form.js index 16bf188c437759..10913bcdbb96d8 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/feature_geometry_filter_form.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/feature_geometry_filter_form.js @@ -16,8 +16,46 @@ import { GeometryFilterForm } from '../../components/geometry_filter_form'; export class FeatureGeometryFilterForm extends Component { - _createFilter = ({ geometryLabel, indexPatternId, geoFieldName, geoFieldType, relation }) => { + state = { + isLoading: false, + } + + componentDidMount() { + this._isMounted = true; + } + + componentWillUnmount() { + this._isMounted = false; + } + + _loadPreIndexedShape = async () => { + this.setState({ + isLoading: true, + }); + + let preIndexedShape; + try { + preIndexedShape = await this.props.loadPreIndexedShape(); + } catch (err) { + // ignore error, just fall back to using geometry if preIndexedShape can not be fetched + } + + if (this._isMounted) { + this.setState({ isLoading: false }); + } + + return preIndexedShape; + } + + _createFilter = async ({ geometryLabel, indexPatternId, geoFieldName, geoFieldType, relation }) => { + const preIndexedShape = await this._loadPreIndexedShape(); + if (!this._isMounted) { + // do not create filter if component is unmounted + return; + } + const filter = createSpatialFilterWithGeometry({ + preIndexedShape, geometry: this.props.geometry, geometryLabel, indexPatternId, @@ -65,6 +103,7 @@ export class FeatureGeometryFilterForm extends Component { onSubmit={this._createFilter} isFilterGeometryClosed={this.props.geometry.type !== GEO_JSON_TYPE.LINE_STRING && this.props.geometry.type !== GEO_JSON_TYPE.MULTI_LINE_STRING} + isLoading={this.state.isLoading} /> ); } diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/feature_tooltip.js b/x-pack/legacy/plugins/maps/public/connected_components/map/feature_tooltip.js index 4075ae54a36956..6c68bd6be4c384 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/feature_tooltip.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/feature_tooltip.js @@ -338,6 +338,15 @@ export class FeatureTooltip extends React.Component { ); } + _loadCurrentFeaturePreIndexedShape = () => { + const filteredFeatures = this._filterFeatures(); + const currentFeature = filteredFeatures[this.state.pageNumber]; + return this.props.loadPreIndexedShape({ + layerId: currentFeature.layerId, + featureId: currentFeature.id + }); + } + render() { const filteredFeatures = this._filterFeatures(); const currentFeature = filteredFeatures[this.state.pageNumber]; @@ -355,6 +364,7 @@ export class FeatureTooltip extends React.Component { geometry={currentFeatureGeometry} geoFields={filteredGeoFields} addFilters={this.props.addFilters} + loadPreIndexedShape={this._loadCurrentFeaturePreIndexedShape} /> ); } diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tool_control.test.js.snap b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tool_control.test.js.snap index 1dfc4d681e569e..8c09b7a160ec8d 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tool_control.test.js.snap +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tool_control.test.js.snap @@ -111,6 +111,7 @@ exports[`TooltipControl render tooltipState is provided should render tooltip po isLocked={false} loadFeatureGeometry={[Function]} loadFeatureProperties={[Function]} + loadPreIndexedShape={[Function]} /> diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js index 165d73222a99a4..d788f93f915644 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js @@ -251,6 +251,7 @@ export class TooltipControl extends React.Component { if (!tooltipLayer) { return null; } + const targetFeature = tooltipLayer.getFeatureById(featureId); if (!targetFeature) { return null; @@ -264,6 +265,7 @@ export class TooltipControl extends React.Component { if (!tooltipLayer) { return []; } + const targetFeature = tooltipLayer.getFeatureById(featureId); if (!targetFeature) { return []; @@ -271,6 +273,20 @@ export class TooltipControl extends React.Component { return await tooltipLayer.getPropertiesForTooltip(targetFeature.properties); }; + _loadPreIndexedShape = async ({ layerId, featureId }) => { + const tooltipLayer = this._findLayerById(layerId); + if (!tooltipLayer) { + return null; + } + + const targetFeature = tooltipLayer.getFeatureById(featureId); + if (!targetFeature) { + return null; + } + + return await tooltipLayer.getSource().getPreIndexedShape(targetFeature.properties); + }; + _findLayerById = (layerId) => { return this.props.layerList.find(layer => { return layer.getId() === layerId; @@ -308,6 +324,7 @@ export class TooltipControl extends React.Component { anchorLocation={this.props.tooltipState.location} findLayerById={this._findLayerById} geoFields={this.props.geoFields} + loadPreIndexedShape={this._loadPreIndexedShape} /> ); diff --git a/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js index 2ad8cc856838cf..bfb3f710116e06 100644 --- a/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js +++ b/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js @@ -297,6 +297,7 @@ export function createSpatialFilterWithGeometry(options) { } function createGeometryFilterWithMeta({ + preIndexedShape, geometry, geometryLabel, indexPatternId, @@ -320,14 +321,21 @@ function createGeometryFilterWithMeta({ }; if (geoFieldType === ES_GEO_FIELD_TYPE.GEO_SHAPE) { + const shapeQuery = { + relation + }; + + if (preIndexedShape) { + shapeQuery.indexed_shape = preIndexedShape; + } else { + shapeQuery.shape = geometry; + } + return { meta, geo_shape: { ignore_unmapped: true, - [geoFieldName]: { - shape: geometry, - relation - } + [geoFieldName]: shapeQuery } }; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index 6947333409515b..071e231bb6e2da 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -240,7 +240,7 @@ export class ESSearchSource extends AbstractESSource { const indexPattern = await this._getIndexPattern(); const unusedMetaFields = indexPattern.metaFields.filter(metaField => { - return metaField !== '_id'; + return !['_id', '_index'].includes(metaField); }); const flattenHit = hit => { const properties = indexPattern.flattenHit(hit); @@ -412,4 +412,14 @@ export class ESSearchSource extends AbstractESSource { topHitsSize: this._descriptor.topHitsSize, }; } + + async getPreIndexedShape(properties) { + const geoField = await this._getGeoField(); + + return { + index: properties._index, // Can not use index pattern title because it may reference many indices + id: properties._id, + path: geoField.name, + }; + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source.js b/x-pack/legacy/plugins/maps/public/layers/sources/source.js index 01479c4319d940..a9df3592af2765 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/source.js @@ -122,6 +122,11 @@ export class AbstractSource { supportsElasticsearchFilters() { return false; } + + // Returns geo_shape indexed_shape context for spatial quering by pre-indexed shapes + async getPreIndexedShape(/* properties */) { + return null; + } }