[Maps] fix choropleth map with applyGlobalQuery set to false still creates filter for source. (#108999) (#109915)

* [Maps] fix choropleth map with applyGlobalQuery set to false still creates filter for source.

* cleanup functional test

* eslint

* fix functional test

* add inidication in tooltip when field is join key

* copy updates

* update jest test

* eslint

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Nathan Reese <reese.nathan@gmail.com>
This commit is contained in:
Kibana Machine 2021-08-24 17:10:40 -04:00 committed by GitHub
parent 5e3572ef51
commit 7b0adb268c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 168 additions and 31 deletions

View file

@ -62,7 +62,13 @@ export class CountAggField implements IESAggField {
async createTooltipProperty(value: string | string[] | undefined): Promise<ITooltipProperty> {
const indexPattern = await this._source.getIndexPattern();
const tooltipProperty = new TooltipProperty(this.getName(), await this.getLabel(), value);
return new ESAggTooltipProperty(tooltipProperty, indexPattern, this, this._getAggType());
return new ESAggTooltipProperty(
tooltipProperty,
indexPattern,
this,
this._getAggType(),
this._source.getApplyGlobalQuery()
);
}
getValueAggDsl(indexPattern: IndexPattern): unknown | null {

View file

@ -53,7 +53,12 @@ export class ESDocField extends AbstractField implements IField {
async createTooltipProperty(value: string | string[] | undefined): Promise<ITooltipProperty> {
const indexPattern = await this._source.getIndexPattern();
const tooltipProperty = new TooltipProperty(this.getName(), await this.getLabel(), value);
return new ESTooltipProperty(tooltipProperty, indexPattern, this as IField);
return new ESTooltipProperty(
tooltipProperty,
indexPattern,
this as IField,
this._source.getApplyGlobalQuery()
);
}
async getDataType(): Promise<string> {

View file

@ -75,7 +75,6 @@ export interface IVectorSource extends ISource {
defaultFields: Record<string, Record<string, string>>
): Promise<void>;
deleteFeature(featureId: string): Promise<void>;
isFilterByMapBounds(): boolean;
}
export class AbstractVectorSource extends AbstractSource implements IVectorSource {

View file

@ -18,13 +18,14 @@ export class ESAggTooltipProperty extends ESTooltipProperty {
tooltipProperty: ITooltipProperty,
indexPattern: IndexPattern,
field: IField,
aggType: AGG_TYPE
aggType: AGG_TYPE,
applyGlobalQuery: boolean
) {
super(tooltipProperty, indexPattern, field);
super(tooltipProperty, indexPattern, field, applyGlobalQuery);
this._aggType = aggType;
}
isFilterable(): boolean {
return this._aggType === AGG_TYPE.TERMS;
return this._aggType === AGG_TYPE.TERMS ? super.isFilterable() : false;
}
}

View file

@ -13,6 +13,9 @@ import { FIELD_ORIGIN } from '../../../common/constants';
class MockField extends AbstractField {}
const APPLY_GLOBAL_QUERY = true;
const DO_NOT_APPLY_GLOBAL_QUERY = false;
const indexPatternField = {
name: 'machine.os',
type: 'string',
@ -29,11 +32,33 @@ const featurePropertyField = new MockField({
origin: FIELD_ORIGIN.SOURCE,
});
const nonFilterableIndexPatternField = {
name: 'location',
type: 'geo_point',
esTypes: ['geo_point'],
count: 0,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: false,
} as IFieldType;
const nonFilterableFeaturePropertyField = new MockField({
fieldName: 'location',
origin: FIELD_ORIGIN.SOURCE,
});
const indexPattern = {
id: 'indexPatternId',
fields: {
getByName: (name: string): IFieldType | null => {
return name === 'machine.os' ? indexPatternField : null;
if (name === 'machine.os') {
return indexPatternField;
}
if (name === 'location') {
return nonFilterableIndexPatternField;
}
return null;
},
},
title: 'my index pattern',
@ -52,7 +77,8 @@ describe('getESFilters', () => {
'my value'
),
indexPattern,
notFoundFeaturePropertyField
notFoundFeaturePropertyField,
APPLY_GLOBAL_QUERY
);
expect(await esTooltipProperty.getESFilters()).toEqual([]);
});
@ -65,7 +91,8 @@ describe('getESFilters', () => {
'my value'
),
indexPattern,
featurePropertyField
featurePropertyField,
APPLY_GLOBAL_QUERY
);
expect(await esTooltipProperty.getESFilters()).toEqual([
{
@ -89,7 +116,8 @@ describe('getESFilters', () => {
undefined
),
indexPattern,
featurePropertyField
featurePropertyField,
APPLY_GLOBAL_QUERY
);
expect(await esTooltipProperty.getESFilters()).toEqual([
{
@ -103,4 +131,62 @@ describe('getESFilters', () => {
},
]);
});
test('Should return empty array when applyGlobalQuery is false', async () => {
const esTooltipProperty = new ESTooltipProperty(
new TooltipProperty(
featurePropertyField.getName(),
await featurePropertyField.getLabel(),
'my value'
),
indexPattern,
featurePropertyField,
DO_NOT_APPLY_GLOBAL_QUERY
);
expect(await esTooltipProperty.getESFilters()).toEqual([]);
});
});
describe('isFilterable', () => {
test('Should by true when field is filterable and apply global query is true', async () => {
const esTooltipProperty = new ESTooltipProperty(
new TooltipProperty(
featurePropertyField.getName(),
await featurePropertyField.getLabel(),
'my value'
),
indexPattern,
featurePropertyField,
APPLY_GLOBAL_QUERY
);
expect(esTooltipProperty.isFilterable()).toBe(true);
});
test('Should by false when field is not filterable and apply global query is true', async () => {
const esTooltipProperty = new ESTooltipProperty(
new TooltipProperty(
nonFilterableFeaturePropertyField.getName(),
await nonFilterableFeaturePropertyField.getLabel(),
'my value'
),
indexPattern,
nonFilterableFeaturePropertyField,
APPLY_GLOBAL_QUERY
);
expect(esTooltipProperty.isFilterable()).toBe(false);
});
test('Should by false when field is filterable and apply global query is false', async () => {
const esTooltipProperty = new ESTooltipProperty(
new TooltipProperty(
featurePropertyField.getName(),
await featurePropertyField.getLabel(),
'my value'
),
indexPattern,
featurePropertyField,
DO_NOT_APPLY_GLOBAL_QUERY
);
expect(esTooltipProperty.isFilterable()).toBe(false);
});
});

View file

@ -19,18 +19,25 @@ export class ESTooltipProperty implements ITooltipProperty {
private readonly _tooltipProperty: ITooltipProperty;
private readonly _indexPattern: IndexPattern;
private readonly _field: IField;
private readonly _applyGlobalQuery: boolean;
constructor(tooltipProperty: ITooltipProperty, indexPattern: IndexPattern, field: IField) {
constructor(
tooltipProperty: ITooltipProperty,
indexPattern: IndexPattern,
field: IField,
applyGlobalQuery: boolean
) {
this._tooltipProperty = tooltipProperty;
this._indexPattern = indexPattern;
this._field = field;
this._applyGlobalQuery = applyGlobalQuery;
}
getPropertyKey(): string {
return this._tooltipProperty.getPropertyKey();
}
getPropertyName(): string {
getPropertyName() {
return this._tooltipProperty.getPropertyName();
}
@ -65,6 +72,10 @@ export class ESTooltipProperty implements ITooltipProperty {
}
isFilterable(): boolean {
if (!this._applyGlobalQuery) {
return false;
}
const indexPatternField = this._getIndexPatternField();
return (
!!indexPatternField &&
@ -76,6 +87,10 @@ export class ESTooltipProperty implements ITooltipProperty {
}
async getESFilters(): Promise<Filter[]> {
if (!this._applyGlobalQuery) {
return [];
}
const indexPatternField = this._getIndexPatternField();
if (!indexPatternField) {
return [];

View file

@ -5,17 +5,20 @@
* 2.0.
*/
import React, { ReactNode } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiIcon, EuiToolTip } from '@elastic/eui';
import { ITooltipProperty } from './tooltip_property';
import { InnerJoin } from '../joins/inner_join';
import { Filter } from '../../../../../../src/plugins/data/public';
export class JoinTooltipProperty implements ITooltipProperty {
private readonly _tooltipProperty: ITooltipProperty;
private readonly _leftInnerJoins: InnerJoin[];
private readonly _innerJoins: InnerJoin[];
constructor(tooltipProperty: ITooltipProperty, leftInnerJoins: InnerJoin[]) {
constructor(tooltipProperty: ITooltipProperty, innerJoins: InnerJoin[]) {
this._tooltipProperty = tooltipProperty;
this._leftInnerJoins = leftInnerJoins;
this._innerJoins = innerJoins;
}
isFilterable(): boolean {
@ -26,8 +29,28 @@ export class JoinTooltipProperty implements ITooltipProperty {
return this._tooltipProperty.getPropertyKey();
}
getPropertyName(): string {
return this._tooltipProperty.getPropertyName();
getPropertyName(): ReactNode {
const content = i18n.translate('xpack.maps.tooltip.joinPropertyTooltipContent', {
defaultMessage: `Shared key '{leftFieldName}' is joined with {rightSources}`,
values: {
leftFieldName: this._tooltipProperty.getPropertyName() as string,
rightSources: this._innerJoins
.map((innerJoin) => {
const rightSource = innerJoin.getRightJoinSource();
const termField = rightSource.getTermField();
return `'${termField.getName()}'`;
})
.join(','),
},
});
return (
<>
{this._tooltipProperty.getPropertyName()}
<EuiToolTip position="bottom" content={content}>
<EuiIcon type="link" />
</EuiToolTip>
</>
);
}
getRawValue(): string | string[] | undefined {
@ -40,13 +63,11 @@ export class JoinTooltipProperty implements ITooltipProperty {
async getESFilters(): Promise<Filter[]> {
const esFilters = [];
if (this._tooltipProperty.isFilterable()) {
const filters = await this._tooltipProperty.getESFilters();
esFilters.push(...filters);
}
for (let i = 0; i < this._leftInnerJoins.length; i++) {
const rightSource = this._leftInnerJoins[i].getRightJoinSource();
// only create filters for right sources.
// do not create filters for left source.
for (let i = 0; i < this._innerJoins.length; i++) {
const rightSource = this._innerJoins[i].getRightJoinSource();
const termField = rightSource.getTermField();
try {
const esTooltipProperty = await termField.createTooltipProperty(

View file

@ -6,6 +6,7 @@
*/
import _ from 'lodash';
import { ReactNode } from 'react';
import { GeoJsonProperties, Geometry } from 'geojson';
import { Filter } from 'src/plugins/data/public';
import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
@ -14,7 +15,7 @@ import type { TooltipFeature } from '../../../../../plugins/maps/common/descript
export interface ITooltipProperty {
getPropertyKey(): string;
getPropertyName(): string;
getPropertyName(): string | ReactNode;
getHtmlDisplayValue(): string;
getRawValue(): string | string[] | undefined;
isFilterable(): boolean;

View file

@ -31,6 +31,10 @@ class MockTooltipProperty {
return this._value;
}
getPropertyKey() {
return this._key;
}
getPropertyName() {
return this._key;
}

View file

@ -329,10 +329,11 @@ export class FeatureProperties extends Component<Props, State> {
}
const rows = this.state.properties.map((tooltipProperty) => {
const label = tooltipProperty.getPropertyName();
return (
<tr key={label} className="mapFeatureTooltip_row">
<td className="eui-textOverflowWrap mapFeatureTooltip__propertyLabel">{label}</td>
<tr key={tooltipProperty.getPropertyKey()} className="mapFeatureTooltip_row">
<td className="eui-textOverflowWrap mapFeatureTooltip__propertyLabel">
{tooltipProperty.getPropertyName()}
</td>
<td
className="eui-textOverflowWrap"
/*

View file

@ -53,11 +53,9 @@ export default function ({ getPageObjects, getService }) {
it('should create filters when create filter button is clicked', async () => {
await testSubjects.click('mapTooltipCreateFilterButton');
await testSubjects.click('applyFiltersPopoverButton');
// TODO: Fix me #64861
// const hasSourceFilter = await filterBar.hasFilter('name', 'charlie');
// expect(hasSourceFilter).to.be(true);
const numFilters = await filterBar.getFilterCount();
expect(numFilters).to.be(1);
const hasJoinFilter = await filterBar.hasFilter('runtime_shape_name', 'charlie');
expect(hasJoinFilter).to.be(true);