name description import { setWith } from '@elastic/safer-lodash-set' ; import { uniqueId , uniq , extend , pick , difference , omit , isObject , keys , isFunction } from 'lodash' ; import { normalizeSortRequest } from './normalize_sort_request' ; import { filterDocvalueFields } from './filter_docvalue_fields' ; import { fieldWildcardFilter } from '../../../../opensearch_dashboards_utils/common' ; import { IIndexPattern } from '../../index_patterns' ; import { IOpenSearchSearchRequest , IOpenSearchSearchResponse , ISearchOptions } from '../..' ; import { IOpenSearchDashboardsSearchRequest , IOpenSearchDashboardsSearchResponse } from '../types' ; import { ISearchSource , SearchSourceOptions , SearchSourceFields } from './types' ; import { FetchHandlers , RequestFailure , getSearchParamsFromRequest , SearchRequest } from './fetch' ; import { getOpenSearchQueryConfig , buildOpenSearchQuery , Filter , UI_SETTINGS , } from '../../../common' ; import { getHighlightRequest } from '../../../common/field_formats' ; import { fetchSoon } from './legacy' ; import { extractReferences } from './extract_references' ; internal export const searchSourceRequiredUiSettings = [ 'dateFormat:tz' , UI_SETTINGS . COURIER_BATCH_SEARCHES , UI_SETTINGS . COURIER_CUSTOM_REQUEST_PREFERENCE , UI_SETTINGS . COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX , UI_SETTINGS . COURIER_MAX_CONCURRENT_SHARD_REQUESTS , UI_SETTINGS . COURIER_SET_REQUEST_PREFERENCE , UI_SETTINGS . DOC_HIGHLIGHT , UI_SETTINGS . META_FIELDS , UI_SETTINGS . QUERY_ALLOW_LEADING_WILDCARDS , UI_SETTINGS . QUERY_STRING_OPTIONS , UI_SETTINGS . SEARCH_INCLUDE_FROZEN , UI_SETTINGS . SORT_OPTIONS , ] ; export interface SearchSourceDependencies extends FetchHandlers { search : < SearchStrategyRequest extends IOpenSearchDashboardsSearchRequest = IOpenSearchSearchRequest , SearchStrategyResponse extends IOpenSearchDashboardsSearchResponse = IOpenSearchSearchResponse > ( request : SearchStrategyRequest , options : ISearchOptions ) => Promise < SearchStrategyResponse > ; } public export class SearchSource { private id : string = uniqueId ( 'data_source' ) ; private searchStrategyId ? : string ; private parent ? : SearchSource ; private requestStartHandlers : Array < ( searchSource : SearchSource , options ? : ISearchOptions ) => Promise < unknown > > = [ ] ; private inheritOptions : SearchSourceOptions = { } ; public history : SearchRequest [ ] = [ ] ; private fields : SearchSourceFields ; private readonly dependencies : SearchSourceDependencies ; constructor ( fields = { } , dependencies ) { this . fields = fields ; this . dependencies = dependencies ; } /** ***
     * PUBLIC API
     *****/ param searchStrategyId setPreferredSearchStrategyId ( searchStrategyId ) { this . searchStrategyId = searchStrategyId ; } param field param value setField < K extends keyof SearchSourceFields > ( field , value ) { if ( value == null ) { delete this . fields [ field ] ; } else { this . fields [ field ] = value ; } return this ; } private param newFields setFields ( newFields ) { this . fields = newFields ; return this ; } /**
     * returns search source id
     */ getId ( ) { return this . id ; } /**
     * returns all search source fields
     */ getFields ( ) { return { ... this . fields } ; } /**
     * Gets a single field from the fields
     */ getField < K extends keyof SearchSourceFields > ( field , recurse = true ) { if ( ! recurse || this . fields [ field ] !== void 0 ) { return this . fields [ field ] ; } const parent = this . getParent ( ) ; return parent && parent . getField ( field ) ; } /**
     * Get the field from our own fields, don't traverse up the chain
     */ getOwnField < K extends keyof SearchSourceFields > ( field ) { return this . getField ( field , false ) ; } deprecated create ( ) { return new SearchSource ( { } , this . dependencies ) ; } /**
     * creates a copy of this search source (without its children)
     */ createCopy ( ) { const newSearchSource = new SearchSource ( { } , this . dependencies ) ; newSearchSource . setFields ( { ... this . fields } ) ; newSearchSource . setField ( 'index' , this . getField ( 'index' ) ) ; newSearchSource . setParent ( this . getParent ( ) ) ; return newSearchSource ; } param options createChild ( options = { } ) { const childSearchSource = new SearchSource ( { } , this . dependencies ) ; childSearchSource . setParent ( this , options ) ; return childSearchSource ; } param { SearchSource } parent param { SearchSourceOptions } options return { this } setParent ( parent ? , options = { } ) { this . parent = parent as SearchSource ; this . inheritOptions = options ; return this ; } return { undefined | searchSource } getParent ( ) { return this . parent ; } async async fetch ( options = { } ) { const { getConfig } = this . dependencies ; await this . requestIsStarting ( options ) ; const searchRequest = await this . flatten ( ) ; this . history = [ searchRequest ] ; let response ; if ( getConfig ( UI_SETTINGS . COURIER_BATCH_SEARCHES ) ) { response = await this . legacyFetch ( searchRequest , options ) ; } else { response = await this . fetchSearch ( searchRequest , options ) ; } if ( ( response as any ) . error ) { throw new RequestFailure ( null , response ) ; } return response ; } param { Function } handler return { undefined } onRequestStart ( handler ) { this . requestStartHandlers . push ( handler ) ; } /**
     * Returns body contents of the search request, often referred as query DSL.
     */ async getSearchRequestBody ( ) { const searchRequest = await this . flatten ( ) ; return searchRequest . body ; } return { undefined } destroy ( ) { this . requestStartHandlers . length = 0 ; } /** ****
     * PRIVATE APIS
     ******/ return { Promise < SearchResponse < unknown > > } private fetchSearch ( searchRequest , options ) { const { search , getConfig , onResponse } = this . dependencies ; const params = getSearchParamsFromRequest ( searchRequest , { getConfig , } ) ; return search ( { params , indexType : searchRequest . indexType } , options ) . then ( ( { rawResponse } ) => onResponse ( searchRequest , rawResponse ) ) ; } return { Promise < SearchResponse < unknown > > } private async legacyFetch ( searchRequest , options ) { const { getConfig , legacy , onResponse } = this . dependencies ; return await fetchSoon ( searchRequest , { ... ( this . searchStrategyId && { searchStrategyId : this . searchStrategyId } ) , ... options , } , { getConfig , onResponse , legacy , } ) ; } param options return { Promise < undefined > } private requestIsStarting ( options = { } ) { const handlers = [ ... this . requestStartHandlers ] ; if ( this . inheritOptions . callParentStartHandlers ) { let searchSource = this . getParent ( ) ; while ( searchSource ) { handlers . push ( ... searchSource . requestStartHandlers ) ; searchSource = searchSource . getParent ( ) ; } } return Promise . all ( handlers . map ( ( fn ) => fn ( this , options ) ) ) ; } param { object } data param { * } val param { * } key return { undefined } private mergeProp < K extends keyof SearchSourceFields > ( data , val , key ) { val = typeof val === 'function' ? val ( this ) : val ; if ( val == null || ! key ) return ; const addToRoot = ( rootKey , value ) => { data [ rootKey ] = value ; } ; /**
         * Add the key and val to the body of the request
         */ const addToBody = ( bodyKey , value ) => { if ( data . body [ bodyKey ] == null ) { data . body [ bodyKey ] = value ; } } ; const { getConfig } = this . dependencies ; switch ( key ) { case 'filter' : return addToRoot ( 'filters' , ( data . filters || [ ] ) . concat ( val ) ) ; case 'query' : return addToRoot ( key , ( data [ key ] || [ ] ) . concat ( val ) ) ; case 'fields' : const fields = uniq ( ( data [ key ] || [ ] ) . concat ( val ) ) ; return addToRoot ( key , fields ) ; case 'index' : case 'type' : case 'highlightAll' : return key && data [ key ] == null && addToRoot ( key , val ) ; case 'searchAfter' : return addToBody ( 'search_after' , val ) ; case 'source' : return addToBody ( '_source' , val ) ; case 'sort' : const sort = normalizeSortRequest ( val , this . getField ( 'index' ) , getConfig ( UI_SETTINGS . SORT_OPTIONS ) ) ; return addToBody ( key , sort ) ; default : return addToBody ( key , val ) ; } } returns { Promise } resolved private mergeProps ( root = this , searchRequest = { body : { } } ) { Object . entries ( this . fields ) . forEach ( ( [ key , value ] ) => { this . mergeProp ( searchRequest , value , key as keyof SearchSourceFields ) ; } ) ; if ( this . parent ) { this . parent . mergeProps ( root , searchRequest ) ; } return searchRequest ; } private getIndexType ( index ? ) { if ( this . searchStrategyId ) { return this . searchStrategyId === 'default' ? undefined : this . searchStrategyId ; } else { return index ?. type ; } } private flatten ( ) { const searchRequest = this . mergeProps ( ) ; searchRequest . body = searchRequest . body || { } ; const { body , index , fields , query , filters , highlightAll } = searchRequest ; searchRequest . indexType = this . getIndexType ( index ) ; const computedFields = index ? index . getComputedFields ( ) : { } ; body . stored_fields = computedFields . storedFields ; body . script_fields = body . script_fields || { } ; extend ( body . script_fields , computedFields . scriptFields ) ; const defaultDocValueFields = computedFields . docvalueFields ? computedFields . docvalueFields : [ ] ; body . docvalue_fields = body . docvalue_fields || defaultDocValueFields ; if ( ! body . hasOwnProperty ( '_source' ) && index ) { body . _source = index . getSourceFiltering ( ) ; } const { getConfig } = this . dependencies ; if ( body . _source ) { const filter = fieldWildcardFilter ( body . _source . excludes , getConfig ( UI_SETTINGS . META_FIELDS ) ) ; body . docvalue_fields = body . docvalue_fields . filter ( ( docvalueField ) => filter ( docvalueField . field ) ) ; } if ( fields ) { body . docvalue_fields = filterDocvalueFields ( body . docvalue_fields , fields ) ; body . script_fields = pick ( body . script_fields , fields ) ; const remainingFields = difference ( fields , keys ( body . script_fields ) ) ; body . stored_fields = remainingFields ; setWith ( body , '_source.includes' , remainingFields , ( nsValue ) => isObject ( nsValue ) ? { } : nsValue ) ; } const opensearchQueryConfigs = getOpenSearchQueryConfig ( { get : getConfig } ) ; body . query = buildOpenSearchQuery ( index , query , filters , opensearchQueryConfigs ) ; if ( highlightAll && body . query ) { body . highlight = getHighlightRequest ( body . query , getConfig ( UI_SETTINGS . DOC_HIGHLIGHT ) ) ; delete searchRequest . highlightAll ; } return searchRequest ; } /**
     * serializes search source fields (which can later be passed to  ISearchStartSearchSource )
      public getSerializedFields ( ) { const { filter : originalFilters , ... searchSourceFields } = omit ( this . getFields ( ) , [ 'sort' , 'size' , ] ) ; let serializedSearchSourceFields = { ... searchSourceFields , index : ( searchSourceFields . index ? searchSourceFields . index . id : undefined ) as any , } ; if ( originalFilters ) { const filters = this . getFilters ( originalFilters ) ; serializedSearchSourceFields = { ... serializedSearchSourceFields , filter : filters , } ; } return serializedSearchSourceFields ; } public public serialize ( ) { const [ searchSourceFields , references ] = extractReferences ( this . getSerializedFields ( ) ) ; return { searchSourceJSON : JSON . stringify ( searchSourceFields ) , references } ; } private getFilters ( filterField ) { if ( ! filterField ) { return [ ] ; } if ( Array . isArray ( filterField ) ) { return filterField ; } if ( isFunction ( filterField ) ) { return this . getFilters ( filterField ( ) ) ; } return [ filterField ] ; } }