import _ from 'lodash' ; import { i18n } from '@kbn/i18n' ; import { ISearchSource , Query } from 'src/plugins/data/public' ; import { AGG_TYPE , DEFAULT_MAX_BUCKETS_LIMIT , FIELD_ORIGIN , SOURCE_TYPES , } from '../../../../common/constants' ; import { getJoinAggKey } from '../../../../common/get_agg_key' ; import { ESDocField } from '../../fields/es_doc_field' ; import { AbstractESAggSource } from '../es_agg_source' ; import { getField , addFieldToDSL , extractPropertiesFromBucket , BucketProperties , } from '../../../../common/elasticsearch_util' ; import { ESTermSourceDescriptor , VectorJoinSourceRequestMeta , } from '../../../../common/descriptor_types' ; import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters' ; import { PropertiesMap } from '../../../../common/elasticsearch_util' ; import { isValidStringConfig } from '../../util/valid_string_config' ; import { ITermJoinSource } from '../term_join_source' ; import { IField } from '../../fields/field' ; const TERMS_AGG_NAME = 'join' ; const TERMS_BUCKET_KEYS_TO_IGNORE = [ 'key' , 'doc_count' ] ; type ESTermSourceSyncMeta = Pick < ESTermSourceDescriptor , 'indexPatternId' | 'size' | 'term' > ; export function extractPropertiesMap ( rawEsData , countPropertyName ) { const propertiesMap = new Map < string , BucketProperties > ( ) ; const buckets = _ . get ( rawEsData , [ 'aggregations' , TERMS_AGG_NAME , 'buckets' ] , [ ] ) ; buckets . forEach ( ( termBucket ) => { const properties = extractPropertiesFromBucket ( termBucket , TERMS_BUCKET_KEYS_TO_IGNORE ) ; if ( countPropertyName ) { properties [ countPropertyName ] = termBucket . doc_count ; } propertiesMap . set ( termBucket . key . toString ( ) , properties ) ; } ) ; return propertiesMap ; } export class ESTermSource extends AbstractESAggSource implements ITermJoinSource { static type = SOURCE_TYPES . ES_TERM_SOURCE ; static createDescriptor ( descriptor ) { const normalizedDescriptor = AbstractESAggSource . createDescriptor ( descriptor ) ; if ( ! isValidStringConfig ( descriptor . term ) ) { throw new Error ( 'Cannot create an ESTermSource without a term' ) ; } return { ... normalizedDescriptor , indexPatternTitle : descriptor . indexPatternTitle ? descriptor . indexPatternTitle : descriptor . indexPatternId , term : descriptor . term ! , type : SOURCE_TYPES . ES_TERM_SOURCE , } ; } private readonly _termField : ESDocField ; readonly _descriptor : ESTermSourceDescriptor ; constructor ( descriptor , inspectorAdapters ? ) { const sourceDescriptor = ESTermSource . createDescriptor ( descriptor ) ; super ( sourceDescriptor , inspectorAdapters ) ; this . _descriptor = sourceDescriptor ; this . _termField = new ESDocField ( { fieldName : this . _descriptor . term , source : this , origin : this . getOriginForField ( ) , } ) ; } hasCompleteConfig ( ) { return _ . has ( this . _descriptor , 'indexPatternId' ) && _ . has ( this . _descriptor , 'term' ) ; } getTermField ( ) { return this . _termField ; } getOriginForField ( ) { return FIELD_ORIGIN . JOIN ; } getWhereQuery ( ) { return this . _descriptor . whereQuery ; } getAggKey ( aggType , fieldName ? ) { return getJoinAggKey ( { aggType , aggFieldName : fieldName , rightSourceId : this . _descriptor . id , } ) ; } getAggLabel ( aggType , fieldLabel ) { return aggType === AGG_TYPE . COUNT ? i18n . translate ( 'xpack.maps.source.esJoin.countLabel' , { defaultMessage : `Count of {indexPatternTitle}` , values : { indexPatternTitle : this . _descriptor . indexPatternTitle } , } ) : super . getAggLabel ( aggType , fieldLabel ) ; } async getPropertiesMap ( searchFilters , leftSourceName , leftFieldName , registerCancelCallback ) { if ( ! this . hasCompleteConfig ( ) ) { return new Map < string , BucketProperties > ( ) ; } const indexPattern = await this . getIndexPattern ( ) ; const searchSource = await this . makeSearchSource ( searchFilters , 0 ) ; searchSource . setField ( 'trackTotalHits' , false ) ; const termsField = getField ( indexPattern , this . _termField . getName ( ) ) ; const termsAgg = { size : this . _descriptor . size !== undefined ? this . _descriptor . size : DEFAULT_MAX_BUCKETS_LIMIT , } ; searchSource . setField ( 'aggs' , { [ TERMS_AGG_NAME ] : { terms : addFieldToDSL ( termsAgg , termsField ) , aggs : { ... this . getValueAggsDsl ( indexPattern ) } , } , } ) ; const rawEsData = await this . _runEsQuery ( { requestId : this . getId ( ) , requestName : `${ this . _descriptor . indexPatternTitle }.${ this . _termField . getName ( ) }` , searchSource , registerCancelCallback , requestDescription : i18n . translate ( 'xpack.maps.source.esJoin.joinDescription' , { defaultMessage : `Elasticsearch terms aggregation request, left source: {leftSource}, right source: {rightSource}` , values : { leftSource : `${ leftSourceName }:${ leftFieldName }` , rightSource : `${ this . _descriptor . indexPatternTitle }:${ this . _termField . getName ( ) }` , } , } ) , searchSessionId : searchFilters . searchSessionId , } ) ; const countPropertyName = this . getAggKey ( AGG_TYPE . COUNT ) ; return extractPropertiesMap ( rawEsData , countPropertyName ) ; } isFilterByMapBounds ( ) { return false ; } async getDisplayName ( ) { return `es_table ${ this . getIndexPatternId ( ) }` ; } getFieldNames ( ) { return this . getMetricFields ( ) . map ( ( esAggMetricField ) => esAggMetricField . getName ( ) ) ; } getSyncMeta ( ) { return { indexPatternId : this . _descriptor . indexPatternId , size : this . _descriptor . size , term : this . _descriptor . term , } ; } getRightFields ( ) { return this . getMetricFields ( ) ; } }