diff --git a/src/platform/plugins/private/vis_types/vega/public/data_model/search_api.ts b/src/platform/plugins/private/vis_types/vega/public/data_model/search_api.ts index 0fa1d15371539..2d2a32fd9c8a5 100644 --- a/src/platform/plugins/private/vis_types/vega/public/data_model/search_api.ts +++ b/src/platform/plugins/private/vis_types/vega/public/data_model/search_api.ts @@ -108,7 +108,7 @@ export class SearchAPI { ), map((data) => ({ name: requestId, - rawResponse: data.rawResponse, + rawResponse: structuredClone(data.rawResponse), })) ) ) diff --git a/src/platform/plugins/shared/data/common/search/aggs/agg_configs.ts b/src/platform/plugins/shared/data/common/search/aggs/agg_configs.ts index 990a8e70269df..8d438373f2a48 100644 --- a/src/platform/plugins/shared/data/common/search/aggs/agg_configs.ts +++ b/src/platform/plugins/shared/data/common/search/aggs/agg_configs.ts @@ -8,7 +8,7 @@ */ import moment from 'moment-timezone'; -import _, { cloneDeep } from 'lodash'; +import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import type { Assign } from '@kbn/utility-types'; import { isRangeFilter, TimeRange, RangeFilter } from '@kbn/es-query'; @@ -472,9 +472,8 @@ export class AggConfigs { if (!this.hasTimeShifts()) { return response; } - let transformedRawResponse = response.rawResponse; + const transformedRawResponse = structuredClone(response.rawResponse); if (!response.rawResponse.aggregations) { - transformedRawResponse = cloneDeep(response.rawResponse); transformedRawResponse.aggregations = { doc_count: response.rawResponse.hits?.total as estypes.AggregationsAggregate, }; diff --git a/src/platform/plugins/shared/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts b/src/platform/plugins/shared/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts index a7a43686bfba6..6cf37d98f5d01 100644 --- a/src/platform/plugins/shared/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts +++ b/src/platform/plugins/shared/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { isNumber, keys, values, find, each, cloneDeep, flatten } from 'lodash'; +import { isNumber, keys, values, find, each, flatten } from 'lodash'; import { i18n } from '@kbn/i18n'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { @@ -227,7 +227,7 @@ export const buildOtherBucketAgg = ( bucket, isNumber(bucketObjKey) ? undefined : bucketObjKey ); - const filter = cloneDeep(bucket.filters) || currentAgg.createFilter(bucketKey); + const filter = structuredClone(bucket.filters) || currentAgg.createFilter(bucketKey); const newFilters = flatten([...filters, filter]); walkBucketTree( newAggIndex, @@ -306,8 +306,12 @@ export const mergeOtherBucketAggResponse = ( requestAgg: Record, otherFilterBuilder: (requestAgg: Record, key: string, otherAgg: IAggConfig) => Filter ): estypes.SearchResponse => { - const updatedResponse = cloneDeep(response); - const aggregationsRoot = getCorrectAggregationsCursorFromResponse(otherResponse, aggsConfig); + const updatedResponse = structuredClone(response); + const updatedOtherResponse = structuredClone(otherResponse); + const aggregationsRoot = getCorrectAggregationsCursorFromResponse( + updatedOtherResponse, + aggsConfig + ); const updatedAggregationsRoot = getCorrectAggregationsCursorFromResponse( updatedResponse, aggsConfig @@ -349,7 +353,7 @@ export const updateMissingBucket = ( aggConfigs: IAggConfigs, agg: IAggConfig ) => { - const updatedResponse = cloneDeep(response); + const updatedResponse = structuredClone(response); const aggResultBuckets = getAggConfigResultMissingBuckets( getCorrectAggregationsCursorFromResponse(updatedResponse, aggConfigs), agg.id diff --git a/src/platform/plugins/shared/data/public/search/search_interceptor/search_interceptor.ts b/src/platform/plugins/shared/data/public/search/search_interceptor/search_interceptor.ts index ca8667860008b..3043c4eb18c6a 100644 --- a/src/platform/plugins/shared/data/public/search/search_interceptor/search_interceptor.ts +++ b/src/platform/plugins/shared/data/public/search/search_interceptor/search_interceptor.ts @@ -61,6 +61,7 @@ import type { } from '@kbn/search-types'; import { createEsError, isEsError, renderSearchError } from '@kbn/search-errors'; import type { IKibanaSearchResponse, ISearchOptions } from '@kbn/search-types'; +import { defaultFreeze } from '@kbn/kibana-utils-plugin/common'; import { AsyncSearchGetResponse, ErrorResponseBase, @@ -653,6 +654,8 @@ export class SearchInterceptor { ) { this.showRestoreWarning(sessionId); } + + defaultFreeze(response); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); diff --git a/src/platform/plugins/shared/kibana_utils/common/index.ts b/src/platform/plugins/shared/kibana_utils/common/index.ts index d7be4c2159d0e..71e57a672780c 100644 --- a/src/platform/plugins/shared/kibana_utils/common/index.ts +++ b/src/platform/plugins/shared/kibana_utils/common/index.ts @@ -38,6 +38,7 @@ export { useContainerSelector, useContainerState, createStateContainer, + defaultFreeze, } from './state_containers'; export type { KibanaServerError } from './errors'; export { diff --git a/src/platform/plugins/shared/kibana_utils/common/state_containers/create_state_container.ts b/src/platform/plugins/shared/kibana_utils/common/state_containers/create_state_container.ts index c06eecb2849c2..27ebc952af959 100644 --- a/src/platform/plugins/shared/kibana_utils/common/state_containers/create_state_container.ts +++ b/src/platform/plugins/shared/kibana_utils/common/state_containers/create_state_container.ts @@ -26,7 +26,7 @@ const isProduction = ? process.env.NODE_ENV === 'production' : !process.env.NODE_ENV || process.env.NODE_ENV === 'production'; -const defaultFreeze: (value: T) => T = isProduction +export const defaultFreeze: (value: T) => T = isProduction ? (value: T) => value as T : (value: T): T => { const isFreezable = value !== null && typeof value === 'object'; diff --git a/src/platform/plugins/shared/kibana_utils/common/state_containers/index.ts b/src/platform/plugins/shared/kibana_utils/common/state_containers/index.ts index bd14eb47fb010..34bbf935b6259 100644 --- a/src/platform/plugins/shared/kibana_utils/common/state_containers/index.ts +++ b/src/platform/plugins/shared/kibana_utils/common/state_containers/index.ts @@ -41,7 +41,7 @@ export type { export type { CreateStateContainerOptions } from './create_state_container'; -export { createStateContainer } from './create_state_container'; +export { createStateContainer, defaultFreeze } from './create_state_container'; export { createStateContainerReactHelpers, diff --git a/x-pack/platform/plugins/shared/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/platform/plugins/shared/maps/public/classes/sources/es_search_source/es_search_source.tsx index dffe9628f29ec..1d9fa17a913c3 100644 --- a/x-pack/platform/plugins/shared/maps/public/classes/sources/es_search_source/es_search_source.tsx +++ b/x-pack/platform/plugins/shared/maps/public/classes/sources/es_search_source/es_search_source.tsx @@ -400,7 +400,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource entityBuckets.forEach((entityBucket: any) => { const hits = _.get(entityBucket, 'entityHits.hits.hits', []); // Reverse hits list so top documents by sort are drawn on top - allHits.push(...hits.reverse()); + allHits.push(...hits.slice().reverse()); if (isTotalHitsGreaterThan(entityBucket.entityHits.hits.total, hits.length)) { areTopHitsTrimmed = true; } @@ -489,7 +489,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource const isTimeExtentForTimeslice = requestMeta.timeslice !== undefined && !useRequestMetaWithoutTimeslice; return { - hits: resp.hits.hits.reverse(), // Reverse hits so top documents by sort are drawn on top + hits: resp.hits.hits.slice().reverse(), // Reverse hits so top documents by sort are drawn on top meta: { resultsCount: resp.hits.hits.length, areResultsTrimmed: isTotalHitsGreaterThan(resp.hits.total, resp.hits.hits.length),