Skip to content

Commit a13783a

Browse files
committed
value suggestions server route -> data plugin (#53191)
* value suggestions server route -> data plugin Closes #52842 * fix PR comments * fix PR comments
1 parent 4ecad44 commit a13783a

11 files changed

Lines changed: 250 additions & 124 deletions

File tree

src/legacy/core_plugins/kibana/index.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import { exportApi } from './server/routes/api/export';
2727
import { homeApi } from './server/routes/api/home';
2828
import { managementApi } from './server/routes/api/management';
2929
import { scriptsApi } from './server/routes/api/scripts';
30-
import { registerSuggestionsApi } from './server/routes/api/suggestions';
3130
import { registerKqlTelemetryApi } from './server/routes/api/kql_telemetry';
3231
import { registerFieldFormats } from './server/field_formats/register';
3332
import { registerTutorials } from './server/tutorials/register';
@@ -331,7 +330,6 @@ export default function(kibana) {
331330
exportApi(server);
332331
homeApi(server);
333332
managementApi(server);
334-
registerSuggestionsApi(server);
335333
registerKqlTelemetryApi(server);
336334
registerFieldFormats(server);
337335
registerTutorials(server);

src/legacy/core_plugins/kibana/server/routes/api/suggestions/register_value_suggestions.js

Lines changed: 0 additions & 117 deletions
This file was deleted.

src/plugins/data/common/index_patterns/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import { IFieldType } from './fields';
2121

2222
export interface IIndexPattern {
23+
[key: string]: any;
2324
fields: IFieldType[];
2425
title: string;
2526
id?: string;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 { CoreSetup, Plugin } from 'kibana/server';
21+
import { registerRoutes } from './routes';
22+
23+
export class AutocompleteService implements Plugin<void> {
24+
public setup(core: CoreSetup) {
25+
registerRoutes(core);
26+
}
27+
28+
public start() {}
29+
}

src/legacy/core_plugins/kibana/server/routes/api/suggestions/index.js renamed to src/plugins/data/server/autocomplete/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,4 @@
1717
* under the License.
1818
*/
1919

20-
import { registerValueSuggestions } from './register_value_suggestions';
21-
22-
export function registerSuggestionsApi(server) {
23-
registerValueSuggestions(server);
24-
}
20+
export { AutocompleteService } from './autocomplete_service';
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 { CoreSetup } from 'kibana/server';
21+
import { registerValueSuggestionsRoute } from './value_suggestions_route';
22+
23+
export function registerRoutes({ http }: CoreSetup): void {
24+
const router = http.createRouter();
25+
26+
registerValueSuggestionsRoute(router);
27+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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 { get, map } from 'lodash';
21+
import { schema } from '@kbn/config-schema';
22+
import { IRouter } from 'kibana/server';
23+
24+
import { IFieldType, indexPatterns, esFilters } from '../index';
25+
26+
export function registerValueSuggestionsRoute(router: IRouter) {
27+
router.post(
28+
{
29+
path: '/api/kibana/suggestions/values/{index}',
30+
validate: {
31+
params: schema.object(
32+
{
33+
index: schema.string(),
34+
},
35+
{ allowUnknowns: false }
36+
),
37+
body: schema.object(
38+
{
39+
field: schema.string(),
40+
query: schema.string(),
41+
boolFilter: schema.maybe(schema.any()),
42+
},
43+
{ allowUnknowns: false }
44+
),
45+
},
46+
},
47+
async (context, request, response) => {
48+
const { client: uiSettings } = context.core.uiSettings;
49+
const { field: fieldName, query, boolFilter } = request.body;
50+
const { index } = request.params;
51+
const { dataClient } = context.core.elasticsearch;
52+
53+
const autocompleteSearchOptions = {
54+
timeout: await uiSettings.get<number>('kibana.autocompleteTimeout'),
55+
terminate_after: await uiSettings.get<number>('kibana.autocompleteTerminateAfter'),
56+
};
57+
58+
const indexPattern = await indexPatterns.findIndexPatternById(
59+
context.core.savedObjects.client,
60+
index
61+
);
62+
63+
const field = indexPattern && indexPatterns.getFieldByName(fieldName, indexPattern);
64+
const body = await getBody(autocompleteSearchOptions, field || fieldName, query, boolFilter);
65+
66+
try {
67+
const result = await dataClient.callAsCurrentUser('search', { index, body });
68+
69+
const buckets: any[] =
70+
get(result, 'aggregations.suggestions.buckets') ||
71+
get(result, 'aggregations.nestedSuggestions.suggestions.buckets');
72+
73+
return response.ok({ body: map(buckets || [], 'key') });
74+
} catch (error) {
75+
return response.internalError({ body: error });
76+
}
77+
}
78+
);
79+
}
80+
81+
async function getBody(
82+
{ timeout, terminate_after }: Record<string, any>,
83+
field: IFieldType | string,
84+
query: string,
85+
boolFilter: esFilters.Filter[] = []
86+
) {
87+
const isFieldObject = (f: any): f is IFieldType => Boolean(f && f.name);
88+
89+
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#_standard_operators
90+
const getEscapedQuery = (q: string = '') =>
91+
q.replace(/[.?+*|{}[\]()"\\#@&<>~]/g, match => `\\${match}`);
92+
93+
// Helps ensure that the regex is not evaluated eagerly against the terms dictionary
94+
const executionHint = 'map';
95+
96+
// We don't care about the accuracy of the counts, just the content of the terms, so this reduces
97+
// the amount of information that needs to be transmitted to the coordinating node
98+
const shardSize = 10;
99+
const body = {
100+
size: 0,
101+
timeout,
102+
terminate_after,
103+
query: {
104+
bool: {
105+
filter: boolFilter,
106+
},
107+
},
108+
aggs: {
109+
suggestions: {
110+
terms: {
111+
field: isFieldObject(field) ? field.name : field,
112+
include: `${getEscapedQuery(query)}.*`,
113+
execution_hint: executionHint,
114+
shard_size: shardSize,
115+
},
116+
},
117+
},
118+
};
119+
120+
if (isFieldObject(field) && field.subType && field.subType.nested) {
121+
return {
122+
...body,
123+
aggs: {
124+
nestedSuggestions: {
125+
nested: {
126+
path: field.subType.nested.path,
127+
},
128+
aggs: body.aggs,
129+
},
130+
},
131+
};
132+
}
133+
134+
return body;
135+
}

src/plugins/data/server/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export {
5757
IndexPatternsFetcher,
5858
FieldDescriptor,
5959
shouldReadFieldFromDocValues,
60+
indexPatterns,
6061
} from './index_patterns';
6162
export * from './search';
6263
export {

src/plugins/data/server/index_patterns/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19+
import * as indexPatterns from './utils';
1920

2021
export { IndexPatternsFetcher, FieldDescriptor, shouldReadFieldFromDocValues } from './fetcher';
2122
export { IndexPatternsService } from './index_patterns_service';
23+
export { indexPatterns };

0 commit comments

Comments
 (0)