diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md index faff901bfc167d..b0ccedb819c95d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md @@ -9,45 +9,16 @@ returns all search source fields Signature: ```typescript -getFields(): { - type?: string | undefined; - query?: import("../..").Query | undefined; - filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; - highlight?: any; - highlightAll?: boolean | undefined; - aggs?: any; - from?: number | undefined; - size?: number | undefined; - source?: string | boolean | string[] | undefined; - version?: boolean | undefined; - fields?: SearchFieldValue[] | undefined; - fieldsFromSource?: string | boolean | string[] | undefined; - index?: import("../..").IndexPattern | undefined; - searchAfter?: import("./types").EsQuerySearchAfter | undefined; - timeout?: string | undefined; - terminate_after?: number | undefined; - }; +getFields(recurse?: boolean): SearchSourceFields; ``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| recurse | boolean | | + Returns: -`{ - type?: string | undefined; - query?: import("../..").Query | undefined; - filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; - highlight?: any; - highlightAll?: boolean | undefined; - aggs?: any; - from?: number | undefined; - size?: number | undefined; - source?: string | boolean | string[] | undefined; - version?: boolean | undefined; - fields?: SearchFieldValue[] | undefined; - fieldsFromSource?: string | boolean | string[] | undefined; - index?: import("../..").IndexPattern | undefined; - searchAfter?: import("./types").EsQuerySearchAfter | undefined; - timeout?: string | undefined; - terminate_after?: number | undefined; - }` +`SearchSourceFields` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getserializedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getserializedfields.md index 3f58a76b24cd08..19bd4a7888bf24 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getserializedfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getserializedfields.md @@ -9,8 +9,15 @@ serializes search source fields (which can later be passed to [ISearchStartSearc Signature: ```typescript -getSerializedFields(): SearchSourceFields; +getSerializedFields(recurse?: boolean): SearchSourceFields; ``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| recurse | boolean | | + Returns: `SearchSourceFields` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md index 2af9cc14e36689..3250561c8b82e9 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md @@ -35,12 +35,12 @@ export declare class SearchSource | [fetch(options)](./kibana-plugin-plugins-data-public.searchsource.fetch.md) | | Fetch this source and reject the returned Promise on error | | [fetch$(options)](./kibana-plugin-plugins-data-public.searchsource.fetch_.md) | | Fetch this source from Elasticsearch, returning an observable over the response(s) | | [getField(field, recurse)](./kibana-plugin-plugins-data-public.searchsource.getfield.md) | | Gets a single field from the fields | -| [getFields()](./kibana-plugin-plugins-data-public.searchsource.getfields.md) | | returns all search source fields | +| [getFields(recurse)](./kibana-plugin-plugins-data-public.searchsource.getfields.md) | | returns all search source fields | | [getId()](./kibana-plugin-plugins-data-public.searchsource.getid.md) | | returns search source id | | [getOwnField(field)](./kibana-plugin-plugins-data-public.searchsource.getownfield.md) | | Get the field from our own fields, don't traverse up the chain | | [getParent()](./kibana-plugin-plugins-data-public.searchsource.getparent.md) | | Get the parent of this SearchSource {undefined\|searchSource} | | [getSearchRequestBody()](./kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md) | | Returns body contents of the search request, often referred as query DSL. | -| [getSerializedFields()](./kibana-plugin-plugins-data-public.searchsource.getserializedfields.md) | | serializes search source fields (which can later be passed to [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md)) | +| [getSerializedFields(recurse)](./kibana-plugin-plugins-data-public.searchsource.getserializedfields.md) | | serializes search source fields (which can later be passed to [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md)) | | [onRequestStart(handler)](./kibana-plugin-plugins-data-public.searchsource.onrequeststart.md) | | Add a handler that will be notified whenever requests start | | [removeField(field)](./kibana-plugin-plugins-data-public.searchsource.removefield.md) | | remove field | | [serialize()](./kibana-plugin-plugins-data-public.searchsource.serialize.md) | | Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object.The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named kibanaSavedObjectMeta.searchSourceJSON.index and kibanaSavedObjectMeta.searchSourceJSON.filter[<number>].meta.index.Using createSearchSource, the instance can be re-created. | diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/data/common/field_formats/field_formats_registry.ts index aa6cf4500c933e..b5cc1b944094c4 100644 --- a/src/plugins/data/common/field_formats/field_formats_registry.ts +++ b/src/plugins/data/common/field_formats/field_formats_registry.ts @@ -23,6 +23,7 @@ import { FormatFactory } from './utils'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../kbn_field_types/types'; import { UI_SETTINGS } from '../constants'; import { FieldFormatNotFoundError } from '../field_formats'; +import { SerializedFieldFormat } from '../../../expressions/common/types'; export class FieldFormatsRegistry { protected fieldFormats: Map = new Map(); @@ -30,7 +31,20 @@ export class FieldFormatsRegistry { protected metaParamsOptions: Record = {}; protected getConfig?: FieldFormatsGetConfigFn; // overriden on the public contract - public deserialize: FormatFactory = () => { + public deserialize: FormatFactory = (mapping?: SerializedFieldFormat) => { + if (!mapping) { + return new (FieldFormat.from(identity))(); + } + + const { id, params = {} } = mapping; + if (id) { + const Format = this.getType(id); + + if (Format) { + return new Format(params, this.getConfig); + } + } + return new (FieldFormat.from(identity))(); }; diff --git a/src/plugins/data/common/search/search_source/normalize_sort_request.ts b/src/plugins/data/common/search/search_source/normalize_sort_request.ts index 7f1cbbd7f2da66..7461b6c1788f80 100644 --- a/src/plugins/data/common/search/search_source/normalize_sort_request.ts +++ b/src/plugins/data/common/search/search_source/normalize_sort_request.ts @@ -49,6 +49,11 @@ function normalize( } } + // FIXME: for unknown reason on the server this setting is serialized + // https://github.com/elastic/kibana/issues/89902 + if (typeof defaultSortOptions === 'string') { + defaultSortOptions = JSON.parse(defaultSortOptions); + } // Don't include unmapped_type for _score field // eslint-disable-next-line @typescript-eslint/naming-convention const { unmapped_type, ...otherSortOptions } = defaultSortOptions; diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index c2a4beb9b61a52..49fb1fa62f4907 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -80,6 +80,175 @@ describe('SearchSource', () => { }); }); + describe('#getFields()', () => { + test('gets the value for the property', () => { + searchSource.setField('aggs', 5); + expect(searchSource.getFields()).toMatchInlineSnapshot(` + Object { + "aggs": 5, + } + `); + }); + + test('recurses parents to get the entire filters: plain object filter', () => { + const RECURSE = true; + + const parent = new SearchSource({}, searchSourceDependencies); + parent.setField('filter', [ + { + meta: { + index: 'd180cae0-60c3-11eb-8569-bd1f5ed24bc9', + params: {}, + alias: null, + disabled: false, + negate: false, + }, + query: { + range: { + '@date': { + gte: '2016-01-27T18:11:05.010Z', + lte: '2021-01-27T18:11:05.010Z', + format: 'strict_date_optional_time', + }, + }, + }, + }, + ]); + searchSource.setParent(parent); + searchSource.setField('aggs', 5); + expect(searchSource.getFields(RECURSE)).toMatchInlineSnapshot(` + Object { + "aggs": 5, + "filter": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "index": "d180cae0-60c3-11eb-8569-bd1f5ed24bc9", + "negate": false, + "params": Object {}, + }, + "query": Object { + "range": Object { + "@date": Object { + "format": "strict_date_optional_time", + "gte": "2016-01-27T18:11:05.010Z", + "lte": "2021-01-27T18:11:05.010Z", + }, + }, + }, + }, + ], + } + `); + + // calling twice gives the same result: no searchSources in the hierarchy were modified + expect(searchSource.getFields(RECURSE)).toMatchInlineSnapshot(` + Object { + "aggs": 5, + "filter": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "index": "d180cae0-60c3-11eb-8569-bd1f5ed24bc9", + "negate": false, + "params": Object {}, + }, + "query": Object { + "range": Object { + "@date": Object { + "format": "strict_date_optional_time", + "gte": "2016-01-27T18:11:05.010Z", + "lte": "2021-01-27T18:11:05.010Z", + }, + }, + }, + }, + ], + } + `); + }); + + test('recurses parents to get the entire filters: function filter', () => { + const RECURSE = true; + + const parent = new SearchSource({}, searchSourceDependencies); + parent.setField('filter', () => ({ + meta: { + index: 'd180cae0-60c3-11eb-8569-bd1f5ed24bc9', + params: {}, + alias: null, + disabled: false, + negate: false, + }, + query: { + range: { + '@date': { + gte: '2016-01-27T18:11:05.010Z', + lte: '2021-01-27T18:11:05.010Z', + format: 'strict_date_optional_time', + }, + }, + }, + })); + searchSource.setParent(parent); + searchSource.setField('aggs', 5); + expect(searchSource.getFields(RECURSE)).toMatchInlineSnapshot(` + Object { + "aggs": 5, + "filter": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "index": "d180cae0-60c3-11eb-8569-bd1f5ed24bc9", + "negate": false, + "params": Object {}, + }, + "query": Object { + "range": Object { + "@date": Object { + "format": "strict_date_optional_time", + "gte": "2016-01-27T18:11:05.010Z", + "lte": "2021-01-27T18:11:05.010Z", + }, + }, + }, + }, + ], + } + `); + + // calling twice gives the same result: no double-added filters + expect(searchSource.getFields(RECURSE)).toMatchInlineSnapshot(` + Object { + "aggs": 5, + "filter": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "index": "d180cae0-60c3-11eb-8569-bd1f5ed24bc9", + "negate": false, + "params": Object {}, + }, + "query": Object { + "range": Object { + "@date": Object { + "format": "strict_date_optional_time", + "gte": "2016-01-27T18:11:05.010Z", + "lte": "2021-01-27T18:11:05.010Z", + }, + }, + }, + }, + ], + } + `); + }); + }); + describe('#removeField()', () => { test('remove property', () => { searchSource = new SearchSource({}, searchSourceDependencies); @@ -619,13 +788,13 @@ describe('SearchSource', () => { expect(JSON.parse(searchSourceJSON).from).toEqual(123456); }); - test('should omit sort and size', () => { + test('should omit size but not sort', () => { searchSource.setField('highlightAll', true); searchSource.setField('from', 123456); searchSource.setField('sort', { field: SortDirection.asc }); searchSource.setField('size', 200); const { searchSourceJSON } = searchSource.serialize(); - expect(Object.keys(JSON.parse(searchSourceJSON))).toEqual(['highlightAll', 'from']); + expect(Object.keys(JSON.parse(searchSourceJSON))).toEqual(['highlightAll', 'from', 'sort']); }); test('should serialize filters', () => { diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index bb60f0d7b4ad48..36c0aab6bc190e 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -172,7 +172,49 @@ export class SearchSource { /** * returns all search source fields */ - getFields() { + getFields(recurse = false): SearchSourceFields { + let thisFilter = this.fields.filter; // type is single value, array, or function + if (thisFilter) { + if (typeof thisFilter === 'function') { + thisFilter = thisFilter() || []; // type is single value or array + } + + if (Array.isArray(thisFilter)) { + thisFilter = [...thisFilter]; + } else { + thisFilter = [thisFilter]; + } + } else { + thisFilter = []; + } + + if (recurse) { + const parent = this.getParent(); + if (parent) { + const parentFields = parent.getFields(recurse); + + let parentFilter = parentFields.filter; // type is single value, array, or function + if (parentFilter) { + if (typeof parentFilter === 'function') { + parentFilter = parentFilter() || []; // type is single value or array + } + + if (Array.isArray(parentFilter)) { + thisFilter.push(...parentFilter); + } else { + thisFilter.push(parentFilter); + } + } + + // add combined filters to the fields + const thisFields = { + ...this.fields, + filter: thisFilter, + }; + + return { ...parentFields, ...thisFields }; + } + } return { ...this.fields }; } @@ -605,9 +647,8 @@ export class SearchSource { /** * serializes search source fields (which can later be passed to {@link ISearchStartSearchSource}) */ - public getSerializedFields() { - const { filter: originalFilters, ...searchSourceFields } = omit(this.getFields(), [ - 'sort', + public getSerializedFields(recurse = false) { + const { filter: originalFilters, ...searchSourceFields } = omit(this.getFields(recurse), [ 'size', ]); let serializedSearchSourceFields: SearchSourceFields = { diff --git a/src/plugins/data/common/search/tabify/__snapshots__/tabify_docs.test.ts.snap b/src/plugins/data/common/search/tabify/__snapshots__/tabify_docs.test.ts.snap index d5ddaa31b8ac3b..6cc191a67633c4 100644 --- a/src/plugins/data/common/search/tabify/__snapshots__/tabify_docs.test.ts.snap +++ b/src/plugins/data/common/search/tabify/__snapshots__/tabify_docs.test.ts.snap @@ -26,23 +26,38 @@ Object { "name": "invalidMapping", }, Object { - "id": "nested.field", + "id": "nested", + "meta": Object { + "field": "nested", + "index": "test-index", + "params": undefined, + "type": "object", + }, + "name": "nested", + }, + Object { + "id": "sourceTest", "meta": Object { - "field": "nested.field", + "field": "sourceTest", "index": "test-index", "params": Object { "id": "number", }, "type": "number", }, - "name": "nested.field", + "name": "sourceTest", }, ], "rows": Array [ Object { "fieldTest": 123, "invalidMapping": 345, - "nested.field": 123, + "nested": Array [ + Object { + "field": 123, + }, + ], + "sourceTest": 123, }, ], "type": "datatable", @@ -52,6 +67,38 @@ Object { exports[`tabifyDocs converts source if option is set 1`] = ` Object { "columns": Array [ + Object { + "id": "fieldTest", + "meta": Object { + "field": "fieldTest", + "index": "test-index", + "params": Object { + "id": "number", + }, + "type": "number", + }, + "name": "fieldTest", + }, + Object { + "id": "invalidMapping", + "meta": Object { + "field": "invalidMapping", + "index": "test-index", + "params": undefined, + "type": "number", + }, + "name": "invalidMapping", + }, + Object { + "id": "nested", + "meta": Object { + "field": "nested", + "index": "test-index", + "params": undefined, + "type": "object", + }, + "name": "nested", + }, Object { "id": "sourceTest", "meta": Object { @@ -67,6 +114,13 @@ Object { ], "rows": Array [ Object { + "fieldTest": 123, + "invalidMapping": 345, + "nested": Array [ + Object { + "field": 123, + }, + ], "sourceTest": 123, }, ], @@ -109,6 +163,18 @@ Object { }, "name": "nested", }, + Object { + "id": "sourceTest", + "meta": Object { + "field": "sourceTest", + "index": "test-index", + "params": Object { + "id": "number", + }, + "type": "number", + }, + "name": "sourceTest", + }, ], "rows": Array [ Object { @@ -119,6 +185,7 @@ Object { "field": 123, }, ], + "sourceTest": 123, }, ], "type": "datatable", @@ -149,21 +216,36 @@ Object { "name": "invalidMapping", }, Object { - "id": "nested.field", + "id": "nested", + "meta": Object { + "field": "nested", + "index": undefined, + "params": undefined, + "type": "object", + }, + "name": "nested", + }, + Object { + "id": "sourceTest", "meta": Object { - "field": "nested.field", + "field": "sourceTest", "index": undefined, "params": undefined, "type": "number", }, - "name": "nested.field", + "name": "sourceTest", }, ], "rows": Array [ Object { "fieldTest": 123, "invalidMapping": 345, - "nested.field": 123, + "nested": Array [ + Object { + "field": 123, + }, + ], + "sourceTest": 123, }, ], "type": "datatable", diff --git a/src/plugins/data/common/search/tabify/index.ts b/src/plugins/data/common/search/tabify/index.ts index 08d54316d9d918..9c650061fb0134 100644 --- a/src/plugins/data/common/search/tabify/index.ts +++ b/src/plugins/data/common/search/tabify/index.ts @@ -26,6 +26,8 @@ export const tabify = ( ); }; +export { tabifyDocs }; + export { tabifyAggResponse } from './tabify'; export { tabifyGetColumns } from './get_columns'; diff --git a/src/plugins/data/common/search/tabify/tabify_docs.ts b/src/plugins/data/common/search/tabify/tabify_docs.ts index d66be3c5748fe0..7d4d0fad20730d 100644 --- a/src/plugins/data/common/search/tabify/tabify_docs.ts +++ b/src/plugins/data/common/search/tabify/tabify_docs.ts @@ -12,9 +12,9 @@ import { IndexPattern } from '../../index_patterns/index_patterns'; import { Datatable, DatatableColumn, DatatableColumnType } from '../../../../expressions/common'; export function flattenHit( - hit: Record, + hit: SearchResponse['hits']['hits'][0], indexPattern?: IndexPattern, - shallow: boolean = false + params?: TabifyDocsOptions ) { const flat = {} as Record; @@ -24,7 +24,7 @@ export function flattenHit( const field = indexPattern?.fields.getByName(key); - if (!shallow) { + if (params?.shallow === false) { const isNestedField = field?.type === 'nested'; if (Array.isArray(val) && !isNestedField) { val.forEach((v) => isPlainObject(v) && flatten(v, key + '.')); @@ -52,7 +52,10 @@ export function flattenHit( } } - flatten(hit); + flatten(hit.fields); + if (params?.source !== false && hit._source) { + flatten(hit._source as Record); + } return flat; } @@ -70,8 +73,7 @@ export const tabifyDocs = ( const rows = esResponse.hits.hits .map((hit) => { - const toConvert = params.source ? hit._source : hit.fields; - const flat = flattenHit(toConvert, index, params.shallow); + const flat = flattenHit(hit, index, params); for (const [key, value] of Object.entries(flat)) { const field = index?.fields.getByName(key); const fieldName = field?.name || key; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 694ca24ac3a98a..9561adda7bc96e 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -2376,30 +2376,12 @@ export class SearchSource { // @deprecated fetch(options?: ISearchOptions): Promise>; getField(field: K, recurse?: boolean): SearchSourceFields[K]; - getFields(): { - type?: string | undefined; - query?: import("../..").Query | undefined; - filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; - highlight?: any; - highlightAll?: boolean | undefined; - aggs?: any; - from?: number | undefined; - size?: number | undefined; - source?: string | boolean | string[] | undefined; - version?: boolean | undefined; - fields?: SearchFieldValue[] | undefined; - fieldsFromSource?: string | boolean | string[] | undefined; - index?: import("../..").IndexPattern | undefined; - searchAfter?: import("./types").EsQuerySearchAfter | undefined; - timeout?: string | undefined; - terminate_after?: number | undefined; - }; + getFields(recurse?: boolean): SearchSourceFields; getId(): string; getOwnField(field: K): SearchSourceFields[K]; getParent(): SearchSource | undefined; getSearchRequestBody(): Promise; - getSerializedFields(): SearchSourceFields; + getSerializedFields(recurse?: boolean): SearchSourceFields; // Warning: (ae-incompatible-release-tags) The symbol "history" is marked as @public, but its signature references "SearchRequest" which is marked as @internal // // (undocumented) @@ -2423,6 +2405,7 @@ export class SearchSource { export interface SearchSourceFields { // (undocumented) aggs?: any; + // Warning: (ae-forgotten-export) The symbol "SearchFieldValue" needs to be exported by the entry point index.d.ts fields?: SearchFieldValue[]; // @deprecated fieldsFromSource?: NameList; @@ -2615,7 +2598,6 @@ export const UI_SETTINGS: { // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:139:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/search/search_source/search_source.ts:187:7 - (ae-forgotten-export) The symbol "SearchFieldValue" needs to be exported by the entry point index.d.ts // src/plugins/data/public/field_formats/field_formats_service.ts:56:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:55:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:55:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts