import _ from 'lodash' ; import React , { ReactElement } from 'react' ; import rison from 'rison-node' ; import { i18n } from '@kbn/i18n' ; import { GeoJsonProperties , Geometry , Position } from 'geojson' ; import { Filter } from '@kbn/es-query' ; import type { IndexPatternField , IndexPattern } from 'src/plugins/data/public' ; import { esFilters } from '../../../../../../../src/plugins/data/public' ; import { AbstractESSource } from '../es_source' ; import { getHttp , getSearchService , getSecurityService , getTimeFilter , } from '../../../kibana_services' ; import { addFieldToDSL , getField , hitsToGeoJson , isTotalHitsGreaterThan , PreIndexedShape , TotalHits , } from '../../../../common/elasticsearch_util' ; import { UpdateSourceEditor } from './update_source_editor' ; import { DEFAULT_MAX_BUCKETS_LIMIT , ES_GEO_FIELD_TYPE , FIELD_ORIGIN , GIS_API_PATH , MVT_GETTILE_API_PATH , SCALING_TYPES , SOURCE_TYPES , VECTOR_SHAPE_TYPE , } from '../../../../common/constants' ; import { getDataSourceLabel , getDataViewLabel } from '../../../../common/i18n_getters' ; import { getSourceFields } from '../../../index_pattern_util' ; import { loadIndexSettings } from './util/load_index_settings' ; import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants' ; import { ESDocField } from '../../fields/es_doc_field' ; import { registerSource } from '../source_registry' ; import { DataRequestMeta , ESSearchSourceDescriptor , Timeslice , VectorSourceRequestMeta , } from '../../../../common/descriptor_types' ; import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters' ; import { SortDirection , SortDirectionNumeric , TimeRange , } from '../../../../../../../src/plugins/data/common' ; import { ImmutableSourceProperty , SourceEditorArgs } from '../source' ; import { IField } from '../../fields/field' ; import { GeoJsonWithMeta , IMvtVectorSource , SourceStatus } from '../vector_source' ; import { ITooltipProperty } from '../../tooltips/tooltip_property' ; import { DataRequest } from '../../util/data_request' ; import { isValidStringConfig } from '../../util/valid_string_config' ; import { TopHitsUpdateSourceEditor } from './top_hits' ; import { getDocValueAndSourceFields , ScriptField } from './util/get_docvalue_source_fields' ; import { addFeatureToIndex , deleteFeatureFromIndex , getIsDrawLayer , getMatchingIndexes , } from './util/feature_edit' ; type ESSearchSourceSyncMeta = Pick < ESSearchSourceDescriptor , 'filterByMapBounds' | 'sortField' | 'sortOrder' | 'scalingType' | 'topHitsSplitField' | 'topHitsSize' > ; export function timerangeToTimeextent ( timerange ) { const timeRangeBounds = getTimeFilter ( ) . calculateBounds ( timerange ) ; return timeRangeBounds . min !== undefined && timeRangeBounds . max !== undefined ? { from : timeRangeBounds . min . valueOf ( ) , to : timeRangeBounds . max . valueOf ( ) , } : undefined ; } export const sourceTitle = i18n . translate ( 'xpack.maps.source.esSearchTitle' , { defaultMessage : 'Documents' , } ) ; export class ESSearchSource extends AbstractESSource implements IMvtVectorSource { readonly _descriptor : ESSearchSourceDescriptor ; protected readonly _tooltipFields : ESDocField [ ] ; static createDescriptor ( descriptor ) { const normalizedDescriptor = AbstractESSource . createDescriptor ( descriptor ) ; if ( ! isValidStringConfig ( normalizedDescriptor . geoField ) ) { throw new Error ( 'Cannot create an ESSearchSourceDescriptor without a geoField' ) ; } return { ... normalizedDescriptor , type : SOURCE_TYPES . ES_SEARCH , geoField : normalizedDescriptor . geoField ! , filterByMapBounds : typeof descriptor . filterByMapBounds === 'boolean' ? descriptor . filterByMapBounds : DEFAULT_FILTER_BY_MAP_BOUNDS , tooltipProperties : Array . isArray ( descriptor . tooltipProperties ) ? descriptor . tooltipProperties : [ ] , sortField : isValidStringConfig ( descriptor . sortField ) ? ( descriptor . sortField as string ) : '' , sortOrder : isValidStringConfig ( descriptor . sortOrder ) ? descriptor . sortOrder ! : SortDirection . desc , scalingType : isValidStringConfig ( descriptor . scalingType ) ? descriptor . scalingType ! : SCALING_TYPES . MVT , topHitsSplitField : isValidStringConfig ( descriptor . topHitsSplitField ) ? descriptor . topHitsSplitField ! : '' , topHitsSize : typeof descriptor . topHitsSize === 'number' && descriptor . topHitsSize > 0 ? descriptor . topHitsSize : 1 , } ; } constructor ( descriptor , inspectorAdapters ? ) { const sourceDescriptor = ESSearchSource . createDescriptor ( descriptor ) ; super ( sourceDescriptor , inspectorAdapters ) ; this . _descriptor = sourceDescriptor ; this . _tooltipFields = this . _descriptor . tooltipProperties ? this . _descriptor . tooltipProperties . map ( ( property ) => { return this . createField ( { fieldName : property } ) ; } ) : [ ] ; } createField ( { fieldName } ) { return new ESDocField ( { fieldName , source : this , origin : FIELD_ORIGIN . SOURCE , } ) ; } renderSourceSettingsEditor ( sourceEditorArgs ) { if ( this . _isTopHits ( ) ) { return ( < TopHitsUpdateSourceEditor source = { this } indexPatternId = { this . getIndexPatternId ( ) } onChange = { sourceEditorArgs . onChange } tooltipFields = { this . _tooltipFields } sortField = { this . _descriptor . sortField } sortOrder = { this . _descriptor . sortOrder } filterByMapBounds = { this . isFilterByMapBounds ( ) } topHitsSplitField = { this . _descriptor . topHitsSplitField } topHitsSize = { this . _descriptor . topHitsSize } / > ) ; } const getGeoField = ( ) => { return this . _getGeoField ( ) ; } ; return ( < UpdateSourceEditor source = { this } indexPatternId = { this . getIndexPatternId ( ) } getGeoField = { getGeoField } onChange = { sourceEditorArgs . onChange } tooltipFields = { this . _tooltipFields } sortField = { this . _descriptor . sortField } sortOrder = { this . _descriptor . sortOrder } scalingType = { this . _descriptor . scalingType } filterByMapBounds = { this . isFilterByMapBounds ( ) } hasJoins = { sourceEditorArgs . hasJoins } clearJoins = { sourceEditorArgs . clearJoins } / > ) ; } async getFields ( ) { try { const indexPattern = await this . getIndexPattern ( ) ; const fields = indexPattern . fields . filter ( ( field ) => { return field . aggregatable ; } ) ; return fields . map ( ( field ) : IField => { return this . createField ( { fieldName : field . name } ) ; } ) ; } catch ( error ) { return [ ] ; } } getFieldNames ( ) { return [ this . _descriptor . geoField ] ; } isMvt ( ) { return this . _descriptor . scalingType === SCALING_TYPES . MVT ; } async getImmutableProperties ( ) { let indexPatternName = this . getIndexPatternId ( ) ; let geoFieldType = '' ; try { const indexPattern = await this . getIndexPattern ( ) ; indexPatternName = indexPattern . title ; const geoField = await this . _getGeoField ( ) ; geoFieldType = geoField . type ; } catch ( error ) { } return [ { label : getDataSourceLabel ( ) , value : sourceTitle , } , { label : getDataViewLabel ( ) , value : indexPatternName , } , { label : i18n . translate ( 'xpack.maps.source.esSearch.geoFieldLabel' , { defaultMessage : 'Geospatial field' , } ) , value : this . _descriptor . geoField , } , { label : i18n . translate ( 'xpack.maps.source.esSearch.geoFieldTypeLabel' , { defaultMessage : 'Geospatial field type' , } ) , value : geoFieldType , } , ] ; } _buildEsSort ( ) { const { sortField , sortOrder } = this . _descriptor ; if ( ! sortField ) { throw new Error ( 'Cannot build sort' ) ; } return [ { [ sortField ] : { order : sortOrder , } , } , ] ; } async _getTopHits ( layerName , searchFilters , registerCancelCallback ) { const { topHitsSplitField : topHitsSplitFieldName , topHitsSize } = this . _descriptor ; if ( ! topHitsSplitFieldName ) { throw new Error ( 'Cannot _getTopHits without topHitsSplitField' ) ; } const indexPattern = await this . getIndexPattern ( ) ; const { docValueFields , sourceOnlyFields , scriptFields } = getDocValueAndSourceFields ( indexPattern , searchFilters . fieldNames , 'epoch_millis' ) ; const topHits = { size : topHitsSize , script_fields : scriptFields , docvalue_fields : docValueFields , } ; if ( this . _hasSort ( ) ) { topHits . sort = this . _buildEsSort ( ) ; } if ( sourceOnlyFields . length === 0 ) { topHits . _source = false ; } else { topHits . _source = { includes : sourceOnlyFields , } ; } const topHitsSplitField = getField ( indexPattern , topHitsSplitFieldName ) ; const cardinalityAgg = { precision_threshold : 1 } ; const termsAgg = { size : DEFAULT_MAX_BUCKETS_LIMIT , shard_size : DEFAULT_MAX_BUCKETS_LIMIT , } ; const searchSource = await this . makeSearchSource ( searchFilters , 0 ) ; searchSource . setField ( 'trackTotalHits' , false ) ; searchSource . setField ( 'aggs' , { totalEntities : { cardinality : addFieldToDSL ( cardinalityAgg , topHitsSplitField ) , } , entitySplit : { terms : addFieldToDSL ( termsAgg , topHitsSplitField ) , aggs : { entityHits : { top_hits : topHits , } , } , } , } ) ; if ( topHitsSplitField . type === 'string' ) { const entityIsNotEmptyFilter = esFilters . buildPhraseFilter ( topHitsSplitField , '' , indexPattern ) ; entityIsNotEmptyFilter . meta . negate = true ; searchSource . setField ( 'filter' , [ ... ( searchSource . getField ( 'filter' ) as Filter [ ] ) , entityIsNotEmptyFilter , ] ) ; } const resp = await this . _runEsQuery ( { requestId : this . getId ( ) , requestName : layerName , searchSource , registerCancelCallback , requestDescription : 'Elasticsearch document top hits request' , searchSessionId : searchFilters . searchSessionId , } ) ; const allHits = [ ] ; const entityBuckets = _ . get ( resp , 'aggregations.entitySplit.buckets' , [ ] ) ; const totalEntities = _ . get ( resp , 'aggregations.totalEntities.value' , 0 ) ; const areEntitiesTrimmed = entityBuckets . length >= DEFAULT_MAX_BUCKETS_LIMIT ; let areTopHitsTrimmed = false ; entityBuckets . forEach ( ( entityBucket ) => { const hits = _ . get ( entityBucket , 'entityHits.hits.hits' , [ ] ) ; allHits . push ( ... hits . reverse ( ) ) ; if ( isTotalHitsGreaterThan ( entityBucket . entityHits . hits . total , hits . length ) ) { areTopHitsTrimmed = true ; } } ) ; return { hits : allHits , meta : { areResultsTrimmed : areEntitiesTrimmed || areTopHitsTrimmed , areEntitiesTrimmed , entityCount : entityBuckets . length , totalEntities , } , } ; } async _getSearchHits ( layerName , searchFilters , registerCancelCallback ) { const indexPattern = await this . getIndexPattern ( ) ; const { docValueFields , sourceOnlyFields } = getDocValueAndSourceFields ( indexPattern , searchFilters . fieldNames , 'epoch_millis' ) ; const initialSearchContext = { docvalue_fields : docValueFields } ; const searchFiltersWithoutTimeslice = { ... searchFilters } ; delete searchFiltersWithoutTimeslice . timeslice ; const useSearchFiltersWithoutTimeslice = searchFilters . timeslice !== undefined && ( await this . canLoadAllDocuments ( searchFiltersWithoutTimeslice , registerCancelCallback ) ) ; const maxResultWindow = await this . getMaxResultWindow ( ) ; const searchSource = await this . makeSearchSource ( useSearchFiltersWithoutTimeslice ? searchFiltersWithoutTimeslice : searchFilters , maxResultWindow , initialSearchContext ) ; searchSource . setField ( 'trackTotalHits' , maxResultWindow + 1 ) ; searchSource . setField ( 'fieldsFromSource' , searchFilters . fieldNames ) ; if ( sourceOnlyFields . length === 0 ) { searchSource . setField ( 'source' , false ) ; } else { searchSource . setField ( 'source' , sourceOnlyFields ) ; } if ( this . _hasSort ( ) ) { searchSource . setField ( 'sort' , this . _buildEsSort ( ) ) ; } const resp = await this . _runEsQuery ( { requestId : this . getId ( ) , requestName : layerName , searchSource , registerCancelCallback , requestDescription : 'Elasticsearch document request' , searchSessionId : searchFilters . searchSessionId , } ) ; const isTimeExtentForTimeslice = searchFilters . timeslice !== undefined && ! useSearchFiltersWithoutTimeslice ; return { hits : resp . hits . hits . reverse ( ) , meta : { resultsCount : resp . hits . hits . length , areResultsTrimmed : isTotalHitsGreaterThan ( resp . hits . total , resp . hits . hits . length ) , timeExtent : isTimeExtentForTimeslice ? searchFilters . timeslice : timerangeToTimeextent ( searchFilters . timeFilters ) , isTimeExtentForTimeslice , } , } ; } _isTopHits ( ) { const { scalingType , topHitsSplitField } = this . _descriptor ; return ! ! ( scalingType === SCALING_TYPES . TOP_HITS && topHitsSplitField ) ; } async getSourceIndexList ( ) { await this . getIndexPattern ( ) ; if ( ! ( this . indexPattern && this . indexPattern . title ) ) { return [ ] ; } try { const { success , matchingIndexes } = await getMatchingIndexes ( this . indexPattern . title ) ; return success ? matchingIndexes : [ ] ; } catch ( e ) { return [ ] ; } } async supportsFeatureEditing ( ) { const matchingIndexes = await this . getSourceIndexList ( ) ; return matchingIndexes . length === 1 ; } async getDefaultFields ( ) { if ( ! ( await this . _isDrawingIndex ( ) ) ) { return { } ; } const user = await getSecurityService ( ) ?. authc . getCurrentUser ( ) ; const timestamp = new Date ( ) . toISOString ( ) ; return { created : { ... ( user ? { user : user . username } : { } ) , '@timestamp' : timestamp , } , } ; } async _isDrawingIndex ( ) { await this . getIndexPattern ( ) ; if ( ! ( this . indexPattern && this . indexPattern . title ) ) { return false ; } const { success , isDrawingIndex } = await getIsDrawLayer ( this . indexPattern . title ) ; return success && isDrawingIndex ; } _hasSort ( ) { const { sortField , sortOrder } = this . _descriptor ; return ! ! sortField && ! ! sortOrder ; } async getMaxResultWindow ( ) { const indexPattern = await this . getIndexPattern ( ) ; const indexSettings = await loadIndexSettings ( indexPattern . title ) ; return indexSettings . maxResultWindow ; } async getGeoJsonWithMeta ( layerName , searchFilters , registerCancelCallback , isRequestStillActive ) { const indexPattern = await this . getIndexPattern ( ) ; const { hits , meta } = this . _isTopHits ( ) ? await this . _getTopHits ( layerName , searchFilters , registerCancelCallback ) : await this . _getSearchHits ( layerName , searchFilters , registerCancelCallback ) ; const unusedMetaFields = indexPattern . metaFields . filter ( ( metaField ) => { return ! [ '_id' , '_index' ] . includes ( metaField ) ; } ) ; const flattenHit = ( hit ) => { const properties = indexPattern . flattenHit ( hit ) ; unusedMetaFields . forEach ( ( metaField ) => { delete properties [ metaField ] ; } ) ; return properties ; } ; const epochMillisFields = searchFilters . fieldNames . filter ( ( fieldName ) => { const field = getField ( indexPattern , fieldName ) ; return field . readFromDocValues && field . type === 'date' ; } ) ; let featureCollection ; try { const geoField = await this . _getGeoField ( ) ; featureCollection = hitsToGeoJson ( hits , flattenHit , geoField . name , geoField . type as ES_GEO_FIELD_TYPE , epochMillisFields ) ; } catch ( error ) { throw new Error ( i18n . translate ( 'xpack.maps.source.esSearch.convertToGeoJsonErrorMsg' , { defaultMessage : 'Unable to convert search response to geoJson feature collection, error: {errorMsg}' , values : { errorMsg : error . message } , } ) ) ; } return { data : featureCollection , meta , } ; } hasTooltipProperties ( ) { return this . _tooltipFields . length > 0 ; } async _loadTooltipProperties ( docId , index , indexPattern ) { if ( this . _tooltipFields . length === 0 ) { return { } ; } const { docValueFields } = getDocValueAndSourceFields ( indexPattern , this . _getTooltipPropertyNames ( ) , 'strict_date_optional_time' ) ; const initialSearchContext = { docvalue_fields : docValueFields } ; const searchService = getSearchService ( ) ; const searchSource = await searchService . searchSource . create ( initialSearchContext as object ) ; searchSource . setField ( 'trackTotalHits' , false ) ; searchSource . setField ( 'index' , indexPattern ) ; searchSource . setField ( 'size' , 1 ) ; const query = { language : 'kuery' , query : `_id:"${ docId }" and _index:"${ index }"` , } ; searchSource . setField ( 'query' , query ) ; searchSource . setField ( 'fieldsFromSource' , this . _getTooltipPropertyNames ( ) ) ; const resp = await searchSource . fetch ( { legacyHitsTotal : false } ) ; const hit = _ . get ( resp , 'hits.hits[0]' ) ; if ( ! hit ) { throw new Error ( i18n . translate ( 'xpack.maps.source.esSearch.loadTooltipPropertiesErrorMsg' , { defaultMessage : 'Unable to find document, _id: {docId}' , values : { docId } , } ) ) ; } const properties = indexPattern . flattenHit ( hit ) ; indexPattern . metaFields . forEach ( ( metaField ) => { if ( ! this . _getTooltipPropertyNames ( ) . includes ( metaField ) ) { delete properties [ metaField ] ; } } ) ; return properties ; } _getTooltipPropertyNames ( ) { return this . _tooltipFields . map ( ( field ) => field . getName ( ) ) ; } async getTooltipProperties ( properties ) { if ( properties === null ) { throw new Error ( 'properties cannot be null' ) ; } const indexPattern = await this . getIndexPattern ( ) ; const propertyValues = await this . _loadTooltipProperties ( properties . _id , properties . _index , indexPattern ) ; const tooltipProperties = this . _tooltipFields . map ( ( field ) => { const value = propertyValues [ field . getName ( ) ] ; return field . createTooltipProperty ( value ) ; } ) ; return Promise . all ( tooltipProperties ) ; } isFilterByMapBounds ( ) { if ( this . _descriptor . scalingType === SCALING_TYPES . CLUSTERS ) { return true ; } else if ( this . _descriptor . scalingType === SCALING_TYPES . MVT ) { return false ; } else { return ! ! this . _descriptor . filterByMapBounds ; } } async getLeftJoinFields ( ) { const indexPattern = await this . getIndexPattern ( ) ; return getSourceFields ( indexPattern . fields ) . map ( ( field ) : IField => { return this . createField ( { fieldName : field . name } ) ; } ) ; } async getSupportedShapeTypes ( ) { let geoFieldType ; try { const geoField = await this . _getGeoField ( ) ; geoFieldType = geoField . type ; } catch ( error ) { } if ( geoFieldType === ES_GEO_FIELD_TYPE . GEO_POINT ) { return [ VECTOR_SHAPE_TYPE . POINT ] ; } return [ VECTOR_SHAPE_TYPE . POINT , VECTOR_SHAPE_TYPE . LINE , VECTOR_SHAPE_TYPE . POLYGON ] ; } getSourceStatus ( sourceDataRequest ? ) { const meta = sourceDataRequest ? sourceDataRequest . getMeta ( ) : null ; if ( ! meta ) { return { tooltipContent : null , areResultsTrimmed : false , } ; } if ( this . _isTopHits ( ) ) { const entitiesFoundMsg = meta . areEntitiesTrimmed ? i18n . translate ( 'xpack.maps.esSearch.topHitsResultsTrimmedMsg' , { defaultMessage : `Results limited to first {entityCount} entities of ~{totalEntities}.` , values : { entityCount : meta . entityCount ?. toLocaleString ( ) , totalEntities : meta . totalEntities ?. toLocaleString ( ) , } , } ) : i18n . translate ( 'xpack.maps.esSearch.topHitsEntitiesCountMsg' , { defaultMessage : `Found {entityCount} entities.` , values : { entityCount : meta . entityCount ?. toLocaleString ( ) } , } ) ; const docsPerEntityMsg = i18n . translate ( 'xpack.maps.esSearch.topHitsSizeMsg' , { defaultMessage : `Showing top {topHitsSize} documents per entity.` , values : { topHitsSize : this . _descriptor . topHitsSize ?. toLocaleString ( ) } , } ) ; return { tooltipContent : `${ entitiesFoundMsg } ${ docsPerEntityMsg }` , areResultsTrimmed : ! ! meta . areEntitiesTrimmed , } ; } if ( meta . areResultsTrimmed ) { return { tooltipContent : i18n . translate ( 'xpack.maps.esSearch.resultsTrimmedMsg' , { defaultMessage : `Results limited to first {count} documents.` , values : { count : meta . resultsCount ?. toLocaleString ( ) } , } ) , areResultsTrimmed : true , } ; } return { tooltipContent : i18n . translate ( 'xpack.maps.esSearch.featureCountMsg' , { defaultMessage : `Found {count} documents.` , values : { count : meta . resultsCount ?. toLocaleString ( ) } , } ) , areResultsTrimmed : false , } ; } getSyncMeta ( ) { return { filterByMapBounds : this . _descriptor . filterByMapBounds , sortField : this . _descriptor . sortField , sortOrder : this . _descriptor . sortOrder , scalingType : this . _descriptor . scalingType , topHitsSplitField : this . _descriptor . topHitsSplitField , topHitsSize : this . _descriptor . topHitsSize , } ; } async getPreIndexedShape ( properties ) { if ( properties === null ) { return null ; } const geoField = await this . _getGeoField ( ) ; return { index : properties . _index , id : properties . _id , path : geoField . name , } ; } getJoinsDisabledReason ( ) { let reason ; if ( this . _descriptor . scalingType === SCALING_TYPES . CLUSTERS ) { reason = i18n . translate ( 'xpack.maps.source.esSearch.joinsDisabledReason' , { defaultMessage : 'Joins are not supported when scaling by clusters' , } ) ; } else if ( this . _descriptor . scalingType === SCALING_TYPES . MVT ) { reason = i18n . translate ( 'xpack.maps.source.esSearch.joinsDisabledReasonMvt' , { defaultMessage : 'Joins are not supported when scaling by vector tiles' , } ) ; } else { reason = null ; } return reason ; } async _getEditableIndex ( ) { const indexList = await this . getSourceIndexList ( ) ; if ( indexList . length === 0 ) { throw new Error ( i18n . translate ( 'xpack.maps.source.esSearch.indexZeroLengthEditError' , { defaultMessage : `Your data view doesn't point to any indices.` , } ) ) ; } if ( indexList . length > 1 ) { throw new Error ( i18n . translate ( 'xpack.maps.source.esSearch.indexOverOneLengthEditError' , { defaultMessage : `Your data view points to multiple indices. Only one index is allowed per data view.` , } ) ) ; } return indexList [ 0 ] ; } async addFeature ( geometry , defaultFields ) { const index = await this . _getEditableIndex ( ) ; await addFeatureToIndex ( index , geometry , this . getGeoFieldName ( ) , defaultFields ) ; } async deleteFeature ( featureId ) { const index = await this . _getEditableIndex ( ) ; await deleteFeatureFromIndex ( index , featureId ) ; } getTileSourceLayer ( ) { return 'hits' ; } async getTileUrl ( searchFilters , refreshToken ) { const indexPattern = await this . getIndexPattern ( ) ; const indexSettings = await loadIndexSettings ( indexPattern . title ) ; const { docValueFields , sourceOnlyFields } = getDocValueAndSourceFields ( indexPattern , searchFilters . fieldNames , 'epoch_millis' ) ; const initialSearchContext = { docvalue_fields : docValueFields } ; const searchSource = await this . makeSearchSource ( searchFilters , indexSettings . maxResultWindow , initialSearchContext ) ; searchSource . setField ( 'fieldsFromSource' , searchFilters . fieldNames ) ; if ( sourceOnlyFields . length === 0 ) { searchSource . setField ( 'source' , false ) ; } else { searchSource . setField ( 'source' , sourceOnlyFields ) ; } if ( this . _hasSort ( ) ) { searchSource . setField ( 'sort' , this . _buildEsSort ( ) ) ; } const dsl = searchSource . getSearchRequestBody ( ) ; const risonDsl = rison . encode ( dsl ) ; const mvtUrlServicePath = getHttp ( ) . basePath . prepend ( `/${ GIS_API_PATH }/${ MVT_GETTILE_API_PATH }/{z}/{x}/{y}.pbf` ) ; return `${ mvtUrlServicePath }\
?geometryFieldName=${ this . _descriptor . geoField }\
&index=${ indexPattern . title }\
&requestBody=${ risonDsl }\
&token=${ refreshToken }` ; } async getTimesliceMaskFieldName ( ) { if ( this . _isTopHits ( ) || this . _descriptor . scalingType === SCALING_TYPES . MVT ) { return null ; } try { const indexPattern = await this . getIndexPattern ( ) ; return indexPattern . timeFieldName ? indexPattern . timeFieldName : null ; } catch ( e ) { return null ; } } getUpdateDueToTimeslice ( prevMeta , timeslice ? ) { if ( this . _isTopHits ( ) || this . _descriptor . scalingType === SCALING_TYPES . MVT ) { return true ; } if ( prevMeta . timeExtent === undefined || prevMeta . areResultsTrimmed === undefined || prevMeta . areResultsTrimmed ) { return true ; } const isTimeExtentForTimeslice = prevMeta . isTimeExtentForTimeslice !== undefined ? prevMeta . isTimeExtentForTimeslice : false ; if ( ! timeslice ) { return isTimeExtentForTimeslice ? true : false ; } const isWithin = isTimeExtentForTimeslice ? timeslice . from >= prevMeta . timeExtent . from && timeslice . to <= prevMeta . timeExtent . to : true ; return ! isWithin ; } async canLoadAllDocuments ( searchFilters , registerCancelCallback ) { const abortController = new AbortController ( ) ; registerCancelCallback ( ( ) => abortController . abort ( ) ) ; const maxResultWindow = await this . getMaxResultWindow ( ) ; const searchSource = await this . makeSearchSource ( searchFilters , 0 ) ; searchSource . setField ( 'trackTotalHits' , maxResultWindow + 1 ) ; const resp = await searchSource . fetch ( { abortSignal : abortController . signal , sessionId : searchFilters . searchSessionId , legacyHitsTotal : false , } ) ; return ! isTotalHitsGreaterThan ( resp . hits . total as unknown as TotalHits , maxResultWindow ) ; } } registerSource ( { ConstructorFunction : ESSearchSource , type : SOURCE_TYPES . ES_SEARCH , } ) ;