/* * 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 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ import { ValuesType } from 'utility-types'; import { estypes } from '@elastic/elasticsearch'; type InvalidAggregationRequest = unknown; // ensures aggregations work with requests where aggregation options are a union type, // e.g. { transaction_groups: { composite: any } | { terms: any } }. // Union keys are not included in keyof, but extends iterates over the types in a union. type ValidAggregationKeysOf> = T extends T ? keyof T : never; type KeyOfSource = Record< keyof T, (T extends Record ? null : never) | string | number >; type KeysOfSources = T extends [infer U, ...infer V] ? KeyOfSource & KeysOfSources : T extends Array ? KeyOfSource : {}; type CompositeKeysOf< TAggregationContainer extends estypes.AggregationContainer > = TAggregationContainer extends { composite: { sources: [...infer TSource] }; } ? KeysOfSources : unknown; type Source = estypes.SourceFilter | boolean | estypes.Fields; type ValueTypeOfField = T extends Record ? ValuesType : T extends string[] | number[] ? ValueTypeOfField> : T extends { field: estypes.Field } ? T['field'] : T extends string | number ? T : never; type MaybeArray = T | T[]; type Fields = MaybeArray; type DocValueFields = MaybeArray; export type SearchHit< TSource extends any = unknown, TFields extends Fields | undefined = undefined, TDocValueFields extends DocValueFields | undefined = undefined > = Omit & (TSource extends false ? {} : { _source: TSource }) & (TFields extends estypes.Fields ? { fields: Partial, unknown[]>>; } : {}) & (TDocValueFields extends DocValueFields ? { fields: Partial, unknown[]>>; } : {}); type HitsOf< TOptions extends | { _source?: Source; fields?: Fields; docvalue_fields?: DocValueFields } | undefined, TDocument extends unknown > = Array< SearchHit< TOptions extends { _source: false } ? undefined : TDocument, TOptions extends { fields: estypes.Fields } ? TOptions['fields'] : undefined, TOptions extends { docvalue_fields: DocValueFields } ? TOptions['docvalue_fields'] : undefined > >; type AggregationTypeName = Exclude; type AggregationMap = Partial>; type TopLevelAggregationRequest = Pick; type MaybeKeyed< TAggregationContainer, TBucket, TKeys extends string = string > = TAggregationContainer extends Record ? Record : { buckets: TBucket[] }; export type AggregateOf< TAggregationContainer extends estypes.AggregationContainer, TDocument > = (Record & { adjacency_matrix: { buckets: Array< { key: string; doc_count: number; } & SubAggregateOf >; }; auto_date_histogram: { interval: string; buckets: Array< { key: number; key_as_string: string; doc_count: number; } & SubAggregateOf >; }; avg: { value: number | null; value_as_string?: string; }; avg_bucket: { value: number | null; }; boxplot: { min: number | null; max: number | null; q1: number | null; q2: number | null; q3: number | null; }; bucket_script: { value: unknown; }; cardinality: { value: number; }; children: { doc_count: number; } & SubAggregateOf; composite: { after_key: CompositeKeysOf; buckets: Array< { doc_count: number; key: CompositeKeysOf; } & SubAggregateOf >; }; cumulative_cardinality: { value: number; }; cumulative_sum: { value: number; }; date_histogram: MaybeKeyed< TAggregationContainer, { key: number; key_as_string: string; doc_count: number; } & SubAggregateOf >; date_range: MaybeKeyed< TAggregationContainer, Partial<{ from: string | number; from_as_string: string }> & Partial<{ to: string | number; to_as_string: string }> & { doc_count: number; key: string; } >; derivative: | { value: number | null; } | undefined; extended_stats: { count: number; min: number | null; max: number | null; avg: number | null; sum: number; sum_of_squares: number | null; variance: number | null; variance_population: number | null; variance_sampling: number | null; std_deviation: number | null; std_deviation_population: number | null; std_deviation_sampling: number | null; std_deviation_bounds: { upper: number | null; lower: number | null; upper_population: number | null; lower_population: number | null; upper_sampling: number | null; lower_sampling: number | null; }; } & ( | { min_as_string: string; max_as_string: string; avg_as_string: string; sum_of_squares_as_string: string; variance_population_as_string: string; variance_sampling_as_string: string; std_deviation_as_string: string; std_deviation_population_as_string: string; std_deviation_sampling_as_string: string; std_deviation_bounds_as_string: { upper: string; lower: string; upper_population: string; lower_population: string; upper_sampling: string; lower_sampling: string; }; } | {} ); extended_stats_bucket: { count: number; min: number | null; max: number | null; avg: number | null; sum: number | null; sum_of_squares: number | null; variance: number | null; variance_population: number | null; variance_sampling: number | null; std_deviation: number | null; std_deviation_population: number | null; std_deviation_sampling: number | null; std_deviation_bounds: { upper: number | null; lower: number | null; upper_population: number | null; lower_population: number | null; upper_sampling: number | null; lower_sampling: number | null; }; }; filter: { doc_count: number; } & SubAggregateOf; filters: { buckets: TAggregationContainer extends { filters: { filters: any[] } } ? Array< { doc_count: number; } & SubAggregateOf > : TAggregationContainer extends { filters: { filters: Record } } ? { [key in keyof TAggregationContainer['filters']['filters']]: { doc_count: number; } & SubAggregateOf; } & (TAggregationContainer extends { filters: { other_bucket_key: infer TOtherBucketKey } } ? Record< TOtherBucketKey & string, { doc_count: number } & SubAggregateOf > : unknown) & (TAggregationContainer extends { filters: { other_bucket: true } } ? { _other: { doc_count: number } & SubAggregateOf } : unknown) : unknown; }; geo_bounds: { top_left: { lat: number | null; lon: number | null; }; bottom_right: { lat: number | null; lon: number | null; }; }; geo_centroid: { count: number; location: { lat: number; lon: number; }; }; geo_distance: MaybeKeyed< TAggregationContainer, { from: number; to?: number; doc_count: number; } & SubAggregateOf >; geo_hash: { buckets: Array< { doc_count: number; key: string; } & SubAggregateOf >; }; geotile_grid: { buckets: Array< { doc_count: number; key: string; } & SubAggregateOf >; }; global: { doc_count: number; } & SubAggregateOf; histogram: MaybeKeyed< TAggregationContainer, { key: number; doc_count: number; } & SubAggregateOf >; ip_range: MaybeKeyed< TAggregationContainer, { key: string; from?: string; to?: string; doc_count: number; }, TAggregationContainer extends { ip_range: { ranges: Array } } ? TRangeType extends { key: infer TKeys } ? TKeys : string : string >; inference: { value: number; prediction_probability: number; prediction_score: number; }; max: { value: number | null; value_as_string?: string; }; max_bucket: { value: number | null; }; min: { value: number | null; value_as_string?: string; }; min_bucket: { value: number | null; }; median_absolute_deviation: { value: number | null; }; moving_avg: | { value: number | null; } | undefined; moving_fn: { value: number | null; }; moving_percentiles: TAggregationContainer extends Record ? Array<{ key: number; value: number | null }> : Record | undefined; missing: { doc_count: number; } & SubAggregateOf; multi_terms: { doc_count_error_upper_bound: number; sum_other_doc_count: number; buckets: Array< { doc_count: number; key: string[]; } & SubAggregateOf >; }; nested: { doc_count: number; } & SubAggregateOf; normalize: { value: number | null; // TODO: should be perhaps based on input? ie when `format` is specified value_as_string?: string; }; parent: { doc_count: number; } & SubAggregateOf; percentiles: { values: TAggregationContainer extends Record ? Array<{ key: number; value: number | null }> : Record; }; percentile_ranks: { values: TAggregationContainer extends Record ? Array<{ key: number; value: number | null }> : Record; }; percentiles_bucket: { values: TAggregationContainer extends Record ? Array<{ key: number; value: number | null }> : Record; }; range: MaybeKeyed< TAggregationContainer, { key: string; from?: number; to?: number; doc_count: number; }, TAggregationContainer extends { range: { ranges: Array } } ? TRangeType extends { key: infer TKeys } ? TKeys : string : string >; rare_terms: Array< { key: string | number; doc_count: number; } & SubAggregateOf >; rate: { value: number | null; }; reverse_nested: { doc_count: number; } & SubAggregateOf; sampler: { doc_count: number; } & SubAggregateOf; scripted_metric: { value: unknown; }; serial_diff: { value: number | null; // TODO: should be perhaps based on input? ie when `format` is specified value_as_string?: string; }; significant_terms: { doc_count: number; bg_count: number; buckets: Array< { key: string | number; score: number; doc_count: number; bg_count: number; } & SubAggregateOf >; }; significant_text: { doc_count: number; buckets: Array<{ key: string; doc_count: number; score: number; bg_count: number; }>; }; stats: { count: number; min: number | null; max: number | null; avg: number | null; sum: number; } & ( | { min_as_string: string; max_as_string: string; avg_as_string: string; sum_as_string: string; } | {} ); stats_bucket: { count: number; min: number | null; max: number | null; avg: number | null; sum: number; }; string_stats: { count: number; min_length: number | null; max_length: number | null; avg_length: number | null; entropy: number | null; distribution: Record; }; sum: { value: number | null; value_as_string?: string; }; sum_bucket: { value: number | null; }; terms: { doc_count_error_upper_bound: number; sum_other_doc_count: number; buckets: Array< { doc_count: number; key: string | number; } & SubAggregateOf >; }; top_hits: { hits: { total: { value: number; relation: 'eq' | 'gte'; }; max_score: number | null; hits: TAggregationContainer extends { top_hits: estypes.TopHitsAggregation } ? HitsOf : estypes.HitsMetadata; }; }; top_metrics: { top: Array<{ sort: number[] | string[]; metrics: Record< TAggregationContainer extends Record }> ? TKeys : string, string | number | null >; }>; }; weighted_avg: { value: number | null }; value_count: { value: number; }; // t_test: {} not defined })[ValidAggregationKeysOf & AggregationTypeName]; type AggregateOfMap = { [TAggregationName in keyof TAggregationMap]: TAggregationMap[TAggregationName] extends estypes.AggregationContainer ? AggregateOf : never; // using never means we effectively ignore optional keys, using {} creates a union type of { ... } | {} }; type SubAggregateOf = TAggregationRequest extends { aggs?: AggregationMap; } ? AggregateOfMap : TAggregationRequest extends { aggregations?: AggregationMap } ? AggregateOfMap : {}; type SearchResponseOf< TAggregationRequest extends TopLevelAggregationRequest, TDocument > = SubAggregateOf; // if aggregation response cannot be inferred, fall back to unknown type WrapAggregationResponse = keyof T extends never ? { aggregations?: unknown } : { aggregations?: T }; export type InferSearchResponseOf< TDocument = unknown, TSearchRequest extends estypes.SearchRequest = estypes.SearchRequest, TOptions extends { restTotalHitsAsInt?: boolean } = {} > = Omit, 'aggregations' | 'hits'> & (TSearchRequest['body'] extends TopLevelAggregationRequest ? WrapAggregationResponse> : { aggregations?: InvalidAggregationRequest }) & { hits: Omit['hits'], 'total' | 'hits'> & (TOptions['restTotalHitsAsInt'] extends true ? { total: number; } : { total: { value: number; relation: 'eq' | 'gte'; }; }) & { hits: HitsOf }; };