From acb78a60ba48304c812888965f39909a6c55bf16 Mon Sep 17 00:00:00 2001
From: Nathan Reese
Date: Sat, 27 Apr 2019 19:14:37 -0600
Subject: [PATCH] [Maps] ignore global query layer setting (#35542) (#35694)
* [Maps] ignore global query layer setting
* do not include index pattern id in query bar indexPatterns when applyGlobalQuery is disabled
* update how embeddable factory extracts indexPatterns so only queryable index patterns are included in list
* support applyGlobalQuery for heatmap and getBounds
* add functional tests to join layer
* show filter section when layer has join
* move checkbox to layer settings panel
* text review
* set checkbox to false when disabled
* do not trigger refetch when global query and global filter changes and applyGlobalQuery is disabled
* rename applyGlobalQuery to getApplyGlobalQuery
* throw error in map embeddable factory if layerListJSON can not be parsed
* remove extra space
* update zoom range slider label and remove nested EuiFormRow
---
.../maps/public/actions/store_actions.js | 22 +++
.../public/angular/get_initial_layers.test.js | 35 ++--
.../maps/public/angular/map_controller.js | 4 +-
.../filter_editor/filter_editor.js | 6 +
.../__snapshots__/layer_errors.test.js.snap | 2 +-
.../layer_panel/layer_errors/layer_errors.js | 2 +-
.../layer_panel/layer_settings/index.js | 5 +
.../layer_settings/layer_settings.js | 84 +++++---
.../source_settings.test.js.snap | 2 +-
.../source_settings/source_settings.js | 2 +-
.../components/layer_panel/style_tabs/view.js | 2 +-
.../embeddable/map_embeddable_factory.js | 23 ++-
.../maps/public/selectors/map_selectors.js | 13 ++
.../public/shared/layers/heatmap_layer.js | 20 +-
.../maps/public/shared/layers/layer.js | 14 ++
.../public/shared/layers/sources/es_source.js | 12 +-
.../layers/util/is_refresh_only_query.js | 13 ++
.../maps/public/shared/layers/vector_layer.js | 46 +++--
.../public/shared/layers/vector_layer.test.js | 185 ++++++++++++++++++
x-pack/test/functional/apps/maps/joins.js | 29 ++-
.../test/functional/page_objects/gis_page.js | 16 ++
21 files changed, 458 insertions(+), 79 deletions(-)
create mode 100644 x-pack/plugins/maps/public/shared/layers/util/is_refresh_only_query.js
create mode 100644 x-pack/plugins/maps/public/shared/layers/vector_layer.test.js
diff --git a/x-pack/plugins/maps/public/actions/store_actions.js b/x-pack/plugins/maps/public/actions/store_actions.js
index 783b5c2e66a19..1482f9abc7b33 100644
--- a/x-pack/plugins/maps/public/actions/store_actions.js
+++ b/x-pack/plugins/maps/public/actions/store_actions.js
@@ -156,6 +156,15 @@ export function addLayer(layerDescriptor) {
};
}
+// Do not use when rendering a map. Method exists to enable selectors for getLayerList when
+// rendering is not needed.
+export function addLayerWithoutDataSync(layerDescriptor) {
+ return {
+ type: ADD_LAYER,
+ layer: layerDescriptor,
+ };
+}
+
function setLayerDataLoadErrorStatus(layerId, errorMessage) {
return dispatch => {
dispatch({
@@ -511,6 +520,19 @@ export function setLayerQuery(id, query) {
};
}
+export function setLayerApplyGlobalQuery(id, applyGlobalQuery) {
+ return (dispatch) => {
+ dispatch({
+ type: UPDATE_LAYER_PROP,
+ id,
+ propName: 'applyGlobalQuery',
+ newValue: applyGlobalQuery,
+ });
+
+ dispatch(syncDataForLayer(id));
+ };
+}
+
export function removeSelectedLayer() {
return (dispatch, getState) => {
const state = getState();
diff --git a/x-pack/plugins/maps/public/angular/get_initial_layers.test.js b/x-pack/plugins/maps/public/angular/get_initial_layers.test.js
index fe675642b0adf..1ed53f4286857 100644
--- a/x-pack/plugins/maps/public/angular/get_initial_layers.test.js
+++ b/x-pack/plugins/maps/public/angular/get_initial_layers.test.js
@@ -62,22 +62,23 @@ describe('Saved object does not have layer list', () => {
};
const layers = getInitialLayers(null);
expect(layers).toEqual([{
- 'alpha': 1,
- '__dataRequests': [],
- 'id': layers[0].id,
- 'label': null,
- 'maxZoom': 24,
- 'minZoom': 0,
- 'sourceDescriptor': {
- 'type': 'EMS_TMS',
- 'id': 'road_map',
+ alpha: 1,
+ __dataRequests: [],
+ id: layers[0].id,
+ applyGlobalQuery: true,
+ label: null,
+ maxZoom: 24,
+ minZoom: 0,
+ sourceDescriptor: {
+ type: 'EMS_TMS',
+ id: 'road_map',
},
- 'style': {
- 'properties': {},
- 'type': 'TILE',
+ style: {
+ properties: {},
+ type: 'TILE',
},
- 'type': 'TILE',
- 'visible': true,
+ type: 'TILE',
+ visible: true,
}]);
});
@@ -91,9 +92,10 @@ describe('Saved object does not have layer list', () => {
const layers = getInitialLayers(null);
expect(layers).toEqual([{
- 'alpha': 1,
+ alpha: 1,
__dataRequests: [],
id: layers[0].id,
+ applyGlobalQuery: true,
label: null,
maxZoom: 24,
minZoom: 0,
@@ -117,9 +119,10 @@ describe('Saved object does not have layer list', () => {
const layers = getInitialLayers(null);
expect(layers).toEqual([{
- 'alpha': 1,
+ alpha: 1,
__dataRequests: [],
id: layers[0].id,
+ applyGlobalQuery: true,
label: null,
maxZoom: 24,
minZoom: 0,
diff --git a/x-pack/plugins/maps/public/angular/map_controller.js b/x-pack/plugins/maps/public/angular/map_controller.js
index 7603ecdc44aa5..13db2dc3a6e1d 100644
--- a/x-pack/plugins/maps/public/angular/map_controller.js
+++ b/x-pack/plugins/maps/public/angular/map_controller.js
@@ -35,7 +35,7 @@ import {
setReadOnly,
setIsLayerTOCOpen
} from '../store/ui';
-import { getUniqueIndexPatternIds } from '../selectors/map_selectors';
+import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors';
import { getInspectorAdapters } from '../store/non_serializable_instances';
import { Inspector } from 'ui/inspector';
import { DocTitleProvider } from 'ui/doc_title';
@@ -197,7 +197,7 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
});
}
- const nextIndexPatternIds = getUniqueIndexPatternIds(store.getState());
+ const nextIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState());
if (nextIndexPatternIds !== prevIndexPatternIds) {
prevIndexPatternIds = nextIndexPatternIds;
updateIndexPatterns(nextIndexPatternIds);
diff --git a/x-pack/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js b/x-pack/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js
index 6d5cfe28c490a..ab8b5f64f4d89 100644
--- a/x-pack/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js
+++ b/x-pack/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js
@@ -128,6 +128,7 @@ export class FilterEditor extends Component {
+
);
}
@@ -174,8 +175,13 @@ export class FilterEditor extends Component {
/>
+
+
@@ -131,13 +161,15 @@ export function LayerSettings(props) {
-
+
{renderLabel()}
{renderZoomSliders()}
{renderAlphaSlider()}
+
+ {renderApplyGlobalQueryCheckbox()}
diff --git a/x-pack/plugins/maps/public/components/layer_panel/source_settings/__snapshots__/source_settings.test.js.snap b/x-pack/plugins/maps/public/components/layer_panel/source_settings/__snapshots__/source_settings.test.js.snap
index d83030e544574..f1bc275380b6a 100644
--- a/x-pack/plugins/maps/public/components/layer_panel/source_settings/__snapshots__/source_settings.test.js.snap
+++ b/x-pack/plugins/maps/public/components/layer_panel/source_settings/__snapshots__/source_settings.test.js.snap
@@ -23,7 +23,7 @@ exports[`Should render source settings editor 1`] = `
mockSourceEditor
diff --git a/x-pack/plugins/maps/public/components/layer_panel/source_settings/source_settings.js b/x-pack/plugins/maps/public/components/layer_panel/source_settings/source_settings.js
index 6a1a75327427a..fa6fd90cce0ec 100644
--- a/x-pack/plugins/maps/public/components/layer_panel/source_settings/source_settings.js
+++ b/x-pack/plugins/maps/public/components/layer_panel/source_settings/source_settings.js
@@ -44,7 +44,7 @@ export function SourceSettings({ layer, updateSourceProp }) {
-
+
{sourceSettingsEditor}
diff --git a/x-pack/plugins/maps/public/components/layer_panel/style_tabs/view.js b/x-pack/plugins/maps/public/components/layer_panel/style_tabs/view.js
index a692dc20a4788..5aedcc02ecc1c 100644
--- a/x-pack/plugins/maps/public/components/layer_panel/style_tabs/view.js
+++ b/x-pack/plugins/maps/public/components/layer_panel/style_tabs/view.js
@@ -40,7 +40,7 @@ export function StyleTabs({ layer, updateStyle }) {
{Style.getDisplayName()}
{description}
-
+
{styleEditor}
);
diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.js b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.js
index 50e5ce35d6eb6..ace9d42494783 100644
--- a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.js
+++ b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.js
@@ -11,6 +11,9 @@ import { MapEmbeddable } from './map_embeddable';
import { indexPatternService } from '../kibana_services';
import { i18n } from '@kbn/i18n';
import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants';
+import { createMapStore } from '../store/store';
+import { addLayerWithoutDataSync } from '../actions/store_actions';
+import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors';
export class MapEmbeddableFactory extends EmbeddableFactory {
@@ -28,8 +31,21 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
this._savedObjectLoader = gisMapSavedObjectLoader;
}
- async _getIndexPatterns(indexPatternIds = []) {
- const promises = indexPatternIds.map(async (indexPatternId) => {
+ async _getIndexPatterns(layerListJSON) {
+ // Need to extract layerList from store to get queryable index pattern ids
+ const store = createMapStore();
+ try {
+ JSON.parse(layerListJSON).forEach(layerDescriptor => {
+ store.dispatch(addLayerWithoutDataSync(layerDescriptor));
+ });
+ } catch (error) {
+ throw new Error(i18n.translate('xpack.maps.mapEmbeddableFactory', {
+ defaultMessage: 'Unable to load map, malformed saved object',
+ }));
+ }
+ const queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState());
+
+ const promises = queryableIndexPatternIds.map(async (indexPatternId) => {
try {
return await indexPatternService.get(indexPatternId);
} catch (error) {
@@ -44,7 +60,8 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
async create(panelMetadata, onEmbeddableStateChanged) {
const savedMap = await this._savedObjectLoader.get(panelMetadata.id);
- const indexPatterns = await this._getIndexPatterns(savedMap.indexPatternIds);
+
+ const indexPatterns = await this._getIndexPatterns(savedMap.layerListJSON);
return new MapEmbeddable({
onEmbeddableStateChanged,
diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.js b/x-pack/plugins/maps/public/selectors/map_selectors.js
index e8f7af3c7dc4d..a83f609d26c2b 100644
--- a/x-pack/plugins/maps/public/selectors/map_selectors.js
+++ b/x-pack/plugins/maps/public/selectors/map_selectors.js
@@ -170,6 +170,7 @@ export const getSelectedLayerJoinDescriptors = createSelector(
});
});
+// Get list of unique index patterns used by all layers
export const getUniqueIndexPatternIds = createSelector(
getLayerList,
(layerList) => {
@@ -181,6 +182,18 @@ export const getUniqueIndexPatternIds = createSelector(
}
);
+// Get list of unique index patterns, excluding index patterns from layers that disable applyGlobalQuery
+export const getQueryableUniqueIndexPatternIds = createSelector(
+ getLayerList,
+ (layerList) => {
+ const indexPatternIds = [];
+ layerList.forEach(layer => {
+ indexPatternIds.push(...layer.getQueryableIndexPatternIds());
+ });
+ return _.uniq(indexPatternIds);
+ }
+);
+
export const hasDirtyState = createSelector(getLayerListRaw, (layerListRaw) => {
return layerListRaw.some(layerDescriptor => {
const currentState = copyPersistentState(layerDescriptor);
diff --git a/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js b/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js
index 93323f7c96465..fe93b9ffb689d 100644
--- a/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js
+++ b/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js
@@ -10,6 +10,7 @@ import { AbstractLayer } from './layer';
import { EuiIcon } from '@elastic/eui';
import { HeatmapStyle } from './styles/heatmap_style';
import { SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
+import { isRefreshOnlyQuery } from './util/is_refresh_only_query';
const SCALED_PROPERTY_NAME = '__kbn_heatmap_weight__';//unique name to store scaled value for weighting
@@ -45,7 +46,6 @@ export class HeatmapLayer extends AbstractLayer {
return metricfields[0].propertyKey;
}
-
_getMbLayerId() {
return this.getId() + '_heatmap';
}
@@ -134,13 +134,19 @@ export class HeatmapLayer extends AbstractLayer {
const updateDueToExtent = this.updateDueToExtent(this._source, meta, searchFilters);
- const updateDueToQuery = searchFilters.query
- && !_.isEqual(meta.query, searchFilters.query);
+ let updateDueToQuery = false;
+ let updateDueToFilters = false;
+ if (searchFilters.applyGlobalQuery) {
+ updateDueToQuery = !_.isEqual(meta.query, searchFilters.query);
+ updateDueToFilters = !_.isEqual(meta.filters, searchFilters.filters);
+ } else {
+ // Global filters and query are not applied to layer search request so no re-fetch required.
+ // Exception is "Refresh" query.
+ updateDueToQuery = isRefreshOnlyQuery(meta.query, searchFilters.query);
+ }
const updateDueToLayerQuery = searchFilters.layerQuery
&& !_.isEqual(meta.layerQuery, searchFilters.layerQuery);
-
- const updateDueToFilters = searchFilters.filters
- && !_.isEqual(meta.filters, searchFilters.filters);
+ const updateDueToApplyGlobalQuery = meta.applyGlobalQuery !== searchFilters.applyGlobalQuery;
const updateDueToMetricChange = !_.isEqual(meta.metric, searchFilters.metric);
@@ -150,6 +156,7 @@ export class HeatmapLayer extends AbstractLayer {
&& !updateDueToRefreshTimer
&& !updateDueToQuery
&& !updateDueToLayerQuery
+ && !updateDueToApplyGlobalQuery
&& !updateDueToFilters
&& !updateDueToMetricChange
) {
@@ -163,6 +170,7 @@ export class HeatmapLayer extends AbstractLayer {
return {
...dataFilters,
layerQuery: this.getQuery(),
+ applyGlobalQuery: this.getApplyGlobalQuery(),
geogridPrecision: this._source.getGeoGridPrecision(dataFilters.zoom),
metric: this._getPropKeyOfSelectedMetric()
};
diff --git a/x-pack/plugins/maps/public/shared/layers/layer.js b/x-pack/plugins/maps/public/shared/layers/layer.js
index e32be4554424e..0e72e3960dc86 100644
--- a/x-pack/plugins/maps/public/shared/layers/layer.js
+++ b/x-pack/plugins/maps/public/shared/layers/layer.js
@@ -42,7 +42,9 @@ export class AbstractLayer {
layerDescriptor.maxZoom = _.get(options, 'maxZoom', 24);
layerDescriptor.alpha = _.get(options, 'alpha', 0.75);
layerDescriptor.visible = _.get(options, 'visible', true);
+ layerDescriptor.applyGlobalQuery = _.get(options, 'applyGlobalQuery', true);
layerDescriptor.style = _.get(options, 'style', {});
+
return layerDescriptor;
}
@@ -144,6 +146,10 @@ export class AbstractLayer {
return this._descriptor.query;
}
+ getApplyGlobalQuery() {
+ return this._descriptor.applyGlobalQuery;
+ }
+
getZoomConfig() {
return {
minZoom: this._descriptor.minZoom,
@@ -262,6 +268,14 @@ export class AbstractLayer {
return [];
}
+ getQueryableIndexPatternIds() {
+ if (this.getApplyGlobalQuery()) {
+ return this.getIndexPatternIds();
+ }
+
+ return [];
+ }
+
async getOrdinalFields() {
return [];
}
diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_source.js b/x-pack/plugins/maps/public/shared/layers/sources/es_source.js
index c348d22c33fb0..c089bede1a14d 100644
--- a/x-pack/plugins/maps/public/shared/layers/sources/es_source.js
+++ b/x-pack/plugins/maps/public/shared/layers/sources/es_source.js
@@ -142,7 +142,9 @@ export class AbstractESSource extends AbstractVectorSource {
async _makeSearchSource(searchFilters, limit) {
const indexPattern = await this._getIndexPattern();
const isTimeAware = await this.isTimeAware();
- const allFilters = [...searchFilters.filters];
+ const applyGlobalQuery = _.get(searchFilters, 'applyGlobalQuery', true);
+ const globalFilters = applyGlobalQuery ? searchFilters.filters : [];
+ const allFilters = [...globalFilters];
if (this.isFilterByMapBounds() && searchFilters.buffer) {//buffer can be empty
const geoField = await this._getGeoField();
allFilters.push(createExtentFilter(searchFilters.buffer, geoField.name, geoField.type));
@@ -155,7 +157,9 @@ export class AbstractESSource extends AbstractVectorSource {
searchSource.setField('index', indexPattern);
searchSource.setField('size', limit);
searchSource.setField('filter', allFilters);
- searchSource.setField('query', searchFilters.query);
+ if (applyGlobalQuery) {
+ searchSource.setField('query', searchFilters.query);
+ }
if (searchFilters.layerQuery) {
const layerSearchSource = new SearchSource();
@@ -167,9 +171,9 @@ export class AbstractESSource extends AbstractVectorSource {
return searchSource;
}
- async getBoundsForFilters({ layerQuery, query, timeFilters, filters }) {
+ async getBoundsForFilters({ layerQuery, query, timeFilters, filters, applyGlobalQuery }) {
- const searchSource = await this._makeSearchSource({ layerQuery, query, timeFilters, filters }, 0);
+ const searchSource = await this._makeSearchSource({ layerQuery, query, timeFilters, filters, applyGlobalQuery }, 0);
const geoField = await this._getGeoField();
const indexPattern = await this._getIndexPattern();
diff --git a/x-pack/plugins/maps/public/shared/layers/util/is_refresh_only_query.js b/x-pack/plugins/maps/public/shared/layers/util/is_refresh_only_query.js
new file mode 100644
index 0000000000000..a8c6341863517
--- /dev/null
+++ b/x-pack/plugins/maps/public/shared/layers/util/is_refresh_only_query.js
@@ -0,0 +1,13 @@
+/*
+ * 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.
+ */
+
+// Refresh only query is query where timestamps are different but query is the same.
+// Triggered by clicking "Refresh" button in QueryBar
+export function isRefreshOnlyQuery(prevQuery, newQuery) {
+ return prevQuery.queryLastTriggeredAt !== newQuery.queryLastTriggeredAt
+ && prevQuery.language === newQuery.language
+ && prevQuery.query === newQuery.query;
+}
diff --git a/x-pack/plugins/maps/public/shared/layers/vector_layer.js b/x-pack/plugins/maps/public/shared/layers/vector_layer.js
index 6262e26b81171..1abca50abcc9b 100644
--- a/x-pack/plugins/maps/public/shared/layers/vector_layer.js
+++ b/x-pack/plugins/maps/public/shared/layers/vector_layer.js
@@ -11,6 +11,7 @@ import { LeftInnerJoin } from './joins/left_inner_join';
import { FEATURE_ID_PROPERTY_NAME, SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
import _ from 'lodash';
import { JoinTooltipProperty } from './tooltips/join_tooltip_property';
+import { isRefreshOnlyQuery } from './util/is_refresh_only_query';
const EMPTY_FEATURE_COLLECTION = {
type: 'FeatureCollection',
@@ -207,10 +208,18 @@ export class VectorLayer extends AbstractLayer {
let updateDueToQuery = false;
let updateDueToFilters = false;
let updateDueToLayerQuery = false;
+ let updateDueToApplyGlobalQuery = false;
if (isQueryAware) {
- updateDueToQuery = !_.isEqual(meta.query, searchFilters.query);
- updateDueToFilters = !_.isEqual(meta.filters, searchFilters.filters);
+ updateDueToApplyGlobalQuery = meta.applyGlobalQuery !== searchFilters.applyGlobalQuery;
updateDueToLayerQuery = !_.isEqual(meta.layerQuery, searchFilters.layerQuery);
+ if (searchFilters.applyGlobalQuery) {
+ updateDueToQuery = !_.isEqual(meta.query, searchFilters.query);
+ updateDueToFilters = !_.isEqual(meta.filters, searchFilters.filters);
+ } else {
+ // Global filters and query are not applied to layer search request so no re-fetch required.
+ // Exception is "Refresh" query.
+ updateDueToQuery = isRefreshOnlyQuery(meta.query, searchFilters.query);
+ }
}
let updateDueToPrecisionChange = false;
@@ -227,6 +236,7 @@ export class VectorLayer extends AbstractLayer {
&& !updateDueToQuery
&& !updateDueToFilters
&& !updateDueToLayerQuery
+ && !updateDueToApplyGlobalQuery
&& !updateDueToPrecisionChange;
}
@@ -236,22 +246,27 @@ export class VectorLayer extends AbstractLayer {
const sourceDataId = join.getSourceId();
const requestToken = Symbol(`layer-join-refresh:${ this.getId()} - ${sourceDataId}`);
+ const searchFilters = {
+ ...dataFilters,
+ applyGlobalQuery: this.getApplyGlobalQuery(),
+ };
+ const canSkip = await this._canSkipSourceUpdate(joinSource, sourceDataId, searchFilters);
+ if (canSkip) {
+ const sourceDataRequest = this._findDataRequestForSource(sourceDataId);
+ const propertiesMap = sourceDataRequest ? sourceDataRequest.getData() : null;
+ return {
+ dataHasChanged: false,
+ join: join,
+ propertiesMap: propertiesMap
+ };
+ }
+
try {
- const canSkip = await this._canSkipSourceUpdate(joinSource, sourceDataId, dataFilters);
- if (canSkip) {
- const sourceDataRequest = this._findDataRequestForSource(sourceDataId);
- const propertiesMap = sourceDataRequest ? sourceDataRequest.getData() : null;
- return {
- dataHasChanged: false,
- join: join,
- propertiesMap: propertiesMap
- };
- }
- startLoading(sourceDataId, requestToken, dataFilters);
+ startLoading(sourceDataId, requestToken, searchFilters);
const leftSourceName = await this.getSourceName();
const {
propertiesMap
- } = await joinSource.getPropertiesMap(dataFilters, leftSourceName, join.getLeftFieldName());
+ } = await joinSource.getPropertiesMap(searchFilters, leftSourceName, join.getLeftFieldName());
stopLoading(sourceDataId, requestToken, propertiesMap);
return {
dataHasChanged: true,
@@ -289,7 +304,8 @@ export class VectorLayer extends AbstractLayer {
...dataFilters,
fieldNames: _.uniq(fieldNames).sort(),
geogridPrecision: this._source.getGeoGridPrecision(dataFilters.zoom),
- layerQuery: this.getQuery()
+ layerQuery: this.getQuery(),
+ applyGlobalQuery: this.getApplyGlobalQuery(),
};
}
diff --git a/x-pack/plugins/maps/public/shared/layers/vector_layer.test.js b/x-pack/plugins/maps/public/shared/layers/vector_layer.test.js
new file mode 100644
index 0000000000000..85937c2783cce
--- /dev/null
+++ b/x-pack/plugins/maps/public/shared/layers/vector_layer.test.js
@@ -0,0 +1,185 @@
+/*
+ * 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('./joins/left_inner_join', () => ({
+ LeftInnerJoin: Object
+}));
+
+jest.mock('./tooltips/join_tooltip_property', () => ({
+ JoinTooltipProperty: Object
+}));
+
+import { VectorLayer } from './vector_layer';
+
+describe('_canSkipSourceUpdate', () => {
+ const SOURCE_DATA_REQUEST_ID = 'foo';
+
+ describe('isQueryAware', () => {
+
+ const queryAwareSourceMock = {
+ isTimeAware: () => { return false; },
+ isRefreshTimerAware: () => { return false; },
+ isFilterByMapBounds: () => { return false; },
+ isFieldAware: () => { return false; },
+ isQueryAware: () => { return true; },
+ isGeoGridPrecisionAware: () => { return false; },
+ };
+ const prevFilters = [];
+ const prevQuery = {
+ language: 'kuery',
+ query: 'machine.os.keyword : "win 7"',
+ queryLastTriggeredAt: '2019-04-25T20:53:22.331Z'
+ };
+
+ describe('applyGlobalQuery is false', () => {
+
+ const prevApplyGlobalQuery = false;
+
+ const vectorLayer = new VectorLayer({
+ layerDescriptor: {
+ __dataRequests: [
+ {
+ dataId: SOURCE_DATA_REQUEST_ID,
+ dataMeta: {
+ applyGlobalQuery: prevApplyGlobalQuery,
+ filters: prevFilters,
+ query: prevQuery,
+ }
+ }
+ ]
+ }
+ });
+
+ it('can skip update when filter changes', async () => {
+ const searchFilters = {
+ applyGlobalQuery: prevApplyGlobalQuery,
+ filters: [prevQuery],
+ query: prevQuery,
+ };
+
+ const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
+
+ expect(canSkipUpdate).toBe(true);
+ });
+
+ it('can skip update when query changes', async () => {
+ const searchFilters = {
+ applyGlobalQuery: prevApplyGlobalQuery,
+ filters: prevFilters,
+ query: {
+ ...prevQuery,
+ query: 'a new query string',
+ }
+ };
+
+ const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
+
+ expect(canSkipUpdate).toBe(true);
+ });
+
+ it('can not skip update when query is refreshed', async () => {
+ const searchFilters = {
+ applyGlobalQuery: prevApplyGlobalQuery,
+ filters: prevFilters,
+ query: {
+ ...prevQuery,
+ queryLastTriggeredAt: 'sometime layer when Refresh button is clicked'
+ }
+ };
+
+ const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
+
+ expect(canSkipUpdate).toBe(false);
+ });
+
+ it('can not skip update when applyGlobalQuery changes', async () => {
+ const searchFilters = {
+ applyGlobalQuery: !prevApplyGlobalQuery,
+ filters: prevFilters,
+ query: prevQuery
+ };
+
+ const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
+
+ expect(canSkipUpdate).toBe(false);
+ });
+ });
+
+ describe('applyGlobalQuery is true', () => {
+
+ const prevApplyGlobalQuery = true;
+
+ const vectorLayer = new VectorLayer({
+ layerDescriptor: {
+ __dataRequests: [
+ {
+ dataId: SOURCE_DATA_REQUEST_ID,
+ dataMeta: {
+ applyGlobalQuery: prevApplyGlobalQuery,
+ filters: prevFilters,
+ query: prevQuery,
+ }
+ }
+ ]
+ }
+ });
+
+ it('can not skip update when filter changes', async () => {
+ const searchFilters = {
+ applyGlobalQuery: prevApplyGlobalQuery,
+ filters: [prevQuery],
+ query: prevQuery,
+ };
+
+ const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
+
+ expect(canSkipUpdate).toBe(false);
+ });
+
+ it('can not skip update when query changes', async () => {
+ const searchFilters = {
+ applyGlobalQuery: prevApplyGlobalQuery,
+ filters: prevFilters,
+ query: {
+ ...prevQuery,
+ query: 'a new query string',
+ }
+ };
+
+ const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
+
+ expect(canSkipUpdate).toBe(false);
+ });
+
+ it('can not skip update when query is refreshed', async () => {
+ const searchFilters = {
+ applyGlobalQuery: prevApplyGlobalQuery,
+ filters: prevFilters,
+ query: {
+ ...prevQuery,
+ queryLastTriggeredAt: 'sometime layer when Refresh button is clicked'
+ }
+ };
+
+ const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
+
+ expect(canSkipUpdate).toBe(false);
+ });
+
+ it('can not skip update when applyGlobalQuery changes', async () => {
+ const searchFilters = {
+ applyGlobalQuery: !prevApplyGlobalQuery,
+ filters: prevFilters,
+ query: prevQuery
+ };
+
+ const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
+
+ expect(canSkipUpdate).toBe(false);
+ });
+ });
+ });
+});
diff --git a/x-pack/test/functional/apps/maps/joins.js b/x-pack/test/functional/apps/maps/joins.js
index 4eae5e85dbecf..0e750d5fa83a7 100644
--- a/x-pack/test/functional/apps/maps/joins.js
+++ b/x-pack/test/functional/apps/maps/joins.js
@@ -82,12 +82,30 @@ export default function ({ getPageObjects, getService }) {
});
- describe('inspector', () => {
+ describe('query bar', () => {
+ before(async () => {
+ await PageObjects.maps.setAndSubmitQuery('prop1 < 10 or _index : "geo_shapes*"');
+ });
+
afterEach(async () => {
await inspector.close();
});
- it('should contain terms aggregation elasticsearch request', async () => {
+ it('should apply query to join request', async () => {
+ await PageObjects.maps.openInspectorRequest('meta_for_geo_shapes*.shape_name');
+ const requestStats = await inspector.getTableData();
+ const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
+ expect(totalHits).to.equal('3');
+ const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
+ expect(hits).to.equal('0'); // aggregation requests do not return any documents
+ const indexPatternName = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Index pattern');
+ expect(indexPatternName).to.equal('meta_for_geo_shapes*');
+ });
+
+ it('should not apply query to join request when apply global query is disabled', async () => {
+ await PageObjects.maps.openLayerPanel('geo_shapes*');
+ await PageObjects.maps.disableApplyGlobalQuery();
+
await PageObjects.maps.openInspectorRequest('meta_for_geo_shapes*.shape_name');
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
@@ -97,6 +115,13 @@ export default function ({ getPageObjects, getService }) {
const indexPatternName = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Index pattern');
expect(indexPatternName).to.equal('meta_for_geo_shapes*');
});
+ });
+
+
+ describe('inspector', () => {
+ afterEach(async () => {
+ await inspector.close();
+ });
it('should not contain any elasticsearch request after layer is deleted', async () => {
await PageObjects.maps.removeLayer('geo_shapes*');
diff --git a/x-pack/test/functional/page_objects/gis_page.js b/x-pack/test/functional/page_objects/gis_page.js
index 5c3246ef2516e..6f2ecf4625cf6 100644
--- a/x-pack/test/functional/page_objects/gis_page.js
+++ b/x-pack/test/functional/page_objects/gis_page.js
@@ -259,6 +259,22 @@ export function GisPageProvider({ getService, getPageObjects }) {
await testSubjects.click(`mapOpenLayerButton${layerName}`);
}
+ async disableApplyGlobalQuery() {
+ const element = await testSubjects.find('mapLayerPanelApplyGlobalQueryCheckbox');
+ const isSelected = await element.isSelected();
+ if(isSelected) {
+ await retry.try(async () => {
+ log.debug(`disabling applyGlobalQuery`);
+ await testSubjects.click('mapLayerPanelApplyGlobalQueryCheckbox');
+ const isStillSelected = await element.isSelected();
+ if (isStillSelected) {
+ throw new Error('applyGlobalQuery not disabled');
+ }
+ });
+ await this.waitForLayersToLoad();
+ }
+ }
+
async doesLayerExist(layerName) {
layerName = layerName.replace(' ', '_');
log.debug(`Open layer panel, layer: ${layerName}`);