Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Maps] support term joins for Elasticsearch document source with vector tile scaling #129771

Merged
merged 49 commits into from
Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c30da49
support join configuration
nreese Apr 4, 2022
f40866d
set feature state
nreese Apr 4, 2022
ca3a192
pluckStyleMetaFromFeatures
nreese Apr 5, 2022
4a3341d
pluck style meta from joins
nreese Apr 5, 2022
06a3c5c
refactor
nreese Apr 5, 2022
18f602b
eslint
nreese Apr 5, 2022
fb12139
clear feature state on update
nreese Apr 5, 2022
d5ba7ee
fit to bounds
nreese Apr 5, 2022
f205cb9
remove unneeded cast
nreese Apr 5, 2022
6726e7f
Merge branch 'main' of github.com:elastic/kibana into mvt_joins
nreese Apr 6, 2022
b8dadf5
filter out features without join results
nreese Apr 7, 2022
00077ee
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 7, 2022
91299e2
remove/add source if promoteId changes
nreese Apr 7, 2022
0a9a02a
hide all features when there are no results
nreese Apr 7, 2022
b4fc6b3
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 7, 2022
94653ce
disable layout styles
nreese Apr 9, 2022
a015004
Merge branch 'mvt_joins' of github.com:nreese/kibana into mvt_joins
nreese Apr 9, 2022
e19a0b0
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 9, 2022
0b75dba
disable add join button
nreese Apr 10, 2022
38f4156
Merge branch 'mvt_joins' of github.com:nreese/kibana into mvt_joins
nreese Apr 10, 2022
cad1e53
remove multiple term joins when switching to vector tiles
nreese Apr 11, 2022
13a6520
update scaling type in-product help
nreese Apr 11, 2022
5b5f864
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 11, 2022
6726e43
Merge branch 'main' into mvt_joins
kibanamachine Apr 11, 2022
6b0a98f
update chropleth wizard to use MVT scaling
nreese Apr 11, 2022
91b1830
clean up
nreese Apr 11, 2022
249eb64
i18n fixes
nreese Apr 11, 2022
8deb218
remove unused i18n tags
nreese Apr 11, 2022
4920d57
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 11, 2022
a89c8c6
fix jest tests
nreese Apr 11, 2022
2523f2b
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 12, 2022
b8a022a
tslint
nreese Apr 12, 2022
23269b8
Merge branch 'mvt_joins' of github.com:nreese/kibana into mvt_joins
nreese Apr 12, 2022
2b30281
Merge branch 'main' into mvt_joins
kibanamachine Apr 12, 2022
32f195c
fix jest test
nreese Apr 12, 2022
c172c0b
Merge branch 'mvt_joins' of github.com:nreese/kibana into mvt_joins
nreese Apr 12, 2022
a923dac
Merge branch 'main' into mvt_joins
kibanamachine Apr 12, 2022
6a8fe59
add functional test
nreese Apr 12, 2022
5bd4e1e
Merge branch 'mvt_joins' of github.com:nreese/kibana into mvt_joins
nreese Apr 12, 2022
50e4a84
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 12, 2022
6459c5a
more jest fixes
nreese Apr 13, 2022
629ae1a
Merge branch 'mvt_joins' of github.com:nreese/kibana into mvt_joins
nreese Apr 13, 2022
9922313
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 13, 2022
a4167f8
Merge branch 'main' into mvt_joins
kibanamachine Apr 14, 2022
a13e396
merge with main
nreese Apr 18, 2022
e0c95c1
Merge branch 'mvt_joins' of github.com:nreese/kibana into mvt_joins
nreese Apr 18, 2022
b0ecd46
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 18, 2022
2b064ea
Update x-pack/plugins/maps/public/actions/layer_actions.ts
nreese Apr 20, 2022
503d702
merge with main
nreese Apr 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type DataFilters = {
timeslice?: Timeslice;
zoom: number;
isReadOnly: boolean;
joinKeyFilter?: Filter;
};

