[Maps] automatically disable filter by bounds for indexes with small doc counts (#34456)
* [Maps] automatically disable filter by bounds for indexes with small doc counts * text updates * review feedback * minor text update
This commit is contained in:
parent
c4aedfc5ba
commit
23bdee0956
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const DEFAULT_ES_DOC_LIMIT = 2048;
|
||||
|
||||
export const DEFAULT_FILTER_BY_MAP_BOUNDS = true;
|
|
@ -7,17 +7,27 @@
|
|||
import _ from 'lodash';
|
||||
import React, { Fragment, Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFormRow, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiFormRow, EuiSpacer, EuiSwitch, EuiCallOut } from '@elastic/eui';
|
||||
|
||||
import { IndexPatternSelect } from 'ui/index_patterns/components/index_pattern_select';
|
||||
import { SingleFieldSelect } from '../../../components/single_field_select';
|
||||
import { indexPatternService } from '../../../../kibana_services';
|
||||
import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { GIS_API_PATH } from '../../../../../common/constants';
|
||||
import { DEFAULT_ES_DOC_LIMIT, DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants';
|
||||
|
||||
function filterGeoField(field) {
|
||||
return ['geo_point', 'geo_shape'].includes(field.type);
|
||||
}
|
||||
const RESET_INDEX_PATTERN_STATE = {
|
||||
indexPattern: undefined,
|
||||
geoField: undefined,
|
||||
filterByMapBounds: DEFAULT_FILTER_BY_MAP_BOUNDS,
|
||||
showFilterByBoundsSwitch: false,
|
||||
};
|
||||
|
||||
export class CreateSourceEditor extends Component {
|
||||
|
||||
|
@ -27,9 +37,8 @@ export class CreateSourceEditor extends Component {
|
|||
|
||||
state = {
|
||||
isLoadingIndexPattern: false,
|
||||
indexPatternId: '',
|
||||
geoField: '',
|
||||
noGeoIndexPatternsExist: false,
|
||||
...RESET_INDEX_PATTERN_STATE,
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -50,11 +59,20 @@ export class CreateSourceEditor extends Component {
|
|||
loadIndexPattern = (indexPatternId) => {
|
||||
this.setState({
|
||||
isLoadingIndexPattern: true,
|
||||
indexPattern: undefined,
|
||||
geoField: undefined,
|
||||
...RESET_INDEX_PATTERN_STATE
|
||||
}, this.debouncedLoad.bind(null, indexPatternId));
|
||||
};
|
||||
|
||||
loadIndexDocCount = async (indexPatternTitle) => {
|
||||
const { count } = await kfetch({
|
||||
pathname: `../${GIS_API_PATH}/indexCount`,
|
||||
query: {
|
||||
index: indexPatternTitle
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
debouncedLoad = _.debounce(async (indexPatternId) => {
|
||||
if (!indexPatternId || indexPatternId.length === 0) {
|
||||
return;
|
||||
|
@ -68,6 +86,15 @@ export class CreateSourceEditor extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
let indexHasSmallDocCount = false;
|
||||
try {
|
||||
const indexDocCount = await this.loadIndexDocCount(indexPattern.title);
|
||||
indexHasSmallDocCount = indexDocCount <= DEFAULT_ES_DOC_LIMIT;
|
||||
} catch (error) {
|
||||
// retrieving index count is a nice to have and is not essential
|
||||
// do not interrupt user flow if unable to retrieve count
|
||||
}
|
||||
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
}
|
||||
|
@ -80,7 +107,9 @@ export class CreateSourceEditor extends Component {
|
|||
|
||||
this.setState({
|
||||
isLoadingIndexPattern: false,
|
||||
indexPattern: indexPattern
|
||||
indexPattern: indexPattern,
|
||||
filterByMapBounds: !indexHasSmallDocCount, // Turn off filterByMapBounds when index contains a limited number of documents
|
||||
showFilterByBoundsSwitch: indexHasSmallDocCount
|
||||
});
|
||||
|
||||
//make default selection
|
||||
|
@ -97,21 +126,21 @@ export class CreateSourceEditor extends Component {
|
|||
}, this.previewLayer);
|
||||
};
|
||||
|
||||
onLimitChange = e => {
|
||||
const sanitizedValue = parseInt(e.target.value, 10);
|
||||
onFilterByMapBoundsChange = event => {
|
||||
this.setState({
|
||||
limit: isNaN(sanitizedValue) ? '' : sanitizedValue,
|
||||
filterByMapBounds: event.target.checked,
|
||||
}, this.previewLayer);
|
||||
}
|
||||
};
|
||||
|
||||
previewLayer = () => {
|
||||
const {
|
||||
indexPatternId,
|
||||
geoField,
|
||||
filterByMapBounds,
|
||||
} = this.state;
|
||||
|
||||
const sourceConfig = (indexPatternId && geoField)
|
||||
? { indexPatternId, geoField }
|
||||
? { indexPatternId, geoField, filterByMapBounds }
|
||||
: null;
|
||||
this.props.onSelect(sourceConfig);
|
||||
}
|
||||
|
@ -144,6 +173,53 @@ export class CreateSourceEditor extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
_renderFilterByMapBounds() {
|
||||
if (!this.state.showFilterByBoundsSwitch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiCallOut
|
||||
title={
|
||||
i18n.translate('xpack.maps.source.esSearch.disableFilterByMapBoundsTitle', {
|
||||
defaultMessage: `Dynamic data filter disabled`
|
||||
})
|
||||
}
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.maps.source.esSearch.disableFilterByMapBoundsExplainMsg"
|
||||
defaultMessage="Index '{indexPatternTitle}' has a small number of documents and does not require dynamic filtering."
|
||||
values={{
|
||||
indexPatternTitle: this.state.indexPattern ? this.state.indexPattern.title : this.state.indexPatternId,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.maps.source.esSearch.disableFilterByMapBoundsTurnOnMsg"
|
||||
defaultMessage="Turn on dynamic filtering if you expect the number of documents to increase."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFormRow>
|
||||
<EuiSwitch
|
||||
label={
|
||||
i18n.translate('xpack.maps.source.esSearch.extentFilterLabel', {
|
||||
defaultMessage: `Dynamically filter for data in the visible map area.`
|
||||
})
|
||||
|
||||
}
|
||||
checked={this.props.filterByMapBounds}
|
||||
onChange={this.onFilterByMapBoundsChange}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
_renderNoIndexPatternWarning() {
|
||||
if (!this.state.noGeoIndexPatternsExist) {
|
||||
return null;
|
||||
|
@ -183,6 +259,8 @@ export class CreateSourceEditor extends Component {
|
|||
|
||||
{this._renderGeoSelect()}
|
||||
|
||||
{this._renderFilterByMapBounds()}
|
||||
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,7 @@ import { UpdateSourceEditor } from './update_source_editor';
|
|||
import { ES_SEARCH } from '../../../../../common/constants';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getDataSourceLabel } from '../../../../../common/i18n_getters';
|
||||
|
||||
const DEFAULT_LIMIT = 2048;
|
||||
import { DEFAULT_ES_DOC_LIMIT, DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants';
|
||||
|
||||
export class ESSearchSource extends AbstractESSource {
|
||||
|
||||
|
@ -50,8 +49,8 @@ export class ESSearchSource extends AbstractESSource {
|
|||
type: ESSearchSource.type,
|
||||
indexPatternId: descriptor.indexPatternId,
|
||||
geoField: descriptor.geoField,
|
||||
limit: _.get(descriptor, 'limit', DEFAULT_LIMIT),
|
||||
filterByMapBounds: _.get(descriptor, 'filterByMapBounds', true),
|
||||
limit: _.get(descriptor, 'limit', DEFAULT_ES_DOC_LIMIT),
|
||||
filterByMapBounds: _.get(descriptor, 'filterByMapBounds', DEFAULT_FILTER_BY_MAP_BOUNDS),
|
||||
tooltipProperties: _.get(descriptor, 'tooltipProperties', []),
|
||||
}, inspectorAdapters);
|
||||
}
|
||||
|
|
|
@ -78,6 +78,27 @@ export function initRoutes(server, licenseUid) {
|
|||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: `${ROOT}/indexCount`,
|
||||
handler: async (request, h) => {
|
||||
const { server, query } = request;
|
||||
|
||||
if (!query.index) {
|
||||
return h.response().code(400);
|
||||
}
|
||||
|
||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
|
||||
|
||||
try {
|
||||
const { count } = await callWithRequest(request, 'count', { index: query.index });
|
||||
return { count };
|
||||
} catch(error) {
|
||||
return h.response().code(400);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function getEMSResources(licenseUid) {
|
||||
|
||||
if (!mapConfig.includeElasticMapsService) {
|
||||
|
|
Loading…
Reference in a new issue