Skip to content

Commit ccb789d

Browse files
authored
[data.search] Expose SearchSource on the server. (#78383)
1 parent e9acb95 commit ccb789d

39 files changed

+932
-344
lines changed

docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ export interface ISearchStart<SearchStrategyRequest extends IKibanaSearchRequest
1717
| [aggs](./kibana-plugin-plugins-data-server.isearchstart.aggs.md) | <code>AggsStart</code> | |
1818
| [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) | <code>(name: string) =&gt; ISearchStrategy&lt;SearchStrategyRequest, SearchStrategyResponse&gt;</code> | Get other registered search strategies. For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. |
1919
| [search](./kibana-plugin-plugins-data-server.isearchstart.search.md) | <code>(context: RequestHandlerContext, request: SearchStrategyRequest, options: ISearchOptions) =&gt; Promise&lt;SearchStrategyResponse&gt;</code> | |
20+
| [searchSource](./kibana-plugin-plugins-data-server.isearchstart.searchsource.md) | <code>{</code><br/><code> asScoped: (request: KibanaRequest) =&gt; Promise&lt;ISearchStartSearchSource&gt;;</code><br/><code> }</code> | |
2021

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) &gt; [searchSource](./kibana-plugin-plugins-data-server.isearchstart.searchsource.md)
4+
5+
## ISearchStart.searchSource property
6+
7+
<b>Signature:</b>
8+
9+
```typescript
10+
searchSource: {
11+
asScoped: (request: KibanaRequest) => Promise<ISearchStartSearchSource>;
12+
};
13+
```

docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88

99
```typescript
1010
start(core: CoreStart): {
11-
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
1211
fieldFormats: {
1312
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
1413
};
1514
indexPatterns: {
1615
indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest<unknown, unknown, unknown, any>) => Promise<import("../public").IndexPatternsService>;
1716
};
17+
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
1818
};
1919
```
2020

