[Maps] term join limit (#86491)

* [Maps] term join limit

* update region map migration to set size

* display size in join expression

* tslint

* update jest snapshots

* remove default size and just default to max buckets

* add size to sourceMeta so join data is re-requested on size change

* tslint

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2021-01-06 08:43:10 -07:00 committed by GitHub
parent 6c87222e67
commit 8a0c8f35bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 119 additions and 27 deletions

View file

@ -71,6 +71,7 @@ export function getDeprecationMessage(vis: Vis) {
const bucketAggs = vis.data?.aggs?.byType('buckets');
if (bucketAggs?.length && bucketAggs[0].type.dslName === 'terms') {
createUrlParams.termsFieldName = bucketAggs[0].getField()?.name;
createUrlParams.termsSize = bucketAggs[0].getParam('size');
}
const metricAggs = vis.data?.aggs?.byType('metrics');

View file

@ -39,10 +39,15 @@ type ESGeoLineSourceSyncMeta = {
sortField: string;
};
type ESTermSourceSyncMeta = {
size: number;
};
export type VectorSourceSyncMeta =
| ESSearchSourceSyncMeta
| ESGeoGridSourceSyncMeta
| ESGeoLineSourceSyncMeta
| ESTermSourceSyncMeta
| null;
export type VectorSourceRequestMeta = MapFilters & {
@ -54,10 +59,9 @@ export type VectorSourceRequestMeta = MapFilters & {
sourceMeta: VectorSourceSyncMeta;
};
export type VectorJoinSourceRequestMeta = Omit<
VectorSourceRequestMeta,
'geogridPrecision' | 'sourceMeta'
> & { sourceQuery?: Query };
export type VectorJoinSourceRequestMeta = Omit<VectorSourceRequestMeta, 'geogridPrecision'> & {
sourceQuery?: Query;
};
export type VectorStyleRequestMeta = MapFilters & {
dynamicStyleFields: string[];

View file

@ -104,6 +104,7 @@ export type ESTermSourceDescriptor = AbstractESAggSourceDescriptor & {
indexPatternTitle?: string;
term: string; // term field name
whereQuery?: Query;
size?: number;
};
export type KibanaRegionmapSourceDescriptor = AbstractSourceDescriptor & {

View file

@ -8,6 +8,7 @@ import uuid from 'uuid/v4';
import {
AggDescriptor,
ColorDynamicOptions,
ESTermSourceDescriptor,
LayerDescriptor,
} from '../../../common/descriptor_types';
import {
@ -48,6 +49,7 @@ export function createRegionMapLayerDescriptor({
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
indexPatternTitle,
@ -58,6 +60,7 @@ export function createRegionMapLayerDescriptor({
emsLayerId?: string;
leftFieldName?: string;
termsFieldName?: string;
termsSize?: number;
colorSchema: string;
indexPatternId?: string;
indexPatternTitle?: string;
@ -78,21 +81,25 @@ export function createRegionMapLayerDescriptor({
const colorPallette = NUMERICAL_COLOR_PALETTES.find((pallette) => {
return pallette.value.toLowerCase() === colorSchema.toLowerCase();
});
const termSourceDescriptor: ESTermSourceDescriptor = {
type: SOURCE_TYPES.ES_TERM_SOURCE,
id: joinId,
indexPatternId,
indexPatternTitle: indexPatternTitle ? indexPatternTitle : indexPatternId,
term: termsFieldName,
metrics: [metricsDescriptor],
applyGlobalQuery: true,
applyGlobalTime: true,
};
if (termsSize !== undefined) {
termSourceDescriptor.size = termsSize;
}
return VectorLayer.createDescriptor({
label,
joins: [
{
leftField: leftFieldName,
right: {
type: SOURCE_TYPES.ES_TERM_SOURCE,
id: joinId,
indexPatternId,
indexPatternTitle: indexPatternTitle ? indexPatternTitle : indexPatternId,
term: termsFieldName,
metrics: [metricsDescriptor],
applyGlobalQuery: true,
applyGlobalTime: true,
},
right: termSourceDescriptor,
},
],
sourceDescriptor: EMSFileSource.createDescriptor({

View file

@ -349,6 +349,7 @@ export class VectorLayer extends AbstractLayer {
sourceQuery: joinSource.getWhereQuery(),
applyGlobalQuery: joinSource.getApplyGlobalQuery(),
applyGlobalTime: joinSource.getApplyGlobalTime(),
sourceMeta: joinSource.getSyncMeta(),
};
const prevDataRequest = this.getDataRequest(sourceDataId);

View file

@ -25,6 +25,7 @@ import {
import {
ESTermSourceDescriptor,
VectorJoinSourceRequestMeta,
VectorSourceSyncMeta,
} from '../../../../common/descriptor_types';
import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters';
import { PropertiesMap } from '../../../../common/elasticsearch_util';
@ -124,7 +125,9 @@ export class ESTermSource extends AbstractESAggSource {
const indexPattern = await this.getIndexPattern();
const searchSource: ISearchSource = await this.makeSearchSource(searchFilters, 0);
const termsField = getField(indexPattern, this._termField.getName());
const termsAgg = { size: DEFAULT_MAX_BUCKETS_LIMIT };
const termsAgg = {
size: this._descriptor.size !== undefined ? this._descriptor.size : DEFAULT_MAX_BUCKETS_LIMIT,
};
searchSource.setField('aggs', {
[TERMS_AGG_NAME]: {
terms: addFieldToDSL(termsAgg, termsField),
@ -162,4 +165,12 @@ export class ESTermSource extends AbstractESAggSource {
getFieldNames(): string[] {
return this.getMetricFields().map((esAggMetricField) => esAggMetricField.getName());
}
getSyncMeta(): VectorSourceSyncMeta | null {
return this._descriptor.size !== undefined
? {
size: this._descriptor.size,
}
: null;
}
}

View file

@ -3,7 +3,7 @@
exports[`should render with error 1`] = `
<EuiFormRow
describedByIds={Array []}
display="columnCompressed"
display="row"
error={
Array [
"Must be between 0 and 20",
@ -30,7 +30,7 @@ exports[`should render with error 1`] = `
exports[`should render without error 1`] = `
<EuiFormRow
describedByIds={Array []}
display="columnCompressed"
display="row"
error={Array []}
fullWidth={false}
hasChildLabel={true}

View file

@ -154,6 +154,7 @@ export function MetricEditor({
initialValue={
typeof metric.percentile === 'number' ? metric.percentile : DEFAULT_PERCENTILE
}
display="columnCompressed"
/>
);
}

View file

@ -4,8 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Component, ChangeEvent } from 'react';
import { EuiFieldNumber, EuiFormRow } from '@elastic/eui';
import React, { Component, ChangeEvent, ReactNode } from 'react';
// @ts-expect-error
import { EuiFieldNumber, EuiFormRow, EuiFormRowDisplayKeys } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import _ from 'lodash';
@ -21,6 +22,8 @@ interface Props {
max: number;
onChange: (value: number) => void;
label: string;
display?: EuiFormRowDisplayKeys;
helpText?: ReactNode;
}
function getErrorMessage(min: number, max: number): string {
@ -97,7 +100,8 @@ export class ValidatedNumberInput extends Component<Props, State> {
label={this.props.label}
isInvalid={!this.state.isValid}
error={this.state.errorMessage ? [this.state.errorMessage] : []}
display="columnCompressed"
display={this.props.display}
helpText={this.props.helpText}
>
<EuiFieldNumber
isInvalid={!this.state.isValid}

View file

@ -5,6 +5,7 @@
*/
import React, { Fragment } from 'react';
import _ from 'lodash';
import uuid from 'uuid/v4';
import {

View file

@ -19,7 +19,6 @@ exports[`Should render default props 1`] = `
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
withTitle={true}
>
<div
style={
@ -74,7 +73,6 @@ exports[`Should render metrics expression for metrics 1`] = `
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
withTitle={true}
>
<div
style={

View file

@ -77,10 +77,12 @@ export class Join extends Component {
loadError: undefined,
});
this._loadRightFields(indexPatternId);
// eslint-disable-next-line no-unused-vars
const { term, ...restOfRight } = this.props.join.right;
this.props.onChange({
leftField: this.props.join.leftField,
right: {
id: this.props.join.right.id,
...restOfRight,
indexPatternId,
indexPatternTitle,
},
@ -97,6 +99,16 @@ export class Join extends Component {
});
};
_onRightSizeChange = (size) => {
this.props.onChange({
leftField: this.props.join.leftField,
right: {
...this.props.join.right,
size,
},
});
};
_onMetricsChange = (metrics) => {
this.props.onChange({
leftField: this.props.join.leftField,
@ -161,7 +173,7 @@ export class Join extends Component {
);
globalFilterCheckbox = (
<GlobalFilterCheckbox
applyGlobalQuery={right.applyGlobalQuery}
applyGlobalQuery={right.applyGlobalQuery === 'undefined' ? true : right.applyGlobalQuery}
setApplyGlobalQuery={this._onApplyGlobalQueryChange}
label={i18n.translate('xpack.maps.layerPanel.join.applyGlobalQueryCheckboxLabel', {
defaultMessage: `Apply global filter to join`,
@ -209,8 +221,10 @@ export class Join extends Component {
rightSourceName={rightSourceName}
onRightSourceChange={this._onRightSourceChange}
rightValue={right.term}
rightSize={right.size}
rightFields={rightFields}
onRightFieldChange={this._onRightFieldChange}
onRightSizeChange={this._onRightSizeChange}
/>
</EuiFlexItem>

View file

@ -17,7 +17,9 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DEFAULT_MAX_BUCKETS_LIMIT } from '../../../../../common/constants';
import { SingleFieldSelect } from '../../../../components/single_field_select';
import { ValidatedNumberInput } from '../../../../components/validated_number_input';
import { FormattedMessage } from '@kbn/i18n/react';
import { getTermsFields } from '../../../../index_pattern_util';
@ -155,10 +157,49 @@ export class JoinExpression extends Component {
);
}
_renderRightFieldSizeInput() {
if (!this.props.rightValue || !this.props.leftValue) {
return null;
}
return (
<ValidatedNumberInput
initialValue={
this.props.rightSize !== undefined ? this.props.rightSize : DEFAULT_MAX_BUCKETS_LIMIT
}
min={1}
max={DEFAULT_MAX_BUCKETS_LIMIT}
onChange={this.props.onRightSizeChange}
label={i18n.translate('xpack.maps.layerPanel.joinExpression.rightSizeLabel', {
defaultMessage: 'Right size',
})}
helpText={i18n.translate('xpack.maps.layerPanel.joinExpression.rightSizeHelpText', {
defaultMessage: 'Right field term limit.',
})}
/>
);
}
_getExpressionValue() {
const { leftSourceName, leftValue, rightSourceName, rightValue } = this.props;
const { leftSourceName, leftValue, rightSourceName, rightValue, rightSize } = this.props;
if (leftSourceName && leftValue && rightSourceName && rightValue) {
return `${leftSourceName}:${leftValue} with ${rightSourceName}:${rightValue}`;
return i18n.translate('xpack.maps.layerPanel.joinExpression.value', {
defaultMessage:
'{leftSourceName}:{leftValue} with {sizeFragment} {rightSourceName}:{rightValue}',
values: {
leftSourceName,
leftValue,
sizeFragment:
rightSize !== undefined
? i18n.translate('xpack.maps.layerPanel.joinExpression.sizeFragment', {
defaultMessage: 'top {rightSize} terms from',
values: { rightSize },
})
: '',
rightSourceName,
rightValue,
},
});
}
return i18n.translate('xpack.maps.layerPanel.joinExpression.selectPlaceholder', {
@ -213,6 +254,8 @@ export class JoinExpression extends Component {
{this._renderRightSourceSelect()}
{this._renderRightFieldSelect()}
{this._renderRightFieldSizeInput()}
</div>
</EuiPopover>
);
@ -240,8 +283,10 @@ JoinExpression.propTypes = {
// Right field props
rightValue: PropTypes.string,
rightSize: PropTypes.number,
rightFields: PropTypes.array,
onRightFieldChange: PropTypes.func.isRequired,
onRightSizeChange: PropTypes.func.isRequired,
};
function getSelectFieldPlaceholder() {

View file

@ -93,7 +93,6 @@ export class MetricsExpression extends Component {
closePopover={this._closePopover}
ownFocus
initialFocus="body" /* avoid initialFocus on Combobox */
withTitle
anchorPosition="leftCenter"
button={
<EuiExpression

View file

@ -52,6 +52,7 @@ interface LazyLoadedMapModules {
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
indexPatternTitle,
@ -62,6 +63,7 @@ interface LazyLoadedMapModules {
emsLayerId?: string;
leftFieldName?: string;
termsFieldName?: string;
termsSize?: number;
colorSchema: string;
indexPatternId?: string;
indexPatternTitle?: string;

View file

@ -183,6 +183,7 @@ export const createRegionMapUrlGenerator = (
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
indexPatternTitle,
@ -197,6 +198,7 @@ export const createRegionMapUrlGenerator = (
emsLayerId?: string;
leftFieldName?: string;
termsFieldName?: string;
termsSize?: number;
colorSchema: string;
indexPatternId?: string;
indexPatternTitle?: string;
@ -214,6 +216,7 @@ export const createRegionMapUrlGenerator = (
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
indexPatternTitle,