Skip to content

Commit

Permalink
[SearchSource] Combine sort and parent fields when serializing (#89808)
Browse files Browse the repository at this point in the history
* [SearchSource] Combine sort and parent fields when serializing

* fix docs

* add link to issue
;

* fix destructive recursion

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
tsullivan and kibanamachine authored Feb 2, 2021
1 parent 802ac60 commit ad67ee5
Show file tree
Hide file tree
Showing 11 changed files with 358 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,16 @@ returns all search source fields
<b>Signature:</b>

```typescript
getFields(): {
type?: string | undefined;
query?: import("../..").Query | undefined;
filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined;
sort?: Record<string, import("./types").SortDirectionNumeric | import("./types").SortDirection> | Record<string, import("./types").SortDirectionNumeric | import("./types").SortDirection>[] | 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 | <code>boolean</code> | |

<b>Returns:</b>

`{
type?: string | undefined;
query?: import("../..").Query | undefined;
filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined;
sort?: Record<string, import("./types").SortDirectionNumeric | import("./types").SortDirection> | Record<string, import("./types").SortDirectionNumeric | import("./types").SortDirection>[] | 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`

Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ serializes search source fields (which can later be passed to [ISearchStartSearc
<b>Signature:</b>

```typescript
getSerializedFields(): SearchSourceFields;
getSerializedFields(recurse?: boolean): SearchSourceFields;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| recurse | <code>boolean</code> | |

<b>Returns:</b>

`SearchSourceFields`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>kibanaSavedObjectMeta.searchSourceJSON.index</code> and <code>kibanaSavedObjectMeta.searchSourceJSON.filter[&lt;number&gt;].meta.index</code>.<!-- -->Using <code>createSearchSource</code>, the instance can be re-created. |
Expand Down
16 changes: 15 additions & 1 deletion src/plugins/data/common/field_formats/field_formats_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,28 @@ 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<FieldFormatId, FieldFormatInstanceType> = new Map();
protected defaultMap: Record<string, FieldFormatConfig> = {};
protected metaParamsOptions: Record<string, any> = {};
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))();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
173 changes: 171 additions & 2 deletions src/plugins/data/common/search/search_source/search_source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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', () => {
Expand Down
49 changes: 45 additions & 4 deletions src/plugins/data/common/search/search_source/search_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}

Expand Down Expand Up @@ -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 = {
Expand Down
Loading

0 comments on commit ad67ee5

Please sign in to comment.