@@ -27,12 +27,12 @@ start(core: CoreStart): {
2727
<b>Returns:</b>
2828

2929
`{
30-
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
3130
fieldFormats: {
3231
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
3332
};
3433
indexPatterns: {
3534
indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest<unknown, unknown, unknown, any>) => Promise<import("../public").IndexPatternsService>;
3635
};
36+
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
3737
}`
3838

src/plugins/data/common/search/search_source/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717
* under the License.
1818
*/
1919

20-
export { SearchSource, ISearchSource, SearchSourceDependencies } from './search_source';
2120
export { createSearchSource } from './create_search_source';
22-
export { SortDirection, EsQuerySortValue, SearchSourceFields } from './types';
2321
export { injectReferences } from './inject_references';
2422
export { extractReferences } from './extract_references';
2523
export { parseSearchSourceJSON } from './parse_json';
2624
export * from './fetch';
2725
export * from './legacy';
26+
export * from './search_source';
27+
export * from './search_source_service';
28+
export * from './types';
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { SavedObjectReference } from 'src/core/types';
21+
import { SearchSourceFields } from './types';
22+
23+
import { injectReferences } from './inject_references';
24+
25+
describe('injectSearchSourceReferences', () => {
26+
let searchSourceJSON: SearchSourceFields & { indexRefName: string };
27+
let references: SavedObjectReference[];
28+
29+
beforeEach(() => {
30+
searchSourceJSON = {
31+
highlightAll: true,
32+
version: true,
33+
query: {
34+
query: 'play_name:"Henry IV"',
35+
language: 'kuery',
36+
},
37+
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
38+
};
39+
references = [
40+
{
41+
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
42+
type: 'index-pattern',
43+
id: '033af690-fde7-11ea-91f3-fb9e73f9bbe9',
44+
},
45+
];
46+
});
47+
48+
test('injects references', () => {
49+
const actual = injectReferences(searchSourceJSON, references);
50+
expect(actual).toMatchInlineSnapshot(`
51+
Object {
52+
"highlightAll": true,
53+
"index": "033af690-fde7-11ea-91f3-fb9e73f9bbe9",
54+
"query": Object {
55+
"language": "kuery",
56+
"query": "play_name:\\"Henry IV\\"",
57+
},
58+
"version": true,
59+
}
60+
`);
61+
});
62+
63+
test('skips injecting references if none exists', () => {
64+
// @ts-expect-error
65+
delete searchSourceJSON.indexRefName;
66+
references = [];
67+
const actual = injectReferences(searchSourceJSON, references);
68+
expect(actual).toMatchInlineSnapshot(`
69+
Object {
70+
"highlightAll": true,
71+
"query": Object {
72+
"language": "kuery",
73+
"query": "play_name:\\"Henry IV\\"",
74+
},
75+
"version": true,
76+
}
77+
`);
78+
});
79+
80+
test('throws an error if there is a broken reference', () => {
81+
searchSourceJSON.indexRefName = 'oops';
82+
expect(() => injectReferences(searchSourceJSON, references)).toThrowErrorMatchingInlineSnapshot(
83+
`"Could not find reference for oops"`
84+
);
85+
});
86+
87+
test('handles filters', () => {
88+
searchSourceJSON.filter = [
89+
// @ts-expect-error
90+
{ meta: { indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index' } },
91+
];
92+
const actual = injectReferences(searchSourceJSON, references);
93+
expect(actual).toMatchInlineSnapshot(`
94+
Object {
95+
"filter": Array [
96+
Object {
97+
"meta": Object {
98+
"index": "033af690-fde7-11ea-91f3-fb9e73f9bbe9",
99+
},
100+
},
101+
],
102+
"highlightAll": true,
103+
"index": "033af690-fde7-11ea-91f3-fb9e73f9bbe9",
104+
"query": Object {
105+
"language": "kuery",
106+
"query": "play_name:\\"Henry IV\\"",
107+
},
108+
"version": true,
109+
}
110+
`);
111+
});
112+
113+
test('throws an error if there is a broken filter reference', () => {
114+
// @ts-expect-error
115+
searchSourceJSON.filter = [{ meta: { indexRefName: 'oops' } }];
116+
expect(() => injectReferences(searchSourceJSON, references)).toThrowErrorMatchingInlineSnapshot(
117+
`"Could not find reference for oops"`
118+
);
119+
});
120+
});

src/plugins/data/common/search/search_source/legacy/types.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,36 @@
1818
*/
1919

2020
import { BehaviorSubject } from 'rxjs';
21+
import { ApiResponse } from '@elastic/elasticsearch';
2122
import { SearchResponse } from 'elasticsearch';
2223
import { FetchHandlers, SearchRequest } from '../fetch';
2324

25+
interface MsearchHeaders {
26+
index: string;
27+
preference?: number | string;
28+
}
29+
30+
interface MsearchRequest {
31+
header: MsearchHeaders;
32+
body: any;
33+
}
34+
35+
// @internal
36+
export interface MsearchRequestBody {
37+
searches: MsearchRequest[];
38+
}
39+
40+
// @internal
41+
export interface MsearchResponse {
42+
body: ApiResponse<{ responses: Array<SearchResponse<any>> }>;
43+
}
44+
2445
// @internal
2546
export interface LegacyFetchHandlers {
2647
callMsearch: (params: {
27-
body: SearchRequest;
48+
body: MsearchRequestBody;
2849
signal: AbortSignal;
29-
}) => Promise<Array<SearchResponse<any>>>;
50+
}) => Promise<MsearchResponse>;
3051
loadingCount$: BehaviorSubject<number>;
3152
}
3253

src/plugins/data/common/search/search_source/mocks.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
import { BehaviorSubject } from 'rxjs';
2121
import { uiSettingsServiceMock } from '../../../../../core/public/mocks';
2222

23-
import { ISearchSource, SearchSource } from './search_source';
24-
import { SearchSourceFields } from './types';
23+
import { SearchSource } from './search_source';
24+
import { ISearchStartSearchSource, ISearchSource, SearchSourceFields } from './types';
2525

2626
export const searchSourceInstanceMock: MockedKeys<ISearchSource> = {
2727
setPreferredSearchStrategyId: jest.fn(),
@@ -45,7 +45,7 @@ export const searchSourceInstanceMock: MockedKeys<ISearchSource> = {
4545
serialize: jest.fn(),
4646
};
4747

48-
export const searchSourceMock = {
48+
export const searchSourceCommonMock: jest.Mocked<ISearchStartSearchSource> = {
4949
create: jest.fn().mockReturnValue(searchSourceInstanceMock),
5050
createEmpty: jest.fn().mockReturnValue(searchSourceInstanceMock),
5151
};

src/plugins/data/common/search/search_source/search_source.test.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* under the License.
1818
*/
1919

20-
import { Observable, BehaviorSubject } from 'rxjs';
20+
import { BehaviorSubject } from 'rxjs';
2121
import { IndexPattern } from '../../index_patterns';
2222
import { GetConfigFn } from '../../types';
2323
import { fetchSoon } from './legacy';
@@ -53,16 +53,7 @@ describe('SearchSource', () => {
5353
let searchSourceDependencies: SearchSourceDependencies;
5454

5555
beforeEach(() => {
56-
mockSearchMethod = jest.fn(() => {
57-
return new Observable((subscriber) => {
58-
setTimeout(() => {
59-
subscriber.next({
60-
rawResponse: '',
61-
});
62-
subscriber.complete();
63-
}, 100);
64-
});
65-
});
56+
mockSearchMethod = jest.fn().mockResolvedValue({ rawResponse: '' });
6657

6758
searchSourceDependencies = {
6859
getConfig: jest.fn(),

src/plugins/data/common/search/search_source/search_source.ts

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,15 @@
7171

7272
import { setWith } from '@elastic/safer-lodash-set';
7373
import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash';
74-
import { map } from 'rxjs/operators';
7574
import { normalizeSortRequest } from './normalize_sort_request';
7675
import { filterDocvalueFields } from './filter_docvalue_fields';
7776
import { fieldWildcardFilter } from '../../../../kibana_utils/common';
7877
import { IIndexPattern } from '../../index_patterns';
79-
import { ISearchGeneric } from '../..';
80-
import { SearchSourceOptions, SearchSourceFields } from './types';
78+
import { IEsSearchRequest, IEsSearchResponse, ISearchOptions } from '../..';
79+
import { ISearchSource, SearchSourceOptions, SearchSourceFields } from './types';
8180
import { FetchHandlers, RequestFailure, getSearchParamsFromRequest, SearchRequest } from './fetch';
8281

83-
import {
84-
getEsQueryConfig,
85-
buildEsQuery,
86-
Filter,
87-
UI_SETTINGS,
88-
ISearchOptions,
89-
} from '../../../common';
82+
import { getEsQueryConfig, buildEsQuery, Filter, UI_SETTINGS } from '../../../common';
9083
import { getHighlightRequest } from '../../../common/field_formats';
9184
import { fetchSoon } from './legacy';
9285
import { extractReferences } from './extract_references';
@@ -108,7 +101,7 @@ export const searchSourceRequiredUiSettings = [
108101
];
109102

110103
export interface SearchSourceDependencies extends FetchHandlers {
111-
search: ISearchGeneric;
104+
search: (request: IEsSearchRequest, options: ISearchOptions) => Promise<IEsSearchResponse>;
112105
}
113106

114107
/** @public **/
@@ -264,7 +257,7 @@ export class SearchSource {
264257
if (getConfig(UI_SETTINGS.COURIER_BATCH_SEARCHES)) {
265258
response = await this.legacyFetch(searchRequest, options);
266259
} else {
267-
response = await this.fetch$(searchRequest, options).toPromise();
260+
response = await this.fetchSearch(searchRequest, options);
268261
}
269262

270263
// TODO: Remove casting when https://github.com/elastic/elasticsearch-js/issues/1287 is resolved
@@ -308,17 +301,17 @@ export class SearchSource {
308301

309302
/**
310303
* Run a search using the search service
311-
* @return {Observable<SearchResponse<unknown>>}
304+
* @return {Promise<SearchResponse<unknown>>}
312305
*/
313-
private fetch$(searchRequest: SearchRequest, options: ISearchOptions) {
306+
private fetchSearch(searchRequest: SearchRequest, options: ISearchOptions) {
314307
const { search, getConfig, onResponse } = this.dependencies;
315308

316309
const params = getSearchParamsFromRequest(searchRequest, {
317310
getConfig,
318311
});
319312

320-
return search({ params, indexType: searchRequest.indexType }, options).pipe(
321-
map(({ rawResponse }) => onResponse(searchRequest, rawResponse))
313+
return search({ params, indexType: searchRequest.indexType }, options).then(({ rawResponse }) =>
314+
onResponse(searchRequest, rawResponse)
322315
);
323316
}
324317

@@ -558,9 +551,3 @@ export class SearchSource {
558551
return [filterField];
559552
}
560553
}
561-
562-
/**
563-
* search source interface
564-
* @public
565-
*/
566-
export type ISearchSource = Pick<SearchSource, keyof SearchSource>;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { BehaviorSubject } from 'rxjs';
21+
import { IndexPatternsContract } from '../../index_patterns/index_patterns';
22+
import { SearchSourceService, SearchSourceDependencies } from './';
23+
24+
describe('SearchSource service', () => {
25+
let dependencies: jest.Mocked<SearchSourceDependencies>;
26+
27+
beforeEach(() => {
28+
jest.resetModules();
29+
dependencies = {
30+
getConfig: jest.fn(),
31+
search: jest.fn(),
32+
onResponse: jest.fn(),
33+
legacy: {
34+
callMsearch: jest.fn(),
35+
loadingCount$: new BehaviorSubject(0),
36+
},
37+
};
38+
});
39+
40+
describe('start()', () => {
41+
test('exposes proper contract', () => {
42+
const start = new SearchSourceService().start(
43+
(jest.fn() as unknown) as jest.Mocked<IndexPatternsContract>,
44+
dependencies
45+
);
46+
47+
expect(Object.keys(start)).toEqual(['create', 'createEmpty']);
48+
});
49+
});
50+
});

0 commit comments

Comments
 (0)