export type VectorSourceRequestMeta = DataFilters & {
Expand Down Expand Up @@ -77,7 +78,10 @@ export type VectorTileLayerMeta = {
};

// Partial because objects are justified downstream in constructors
export type DataRequestMeta = Partial<
export type DataRequestMeta = {
// request stop time in milliseconds since epoch
requestStopTime?: number;
} & Partial<
VectorSourceRequestMeta &
VectorJoinSourceRequestMeta &
VectorStyleRequestMeta &
Expand Down
49 changes: 43 additions & 6 deletions x-pack/plugins/maps/public/actions/layer_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
createLayerInstance,
getEditState,
getLayerById,
getLayerDescriptor,
getLayerList,
getLayerListRaw,
getMapColors,
Expand Down Expand Up @@ -55,11 +56,12 @@ import {
LayerDescriptor,
StyleDescriptor,
TileMetaFeature,
VectorLayerDescriptor,
} from '../../common/descriptor_types';
import { ILayer } from '../classes/layers/layer';
import { IVectorLayer } from '../classes/layers/vector_layer';
import { OnSourceChangeArgs } from '../classes/sources/source';
import { DRAW_MODE, LAYER_STYLE_TYPE, LAYER_TYPE } from '../../common/constants';
import { DRAW_MODE, LAYER_STYLE_TYPE, LAYER_TYPE, SCALING_TYPES } from '../../common/constants';
import { IVectorStyle } from '../classes/styles/vector/vector_style';
import { notifyLicensedFeatureUsage } from '../licensed_features';
import { IESAggField } from '../classes/fields/agg';
Expand Down Expand Up @@ -362,13 +364,17 @@ function updateSourcePropWithoutSync(
value: unknown,
newLayerType?: LAYER_TYPE
) {
return async (dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) => {
return async (
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
getState: () => MapStoreState
) => {
if (propName === 'metrics') {
if (newLayerType) {
throw new Error('May not change layer-type when modifying metrics source-property');
}
return await dispatch(updateMetricsProp(layerId, value));
}

dispatch({
type: UPDATE_SOURCE_PROP,
layerId,
Expand All @@ -378,6 +384,37 @@ function updateSourcePropWithoutSync(
if (newLayerType) {
dispatch(updateLayerType(layerId, newLayerType));
}

if (propName === 'scalingType') {
// get joins from layer descriptor instead of layer.getJoins()
// 1) IVectorLayer implemenations my return empty array when descriptor has joins
nreese marked this conversation as resolved.
Show resolved Hide resolved
// 2) getJoins returns instances and descriptors are needed.
const layerDescriptor = getLayerDescriptor(getState(), layerId) as VectorLayerDescriptor;
const joins = layerDescriptor.joins ? layerDescriptor.joins : [];
if (value === SCALING_TYPES.CLUSTERS && joins.length) {
// Blended scaling type does not support joins
// It is not possible to display join metrics when showing clusters
dispatch({
type: SET_JOINS,
layerId,
joins: [],
});
await dispatch(updateStyleProperties(layerId));
} else if (value === SCALING_TYPES.MVT) {
if (joins.length > 1) {
// Maplibre feature-state join uses promoteId and there is a limit to one promoteId
// Therefore, Vector tile scaling supports only one join
dispatch({
type: SET_JOINS,
layerId,
joins: [joins[0]],
});
}
// update style props regardless of updating joins
// Allow style to clean-up data driven style properties with join fields that do not support feature-state.
await dispatch(updateStyleProperties(layerId));
}
}
};
}

Expand Down Expand Up @@ -562,7 +599,7 @@ function removeLayerFromLayerList(layerId: string) {
};
}

function updateStyleProperties(layerId: string, previousFields: IField[]) {
function updateStyleProperties(layerId: string, previousFields?: IField[]) {
return async (
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
getState: () => MapStoreState
Expand All @@ -584,7 +621,7 @@ function updateStyleProperties(layerId: string, previousFields: IField[]) {
const nextFields = await (targetLayer as IVectorLayer).getFields(); // take into account all fields, since labels can be driven by any field (source or join)
const { hasChanges, nextStyleDescriptor } = await (
style as IVectorStyle
).getDescriptorWithUpdatedStyleProps(nextFields, previousFields, getMapColors(getState()));
).getDescriptorWithUpdatedStyleProps(nextFields, getMapColors(getState()), previousFields);
if (hasChanges && nextStyleDescriptor) {
dispatch(updateLayerStyle(layerId, nextStyleDescriptor));
}
Expand Down Expand Up @@ -626,9 +663,9 @@ export function updateLayerStyleForSelectedLayer(styleDescriptor: StyleDescripto
export function setJoinsForLayer(layer: ILayer, joins: JoinDescriptor[]) {
return async (dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) => {
const previousFields = await (layer as IVectorLayer).getFields();
await dispatch({
dispatch({
type: SET_JOINS,
layer,
layerId: layer.getId(),
joins,
});
await dispatch(updateStyleProperties(layer.getId(), previousFields));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export async function syncBoundsData({
timeFilters: dataFilters.timeFilters,
timeslice: dataFilters.timeslice,
filters: dataFilters.filters,
joinKeyFilter: dataFilters.joinKeyFilter,
applyGlobalQuery: source.getApplyGlobalQuery(),
applyGlobalTime: source.getApplyGlobalTime(),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import _ from 'lodash';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiIcon } from '@elastic/eui';
Expand All @@ -13,35 +14,30 @@ import type { Map as MbMap, GeoJSONSource as MbGeoJSONSource } from '@kbn/mapbox
import {
EMPTY_FEATURE_COLLECTION,
FEATURE_VISIBLE_PROPERTY_NAME,
FIELD_ORIGIN,
LAYER_TYPE,
} from '../../../../../common/constants';
import {
StyleMetaDescriptor,
Timeslice,
VectorJoinSourceRequestMeta,
VectorLayerDescriptor,
} from '../../../../../common/descriptor_types';
import { PropertiesMap } from '../../../../../common/elasticsearch_util';
import { TimesliceMaskConfig } from '../../../util/mb_filter_expressions';
import { DataRequestContext } from '../../../../actions';
import { IVectorStyle, VectorStyle } from '../../../styles/vector/vector_style';
import { ISource } from '../../../sources/source';
import { IVectorSource } from '../../../sources/vector_source';
import { AbstractLayer, LayerIcon } from '../../layer';
import { InnerJoin } from '../../../joins/inner_join';
import {
AbstractVectorLayer,
noResultsIcon,
NO_RESULTS_ICON_AND_TOOLTIPCONTENT,
} from '../vector_layer';
import { DataRequestAbortError } from '../../../util/data_request';
import { canSkipSourceUpdate } from '../../../util/can_skip_fetch';
import { getFeatureCollectionBounds } from '../../../util/get_feature_collection_bounds';
import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from './assign_feature_ids';
import { syncGeojsonSourceData } from './geojson_source_data';
import { JoinState, performInnerJoins } from './perform_inner_joins';
import { buildVectorRequestMeta } from '../../build_vector_request_meta';
import { performInnerJoins } from './perform_inner_joins';
import { pluckStyleMetaFromFeatures } from './pluck_style_meta_from_features';

export class GeoJsonVectorLayer extends AbstractVectorLayer {
static createDescriptor(
Expand Down Expand Up @@ -127,7 +123,12 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer {
if (!style || !sourceDataRequest) {
return null;
}
return await style.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);

return await pluckStyleMetaFromFeatures(
_.get(sourceDataRequest.getData(), 'features', []),
await this.getSource().getSupportedShapeTypes(),
this.getCurrentStyle().getDynamicPropertiesArray()
);
}

_requiresPrevSourceCleanup(mbMap: MbMap) {
Expand Down Expand Up @@ -159,6 +160,13 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer {
this._setMbLinePolygonProperties(mbMap, undefined, timesliceMaskConfig);
}

_getJoinFilterExpression(): unknown | undefined {
return this.hasJoins()
? // Remove unjoined source features by filtering out features without GeoJSON feature.property[FEATURE_VISIBLE_PROPERTY_NAME] is true
['==', ['get', FEATURE_VISIBLE_PROPERTY_NAME], true]
: undefined;
}

_syncFeatureCollectionWithMb(mbMap: MbMap) {
const mbGeoJSONSource = mbMap.getSource(this.getId()) as MbGeoJSONSource;
const featureCollection = this._getSourceFeatureCollection();
Expand Down Expand Up @@ -260,118 +268,6 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer {
}
}

async _syncJoin({
join,
startLoading,
stopLoading,
onLoadError,
registerCancelCallback,
dataFilters,
isForceRefresh,
}: { join: InnerJoin } & DataRequestContext): Promise<JoinState> {
const joinSource = join.getRightJoinSource();
const sourceDataId = join.getSourceDataRequestId();
const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`);

const joinRequestMeta: VectorJoinSourceRequestMeta = buildVectorRequestMeta(
joinSource,
joinSource.getFieldNames(),
dataFilters,
joinSource.getWhereQuery(),
isForceRefresh
) as VectorJoinSourceRequestMeta;

const prevDataRequest = this.getDataRequest(sourceDataId);
const canSkipFetch = await canSkipSourceUpdate({
source: joinSource,
prevDataRequest,
nextRequestMeta: joinRequestMeta,
extentAware: false, // join-sources are term-aggs that are spatially unaware (e.g. ESTermSource/TableSource).
getUpdateDueToTimeslice: () => {
return true;
},
});

if (canSkipFetch) {
return {
dataHasChanged: false,
join,
propertiesMap: prevDataRequest?.getData() as PropertiesMap,
};
}

try {
startLoading(sourceDataId, requestToken, joinRequestMeta);
const leftSourceName = await this._source.getDisplayName();
const propertiesMap = await joinSource.getPropertiesMap(
joinRequestMeta,
leftSourceName,
join.getLeftField().getName(),
registerCancelCallback.bind(null, requestToken)
);
stopLoading(sourceDataId, requestToken, propertiesMap);
return {
dataHasChanged: true,
join,
propertiesMap,
};
} catch (error) {
if (!(error instanceof DataRequestAbortError)) {
onLoadError(sourceDataId, requestToken, `Join error: ${error.message}`);
}
throw error;
}
}

async _syncJoins(syncContext: DataRequestContext, style: IVectorStyle) {
const joinSyncs = this.getValidJoins().map(async (join) => {
await this._syncJoinStyleMeta(syncContext, join, style);
await this._syncJoinFormatters(syncContext, join, style);
return this._syncJoin({ join, ...syncContext });
});

return await Promise.all(joinSyncs);
}

async _syncJoinStyleMeta(syncContext: DataRequestContext, join: InnerJoin, style: IVectorStyle) {
const joinSource = join.getRightJoinSource();
return this._syncStyleMeta({
source: joinSource,
style,
sourceQuery: joinSource.getWhereQuery(),
dataRequestId: join.getSourceMetaDataRequestId(),
dynamicStyleProps: this.getCurrentStyle()
.getDynamicPropertiesArray()
.filter((dynamicStyleProp) => {
const matchingField = joinSource.getFieldByName(dynamicStyleProp.getFieldName());
return (
dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN &&
!!matchingField &&
dynamicStyleProp.isFieldMetaEnabled()
);
}),
...syncContext,
});
}

async _syncJoinFormatters(syncContext: DataRequestContext, join: InnerJoin, style: IVectorStyle) {
const joinSource = join.getRightJoinSource();
return this._syncFormatters({
source: joinSource,
dataRequestId: join.getSourceFormattersDataRequestId(),
fields: style
.getDynamicPropertiesArray()
.filter((dynamicStyleProp) => {
const matchingField = joinSource.getFieldByName(dynamicStyleProp.getFieldName());
return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && !!matchingField;
})
.map((dynamicStyleProp) => {
return dynamicStyleProp.getField()!;
}),
...syncContext,
});
}

_getSourceFeatureCollection() {
const sourceDataRequest = this.getSourceDataRequest();
return sourceDataRequest ? (sourceDataRequest.getData() as FeatureCollection) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,13 @@ import { FeatureCollection } from 'geojson';
import { i18n } from '@kbn/i18n';
import { FEATURE_VISIBLE_PROPERTY_NAME } from '../../../../../common/constants';
import { DataRequestContext } from '../../../../actions';
import { InnerJoin } from '../../../joins/inner_join';
import { PropertiesMap } from '../../../../../common/elasticsearch_util';
import { JoinState } from '../types';

interface SourceResult {
refreshed: boolean;
featureCollection: FeatureCollection;
}

export interface JoinState {
dataHasChanged: boolean;
join: InnerJoin;
propertiesMap?: PropertiesMap;
}

export async function performInnerJoins(
sourceResult: SourceResult,
joinStates: JoinState[],
Expand Down
Loading