[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:
Nathan Reese 2023-11-16 12:09:43 -07:00 committed by GitHub
parent c3ac89c9f0
commit 0b24e40b54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 690 additions and 400 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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: '',
},
]
: [];

View file

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

View file

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

View file

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

View file

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

View file

@ -12,5 +12,5 @@ export interface JoinState {
dataHasChanged: boolean;
join: InnerJoin;
joinIndex: number;
propertiesMap?: PropertiesMap;
joinMetrics?: PropertiesMap;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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[]> => {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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]}
/>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -74,6 +74,7 @@
"@kbn/content-management-table-list-view",
"@kbn/serverless",
"@kbn/logging",
"@kbn/search-response-warnings",
],
"exclude": [
"target/**/*",

View file

@ -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",

View file

@ -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": "地理空間フィールド",

View file

@ -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": "地理空间字段",

View file

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

View file

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

View file

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