mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[maps] display incomplete results warning in layer legend (#171144)
Closes https://github.com/elastic/kibana/issues/170653 Closes https://github.com/elastic/kibana/issues/170654 PR updates Maps to display incomplete result warnings in layer legend (instead of displaying toast) ### Test setup 1. In console, run: ``` PUT geo1 {} PUT geo1/_mapping { "properties": { "location": { "type": "geo_point" } } } PUT geo1/_doc/1 { "location": "25,25" } PUT geo2 {} PUT geo2/_mapping { "properties": { "location": { "type": "geo_point" } } } PUT geo2/_doc/1 { "location": "35,35" } ``` 2. Create `geo*` data view ### Test vector tile request warning "View details" button for vector tile requests is out of scope for this PR. Vector tile requests use _mvt API instead of _search API. As such, vector tile requests do not use search source or request inspector. 1. create new map, add documents layer from `geo*` data view. 2. add filter ``` { "error_query": { "indices": [ { "error_type": "exception", "message": "local shard failure message 123", "name": "geo2" } ] } } ``` <img width="400" alt="Screenshot 2023-11-13 at 2 08 06 PM" src="8b608400
-79d0-4800-9980-5af76b507a43"> ### Test geojson incomplete results warning with single request 1. create new map, add documents layer from `geo*` data view. 2. Set scaling to "Limit results to 10000" 3. add filter ``` { "error_query": { "indices": [ { "error_type": "exception", "message": "local shard failure message 123", "name": "geo2" } ] } } ``` <img width="400" alt="Screenshot 2023-11-13 at 2 11 48 PM" src="e1b1de01
-1db7-40ad-b221-29f42baf5735"> ### Test geojson incomplete results warning with multiple requests 1. create new map, add documents layer from `geo*` data view. 2. Set scaling to "Show clusters when results exceed 10000" 3. add filter ``` { "error_query": { "indices": [ { "error_type": "exception", "message": "local shard failure message 123", "name": "geo2" } ] } } ``` <img width="400" alt="Screenshot 2023-11-13 at 2 12 57 PM" src="27beffc4
-1dba-4ec4-90f8-e92002f8a63a"> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c3ac89c9f0
commit
0b24e40b54
42 changed files with 690 additions and 400 deletions
|
@ -9,11 +9,15 @@
|
|||
export type { SearchResponseWarning, WarningHandlerCallback } from './src/types';
|
||||
|
||||
export {
|
||||
getWarningsDescription,
|
||||
getWarningsTitle,
|
||||
SearchResponseWarningsBadge,
|
||||
SearchResponseWarningsBadgePopoverContent,
|
||||
SearchResponseWarningsCallout,
|
||||
SearchResponseWarningsEmptyPrompt,
|
||||
ViewDetailsPopover,
|
||||
} from './src/components/search_response_warnings';
|
||||
|
||||
export { extractWarnings } from './src/extract_warnings';
|
||||
export { handleWarnings } from './src/handle_warnings';
|
||||
export { hasUnsupportedDownsampledAggregationFailure } from './src/has_unsupported_downsampled_aggregation_failure';
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { getWarningsDescription, getWarningsTitle } from './i18n_utils';
|
||||
export { SearchResponseWarningsBadge } from './badge';
|
||||
export { SearchResponseWarningsBadgePopoverContent } from './badge_popover_content';
|
||||
export { SearchResponseWarningsCallout } from './callout';
|
||||
export { SearchResponseWarningsEmptyPrompt } from './empty_prompt';
|
||||
export { ViewDetailsPopover } from './view_details_popover';
|
||||
|
|
|
@ -11,6 +11,7 @@ import type { KibanaExecutionContext } from '@kbn/core/public';
|
|||
import type { Query } from '@kbn/data-plugin/common';
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import { MapExtent } from './map_descriptor';
|
||||
|
||||
export type Timeslice = {
|
||||
|
@ -91,6 +92,7 @@ export type VectorTileLayerMeta = {
|
|||
export type DataRequestMeta = {
|
||||
// request stop time in milliseconds since epoch
|
||||
requestStopTime?: number;
|
||||
warnings?: SearchResponseWarning[];
|
||||
} & Partial<
|
||||
SourceRequestMeta &
|
||||
VectorSourceRequestMeta &
|
||||
|
|
|
@ -10,12 +10,18 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { Map as MbMap } from '@kbn/mapbox-gl';
|
||||
import type { Query } from '@kbn/es-query';
|
||||
import {
|
||||
getWarningsTitle,
|
||||
type SearchResponseWarning,
|
||||
ViewDetailsPopover,
|
||||
} from '@kbn/search-response-warnings';
|
||||
import _ from 'lodash';
|
||||
import React, { ReactElement, ReactNode } from 'react';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { FeatureCollection } from 'geojson';
|
||||
import { DataRequest } from '../util/data_request';
|
||||
import { hasIncompleteResults } from '../util/tile_meta_feature_utils';
|
||||
import {
|
||||
LAYER_TYPE,
|
||||
MAX_ZOOM,
|
||||
|
@ -41,9 +47,16 @@ import { LICENSED_FEATURES } from '../../licensed_features';
|
|||
import { IESSource } from '../sources/es_source';
|
||||
import { TileErrorsList } from './tile_errors_list';
|
||||
|
||||
export interface LayerError {
|
||||
export const INCOMPLETE_RESULTS_WARNING = i18n.translate(
|
||||
'xpack.maps.layer.incompleteResultsWarning',
|
||||
{
|
||||
defaultMessage: `Layer had issues returning data and results might be incomplete.`,
|
||||
}
|
||||
);
|
||||
|
||||
export interface LayerMessage {
|
||||
title: string;
|
||||
error: ReactNode;
|
||||
body: ReactNode;
|
||||
}
|
||||
|
||||
export interface ILayer {
|
||||
|
@ -77,7 +90,9 @@ export interface ILayer {
|
|||
isLayerLoading(zoom: number): boolean;
|
||||
isFilteredByGlobalTime(): Promise<boolean>;
|
||||
hasErrors(): boolean;
|
||||
getErrors(): LayerError[];
|
||||
getErrors(): LayerMessage[];
|
||||
hasWarnings(): boolean;
|
||||
getWarnings(): LayerMessage[];
|
||||
|
||||
/*
|
||||
* ILayer.getMbLayerIds returns a list of all mapbox layers assoicated with this layer.
|
||||
|
@ -405,14 +420,14 @@ export class AbstractLayer implements ILayer {
|
|||
});
|
||||
}
|
||||
|
||||
getErrors(): LayerError[] {
|
||||
const errors: LayerError[] = [];
|
||||
getErrors(): LayerMessage[] {
|
||||
const errors: LayerMessage[] = [];
|
||||
|
||||
const sourceError = this.getSourceDataRequest()?.renderError();
|
||||
if (sourceError) {
|
||||
errors.push({
|
||||
title: this._getSourceErrorTitle(),
|
||||
error: sourceError,
|
||||
body: sourceError,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -421,13 +436,59 @@ export class AbstractLayer implements ILayer {
|
|||
title: i18n.translate('xpack.maps.layer.tileErrorTitle', {
|
||||
defaultMessage: `An error occurred when loading layer tiles`,
|
||||
}),
|
||||
error: <TileErrorsList tileErrors={this._descriptor.__tileErrors} />,
|
||||
body: <TileErrorsList tileErrors={this._descriptor.__tileErrors} />,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
hasWarnings(): boolean {
|
||||
const hasDataRequestWarnings = this._dataRequests.some((dataRequest) => {
|
||||
const dataRequestMeta = dataRequest.getMeta();
|
||||
return dataRequestMeta?.warnings?.length;
|
||||
});
|
||||
|
||||
if (hasDataRequestWarnings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this._isTiled() ? this._getTileMetaFeatures().some(hasIncompleteResults) : false;
|
||||
}
|
||||
|
||||
getWarnings(): LayerMessage[] {
|
||||
const warningMessages: LayerMessage[] = [];
|
||||
|
||||
const dataRequestWarnings: SearchResponseWarning[] = [];
|
||||
this._dataRequests.forEach((dataRequest) => {
|
||||
const dataRequestMeta = dataRequest.getMeta();
|
||||
if (dataRequestMeta?.warnings?.length) {
|
||||
dataRequestWarnings.push(...dataRequestMeta.warnings);
|
||||
}
|
||||
});
|
||||
|
||||
if (dataRequestWarnings.length) {
|
||||
warningMessages.push({
|
||||
title: getWarningsTitle(dataRequestWarnings),
|
||||
body: (
|
||||
<>
|
||||
{INCOMPLETE_RESULTS_WARNING}{' '}
|
||||
<ViewDetailsPopover displayAsLink={true} warnings={dataRequestWarnings} />
|
||||
</>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this._isTiled() && this._getTileMetaFeatures().some(hasIncompleteResults)) {
|
||||
warningMessages.push({
|
||||
title: '',
|
||||
body: INCOMPLETE_RESULTS_WARNING,
|
||||
});
|
||||
}
|
||||
|
||||
return warningMessages;
|
||||
}
|
||||
|
||||
async syncData(syncContext: DataRequestContext) {
|
||||
// no-op by default
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
import { ISource, SourceEditorArgs } from '../../sources/source';
|
||||
import { type DataRequestContext } from '../../../actions';
|
||||
import { getLayersExtent } from '../../../actions/get_layers_extent';
|
||||
import { ILayer, LayerIcon, LayerError } from '../layer';
|
||||
import { ILayer, LayerIcon, LayerMessage } from '../layer';
|
||||
import { IStyle } from '../../styles/style';
|
||||
import { LICENSED_FEATURES } from '../../../licensed_features';
|
||||
|
||||
|
@ -299,14 +299,33 @@ export class LayerGroup implements ILayer {
|
|||
});
|
||||
}
|
||||
|
||||
getErrors(): LayerError[] {
|
||||
getErrors(): LayerMessage[] {
|
||||
return this.hasErrors()
|
||||
? [
|
||||
{
|
||||
title: i18n.translate('xpack.maps.layerGroup.childrenErrorMessage', {
|
||||
defaultMessage: `An error occurred when loading nested layers`,
|
||||
}),
|
||||
error: '',
|
||||
body: '',
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
hasWarnings(): boolean {
|
||||
return this._children.some((child) => {
|
||||
return child.hasWarnings();
|
||||
});
|
||||
}
|
||||
|
||||
getWarnings(): LayerMessage[] {
|
||||
return this.hasWarnings()
|
||||
? [
|
||||
{
|
||||
title: i18n.translate('xpack.maps.layerGroup.incompleteResultsWarning', {
|
||||
defaultMessage: `Nested layer(s) had issues returning data and results might be incomplete.`,
|
||||
}),
|
||||
body: '',
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import { IVectorLayer } from '../vector_layer';
|
||||
import { GeoJsonVectorLayer } from '../geojson_vector_layer';
|
||||
import { IVectorStyle, VectorStyle } from '../../../styles/vector/vector_style';
|
||||
|
@ -183,7 +184,7 @@ export class BlendedVectorLayer extends GeoJsonVectorLayer implements IVectorLay
|
|||
return layerDescriptor;
|
||||
}
|
||||
|
||||
private readonly _isClustered: boolean;
|
||||
private _isClustered: boolean;
|
||||
private readonly _clusterSource: ESGeoGridSource;
|
||||
private readonly _clusterStyle: VectorStyle;
|
||||
private readonly _documentSource: ESSearchSource;
|
||||
|
@ -313,11 +314,22 @@ export class BlendedVectorLayer extends GeoJsonVectorLayer implements IVectorLay
|
|||
let isSyncClustered;
|
||||
try {
|
||||
syncContext.startLoading(dataRequestId, requestToken, requestMeta);
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
isSyncClustered = !(await this._documentSource.canLoadAllDocuments(
|
||||
await this.getDisplayName(this._documentSource),
|
||||
requestMeta,
|
||||
syncContext.registerCancelCallback.bind(null, requestToken)
|
||||
syncContext.registerCancelCallback.bind(null, requestToken),
|
||||
syncContext.inspectorAdapters,
|
||||
(warning) => {
|
||||
warnings.push(warning);
|
||||
}
|
||||
));
|
||||
syncContext.stopLoading(dataRequestId, requestToken, { isSyncClustered }, requestMeta);
|
||||
syncContext.stopLoading(
|
||||
dataRequestId,
|
||||
requestToken,
|
||||
{ isSyncClustered },
|
||||
{ ...requestMeta, warnings }
|
||||
);
|
||||
} catch (error) {
|
||||
if (!(error instanceof DataRequestAbortError) || !isSearchSourceAbortError(error)) {
|
||||
syncContext.onLoadError(dataRequestId, requestToken, error);
|
||||
|
@ -325,9 +337,11 @@ export class BlendedVectorLayer extends GeoJsonVectorLayer implements IVectorLay
|
|||
return;
|
||||
}
|
||||
if (isSyncClustered) {
|
||||
this._isClustered = true;
|
||||
activeSource = this._clusterSource;
|
||||
activeStyle = this._clusterStyle;
|
||||
} else {
|
||||
this._isClustered = false;
|
||||
activeSource = this._documentSource;
|
||||
activeStyle = this._documentStyle;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ 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, LayerError, LayerIcon } from '../../layer';
|
||||
import { AbstractLayer, LayerMessage, LayerIcon } from '../../layer';
|
||||
import {
|
||||
AbstractVectorLayer,
|
||||
noResultsIcon,
|
||||
|
@ -158,7 +158,7 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer {
|
|||
);
|
||||
}
|
||||
|
||||
getErrors(): LayerError[] {
|
||||
getErrors(): LayerMessage[] {
|
||||
const errors = super.getErrors();
|
||||
|
||||
this.getValidJoins().forEach((join) => {
|
||||
|
@ -168,7 +168,7 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer {
|
|||
title: i18n.translate('xpack.maps.geojsonVectorLayer.joinErrorTitle', {
|
||||
defaultMessage: `An error occurred when adding join metrics to layer features`,
|
||||
}),
|
||||
error: joinDescriptor.error,
|
||||
body: joinDescriptor.error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -81,8 +81,8 @@ const mockVectorSource = {
|
|||
},
|
||||
} as unknown as IVectorSource;
|
||||
const innerJoin = new InnerJoin(joinDescriptor, mockVectorSource);
|
||||
const propertiesMap = new Map<string, Record<string | number, unknown>>();
|
||||
propertiesMap.set('alpha', { [COUNT_PROPERTY_NAME]: 1 });
|
||||
const joinMetrics = new Map<string, Record<string | number, unknown>>();
|
||||
joinMetrics.set('alpha', { [COUNT_PROPERTY_NAME]: 1 });
|
||||
|
||||
test('should skip join when no state has changed', async () => {
|
||||
const updateSourceData = sinon.spy();
|
||||
|
@ -170,7 +170,7 @@ test('should call updateSourceData with feature collection with updated feature
|
|||
dataHasChanged: false,
|
||||
join: innerJoin,
|
||||
joinIndex: 0,
|
||||
propertiesMap,
|
||||
joinMetrics,
|
||||
},
|
||||
],
|
||||
updateSourceData,
|
||||
|
@ -277,7 +277,7 @@ test('should call updateSourceData when no results returned from terms aggregati
|
|||
dataHasChanged: true,
|
||||
join: innerJoin,
|
||||
joinIndex: 0,
|
||||
propertiesMap: new Map<string, Record<string | number, unknown>>(),
|
||||
joinMetrics: new Map<string, Record<string | number, unknown>>(),
|
||||
},
|
||||
],
|
||||
updateSourceData,
|
||||
|
@ -321,8 +321,8 @@ test('should call onJoinError when there are no matching features', async () =>
|
|||
const setJoinError = sinon.spy();
|
||||
|
||||
// instead of returning military alphabet like "alpha" or "bravo", mismatched key returns numbers, like '1'
|
||||
const propertiesMapFromMismatchedKey = new Map<string, Record<string | number, unknown>>();
|
||||
propertiesMapFromMismatchedKey.set('1', { [COUNT_PROPERTY_NAME]: 1 });
|
||||
const joinMetricsFromMismatchedKey = new Map<string, Record<string | number, unknown>>();
|
||||
joinMetricsFromMismatchedKey.set('1', { [COUNT_PROPERTY_NAME]: 1 });
|
||||
|
||||
await performInnerJoins(
|
||||
{
|
||||
|
@ -334,7 +334,7 @@ test('should call onJoinError when there are no matching features', async () =>
|
|||
dataHasChanged: true,
|
||||
join: innerJoin,
|
||||
joinIndex: 0,
|
||||
propertiesMap: propertiesMapFromMismatchedKey,
|
||||
joinMetrics: joinMetricsFromMismatchedKey,
|
||||
},
|
||||
],
|
||||
updateSourceData,
|
||||
|
|
|
@ -59,8 +59,8 @@ export async function performInnerJoins(
|
|||
if (joinKey !== null) {
|
||||
joinStatus.keys.push(joinKey);
|
||||
}
|
||||
const canJoinOnCurrent = joinState.propertiesMap
|
||||
? innerJoin.joinPropertiesToFeature(feature, joinState.propertiesMap)
|
||||
const canJoinOnCurrent = joinState.joinMetrics
|
||||
? innerJoin.joinPropertiesToFeature(feature, joinState.joinMetrics)
|
||||
: false;
|
||||
if (canJoinOnCurrent && !joinStatus.joinedWithAtLeastOneFeature) {
|
||||
joinStatus.joinedWithAtLeastOneFeature = true;
|
||||
|
@ -108,8 +108,7 @@ async function getJoinError(joinStatus: {
|
|||
return;
|
||||
}
|
||||
|
||||
const hasTerms =
|
||||
joinStatus.joinState.propertiesMap && joinStatus.joinState.propertiesMap.size > 0;
|
||||
const hasTerms = joinStatus.joinState.joinMetrics && joinStatus.joinState.joinMetrics.size > 0;
|
||||
|
||||
if (!hasTerms || joinStatus.joinedWithAtLeastOneFeature) {
|
||||
return;
|
||||
|
@ -129,9 +128,7 @@ async function getJoinError(joinStatus: {
|
|||
leftFieldName,
|
||||
leftFieldValues: prettyPrintArray(joinStatus.keys),
|
||||
rightFieldName,
|
||||
rightFieldValues: prettyPrintArray(
|
||||
Array.from(joinStatus.joinState.propertiesMap!.keys())
|
||||
),
|
||||
rightFieldValues: prettyPrintArray(Array.from(joinStatus.joinState.joinMetrics!.keys())),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,5 +12,5 @@ export interface JoinState {
|
|||
dataHasChanged: boolean;
|
||||
join: InnerJoin;
|
||||
joinIndex: number;
|
||||
propertiesMap?: PropertiesMap;
|
||||
joinMetrics?: PropertiesMap;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ import {
|
|||
VectorStyleRequestMeta,
|
||||
} from '../../../../common/descriptor_types';
|
||||
import { IVectorSource } from '../../sources/vector_source';
|
||||
import { LayerIcon, ILayer, LayerError } from '../layer';
|
||||
import { LayerIcon, ILayer, LayerMessage } from '../layer';
|
||||
import { InnerJoin } from '../../joins/inner_join';
|
||||
import { isSpatialJoin } from '../../joins/is_spatial_join';
|
||||
import { IField } from '../../fields/field';
|
||||
|
@ -274,7 +274,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer {
|
|||
});
|
||||
}
|
||||
|
||||
getErrors(): LayerError[] {
|
||||
getErrors(): LayerMessage[] {
|
||||
const errors = super.getErrors();
|
||||
|
||||
this.getValidJoins().forEach((join) => {
|
||||
|
@ -285,7 +285,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer {
|
|||
title: i18n.translate('xpack.maps.vectorLayer.joinFetchErrorTitle', {
|
||||
defaultMessage: `An error occurred when loading join metrics`,
|
||||
}),
|
||||
error: joinError,
|
||||
body: joinError,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -469,7 +469,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer {
|
|||
startLoading(dataRequestId, requestToken, nextMeta);
|
||||
const layerName = await this.getDisplayName(source);
|
||||
|
||||
const styleMeta = await (source as IESSource).loadStylePropsMeta({
|
||||
const { styleMeta, warnings } = await (source as IESSource).loadStylePropsMeta({
|
||||
layerName,
|
||||
style,
|
||||
dynamicStyleProps,
|
||||
|
@ -481,7 +481,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer {
|
|||
executionContext: dataFilters.executionContext,
|
||||
});
|
||||
|
||||
stopLoading(dataRequestId, requestToken, styleMeta, nextMeta);
|
||||
stopLoading(dataRequestId, requestToken, styleMeta, { ...nextMeta, warnings });
|
||||
} catch (error) {
|
||||
if (!(error instanceof DataRequestAbortError)) {
|
||||
onLoadError(dataRequestId, requestToken, error);
|
||||
|
@ -605,27 +605,25 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer {
|
|||
dataHasChanged: false,
|
||||
join,
|
||||
joinIndex,
|
||||
propertiesMap: prevDataRequest?.getData() as PropertiesMap,
|
||||
joinMetrics: prevDataRequest?.getData() as PropertiesMap,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
startLoading(sourceDataId, requestToken, joinRequestMeta);
|
||||
const leftSourceName = await this._source.getDisplayName();
|
||||
const propertiesMap = await joinSource.getPropertiesMap(
|
||||
const { joinMetrics, warnings } = await joinSource.getJoinMetrics(
|
||||
joinRequestMeta,
|
||||
leftSourceName,
|
||||
join.getLeftField().getName(),
|
||||
await this.getDisplayName(),
|
||||
registerCancelCallback.bind(null, requestToken),
|
||||
inspectorAdapters,
|
||||
featureCollection
|
||||
);
|
||||
stopLoading(sourceDataId, requestToken, propertiesMap);
|
||||
stopLoading(sourceDataId, requestToken, joinMetrics, { warnings });
|
||||
return {
|
||||
dataHasChanged: true,
|
||||
join,
|
||||
joinIndex,
|
||||
propertiesMap,
|
||||
joinMetrics,
|
||||
};
|
||||
} catch (error) {
|
||||
if (!(error instanceof DataRequestAbortError)) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import type {
|
|||
AggregationsCompositeAggregate,
|
||||
SearchResponse,
|
||||
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import type { KibanaExecutionContext } from '@kbn/core/public';
|
||||
import { ISearchSource } from '@kbn/data-plugin/common/search/search_source';
|
||||
import { DataView } from '@kbn/data-plugin/common';
|
||||
|
@ -43,7 +44,12 @@ import { DataRequestAbortError } from '../../util/data_request';
|
|||
import { LICENSED_FEATURES } from '../../../licensed_features';
|
||||
|
||||
import { getHttp } from '../../../kibana_services';
|
||||
import { GetFeatureActionsArgs, GeoJsonWithMeta, IMvtVectorSource } from '../vector_source';
|
||||
import {
|
||||
GetFeatureActionsArgs,
|
||||
GeoJsonWithMeta,
|
||||
IMvtVectorSource,
|
||||
getLayerFeaturesRequestName,
|
||||
} from '../vector_source';
|
||||
import {
|
||||
DataFilters,
|
||||
ESGeoGridSourceDescriptor,
|
||||
|
@ -281,6 +287,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
bufferedExtent,
|
||||
inspectorAdapters,
|
||||
executionContext,
|
||||
onWarning,
|
||||
}: {
|
||||
searchSource: ISearchSource;
|
||||
searchSessionId?: string;
|
||||
|
@ -293,6 +300,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
bufferedExtent: MapExtent;
|
||||
inspectorAdapters: Adapters;
|
||||
executionContext: KibanaExecutionContext;
|
||||
onWarning: (warning: SearchResponseWarning) => void;
|
||||
}) {
|
||||
const gridsPerRequest: number = Math.floor(DEFAULT_MAX_BUCKETS_LIMIT / bucketsPerGrid);
|
||||
const aggs: any = {
|
||||
|
@ -352,34 +360,16 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
: this.getId();
|
||||
const esResponse: SearchResponse<unknown> = await this._runEsQuery({
|
||||
requestId,
|
||||
requestName: i18n.translate('xpack.maps.source.esGrid.compositeInspector.requestName', {
|
||||
defaultMessage: '{layerName} {bucketsName} composite request ({requestCount})',
|
||||
values: {
|
||||
bucketsName: this.getBucketsName(),
|
||||
layerName,
|
||||
requestCount,
|
||||
},
|
||||
}),
|
||||
requestName: getLayerFeaturesRequestName(`${layerName} (${requestCount})`),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate(
|
||||
'xpack.maps.source.esGrid.compositeInspectorDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Get {bucketsName} from data view: {dataViewName}, geospatial field: {geoFieldName}',
|
||||
values: {
|
||||
bucketsName: this.getBucketsName(),
|
||||
dataViewName: indexPattern.getName(),
|
||||
geoFieldName: this._descriptor.geoField,
|
||||
},
|
||||
}
|
||||
),
|
||||
searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_geo_grid_source:cluster_composite' },
|
||||
executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning,
|
||||
});
|
||||
|
||||
features.push(...convertCompositeRespToGeoJson(esResponse, this._descriptor.requestType));
|
||||
|
@ -406,6 +396,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
tooManyBuckets,
|
||||
inspectorAdapters,
|
||||
executionContext,
|
||||
onWarning,
|
||||
}: {
|
||||
searchSource: ISearchSource;
|
||||
searchSessionId?: string;
|
||||
|
@ -417,6 +408,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
tooManyBuckets: boolean;
|
||||
inspectorAdapters: Adapters;
|
||||
executionContext: KibanaExecutionContext;
|
||||
onWarning: (warning: SearchResponseWarning) => void;
|
||||
}): Promise<Feature[]> {
|
||||
const valueAggsDsl = tooManyBuckets
|
||||
? this.getValueAggsDsl(indexPattern, (metric) => {
|
||||
|
@ -446,30 +438,16 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
|
||||
const esResponse = await this._runEsQuery({
|
||||
requestId: this.getId(),
|
||||
requestName: i18n.translate('xpack.maps.source.esGrid.inspector.requestName', {
|
||||
defaultMessage: '{layerName} {bucketsName} request',
|
||||
values: {
|
||||
bucketsName: this.getBucketsName(),
|
||||
layerName,
|
||||
},
|
||||
}),
|
||||
requestName: getLayerFeaturesRequestName(layerName),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate('xpack.maps.source.esGrid.inspector.requestDescription', {
|
||||
defaultMessage:
|
||||
'Get {bucketsName} from data view: {dataViewName}, geospatial field: {geoFieldName}',
|
||||
values: {
|
||||
bucketsName: this.getBucketsName(),
|
||||
dataViewName: indexPattern.getName(),
|
||||
geoFieldName: this._descriptor.geoField,
|
||||
},
|
||||
}),
|
||||
searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_geo_grid_source:cluster' },
|
||||
executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning,
|
||||
});
|
||||
|
||||
return convertRegularRespToGeoJson(esResponse, this._descriptor.requestType);
|
||||
|
@ -516,6 +494,10 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
const supportsCompositeAgg = !(await this._isGeoShape());
|
||||
|
||||
const precision = this.getGeoGridPrecision(requestMeta.zoom);
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
const onWarning = (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
};
|
||||
const features: Feature[] =
|
||||
supportsCompositeAgg && tooManyBuckets
|
||||
? await this._compositeAggRequest({
|
||||
|
@ -530,6 +512,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
bufferedExtent: requestMeta.buffer,
|
||||
inspectorAdapters,
|
||||
executionContext: requestMeta.executionContext,
|
||||
onWarning,
|
||||
})
|
||||
: await this._nonCompositeAggRequest({
|
||||
searchSource,
|
||||
|
@ -542,6 +525,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
tooManyBuckets,
|
||||
inspectorAdapters,
|
||||
executionContext: requestMeta.executionContext,
|
||||
onWarning,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -551,6 +535,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
},
|
||||
meta: {
|
||||
areResultsTrimmed: false,
|
||||
warnings,
|
||||
},
|
||||
} as GeoJsonWithMeta;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import React from 'react';
|
|||
import { GeoJsonProperties } from 'geojson';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { type Filter, buildPhraseFilter } from '@kbn/es-query';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import { Adapters } from '@kbn/inspector-plugin/common/adapters';
|
||||
import {
|
||||
EMPTY_FEATURE_COLLECTION,
|
||||
|
@ -33,7 +34,7 @@ import { ESDocField } from '../../fields/es_doc_field';
|
|||
import { InlineField } from '../../fields/inline_field';
|
||||
import { UpdateSourceEditor } from './update_source_editor';
|
||||
import { ImmutableSourceProperty, SourceEditorArgs } from '../source';
|
||||
import { GeoJsonWithMeta } from '../vector_source';
|
||||
import { GeoJsonWithMeta, getLayerFeaturesRequestName } from '../vector_source';
|
||||
import { isValidStringConfig } from '../../util/valid_string_config';
|
||||
import { IField } from '../../fields/field';
|
||||
import { ITooltipProperty, TooltipProperty } from '../../tooltips/tooltip_property';
|
||||
|
@ -261,33 +262,21 @@ export class ESGeoLineSource extends AbstractESAggSource {
|
|||
},
|
||||
});
|
||||
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
const resp = await this._runEsQuery({
|
||||
requestId: `${this.getId()}_tracks`,
|
||||
requestName: i18n.translate('xpack.maps.source.esGeoLine.timeSeriesTrackRequestName', {
|
||||
defaultMessage: `'{layerName}' tracks request (time series)`,
|
||||
values: {
|
||||
layerName,
|
||||
},
|
||||
}),
|
||||
requestName: getLayerFeaturesRequestName(layerName),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate(
|
||||
'xpack.maps.source.esGeoLine.timeSeriesTrackRequestDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Get tracks from data view: {dataViewName}, geospatial field: {geoFieldName}',
|
||||
values: {
|
||||
dataViewName: indexPattern.getName(),
|
||||
geoFieldName: this._descriptor.geoField,
|
||||
},
|
||||
}
|
||||
),
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_geo_line:time_series_tracks' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
|
||||
const { featureCollection } = convertToGeoJson(resp, TIME_SERIES_ID_FIELD_NAME);
|
||||
|
@ -303,7 +292,8 @@ export class ESGeoLineSource extends AbstractESAggSource {
|
|||
entityCount,
|
||||
numTrimmedTracks: 0, // geo_line by time series never truncates tracks and instead simplifies tracks
|
||||
totalEntities: resp?.aggregations?.totalEntities?.value ?? 0,
|
||||
} as ESGeoLineSourceResponseMeta,
|
||||
warnings,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -333,6 +323,7 @@ export class ESGeoLineSource extends AbstractESAggSource {
|
|||
}
|
||||
|
||||
const indexPattern = await this.getIndexPattern();
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
|
||||
// Request is broken into 2 requests
|
||||
// 1) fetch entities: filtered by buffer so that top entities in view are returned
|
||||
|
@ -367,27 +358,22 @@ export class ESGeoLineSource extends AbstractESAggSource {
|
|||
const entityResp = await this._runEsQuery({
|
||||
requestId: `${this.getId()}_entities`,
|
||||
requestName: i18n.translate('xpack.maps.source.esGeoLine.entityRequestName', {
|
||||
defaultMessage: `'{layerName}' entities request`,
|
||||
defaultMessage: `load track entities ({layerName})`,
|
||||
values: {
|
||||
layerName,
|
||||
},
|
||||
}),
|
||||
searchSource: entitySearchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate('xpack.maps.source.esGeoLine.entityRequestDescription', {
|
||||
defaultMessage:
|
||||
'Get entities within map buffer from data view: {dataViewName}, entities: {splitFieldName}',
|
||||
values: {
|
||||
dataViewName: indexPattern.getName(),
|
||||
splitFieldName: this._descriptor.splitField,
|
||||
},
|
||||
}),
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_geo_line:entities' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
const entityBuckets: Array<{ key: string; doc_count: number }> = _.get(
|
||||
entityResp,
|
||||
|
@ -446,29 +432,18 @@ export class ESGeoLineSource extends AbstractESAggSource {
|
|||
});
|
||||
const tracksResp = await this._runEsQuery({
|
||||
requestId: `${this.getId()}_tracks`,
|
||||
requestName: i18n.translate('xpack.maps.source.esGeoLine.trackRequestName', {
|
||||
defaultMessage: `'{layerName}' tracks request (terms)`,
|
||||
values: {
|
||||
layerName,
|
||||
},
|
||||
}),
|
||||
requestName: getLayerFeaturesRequestName(layerName),
|
||||
searchSource: tracksSearchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate('xpack.maps.source.esGeoLine.trackRequestDescription', {
|
||||
defaultMessage:
|
||||
'Get tracks for {numEntities} entities from data view: {dataViewName}, geospatial field: {geoFieldName}',
|
||||
values: {
|
||||
dataViewName: indexPattern.getName(),
|
||||
geoFieldName: this._descriptor.geoField,
|
||||
numEntities: entityBuckets.length,
|
||||
},
|
||||
}),
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_geo_line:terms_tracks' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
const { featureCollection, numTrimmedTracks } = convertToGeoJson(
|
||||
tracksResp,
|
||||
|
@ -487,7 +462,8 @@ export class ESGeoLineSource extends AbstractESAggSource {
|
|||
entityCount: entityBuckets.length,
|
||||
numTrimmedTracks,
|
||||
totalEntities,
|
||||
} as ESGeoLineSourceResponseMeta,
|
||||
warnings,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import React from 'react';
|
|||
import turfBbox from '@turf/bbox';
|
||||
import { multiPoint } from '@turf/helpers';
|
||||
import { Adapters } from '@kbn/inspector-plugin/common/adapters';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import { type Filter, buildExistsFilter } from '@kbn/es-query';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import type {
|
||||
|
@ -34,7 +35,7 @@ import {
|
|||
VectorSourceRequestMeta,
|
||||
} from '../../../../common/descriptor_types';
|
||||
import { isValidStringConfig } from '../../util/valid_string_config';
|
||||
import { BoundsRequestMeta, GeoJsonWithMeta } from '../vector_source';
|
||||
import { BoundsRequestMeta, GeoJsonWithMeta, getLayerFeaturesRequestName } from '../vector_source';
|
||||
|
||||
const MAX_GEOTILE_LEVEL = 29;
|
||||
|
||||
|
@ -188,29 +189,21 @@ export class ESPewPewSource extends AbstractESAggSource {
|
|||
buildExistsFilter({ name: this._descriptor.sourceGeoField, type: 'geo_point' }, indexPattern),
|
||||
]);
|
||||
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
const esResponse = await this._runEsQuery({
|
||||
requestId: this.getId(),
|
||||
requestName: i18n.translate('xpack.maps.pewPew.requestName', {
|
||||
defaultMessage: '{layerName} paths request',
|
||||
values: { layerName },
|
||||
}),
|
||||
requestName: getLayerFeaturesRequestName(layerName),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate('xpack.maps.source.pewPew.inspectorDescription', {
|
||||
defaultMessage:
|
||||
'Get paths from data view: {dataViewName}, source: {sourceFieldName}, destination: {destFieldName}',
|
||||
values: {
|
||||
dataViewName: indexPattern.getName(),
|
||||
destFieldName: this._descriptor.destGeoField,
|
||||
sourceFieldName: this._descriptor.sourceGeoField,
|
||||
},
|
||||
}),
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_pew_pew_source:connections' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
|
||||
const { featureCollection } = convertToLines(esResponse);
|
||||
|
@ -219,6 +212,7 @@ export class ESPewPewSource extends AbstractESAggSource {
|
|||
data: featureCollection,
|
||||
meta: {
|
||||
areResultsTrimmed: false,
|
||||
warnings,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import _ from 'lodash';
|
|||
import React, { ReactElement } from 'react';
|
||||
import type { QueryDslFieldLookup } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import { GeoJsonProperties, Geometry, Position } from 'geojson';
|
||||
import type { KibanaExecutionContext } from '@kbn/core/public';
|
||||
import { type Filter, buildPhraseFilter, type TimeRange } from '@kbn/es-query';
|
||||
|
@ -58,6 +59,7 @@ import {
|
|||
import { ImmutableSourceProperty, SourceEditorArgs } from '../source';
|
||||
import { IField } from '../../fields/field';
|
||||
import {
|
||||
getLayerFeaturesRequestName,
|
||||
GetFeatureActionsArgs,
|
||||
GeoJsonWithMeta,
|
||||
IMvtVectorSource,
|
||||
|
@ -377,29 +379,21 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
}
|
||||
}
|
||||
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
const resp = await this._runEsQuery({
|
||||
requestId: this.getId(),
|
||||
requestName: i18n.translate('xpack.maps.esSearchSource.topHits.requestName', {
|
||||
defaultMessage: '{layerName} top hits request',
|
||||
values: { layerName },
|
||||
}),
|
||||
requestName: getLayerFeaturesRequestName(layerName),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate('xpack.maps.esSearchSource.topHits.requestDescription', {
|
||||
defaultMessage:
|
||||
'Get top hits from data view: {dataViewName}, entities: {entitiesFieldName}, geospatial field: {geoFieldName}',
|
||||
values: {
|
||||
dataViewName: indexPattern.getName(),
|
||||
entitiesFieldName: topHitsGroupByTimeseries ? '_tsid' : topHitsSplitFieldName,
|
||||
geoFieldName: this._descriptor.geoField,
|
||||
},
|
||||
}),
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_search_source:top_hits' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
|
||||
const allHits: any[] = [];
|
||||
|
@ -424,6 +418,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
areEntitiesTrimmed,
|
||||
entityCount: entityBuckets.length,
|
||||
totalEntities,
|
||||
warnings,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -435,6 +430,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
registerCancelCallback: (callback: () => void) => void,
|
||||
inspectorAdapters: Adapters
|
||||
) {
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
const indexPattern = await this.getIndexPattern();
|
||||
|
||||
const { docValueFields, sourceOnlyFields } = getDocValueAndSourceFields(
|
||||
|
@ -451,7 +447,15 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
delete requestMetaWithoutTimeslice.timeslice;
|
||||
const useRequestMetaWithoutTimeslice =
|
||||
requestMeta.timeslice !== undefined &&
|
||||
(await this.canLoadAllDocuments(requestMetaWithoutTimeslice, registerCancelCallback));
|
||||
(await this.canLoadAllDocuments(
|
||||
layerName,
|
||||
requestMetaWithoutTimeslice,
|
||||
registerCancelCallback,
|
||||
inspectorAdapters,
|
||||
(warning) => {
|
||||
warnings.push(warning);
|
||||
}
|
||||
));
|
||||
|
||||
const maxResultWindow = await this.getMaxResultWindow();
|
||||
const searchSource = await this.makeSearchSource(
|
||||
|
@ -473,26 +477,18 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
|
||||
const resp = await this._runEsQuery({
|
||||
requestId: this.getId(),
|
||||
requestName: i18n.translate('xpack.maps.esSearchSource.requestName', {
|
||||
defaultMessage: '{layerName} documents request',
|
||||
values: { layerName },
|
||||
}),
|
||||
requestName: getLayerFeaturesRequestName(layerName),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate('xpack.maps.esSearchSource.requestDescription', {
|
||||
defaultMessage:
|
||||
'Get documents from data view: {dataViewName}, geospatial field: {geoFieldName}',
|
||||
values: {
|
||||
dataViewName: indexPattern.getName(),
|
||||
geoFieldName: this._descriptor.geoField,
|
||||
},
|
||||
}),
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_search_source:doc_search' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
|
||||
const isTimeExtentForTimeslice =
|
||||
|
@ -506,6 +502,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
? requestMeta.timeslice
|
||||
: timerangeToTimeextent(requestMeta.timeFilters),
|
||||
isTimeExtentForTimeslice,
|
||||
warnings,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -990,25 +987,31 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
}
|
||||
|
||||
async canLoadAllDocuments(
|
||||
layerName: string,
|
||||
requestMeta: VectorSourceRequestMeta,
|
||||
registerCancelCallback: (callback: () => void) => void
|
||||
registerCancelCallback: (callback: () => void) => void,
|
||||
inspectorAdapters: Adapters,
|
||||
onWarning: (warning: SearchResponseWarning) => void
|
||||
) {
|
||||
const abortController = new AbortController();
|
||||
registerCancelCallback(() => abortController.abort());
|
||||
const maxResultWindow = await this.getMaxResultWindow();
|
||||
const searchSource = await this.makeSearchSource(requestMeta, 0);
|
||||
searchSource.setField('trackTotalHits', maxResultWindow + 1);
|
||||
const { rawResponse: resp } = await lastValueFrom(
|
||||
searchSource.fetch$({
|
||||
abortSignal: abortController.signal,
|
||||
sessionId: requestMeta.searchSessionId,
|
||||
legacyHitsTotal: false,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_search_source:all_doc_counts' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
})
|
||||
);
|
||||
const resp = await this._runEsQuery({
|
||||
requestId: this.getId() + 'features_count',
|
||||
requestName: i18n.translate('xpack.maps.vectorSource.featuresCountRequestName', {
|
||||
defaultMessage: 'load features count ({layerName})',
|
||||
values: { layerName },
|
||||
}),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_search_source:all_doc_counts' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning,
|
||||
});
|
||||
return !isTotalHitsGreaterThan(resp.hits.total as unknown as TotalHits, maxResultWindow);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,13 +15,15 @@ import type { KibanaExecutionContext } from '@kbn/core/public';
|
|||
import { RequestAdapter } from '@kbn/inspector-plugin/common/adapters/request';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { extractWarnings, type SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import type { IESAggSource } from '../es_agg_source';
|
||||
import { AbstractVectorSource, BoundsRequestMeta } from '../vector_source';
|
||||
import {
|
||||
getAutocompleteService,
|
||||
getIndexPatternService,
|
||||
getTimeFilter,
|
||||
getInspector,
|
||||
getSearchService,
|
||||
getTimeFilter,
|
||||
} from '../../../kibana_services';
|
||||
import { getDataViewNotFoundMessage } from '../../../../common/i18n_getters';
|
||||
import { createExtentFilter } from '../../../../common/elasticsearch_util';
|
||||
|
@ -34,6 +36,7 @@ import {
|
|||
AbstractSourceDescriptor,
|
||||
DynamicStylePropertyOptions,
|
||||
MapExtent,
|
||||
StyleMetaData,
|
||||
VectorSourceRequestMeta,
|
||||
} from '../../../../common/descriptor_types';
|
||||
import { IVectorStyle } from '../../styles/vector/vector_style';
|
||||
|
@ -78,7 +81,7 @@ export interface IESSource extends IVectorSource {
|
|||
searchSessionId?: string;
|
||||
inspectorAdapters: Adapters;
|
||||
executionContext: KibanaExecutionContext;
|
||||
}): Promise<object>;
|
||||
}): Promise<{ styleMeta: StyleMetaData; warnings: SearchResponseWarning[] }>;
|
||||
}
|
||||
|
||||
export class AbstractESSource extends AbstractVectorSource implements IESSource {
|
||||
|
@ -157,26 +160,28 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
|
||||
async _runEsQuery({
|
||||
registerCancelCallback,
|
||||
requestDescription,
|
||||
requestId,
|
||||
requestName,
|
||||
searchSessionId,
|
||||
searchSource,
|
||||
executionContext,
|
||||
requestsAdapter,
|
||||
onWarning,
|
||||
}: {
|
||||
registerCancelCallback: (callback: () => void) => void;
|
||||
requestDescription: string;
|
||||
requestId: string;
|
||||
requestName: string;
|
||||
searchSessionId?: string;
|
||||
searchSource: ISearchSource;
|
||||
executionContext: KibanaExecutionContext;
|
||||
requestsAdapter: RequestAdapter | undefined;
|
||||
onWarning?: (warning: SearchResponseWarning) => void;
|
||||
}): Promise<any> {
|
||||
const abortController = new AbortController();
|
||||
registerCancelCallback(() => abortController.abort());
|
||||
|
||||
const disableWarningToasts = onWarning !== undefined && requestsAdapter !== undefined;
|
||||
|
||||
try {
|
||||
const { rawResponse: resp } = await lastValueFrom(
|
||||
searchSource.fetch$({
|
||||
|
@ -187,11 +192,18 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
adapter: requestsAdapter,
|
||||
id: requestId,
|
||||
title: requestName,
|
||||
description: requestDescription,
|
||||
},
|
||||
executionContext,
|
||||
disableWarningToasts,
|
||||
})
|
||||
);
|
||||
|
||||
if (disableWarningToasts) {
|
||||
extractWarnings(resp, getInspector(), requestsAdapter, requestName, requestId).forEach(
|
||||
onWarning
|
||||
);
|
||||
}
|
||||
|
||||
return resp;
|
||||
} catch (error) {
|
||||
if (isSearchSourceAbortError(error)) {
|
||||
|
@ -462,7 +474,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
searchSessionId?: string;
|
||||
inspectorAdapters: Adapters;
|
||||
executionContext: KibanaExecutionContext;
|
||||
}): Promise<object> {
|
||||
}) {
|
||||
const promises = dynamicStyleProps.map((dynamicStyleProp) => {
|
||||
return dynamicStyleProp.getFieldMetaRequest();
|
||||
});
|
||||
|
@ -495,30 +507,30 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
}
|
||||
}
|
||||
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
const resp = await this._runEsQuery({
|
||||
requestId: `${this.getId()}_styleMeta`,
|
||||
requestName: i18n.translate('xpack.maps.source.esSource.stylePropsMetaRequestName', {
|
||||
defaultMessage: '{layerName} - metadata',
|
||||
defaultMessage: 'load symbolization ranges ({layerName})',
|
||||
values: { layerName },
|
||||
}),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate(
|
||||
'xpack.maps.source.esSource.stylePropsMetaRequestDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Elasticsearch request retrieving field metadata used for calculating symbolization bands.',
|
||||
}
|
||||
),
|
||||
searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_source:style_meta' },
|
||||
executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
|
||||
return resp.aggregations;
|
||||
return {
|
||||
styleMeta: resp.aggregations,
|
||||
warnings,
|
||||
};
|
||||
}
|
||||
|
||||
getValueSuggestions = async (field: IField, query: string): Promise<string[]> => {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { FeatureCollection } from 'geojson';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import type { Query } from '@kbn/es-query';
|
||||
import { ISearchSource } from '@kbn/data-plugin/public';
|
||||
import { Adapters } from '@kbn/inspector-plugin/common/adapters';
|
||||
|
@ -19,7 +20,6 @@ import {
|
|||
ESDistanceSourceDescriptor,
|
||||
VectorSourceRequestMeta,
|
||||
} from '../../../../../common/descriptor_types';
|
||||
import { PropertiesMap } from '../../../../../common/elasticsearch_util';
|
||||
import { isValidStringConfig } from '../../../util/valid_string_config';
|
||||
import { IJoinSource } from '../types';
|
||||
import type { IESAggSource, ESAggsSourceSyncMeta } from '../../es_agg_source';
|
||||
|
@ -27,6 +27,7 @@ import { IField } from '../../../fields/field';
|
|||
import { mergeExecutionContext } from '../../execution_context_utils';
|
||||
import { processDistanceResponse } from './process_distance_response';
|
||||
import { isSpatialSourceComplete } from '../is_spatial_source_complete';
|
||||
import { getJoinMetricsRequestName } from '../i18n_utils';
|
||||
|
||||
export const DEFAULT_WITHIN_DISTANCE = 5;
|
||||
|
||||
|
@ -80,14 +81,13 @@ export class ESDistanceSource extends AbstractESAggSource implements IJoinSource
|
|||
});
|
||||
}
|
||||
|
||||
async getPropertiesMap(
|
||||
async getJoinMetrics(
|
||||
requestMeta: VectorSourceRequestMeta,
|
||||
leftSourceName: string,
|
||||
leftFieldName: string,
|
||||
layerName: string,
|
||||
registerCancelCallback: (callback: () => void) => void,
|
||||
inspectorAdapters: Adapters,
|
||||
featureCollection?: FeatureCollection
|
||||
): Promise<PropertiesMap> {
|
||||
) {
|
||||
if (featureCollection === undefined) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.maps.esDistanceSource.noFeatureCollectionMsg', {
|
||||
|
@ -97,7 +97,10 @@ export class ESDistanceSource extends AbstractESAggSource implements IJoinSource
|
|||
}
|
||||
|
||||
if (!this.hasCompleteConfig()) {
|
||||
return new Map<string, BucketProperties>();
|
||||
return {
|
||||
joinMetrics: new Map<string, BucketProperties>(),
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
|
||||
const distance = `${this._descriptor.distance}km`;
|
||||
|
@ -119,7 +122,10 @@ export class ESDistanceSource extends AbstractESAggSource implements IJoinSource
|
|||
}
|
||||
|
||||
if (!hasFilters) {
|
||||
return new Map<string, BucketProperties>();
|
||||
return {
|
||||
joinMetrics: new Map<string, BucketProperties>(),
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
|
||||
const indexPattern = await this.getIndexPattern();
|
||||
|
@ -133,31 +139,27 @@ export class ESDistanceSource extends AbstractESAggSource implements IJoinSource
|
|||
aggs: this.getValueAggsDsl(indexPattern),
|
||||
},
|
||||
});
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
const rawEsData = await this._runEsQuery({
|
||||
requestId: this.getId(),
|
||||
requestName: i18n.translate('xpack.maps.distanceSource.requestName', {
|
||||
defaultMessage: '{leftSourceName} within distance join request',
|
||||
values: { leftSourceName },
|
||||
}),
|
||||
requestName: getJoinMetricsRequestName(layerName),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate('xpack.maps.distanceSource.requestDescription', {
|
||||
defaultMessage:
|
||||
'Get metrics from data view: {dataViewName}, geospatial field: {geoFieldName}',
|
||||
values: {
|
||||
dataViewName: indexPattern.getName(),
|
||||
geoFieldName: this._descriptor.geoField,
|
||||
},
|
||||
}),
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_distance_source:distance_join_request' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
|
||||
return processDistanceResponse(rawEsData, this.getAggKey(AGG_TYPE.COUNT));
|
||||
return {
|
||||
joinMetrics: processDistanceResponse(rawEsData, this.getAggKey(AGG_TYPE.COUNT)),
|
||||
warnings,
|
||||
};
|
||||
}
|
||||
|
||||
isFilterByMapBounds(): boolean {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import type { Query } from '@kbn/es-query';
|
||||
import { ISearchSource } from '@kbn/data-plugin/public';
|
||||
import { Adapters } from '@kbn/inspector-plugin/common/adapters';
|
||||
|
@ -36,6 +37,7 @@ import type { IESAggSource, ESAggsSourceSyncMeta } from '../../es_agg_source';
|
|||
import { IField } from '../../../fields/field';
|
||||
import { mergeExecutionContext } from '../../execution_context_utils';
|
||||
import { isTermSourceComplete } from './is_term_source_complete';
|
||||
import { getJoinMetricsRequestName } from '../i18n_utils';
|
||||
|
||||
const TERMS_AGG_NAME = 'join';
|
||||
const TERMS_BUCKET_KEYS_TO_IGNORE = ['key', 'doc_count'];
|
||||
|
@ -125,15 +127,17 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource
|
|||
: super.getAggLabel(aggType, fieldLabel);
|
||||
}
|
||||
|
||||
async getPropertiesMap(
|
||||
async getJoinMetrics(
|
||||
requestMeta: VectorSourceRequestMeta,
|
||||
leftSourceName: string,
|
||||
leftFieldName: string,
|
||||
layerName: string,
|
||||
registerCancelCallback: (callback: () => void) => void,
|
||||
inspectorAdapters: Adapters
|
||||
): Promise<PropertiesMap> {
|
||||
) {
|
||||
if (!this.hasCompleteConfig()) {
|
||||
return new Map<string, BucketProperties>();
|
||||
return {
|
||||
joinMetrics: new Map<string, BucketProperties>(),
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
|
||||
const indexPattern = await this.getIndexPattern();
|
||||
|
@ -150,31 +154,28 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource
|
|||
},
|
||||
});
|
||||
|
||||
const warnings: SearchResponseWarning[] = [];
|
||||
const rawEsData = await this._runEsQuery({
|
||||
requestId: this.getId(),
|
||||
requestName: i18n.translate('xpack.maps.termSource.requestName', {
|
||||
defaultMessage: '{leftSourceName} term join request',
|
||||
values: { leftSourceName },
|
||||
}),
|
||||
requestName: getJoinMetricsRequestName(layerName),
|
||||
searchSource,
|
||||
registerCancelCallback,
|
||||
requestDescription: i18n.translate('xpack.maps.termSource.requestDescription', {
|
||||
defaultMessage: 'Get metrics from data view: {dataViewName}, term field: {termFieldName}',
|
||||
values: {
|
||||
dataViewName: indexPattern.getName(),
|
||||
termFieldName: this._termField.getName(),
|
||||
},
|
||||
}),
|
||||
searchSessionId: requestMeta.searchSessionId,
|
||||
executionContext: mergeExecutionContext(
|
||||
{ description: 'es_term_source:terms' },
|
||||
requestMeta.executionContext
|
||||
),
|
||||
requestsAdapter: inspectorAdapters.requests,
|
||||
onWarning: (warning: SearchResponseWarning) => {
|
||||
warnings.push(warning);
|
||||
},
|
||||
});
|
||||
|
||||
const countPropertyName = this.getAggKey(AGG_TYPE.COUNT);
|
||||
return extractPropertiesMap(rawEsData, countPropertyName);
|
||||
return {
|
||||
joinMetrics: extractPropertiesMap(rawEsData, countPropertyName),
|
||||
warnings,
|
||||
};
|
||||
}
|
||||
|
||||
isFilterByMapBounds(): boolean {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const getJoinMetricsRequestName = (layerName: string) => {
|
||||
return i18n.translate('xpack.maps.joinSource.joinMetricsRequestName', {
|
||||
defaultMessage: 'load join metrics ({layerName})',
|
||||
values: { layerName },
|
||||
});
|
||||
};
|
|
@ -17,7 +17,7 @@ describe('TableSource', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getPropertiesMap', () => {
|
||||
describe('getJoinMetrics', () => {
|
||||
it('should roll up results', async () => {
|
||||
const tableSource = new TableSource({
|
||||
term: 'iso',
|
||||
|
@ -53,18 +53,17 @@ describe('TableSource', () => {
|
|||
],
|
||||
});
|
||||
|
||||
const propertiesMap = await tableSource.getPropertiesMap(
|
||||
const { joinMetrics } = await tableSource.getJoinMetrics(
|
||||
{} as unknown as VectorSourceRequestMeta,
|
||||
'a',
|
||||
'b',
|
||||
'layer1',
|
||||
() => {}
|
||||
);
|
||||
|
||||
expect(propertiesMap.size).toEqual(2);
|
||||
expect(propertiesMap.get('US')).toEqual({
|
||||
expect(joinMetrics.size).toEqual(2);
|
||||
expect(joinMetrics.get('US')).toEqual({
|
||||
population: 100,
|
||||
});
|
||||
expect(propertiesMap.get('CN')).toEqual({
|
||||
expect(joinMetrics.get('CN')).toEqual({
|
||||
population: 400,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -54,12 +54,11 @@ export class TableSource extends AbstractVectorSource implements ITermJoinSource
|
|||
return `table source ${uuidv4()}`;
|
||||
}
|
||||
|
||||
async getPropertiesMap(
|
||||
async getJoinMetrics(
|
||||
requestMeta: VectorSourceRequestMeta,
|
||||
leftSourceName: string,
|
||||
leftFieldName: string,
|
||||
layerName: string,
|
||||
registerCancelCallback: (callback: () => void) => void
|
||||
): Promise<PropertiesMap> {
|
||||
) {
|
||||
const propertiesMap: PropertiesMap = new Map<string, BucketProperties>();
|
||||
|
||||
const columnNames = this._descriptor.__columns.map((column) => {
|
||||
|
@ -86,7 +85,10 @@ export class TableSource extends AbstractVectorSource implements ITermJoinSource
|
|||
}
|
||||
}
|
||||
|
||||
return propertiesMap;
|
||||
return {
|
||||
joinMetrics: propertiesMap,
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
|
||||
getTermField(): IField {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { FeatureCollection, GeoJsonProperties } from 'geojson';
|
||||
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
|
||||
import type { KibanaExecutionContext } from '@kbn/core/public';
|
||||
import { Query } from '@kbn/data-plugin/common/query';
|
||||
import { Adapters } from '@kbn/inspector-plugin/common/adapters';
|
||||
|
@ -18,14 +19,16 @@ import { ISource } from '../source';
|
|||
export interface IJoinSource extends ISource {
|
||||
hasCompleteConfig(): boolean;
|
||||
getWhereQuery(): Query | undefined;
|
||||
getPropertiesMap(
|
||||
getJoinMetrics(
|
||||
requestMeta: VectorSourceRequestMeta,
|
||||
leftSourceName: string,
|
||||
leftFieldName: string,
|
||||
layerName: string,
|
||||
registerCancelCallback: (callback: () => void) => void,
|
||||
inspectorAdapters: Adapters,
|
||||
featureCollection?: FeatureCollection
|
||||
): Promise<PropertiesMap>;
|
||||
): Promise<{
|
||||
joinMetrics: PropertiesMap;
|
||||
warnings: SearchResponseWarning[];
|
||||
}>;
|
||||
|
||||
/*
|
||||
* Use getSyncMeta to expose join configurations that require join data re-fetch when changed.
|
||||
|
|
|
@ -6,4 +6,12 @@
|
|||
*/
|
||||
|
||||
export * from './vector_source';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
export type { IMvtVectorSource } from './mvt_vector_source';
|
||||
|
||||
export const getLayerFeaturesRequestName = (layerName: string) => {
|
||||
return i18n.translate('xpack.maps.vectorSource.featuresRequestName', {
|
||||
defaultMessage: 'load layer features ({layerName})',
|
||||
values: { layerName },
|
||||
});
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ import { AbstractSource, ISource } from '../source';
|
|||
import { IField } from '../../fields/field';
|
||||
import {
|
||||
DataFilters,
|
||||
ESSearchSourceResponseMeta,
|
||||
DataRequestMeta,
|
||||
MapExtent,
|
||||
Timeslice,
|
||||
VectorSourceRequestMeta,
|
||||
|
@ -43,11 +43,9 @@ export interface SourceStatus {
|
|||
isDeprecated?: boolean;
|
||||
}
|
||||
|
||||
export type GeoJsonFetchMeta = ESSearchSourceResponseMeta;
|
||||
|
||||
export interface GeoJsonWithMeta {
|
||||
data: FeatureCollection;
|
||||
meta?: GeoJsonFetchMeta;
|
||||
meta?: DataRequestMeta;
|
||||
}
|
||||
|
||||
export interface BoundsRequestMeta {
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
*/
|
||||
|
||||
import { TileMetaFeature } from '../../../common/descriptor_types';
|
||||
import { getAggsMeta, getAggRange, getHitsMeta } from './tile_meta_feature_utils';
|
||||
import {
|
||||
getAggsMeta,
|
||||
getAggRange,
|
||||
getHitsMeta,
|
||||
hasIncompleteResults,
|
||||
} from './tile_meta_feature_utils';
|
||||
|
||||
describe('getAggsMeta', () => {
|
||||
test('should extract doc_count = 0 from meta features when there are no matches', () => {
|
||||
|
@ -376,3 +381,73 @@ describe('getHitsMeta', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasIncompleteResults', () => {
|
||||
const metaFeature = {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [0, 0],
|
||||
},
|
||||
properties: {
|
||||
'_clusters.skipped': 0,
|
||||
'_clusters.partial': 0,
|
||||
'_shards.failed': 0,
|
||||
timed_out: false,
|
||||
'hits.total.relation': 'eq',
|
||||
'hits.total.value': 28,
|
||||
},
|
||||
} as TileMetaFeature;
|
||||
|
||||
test('should return false when all shards and clusters are successful', () => {
|
||||
expect(hasIncompleteResults(metaFeature)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true when local cluster has time out', () => {
|
||||
expect(
|
||||
hasIncompleteResults({
|
||||
...metaFeature,
|
||||
properties: {
|
||||
...metaFeature.properties,
|
||||
timed_out: true,
|
||||
},
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true when local cluster has shard failure', () => {
|
||||
expect(
|
||||
hasIncompleteResults({
|
||||
...metaFeature,
|
||||
properties: {
|
||||
...metaFeature.properties,
|
||||
'_shards.failed': 1,
|
||||
},
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true when remote cluster is skipped', () => {
|
||||
expect(
|
||||
hasIncompleteResults({
|
||||
...metaFeature,
|
||||
properties: {
|
||||
...metaFeature.properties,
|
||||
'_clusters.skipped': 1,
|
||||
},
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true when remote cluster has shard failure', () => {
|
||||
expect(
|
||||
hasIncompleteResults({
|
||||
...metaFeature,
|
||||
properties: {
|
||||
...metaFeature.properties,
|
||||
'_clusters.partial': 1,
|
||||
},
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -68,3 +68,35 @@ export function getAggRange(
|
|||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
export function hasIncompleteResults(tileMetaFeature: TileMetaFeature) {
|
||||
if (
|
||||
typeof tileMetaFeature.properties?.timed_out === 'boolean' &&
|
||||
tileMetaFeature.properties.timed_out
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof tileMetaFeature.properties?.['_shards.failed'] === 'number' &&
|
||||
tileMetaFeature.properties['_shards.failed'] > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof tileMetaFeature.properties?.['_clusters.skipped'] === 'number' &&
|
||||
tileMetaFeature.properties['_clusters.skipped'] > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof tileMetaFeature.properties?.['_clusters.partial'] === 'number' &&
|
||||
tileMetaFeature.properties['_clusters.partial'] > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ exports[`LayerControl isLayerTOCOpen Should render expand button 1`] = `
|
|||
position="left"
|
||||
>
|
||||
<ExpandButton
|
||||
hasErrors={false}
|
||||
hasErrorsOrWarnings={false}
|
||||
isLoading={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
|
@ -135,7 +135,7 @@ exports[`LayerControl isLayerTOCOpen Should render expand button with error icon
|
|||
position="left"
|
||||
>
|
||||
<ExpandButton
|
||||
hasErrors={true}
|
||||
hasErrorsOrWarnings={true}
|
||||
isLoading={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
|
@ -150,7 +150,7 @@ exports[`LayerControl isLayerTOCOpen Should render expand button with loading ic
|
|||
position="left"
|
||||
>
|
||||
<ExpandButton
|
||||
hasErrors={false}
|
||||
hasErrorsOrWarnings={false}
|
||||
isLoading={true}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
|
|
|
@ -10,12 +10,12 @@ import { EuiButtonEmpty, EuiIcon, EuiLoadingSpinner } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
interface Props {
|
||||
hasErrors: boolean;
|
||||
hasErrorsOrWarnings: boolean;
|
||||
isLoading: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function ExpandButton({ hasErrors, isLoading, onClick }: Props) {
|
||||
export function ExpandButton({ hasErrorsOrWarnings, isLoading, onClick }: Props) {
|
||||
// isLoading indicates at least one layer is loading.
|
||||
// Expand button should never be disabled.
|
||||
// Not using EuiButton* with iconType props because EuiButton* disables button when isLoading prop is true.
|
||||
|
@ -34,7 +34,7 @@ export function ExpandButton({ hasErrors, isLoading, onClick }: Props) {
|
|||
<EuiLoadingSpinner />
|
||||
</div>
|
||||
) : (
|
||||
<EuiIcon type={hasErrors ? 'warning' : 'menuLeft'} />
|
||||
<EuiIcon type={hasErrorsOrWarnings ? 'warning' : 'menuLeft'} />
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
|
|
@ -71,6 +71,9 @@ describe('LayerControl', () => {
|
|||
hasErrors: () => {
|
||||
return false;
|
||||
},
|
||||
hasWarnings: () => {
|
||||
return false;
|
||||
},
|
||||
isLayerLoading: () => {
|
||||
return true;
|
||||
},
|
||||
|
@ -86,6 +89,9 @@ describe('LayerControl', () => {
|
|||
hasErrors: () => {
|
||||
return true;
|
||||
},
|
||||
hasWarnings: () => {
|
||||
return false;
|
||||
},
|
||||
isLayerLoading: () => {
|
||||
return false;
|
||||
},
|
||||
|
|
|
@ -52,8 +52,8 @@ export function LayerControl({
|
|||
if (isScreenshotMode()) {
|
||||
return null;
|
||||
}
|
||||
const hasErrors = layerList.some((layer) => {
|
||||
return layer.hasErrors();
|
||||
const hasErrorsOrWarnings = layerList.some((layer) => {
|
||||
return layer.hasErrors() || layer.hasWarnings();
|
||||
});
|
||||
const isLoading = layerList.some((layer) => {
|
||||
return layer.isLayerLoading(zoom);
|
||||
|
@ -67,7 +67,11 @@ export function LayerControl({
|
|||
})}
|
||||
position="left"
|
||||
>
|
||||
<ExpandButton hasErrors={hasErrors} isLoading={isLoading} onClick={openLayerTOC} />
|
||||
<ExpandButton
|
||||
hasErrorsOrWarnings={hasErrorsOrWarnings}
|
||||
isLoading={isLoading}
|
||||
onClick={openLayerTOC}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -478,9 +478,22 @@ exports[`TOCEntry props should display layer details when isLegendDetailsOpen is
|
|||
className="mapTocEntry__layerDetails"
|
||||
data-test-subj="mapLayerTOCDetailslayer_1"
|
||||
>
|
||||
<div>
|
||||
TOC details mock
|
||||
</div>
|
||||
<LegendDetails
|
||||
layer={
|
||||
Object {
|
||||
"getDisplayName": [Function],
|
||||
"getErrors": [Function],
|
||||
"getId": [Function],
|
||||
"hasErrors": [Function],
|
||||
"hasLegendDetails": [Function],
|
||||
"isPreviewLayer": [Function],
|
||||
"isVisible": [Function],
|
||||
"renderLegendDetails": [Function],
|
||||
"showAtZoomLevel": [Function],
|
||||
"supportsFitToBounds": [Function],
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className="mapTocEntry__detailsToggle"
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { LegendDetails } from './legend_details';
|
||||
import type { ILayer } from '../../../../../classes/layers/layer';
|
||||
|
||||
describe('LegendDetails', () => {
|
||||
const mockLayer = {
|
||||
getErrors: () => {
|
||||
return [
|
||||
{
|
||||
title: 'simulated error',
|
||||
body: <div data-test-subj="layer-error" />,
|
||||
},
|
||||
];
|
||||
},
|
||||
getWarnings: () => {
|
||||
return [
|
||||
{
|
||||
title: 'simulated warning',
|
||||
body: <div data-test-subj="layer-warning" />,
|
||||
},
|
||||
];
|
||||
},
|
||||
renderLegendDetails: () => {
|
||||
return <div data-test-subj="layer-legend" />;
|
||||
},
|
||||
} as unknown as ILayer;
|
||||
|
||||
test('Should only render errors when layer contains errors', () => {
|
||||
render(<LegendDetails layer={mockLayer} />);
|
||||
screen.getByTestId('layer-error');
|
||||
const error = screen.queryByTestId('layer-error');
|
||||
expect(error).not.toBeNull();
|
||||
const warning = screen.queryByTestId('layer-warning');
|
||||
expect(warning).toBeNull();
|
||||
const legend = screen.queryByTestId('layer-legend');
|
||||
expect(legend).toBeNull();
|
||||
});
|
||||
|
||||
test('Should render warnings and legend when layer contains warnings', () => {
|
||||
render(
|
||||
<LegendDetails
|
||||
layer={{
|
||||
...mockLayer,
|
||||
getErrors: () => {
|
||||
return [];
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const error = screen.queryByTestId('layer-error');
|
||||
expect(error).toBeNull();
|
||||
const warning = screen.queryByTestId('layer-warning');
|
||||
expect(warning).not.toBeNull();
|
||||
const legend = screen.queryByTestId('layer-legend');
|
||||
expect(legend).not.toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
|
||||
import type { ILayer } from '../../../../../classes/layers/layer';
|
||||
|
||||
interface Props {
|
||||
layer: ILayer;
|
||||
}
|
||||
|
||||
export function LegendDetails({ layer }: Props) {
|
||||
const errors = layer.getErrors();
|
||||
if (errors.length) {
|
||||
return (
|
||||
<>
|
||||
{errors.map(({ title, body }, index) => (
|
||||
<div key={index}>
|
||||
<EuiCallOut color="danger" size="s" title={title}>
|
||||
{body}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="m" />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const warnings = layer.getWarnings();
|
||||
return warnings.length ? (
|
||||
<>
|
||||
{warnings.map(({ title, body }, index) => (
|
||||
<div key={index}>
|
||||
<EuiCallOut color="warning" size="s">
|
||||
{body}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="m" />
|
||||
</div>
|
||||
))}
|
||||
{layer.renderLegendDetails()}
|
||||
</>
|
||||
) : (
|
||||
layer.renderLegendDetails()
|
||||
);
|
||||
}
|
|
@ -9,14 +9,7 @@ import React, { Component } from 'react';
|
|||
import classNames from 'classnames';
|
||||
import type { DraggableProvidedDragHandleProps } from '@hello-pangea/dnd';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import {
|
||||
EuiIcon,
|
||||
EuiButtonIcon,
|
||||
EuiCallOut,
|
||||
EuiConfirmModal,
|
||||
EuiButtonEmpty,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { EuiIcon, EuiButtonIcon, EuiConfirmModal, EuiButtonEmpty } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TOCEntryActionsPopover } from './toc_entry_actions_popover';
|
||||
import {
|
||||
|
@ -25,6 +18,7 @@ import {
|
|||
EDIT_LAYER_SETTINGS_LABEL,
|
||||
FIT_TO_DATA_LABEL,
|
||||
} from './action_labels';
|
||||
import { LegendDetails } from './legend_details';
|
||||
import { ILayer } from '../../../../../classes/layers/layer';
|
||||
import { isLayerGroup } from '../../../../../classes/layers/layer_group';
|
||||
|
||||
|
@ -111,7 +105,9 @@ export class TOCEntry extends Component<Props, State> {
|
|||
|
||||
async _loadHasLegendDetails() {
|
||||
const hasLegendDetails =
|
||||
(await this.props.layer.hasLegendDetails()) &&
|
||||
((await this.props.layer.hasLegendDetails()) ||
|
||||
this.props.layer.hasErrors() ||
|
||||
this.props.layer.hasWarnings()) &&
|
||||
this.props.layer.isVisible() &&
|
||||
this.props.layer.showAtZoomLevel(this.props.zoom);
|
||||
if (this._isMounted && hasLegendDetails !== this.state.hasLegendDetails) {
|
||||
|
@ -150,10 +146,6 @@ export class TOCEntry extends Component<Props, State> {
|
|||
this.props.toggleVisible(this.props.layer.getId());
|
||||
};
|
||||
|
||||
_getLayerErrors = () => {
|
||||
return isLayerGroup(this.props.layer) ? [] : this.props.layer.getErrors();
|
||||
};
|
||||
|
||||
_renderCancelModal() {
|
||||
if (!this.state.shouldShowModal) {
|
||||
return null;
|
||||
|
@ -240,8 +232,7 @@ export class TOCEntry extends Component<Props, State> {
|
|||
}
|
||||
|
||||
_renderDetailsToggle() {
|
||||
const errors = this._getLayerErrors();
|
||||
if (this.props.isDragging || (!this.state.hasLegendDetails && errors.length === 0)) {
|
||||
if (this.props.isDragging || !this.state.hasLegendDetails) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -304,32 +295,6 @@ export class TOCEntry extends Component<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
_renderLegendDetails = () => {
|
||||
if (!this.props.isLegendDetailsOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const errors = this._getLayerErrors();
|
||||
|
||||
return this.state.hasLegendDetails || errors.length ? (
|
||||
<div
|
||||
className="mapTocEntry__layerDetails"
|
||||
data-test-subj={`mapLayerTOCDetails${escapeLayerName(this.state.displayName)}`}
|
||||
>
|
||||
{errors.length
|
||||
? errors.map(({ title, error }, index) => (
|
||||
<div key={index}>
|
||||
<EuiCallOut color="danger" size="s" title={title}>
|
||||
{error}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="m" />
|
||||
</div>
|
||||
))
|
||||
: this.props.layer.renderLegendDetails()}
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
_hightlightAsSelectedLayer() {
|
||||
if (this.props.isCombineLayer) {
|
||||
return false;
|
||||
|
@ -365,7 +330,16 @@ export class TOCEntry extends Component<Props, State> {
|
|||
>
|
||||
{this._renderLayerHeader()}
|
||||
|
||||
{this._renderLegendDetails()}
|
||||
{this.props.isLegendDetailsOpen &&
|
||||
this.state.hasLegendDetails &&
|
||||
!isLayerGroup(this.props.layer) ? (
|
||||
<div
|
||||
className="mapTocEntry__layerDetails"
|
||||
data-test-subj={`mapLayerTOCDetails${escapeLayerName(this.state.displayName)}`}
|
||||
>
|
||||
<LegendDetails layer={this.props.layer} />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{this._renderDetailsToggle()}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { Component, Fragment, ReactNode } from 'react';
|
|||
|
||||
import { EuiButtonEmpty, EuiIcon, EuiToolTip, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ILayer } from '../../../../../../classes/layers/layer';
|
||||
import { type ILayer, INCOMPLETE_RESULTS_WARNING } from '../../../../../../classes/layers/layer';
|
||||
import { IVectorSource } from '../../../../../../classes/sources/vector_source';
|
||||
import { isLayerGroup } from '../../../../../../classes/layers/layer_group';
|
||||
|
||||
|
@ -18,12 +18,6 @@ interface Footnote {
|
|||
message?: string | null;
|
||||
}
|
||||
|
||||
interface IconAndTooltipContent {
|
||||
icon?: ReactNode;
|
||||
tooltipContent?: ReactNode;
|
||||
footnotes: Footnote[];
|
||||
}
|
||||
|
||||
export interface ReduxStateProps {
|
||||
isUsingSearch: boolean;
|
||||
zoom: number;
|
||||
|
@ -69,22 +63,35 @@ export class TOCEntryButton extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
getIconAndTooltipContent(): IconAndTooltipContent {
|
||||
if (this.props.layer.hasErrors()) {
|
||||
return {
|
||||
icon: (
|
||||
<EuiIcon
|
||||
size="m"
|
||||
type="error"
|
||||
color="danger"
|
||||
data-test-subj={`layerTocErrorIcon${this.props.escapedDisplayName}`}
|
||||
/>
|
||||
),
|
||||
tooltipContent: this.props.layer
|
||||
.getErrors()
|
||||
.map(({ title }) => <div key={title}>{title}</div>),
|
||||
footnotes: [],
|
||||
};
|
||||
getIconAndTooltipContent(): {
|
||||
icon?: ReactNode;
|
||||
tooltipContent?: ReactNode;
|
||||
footnotes: Footnote[];
|
||||
postScript?: string;
|
||||
} {
|
||||
const errors = this.props.layer.getErrors();
|
||||
if (errors.length) {
|
||||
const errorIcon = (
|
||||
<EuiIcon
|
||||
size="m"
|
||||
type="error"
|
||||
color="danger"
|
||||
data-test-subj={`layerTocErrorIcon${this.props.escapedDisplayName}`}
|
||||
/>
|
||||
);
|
||||
return isLayerGroup(this.props.layer)
|
||||
? {
|
||||
icon: errorIcon,
|
||||
footnotes: [],
|
||||
postScript: errors[0].title,
|
||||
}
|
||||
: {
|
||||
icon: errorIcon,
|
||||
tooltipContent: this.props.layer
|
||||
.getErrors()
|
||||
.map(({ title }) => <div key={title}>{title}</div>),
|
||||
footnotes: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (!this.props.layer.isVisible()) {
|
||||
|
@ -118,10 +125,26 @@ export class TOCEntryButton extends Component<Props, State> {
|
|||
};
|
||||
}
|
||||
|
||||
const { icon, tooltipContent } = this.props.layer.getLayerIcon(true);
|
||||
const { icon: layerIcon, tooltipContent } = this.props.layer.getLayerIcon(true);
|
||||
const warnings = this.props.layer.getWarnings();
|
||||
const icon = warnings.length ? (
|
||||
<EuiIcon
|
||||
size="m"
|
||||
type="warning"
|
||||
color="warning"
|
||||
data-test-subj={`layerTocWarningIcon${this.props.escapedDisplayName}`}
|
||||
/>
|
||||
) : (
|
||||
layerIcon
|
||||
);
|
||||
|
||||
if (isLayerGroup(this.props.layer)) {
|
||||
return { icon, tooltipContent, footnotes: [] };
|
||||
return {
|
||||
icon,
|
||||
tooltipContent,
|
||||
footnotes: [],
|
||||
postScript: warnings.length ? warnings[0].title : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const footnotes = [];
|
||||
|
@ -158,11 +181,12 @@ export class TOCEntryButton extends Component<Props, State> {
|
|||
icon,
|
||||
tooltipContent,
|
||||
footnotes,
|
||||
postScript: warnings.length ? INCOMPLETE_RESULTS_WARNING : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { icon, tooltipContent, footnotes } = this.getIconAndTooltipContent();
|
||||
const { icon, tooltipContent, footnotes, postScript } = this.getIconAndTooltipContent();
|
||||
|
||||
const footnoteIcons = footnotes.map((footnote, index) => {
|
||||
return (
|
||||
|
@ -189,6 +213,9 @@ export class TOCEntryButton extends Component<Props, State> {
|
|||
<Fragment>
|
||||
{tooltipContent}
|
||||
{footnoteTooltipContent}
|
||||
{postScript ? (
|
||||
<p style={{ fontStyle: 'italic', marginTop: '16px' }}>{postScript}</p>
|
||||
) : null}
|
||||
</Fragment>
|
||||
}
|
||||
data-test-subj="layerTocTooltip"
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
"@kbn/content-management-table-list-view",
|
||||
"@kbn/serverless",
|
||||
"@kbn/logging",
|
||||
"@kbn/search-response-warnings",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -23510,8 +23510,6 @@
|
|||
"xpack.maps.blendedVectorLayer.clusteredLayerName": "{displayName} en cluster",
|
||||
"xpack.maps.common.esSpatialRelation.clusterFilterLabel": "intersecte le cluster {gridId}",
|
||||
"xpack.maps.deleteLayerConfirmModal.multiLayerWarning": "Le retrait de ce calque retire également {numChildren} {numChildren, plural, one {calque imbriqué} many {les calques} other {les calques}}.",
|
||||
"xpack.maps.distanceSource.requestDescription": "Obtenir des indicateurs de la vue de données : {dataViewName} ; champ géospatial : {geoFieldName}",
|
||||
"xpack.maps.distanceSource.requestName": "{leftSourceName} à distance de la requête de liaison",
|
||||
"xpack.maps.embeddable.boundsFilterLabel": "{geoFieldsLabel} dans les limites de la carte",
|
||||
"xpack.maps.es_geo_utils.convert.unsupportedGeometryTypeErrorMessage": "Conversion de la géométrie {geometryType} en geojson impossible ; non pris en charge",
|
||||
"xpack.maps.es_geo_utils.distanceFilterAlias": "dans un rayon de {distanceKm} km de {pointLabel}",
|
||||
|
@ -23525,10 +23523,6 @@
|
|||
"xpack.maps.esSearch.topHitsEntitiesCountMsg": "{entityCount} entités trouvées.",
|
||||
"xpack.maps.esSearch.topHitsResultsTrimmedMsg": "Résultats limités aux {entityCount} premières entités sur environ {totalEntities}.",
|
||||
"xpack.maps.esSearch.topHitsSizeMsg": "Affichage des {topHitsSize} premiers documents par entité.",
|
||||
"xpack.maps.esSearchSource.requestDescription": "Obtenir des documents de la vue de données : {dataViewName} ; champ géospatial : {geoFieldName}",
|
||||
"xpack.maps.esSearchSource.requestName": "Requête de documents de {layerName}",
|
||||
"xpack.maps.esSearchSource.topHits.requestDescription": "Obtenir les premiers résultats de la vue de données : {dataViewName} ; entités : {entitiesFieldName} ; champ géospatial : {geoFieldName}",
|
||||
"xpack.maps.esSearchSource.topHits.requestName": "Requête des premiers résultats de {layerName}",
|
||||
"xpack.maps.fileUpload.trimmedResultsMsg": "Résultats limités à {numFeatures} fonctionnalités, {previewCoverage} % du fichier.",
|
||||
"xpack.maps.filterByMapExtentMenuItem.displayName": "Filtrer {containerLabel} selon les limites de la carte",
|
||||
"xpack.maps.filterByMapExtentMenuItem.displayNameTooltip": "Quand vous vous déplacez sur la carte ou que vous zoomez, le {containerLabel} se met à jour pour afficher uniquement les données visibles dans les limites de la carte.",
|
||||
|
@ -23551,7 +23545,6 @@
|
|||
"xpack.maps.mask.maskLabel": "Masquer {hideNoun}",
|
||||
"xpack.maps.mask.whenJoinMetric": "{whenLabel} l'indicateur de jonction",
|
||||
"xpack.maps.maskLegend.is": "{aggLabel} est",
|
||||
"xpack.maps.pewPew.requestName": "Requête de chemins de {layerName}",
|
||||
"xpack.maps.scalingDocs.clustersDetails": "Affichez les clusters lorsque les résultats dépassent {maxResultWindow} documents. Affichez les documents lorsqu'il y a moins de {maxResultWindow} résultats.",
|
||||
"xpack.maps.scalingDocs.limitDetails": "Affichez les fonctionnalités des {maxResultWindow} premiers documents.",
|
||||
"xpack.maps.scalingDocs.maxResultWindow": "Contrainte {maxResultWindow} fournie par le paramètre d'index {link}.",
|
||||
|
@ -23567,15 +23560,8 @@
|
|||
"xpack.maps.source.emsTileSourceDescription": "Service de fond de carte de {host}",
|
||||
"xpack.maps.source.esAggSource.topTermLabel": "{fieldLabel} principal",
|
||||
"xpack.maps.source.esGeoGrid.groupBy.termsDescription": "Créez un suivi pour les {maxTermsTracks} termes principaux. Le suivi est tronqué lorsque le nombre d'éléments dépasse la limite.",
|
||||
"xpack.maps.source.esGeoLine.entityRequestDescription": "Obtenir des entités au sein de la mémoire tampon de la carte depuis la vue de données : {dataViewName} ; entités : {splitFieldName}",
|
||||
"xpack.maps.source.esGeoLine.timeSeriesTrackRequestDescription": "Obtenir des suivis de la vue de données : {dataViewName} ; champ géospatial : {geoFieldName}",
|
||||
"xpack.maps.source.esGeoLine.trackRequestDescription": "Obtenir des pistes pour des entités {numEntities} depuis la vue de données : {dataViewName} ; champ géospatial : {geoFieldName}",
|
||||
"xpack.maps.source.esGeoLineDisabledReason": "{title} requiert une licence Gold.",
|
||||
"xpack.maps.source.esGrid.compositeInspector.requestName": "Requête composite de {layerName} {bucketsName} ({requestCount})",
|
||||
"xpack.maps.source.esGrid.compositeInspectorDescription": "Obtenir {bucketsName} de la vue de données : {dataViewName} ; champ géospatial : {geoFieldName}",
|
||||
"xpack.maps.source.esGrid.compositePaginationErrorMessage": "{layerName} génère trop de requêtes. Réduisez \"Résolution de la grille\" et/ou réduisez le nombre d'indicateurs de premier terme.",
|
||||
"xpack.maps.source.esGrid.inspector.requestDescription": "Obtenir {bucketsName} de la vue de données : {dataViewName} ; champ géospatial : {geoFieldName}",
|
||||
"xpack.maps.source.esGrid.inspector.requestName": "Requête de {layerName} {bucketsName}",
|
||||
"xpack.maps.source.esGrid.resolutionParamErrorMessage": "Paramètre de résolution de grille non reconnu : {resolution}",
|
||||
"xpack.maps.source.esJoin.countLabel": "Nombre de {indexPatternLabel}",
|
||||
"xpack.maps.source.esSearch.clusterScalingLabel": "Afficher les clusters lorsque les résultats dépassent {maxResultWindow}",
|
||||
|
@ -23586,15 +23572,12 @@
|
|||
"xpack.maps.source.esSource.noGeoFieldErrorMessage": "La vue de données \"{indexPatternLabel}\" ne contient plus le champ géographique \"{geoField}\"",
|
||||
"xpack.maps.source.esSource.stylePropsMetaRequestName": "{layerName} - Métadonnées",
|
||||
"xpack.maps.source.MVTSingleLayerVectorSourceEditor.urlHelpMessage": "URL du service de cartographie vectoriel .mvt. Par exemple, {url}",
|
||||
"xpack.maps.source.pewPew.inspectorDescription": "Obtenir des chemins depuis la vue de données : {dataViewName} ; source : {sourceFieldName} ; destination : {destFieldName}",
|
||||
"xpack.maps.spatialJoin.wizardForm.withinExpressionValue": "{distance} {units} de fonctionnalités de calque",
|
||||
"xpack.maps.spatialJoinExpression.noDataViewTitle": "Impossible de charger la vue de données {dataViewId}.",
|
||||
"xpack.maps.spatialJoinExpression.value": "fonctionnalités de {geoField}",
|
||||
"xpack.maps.style.fieldSelect.OriginLabel": "Champs de {fieldOrigin}",
|
||||
"xpack.maps.termJoinExpression.topTerms": "{size} principal",
|
||||
"xpack.maps.termJoinExpression.value": "termes {topTerms} de {term}",
|
||||
"xpack.maps.termSource.requestDescription": "Obtenir des indicateurs depuis la vue de données : {dataViewName} ; champ de terme : {termFieldName}",
|
||||
"xpack.maps.termSource.requestName": "Requête de liaison de terme de {leftSourceName}",
|
||||
"xpack.maps.tiles.resultsCompleteMsg": "{countPrefix}{count} documents trouvés.",
|
||||
"xpack.maps.tiles.resultsTrimmedMsg": "Les résultats sont limités à {countPrefix}{count} documents.",
|
||||
"xpack.maps.tooltip.pageNumerText": "{pageNumber} de {total}",
|
||||
|
@ -24034,8 +24017,6 @@
|
|||
"xpack.maps.source.esGeoLine.sortFieldPlaceholder": "Sélectionner le champ de tri",
|
||||
"xpack.maps.source.esGeoLine.splitFieldLabel": "Entité",
|
||||
"xpack.maps.source.esGeoLine.splitFieldPlaceholder": "Sélectionner un champ d'entité",
|
||||
"xpack.maps.source.esGeoLine.timeSeriesTrackRequestName": "Requêtes de piste \"{layerName}\" (séries temporelles)",
|
||||
"xpack.maps.source.esGeoLine.trackRequestName": "Requêtes de piste \"{layerName}\" (termes)",
|
||||
"xpack.maps.source.esGeoLine.trackSettingsLabel": "Pistes",
|
||||
"xpack.maps.source.esGeoLineDescription": "Créer des lignes à partir de points",
|
||||
"xpack.maps.source.esGeoLineTitle": "Pistes",
|
||||
|
@ -24078,7 +24059,6 @@
|
|||
"xpack.maps.source.esSearch.useMVTVectorTiles": "Utiliser les tuiles vectorielles",
|
||||
"xpack.maps.source.esSearchDescription": "Points, lignes et polygones d'Elasticsearch",
|
||||
"xpack.maps.source.esSearchTitle": "Documents",
|
||||
"xpack.maps.source.esSource.stylePropsMetaRequestDescription": "Recherche Elasticsearch récupérant les métadonnées de champ utilisées pour le calcul des champs de symbolisation.",
|
||||
"xpack.maps.source.esTopHitsSearch.sortFieldLabel": "Champ de tri",
|
||||
"xpack.maps.source.esTopHitsSearch.sortOrderLabel": "Ordre de tri",
|
||||
"xpack.maps.source.geofieldLabel": "Champ géospatial",
|
||||
|
|
|
@ -23524,8 +23524,6 @@
|
|||
"xpack.maps.blendedVectorLayer.clusteredLayerName": "クラスター化された{displayName}",
|
||||
"xpack.maps.common.esSpatialRelation.clusterFilterLabel": "クラスター{gridId}と交差します",
|
||||
"xpack.maps.deleteLayerConfirmModal.multiLayerWarning": "このレイヤーを削除すと、{numChildren}個のネストされた{numChildren, plural, other {レイヤー}}も削除されます。",
|
||||
"xpack.maps.distanceSource.requestDescription": "データビューからメトリックを取得します:{dataViewName}、地理空間フィールド:{geoFieldName}",
|
||||
"xpack.maps.distanceSource.requestName": "距離結合リクエスト内の{leftSourceName}",
|
||||
"xpack.maps.embeddable.boundsFilterLabel": "マップ境界内の{geoFieldsLabel}",
|
||||
"xpack.maps.es_geo_utils.convert.unsupportedGeometryTypeErrorMessage": "{geometryType} ジオメトリから Geojson に変換できません。サポートされていません",
|
||||
"xpack.maps.es_geo_utils.distanceFilterAlias": "{pointLabel}の{distanceKm}km以内",
|
||||
|
@ -23539,10 +23537,6 @@
|
|||
"xpack.maps.esSearch.topHitsEntitiesCountMsg": "{entityCount}エンティティが見つかりました。",
|
||||
"xpack.maps.esSearch.topHitsResultsTrimmedMsg": "結果は~{totalEntities}の{entityCount}エンティティに制限されています。",
|
||||
"xpack.maps.esSearch.topHitsSizeMsg": "エンティティごとに上位の{topHitsSize}ドキュメントを表示しています。",
|
||||
"xpack.maps.esSearchSource.requestDescription": "データビューからドキュメントを取得します:{dataViewName}、地理空間フィールド:{geoFieldName}",
|
||||
"xpack.maps.esSearchSource.requestName": "{layerName}ドキュメントリクエスト",
|
||||
"xpack.maps.esSearchSource.topHits.requestDescription": "データビューから上位の一致を取得します:{dataViewName}、表現:{entitiesFieldName}、地理空間フィールド:{geoFieldName}",
|
||||
"xpack.maps.esSearchSource.topHits.requestName": "{layerName}の上位の一致リクエスト",
|
||||
"xpack.maps.fileUpload.trimmedResultsMsg": "結果は{numFeatures}機能、ファイルの{previewCoverage}%に制限されています。",
|
||||
"xpack.maps.filterByMapExtentMenuItem.displayName": "マップ境界で{containerLabel}をフィルター",
|
||||
"xpack.maps.filterByMapExtentMenuItem.displayNameTooltip": "マップをズームおよびパンすると、{containerLabel}が更新され、マップ境界に表示されるデータのみが表示されます。",
|
||||
|
@ -23565,7 +23559,6 @@
|
|||
"xpack.maps.mask.maskLabel": "{hideNoun}を非表示",
|
||||
"xpack.maps.mask.whenJoinMetric": "{whenLabel}結合メトリック",
|
||||
"xpack.maps.maskLegend.is": "{aggLabel}は",
|
||||
"xpack.maps.pewPew.requestName": "{layerName}パスリクエスト",
|
||||
"xpack.maps.scalingDocs.clustersDetails": "結果が{maxResultWindow}ドキュメントを超えたときにクラスターを表示します。結果が{maxResultWindow}未満のときにドキュメントを表示します。",
|
||||
"xpack.maps.scalingDocs.limitDetails": "最初の{maxResultWindow}ドキュメントから特徴量を表示します。",
|
||||
"xpack.maps.scalingDocs.maxResultWindow": "{link}のインデックス設定によって提供される{maxResultWindow}制約。",
|
||||
|
@ -23581,15 +23574,8 @@
|
|||
"xpack.maps.source.emsTileSourceDescription": "{host}からのベースマップサービス",
|
||||
"xpack.maps.source.esAggSource.topTermLabel": "上位{fieldLabel}",
|
||||
"xpack.maps.source.esGeoGrid.groupBy.termsDescription": "上位{maxTermsTracks}語句のトラックを作成します。ポイント数が上限を超えると、トラックは切り捨てられます。",
|
||||
"xpack.maps.source.esGeoLine.entityRequestDescription": "データビューからマップバッファー内の表現を取得します:{dataViewName}、表現:{splitFieldName}",
|
||||
"xpack.maps.source.esGeoLine.timeSeriesTrackRequestDescription": "データビューからトラックを取得します:{dataViewName}、地理空間フィールド:{geoFieldName}",
|
||||
"xpack.maps.source.esGeoLine.trackRequestDescription": "データビューから{numEntities}個の表現のトラックを取得します:{dataViewName}、地理空間フィールド:{geoFieldName}",
|
||||
"xpack.maps.source.esGeoLineDisabledReason": "{title}には Gold ライセンスが必要です。",
|
||||
"xpack.maps.source.esGrid.compositeInspector.requestName": "{layerName} {bucketsName}複合リクエスト({requestCount})",
|
||||
"xpack.maps.source.esGrid.compositeInspectorDescription": "データビューから{bucketsName}を取得します:{dataViewName}、地理空間フィールド:{geoFieldName}",
|
||||
"xpack.maps.source.esGrid.compositePaginationErrorMessage": "{layerName}はリクエスト過多の原因になります。「グリッド解像度」を下げるか、またはトップ用語「メトリック」の数を減らしてください。",
|
||||
"xpack.maps.source.esGrid.inspector.requestDescription": "データビューから{bucketsName}を取得します:{dataViewName}、地理空間フィールド:{geoFieldName}",
|
||||
"xpack.maps.source.esGrid.inspector.requestName": "{layerName} {bucketsName}リクエスト",
|
||||
"xpack.maps.source.esGrid.resolutionParamErrorMessage": "グリッド解像度パラメーターが認識されません: {resolution}",
|
||||
"xpack.maps.source.esJoin.countLabel": "{indexPatternLabel}のカウント",
|
||||
"xpack.maps.source.esSearch.clusterScalingLabel": "結果が{maxResultWindow}を超えたらクラスターを表示",
|
||||
|
@ -23600,15 +23586,12 @@
|
|||
"xpack.maps.source.esSource.noGeoFieldErrorMessage": "データビュー\"{indexPatternLabel}\"には現在ジオフィールド\"{geoField}\"が含まれていません",
|
||||
"xpack.maps.source.esSource.stylePropsMetaRequestName": "{layerName} - メタデータ",
|
||||
"xpack.maps.source.MVTSingleLayerVectorSourceEditor.urlHelpMessage": ".mvtベクトルタイルサービスのURL。例:{url}",
|
||||
"xpack.maps.source.pewPew.inspectorDescription": "データビューからパスを取得します:{dataViewName}、ソース:{sourceFieldName}、デスティネーション:{destFieldName}",
|
||||
"xpack.maps.spatialJoin.wizardForm.withinExpressionValue": "レイヤー特徴量の{distance} {units}",
|
||||
"xpack.maps.spatialJoinExpression.noDataViewTitle": "データビュー{dataViewId}を読み込めません。",
|
||||
"xpack.maps.spatialJoinExpression.value": "{geoField}からの特徴量",
|
||||
"xpack.maps.style.fieldSelect.OriginLabel": "{fieldOrigin}からのフィールド",
|
||||
"xpack.maps.termJoinExpression.topTerms": "上位{size}",
|
||||
"xpack.maps.termJoinExpression.value": "{term}からの{topTerms}用語",
|
||||
"xpack.maps.termSource.requestDescription": "データビューからメトリックを取得します:{dataViewName}、用語フィールド:{termFieldName}",
|
||||
"xpack.maps.termSource.requestName": "{leftSourceName}用語結合リクエスト",
|
||||
"xpack.maps.tiles.resultsCompleteMsg": "{countPrefix}{count}件のドキュメントが見つかりました。",
|
||||
"xpack.maps.tiles.resultsTrimmedMsg": "結果は{countPrefix}{count}ドキュメントに制限されています。",
|
||||
"xpack.maps.tooltip.pageNumerText": "{pageNumber} / {total}",
|
||||
|
@ -24048,8 +24031,6 @@
|
|||
"xpack.maps.source.esGeoLine.sortFieldPlaceholder": "ソートフィールドを選択",
|
||||
"xpack.maps.source.esGeoLine.splitFieldLabel": "エンティティ",
|
||||
"xpack.maps.source.esGeoLine.splitFieldPlaceholder": "エンティティフィールドを選択",
|
||||
"xpack.maps.source.esGeoLine.timeSeriesTrackRequestName": "'{layerName}'トラックリクエスト(時系列)",
|
||||
"xpack.maps.source.esGeoLine.trackRequestName": "'{layerName}'トラックリクエスト(用語)",
|
||||
"xpack.maps.source.esGeoLine.trackSettingsLabel": "追跡",
|
||||
"xpack.maps.source.esGeoLineDescription": "ポイントから線を作成",
|
||||
"xpack.maps.source.esGeoLineTitle": "追跡",
|
||||
|
@ -24092,7 +24073,6 @@
|
|||
"xpack.maps.source.esSearch.useMVTVectorTiles": "ベクトルタイルを使用",
|
||||
"xpack.maps.source.esSearchDescription": "Elasticsearch の点、線、多角形",
|
||||
"xpack.maps.source.esSearchTitle": "ドキュメント",
|
||||
"xpack.maps.source.esSource.stylePropsMetaRequestDescription": "シンボル化バンドを計算するために使用されるフィールドメタデータを取得するElasticsearchリクエスト。",
|
||||
"xpack.maps.source.esTopHitsSearch.sortFieldLabel": "並べ替えフィールド",
|
||||
"xpack.maps.source.esTopHitsSearch.sortOrderLabel": "並べ替え順",
|
||||
"xpack.maps.source.geofieldLabel": "地理空間フィールド",
|
||||
|
|
|
@ -23524,8 +23524,6 @@
|
|||
"xpack.maps.blendedVectorLayer.clusteredLayerName": "集群 {displayName}",
|
||||
"xpack.maps.common.esSpatialRelation.clusterFilterLabel": "相交集群 {gridId}",
|
||||
"xpack.maps.deleteLayerConfirmModal.multiLayerWarning": "移除此图层还会移除 {numChildren} 个嵌套{numChildren, plural, other {图层}}。",
|
||||
"xpack.maps.distanceSource.requestDescription": "获取指标的数据视图:{dataViewName}、地理空间字段:{geoFieldName}",
|
||||
"xpack.maps.distanceSource.requestName": "距离联接请求内的 {leftSourceName}",
|
||||
"xpack.maps.embeddable.boundsFilterLabel": "地图边界内的 {geoFieldsLabel}",
|
||||
"xpack.maps.es_geo_utils.convert.unsupportedGeometryTypeErrorMessage": "无法将 {geometryType} 几何图形转换成 geojson,不支持",
|
||||
"xpack.maps.es_geo_utils.distanceFilterAlias": "{pointLabel} {distanceKm}km 内",
|
||||
|
@ -23539,10 +23537,6 @@
|
|||
"xpack.maps.esSearch.topHitsEntitiesCountMsg": "找到 {entityCount} 个实体。",
|
||||
"xpack.maps.esSearch.topHitsResultsTrimmedMsg": "结果限制为 ~{totalEntities} 个实体中的前 {entityCount} 个。",
|
||||
"xpack.maps.esSearch.topHitsSizeMsg": "显示每个实体排名前 {topHitsSize} 的文档。",
|
||||
"xpack.maps.esSearchSource.requestDescription": "获取文档的数据视图:{dataViewName}、地理空间字段:{geoFieldName}",
|
||||
"xpack.maps.esSearchSource.requestName": "{layerName} 文档请求",
|
||||
"xpack.maps.esSearchSource.topHits.requestDescription": "获取最高命中结果的数据视图:{dataViewName}、实体:{entitiesFieldName}、地理空间字段:{geoFieldName}",
|
||||
"xpack.maps.esSearchSource.topHits.requestName": "{layerName} 最高命中结果请求",
|
||||
"xpack.maps.fileUpload.trimmedResultsMsg": "结果仅限于 {numFeatures} 个特征、{previewCoverage}% 的文件。",
|
||||
"xpack.maps.filterByMapExtentMenuItem.displayName": "按地图边界筛选 {containerLabel}",
|
||||
"xpack.maps.filterByMapExtentMenuItem.displayNameTooltip": "当您缩放和平移地图时,{containerLabel} 会进行更新以仅显示在地图边界中可见的数据。",
|
||||
|
@ -23565,7 +23559,6 @@
|
|||
"xpack.maps.mask.maskLabel": "隐藏 {hideNoun}",
|
||||
"xpack.maps.mask.whenJoinMetric": "{whenLabel} 联接指标",
|
||||
"xpack.maps.maskLegend.is": "{aggLabel} 为",
|
||||
"xpack.maps.pewPew.requestName": "{layerName} 路径请求",
|
||||
"xpack.maps.scalingDocs.clustersDetails": "结果超过 {maxResultWindow} 个文档时显示集群。结果数小于 {maxResultWindow} 时显示文档。",
|
||||
"xpack.maps.scalingDocs.limitDetails": "显示前 {maxResultWindow} 个文档中的特征。",
|
||||
"xpack.maps.scalingDocs.maxResultWindow": "{link} 索引设置提供的 {maxResultWindow} 限制。",
|
||||
|
@ -23581,15 +23574,8 @@
|
|||
"xpack.maps.source.emsTileSourceDescription": "来自 {host} 的基础地图服务",
|
||||
"xpack.maps.source.esAggSource.topTermLabel": "排名靠前 {fieldLabel}",
|
||||
"xpack.maps.source.esGeoGrid.groupBy.termsDescription": "为排名前 {maxTermsTracks} 的词创建轨迹。点数超出限制时,会截短轨迹。",
|
||||
"xpack.maps.source.esGeoLine.entityRequestDescription": "获取地图缓冲内实体的数据视图:{dataViewName}、实体:{splitFieldName}",
|
||||
"xpack.maps.source.esGeoLine.timeSeriesTrackRequestDescription": "获取轨迹的数据视图:{dataViewName}、地理空间字段:{geoFieldName}",
|
||||
"xpack.maps.source.esGeoLine.trackRequestDescription": "获取 {numEntities} 个实体的轨迹的数据视图:{dataViewName}、地理空间字段:{geoFieldName}",
|
||||
"xpack.maps.source.esGeoLineDisabledReason": "{title} 需要黄金级许可证。",
|
||||
"xpack.maps.source.esGrid.compositeInspector.requestName": "{layerName} {bucketsName} 组合请求 ({requestCount})",
|
||||
"xpack.maps.source.esGrid.compositeInspectorDescription": "获取 {bucketsName} 的数据视图:{dataViewName}、地理空间字段:{geoFieldName}",
|
||||
"xpack.maps.source.esGrid.compositePaginationErrorMessage": "{layerName} 正导致过多的请求。降低“网格分辨率”和/或减少热门词“指标”的数量。",
|
||||
"xpack.maps.source.esGrid.inspector.requestDescription": "获取 {bucketsName} 的数据视图:{dataViewName}、地理空间字段:{geoFieldName}",
|
||||
"xpack.maps.source.esGrid.inspector.requestName": "{layerName} {bucketsName} 请求",
|
||||
"xpack.maps.source.esGrid.resolutionParamErrorMessage": "无法识别网格分辨率参数:{resolution}",
|
||||
"xpack.maps.source.esJoin.countLabel": "{indexPatternLabel} 的计数",
|
||||
"xpack.maps.source.esSearch.clusterScalingLabel": "结果超过 {maxResultWindow} 个时显示集群",
|
||||
|
@ -23600,15 +23586,12 @@
|
|||
"xpack.maps.source.esSource.noGeoFieldErrorMessage": "数据视图“{indexPatternLabel}”不再包含地理字段“{geoField}”",
|
||||
"xpack.maps.source.esSource.stylePropsMetaRequestName": "{layerName} - 元数据",
|
||||
"xpack.maps.source.MVTSingleLayerVectorSourceEditor.urlHelpMessage": ".mvt 矢量磁帖服务的 URL。例如 {url}",
|
||||
"xpack.maps.source.pewPew.inspectorDescription": "获取路径的数据视图:{dataViewName}、源:{sourceFieldName}、目标:{destFieldName}",
|
||||
"xpack.maps.spatialJoin.wizardForm.withinExpressionValue": "图层特征的 {distance} {units}",
|
||||
"xpack.maps.spatialJoinExpression.noDataViewTitle": "无法加载数据视图 {dataViewId}。",
|
||||
"xpack.maps.spatialJoinExpression.value": "{geoField} 中的特征",
|
||||
"xpack.maps.style.fieldSelect.OriginLabel": "来自 {fieldOrigin} 的字段",
|
||||
"xpack.maps.termJoinExpression.topTerms": "排名靠前 {size}",
|
||||
"xpack.maps.termJoinExpression.value": "来自 {term} 的 {topTerms} 词",
|
||||
"xpack.maps.termSource.requestDescription": "获取指标的数据视图:{dataViewName}、词字段:{termFieldName}",
|
||||
"xpack.maps.termSource.requestName": "{leftSourceName} 词联接请求",
|
||||
"xpack.maps.tiles.resultsCompleteMsg": "找到 {countPrefix}{count} 个文档。",
|
||||
"xpack.maps.tiles.resultsTrimmedMsg": "结果仅限为 {countPrefix}{count} 个文档。",
|
||||
"xpack.maps.tooltip.pageNumerText": "{total} 的 {pageNumber}",
|
||||
|
@ -24048,8 +24031,6 @@
|
|||
"xpack.maps.source.esGeoLine.sortFieldPlaceholder": "选择排序字段",
|
||||
"xpack.maps.source.esGeoLine.splitFieldLabel": "实体",
|
||||
"xpack.maps.source.esGeoLine.splitFieldPlaceholder": "选择实体字段",
|
||||
"xpack.maps.source.esGeoLine.timeSeriesTrackRequestName": "“{layerName}”轨迹请求(时间序列)",
|
||||
"xpack.maps.source.esGeoLine.trackRequestName": "“{layerName}”轨迹请求(词)",
|
||||
"xpack.maps.source.esGeoLine.trackSettingsLabel": "轨迹",
|
||||
"xpack.maps.source.esGeoLineDescription": "从点创建线",
|
||||
"xpack.maps.source.esGeoLineTitle": "轨迹",
|
||||
|
@ -24092,7 +24073,6 @@
|
|||
"xpack.maps.source.esSearch.useMVTVectorTiles": "使用矢量磁贴",
|
||||
"xpack.maps.source.esSearchDescription": "Elasticsearch 的点、线和多边形",
|
||||
"xpack.maps.source.esSearchTitle": "文档",
|
||||
"xpack.maps.source.esSource.stylePropsMetaRequestDescription": "检索用于计算符号化带的字段元数据的 Elasticsearch 请求。",
|
||||
"xpack.maps.source.esTopHitsSearch.sortFieldLabel": "排序字段",
|
||||
"xpack.maps.source.esTopHitsSearch.sortOrderLabel": "排序顺序",
|
||||
"xpack.maps.source.geofieldLabel": "地理空间字段",
|
||||
|
|
|
@ -13,6 +13,9 @@ export default function ({ getPageObjects, getService }) {
|
|||
const security = getService('security');
|
||||
|
||||
describe('blended vector layer', () => {
|
||||
const LOAD_DOCUMENTS_REQUEST_NAME = 'load layer features (logstash-*)';
|
||||
const LOAD_CLUSTERS_REQUEST_NAME = 'load layer features (Clustered logstash-*)';
|
||||
|
||||
before(async () => {
|
||||
await security.testUser.setRoles(['test_logstash_reader', 'global_maps_all']);
|
||||
await PageObjects.maps.loadSavedMap('blended document example');
|
||||
|
@ -27,20 +30,26 @@ export default function ({ getPageObjects, getService }) {
|
|||
});
|
||||
|
||||
it('should request documents when zoomed to smaller regions showing less data', async () => {
|
||||
const { rawResponse: response } = await PageObjects.maps.getResponse();
|
||||
const { rawResponse: response } = await PageObjects.maps.getResponse(
|
||||
LOAD_DOCUMENTS_REQUEST_NAME
|
||||
);
|
||||
// Allow a range of hits to account for variances in browser window size.
|
||||
expect(response.hits.hits.length).to.be.within(5, 12);
|
||||
});
|
||||
|
||||
it('should request clusters when zoomed to larger regions showing lots of data', async () => {
|
||||
await PageObjects.maps.setView(20, -90, 2);
|
||||
const { rawResponse: response } = await PageObjects.maps.getResponse();
|
||||
const { rawResponse: response } = await PageObjects.maps.getResponse(
|
||||
LOAD_CLUSTERS_REQUEST_NAME
|
||||
);
|
||||
expect(response.aggregations.gridSplit.buckets.length).to.equal(15);
|
||||
});
|
||||
|
||||
it('should request documents when query narrows data', async () => {
|
||||
await PageObjects.maps.setAndSubmitQuery('bytes > 19000');
|
||||
const { rawResponse: response } = await PageObjects.maps.getResponse();
|
||||
const { rawResponse: response } = await PageObjects.maps.getResponse(
|
||||
LOAD_DOCUMENTS_REQUEST_NAME
|
||||
);
|
||||
expect(response.hits.hits.length).to.equal(75);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -77,7 +77,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
await retry.try(async () => {
|
||||
const joinExampleRequestNames = await inspector.getRequestNames();
|
||||
expect(joinExampleRequestNames).to.equal(
|
||||
'geo_shapes* documents request,geo_shapes* term join request'
|
||||
'load layer features (geo_shapes*),load join metrics (geo_shapes*)'
|
||||
);
|
||||
});
|
||||
await inspector.close();
|
||||
|
@ -88,7 +88,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
await inspector.close();
|
||||
|
||||
expect(singleExampleRequest).to.be(true);
|
||||
expect(selectedExampleRequest).to.equal('logstash-* grid request');
|
||||
expect(selectedExampleRequest).to.equal('load layer features (logstash-*)');
|
||||
});
|
||||
|
||||
it('should apply container state (time, query, filters) to embeddable when loaded', async () => {
|
||||
|
@ -120,7 +120,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
const { rawResponse: joinResponse } = await PageObjects.maps.getResponseFromDashboardPanel(
|
||||
'join example',
|
||||
'geo_shapes* term join request'
|
||||
'load join metrics (geo_shapes*)'
|
||||
);
|
||||
expect(joinResponse.aggregations.join.buckets.length).to.equal(1);
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
it('should re-fetch join with refresh timer', async () => {
|
||||
async function getRequestTimestamp() {
|
||||
await PageObjects.maps.openInspectorRequest('geo_shapes* term join request');
|
||||
await PageObjects.maps.openInspectorRequest('load join metrics (geo_shapes*)');
|
||||
const requestStats = await inspector.getTableData();
|
||||
const requestTimestamp = PageObjects.maps.getInspectorStatRowHit(
|
||||
requestStats,
|
||||
|
@ -122,7 +122,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
it('should not apply query to source and apply query to join', async () => {
|
||||
const { rawResponse: joinResponse } = await PageObjects.maps.getResponse(
|
||||
'geo_shapes* term join request'
|
||||
'load join metrics (geo_shapes*)'
|
||||
);
|
||||
expect(joinResponse.aggregations.join.buckets.length).to.equal(2);
|
||||
});
|
||||
|
@ -139,7 +139,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
it('should apply query to join request', async () => {
|
||||
const { rawResponse: joinResponse } = await PageObjects.maps.getResponse(
|
||||
'geo_shapes* term join request'
|
||||
'load join metrics (geo_shapes*)'
|
||||
);
|
||||
expect(joinResponse.aggregations.join.buckets.length).to.equal(1);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue