[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:
Nathan Reese 2019-04-04 18:34:37 -06:00 committed by GitHub
parent c4aedfc5ba
commit 23bdee0956
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 122 additions and 15 deletions

View file

@ -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;

View file

@ -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>
);
}

View file

@ -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);
}

View file

@ -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) {