Skip to content

Commit c2eaff4

Browse files
committed
Merge branch 'master' into actions/license-allowlist
* master: Added `defaultActionMessage` to index threshold alert UI type definition (elastic#80936) [ILM] Migrate Delete phase and name field to Form Lib (elastic#82834) skip flaky suite (elastic#57426) [Alerting] adds an Run When field in the alert flyout to assign the action to an Action Group (elastic#82472) [APM] Expose APM event client as part of plugin contract (elastic#82724) [Logs UI] Fix errors during navigation (elastic#78319) Enable send to background in TSVB (elastic#82835) SavedObjects search_dsl: add match_phrase_prefix clauses when using prefix search (elastic#82693) [Ingest Manager] Unify install* under installPackage (elastic#82916)
2 parents 26faa76 + c78cf35 commit c2eaff4

File tree

92 files changed

+2737
-2347
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+2737
-2347
lines changed

src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts

Lines changed: 311 additions & 162 deletions
Large diffs are not rendered by default.

src/core/server/saved_objects/service/lib/search_dsl/query_params.ts

Lines changed: 148 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,24 @@
2020
import { esKuery } from '../../../es_query';
2121
type KueryNode = any;
2222

23-
import { getRootPropertiesObjects, IndexMapping } from '../../../mappings';
2423
import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry';
2524
import { ALL_NAMESPACES_STRING, DEFAULT_NAMESPACE_STRING } from '../utils';
2625

2726
/**
2827
* Gets the types based on the type. Uses mappings to support
2928
* null type (all types), a single type string or an array
3029
*/
31-
function getTypes(mappings: IndexMapping, type?: string | string[]) {
30+
function getTypes(registry: ISavedObjectTypeRegistry, type?: string | string[]) {
3231
if (!type) {
33-
return Object.keys(getRootPropertiesObjects(mappings));
32+
return registry.getAllTypes().map((registeredType) => registeredType.name);
3433
}
35-
36-
if (Array.isArray(type)) {
37-
return type;
38-
}
39-
40-
return [type];
34+
return Array.isArray(type) ? type : [type];
4135
}
4236

4337
/**
4438
* Get the field params based on the types, searchFields, and rootSearchFields
4539
*/
46-
function getFieldsForTypes(
40+
function getSimpleQueryStringTypeFields(
4741
types: string[],
4842
searchFields: string[] = [],
4943
rootSearchFields: string[] = []
@@ -130,7 +124,6 @@ export interface HasReferenceQueryParams {
130124
export type SearchOperator = 'AND' | 'OR';
131125

132126
interface QueryParams {
133-
mappings: IndexMapping;
134127
registry: ISavedObjectTypeRegistry;
135128
namespaces?: string[];
136129
type?: string | string[];
@@ -188,11 +181,26 @@ export function getClauseForReference(reference: HasReferenceQueryParams) {
188181
};
189182
}
190183

184+
// A de-duplicated set of namespaces makes for a more efficient query.
185+
//
186+
// Additionally, we treat the `*` namespace as the `default` namespace.
187+
// In the Default Distribution, the `*` is automatically expanded to include all available namespaces.
188+
// However, the OSS distribution (and certain configurations of the Default Distribution) can allow the `*`
189+
// to pass through to the SO Repository, and eventually to this module. When this happens, we translate to `default`,
190+
// since that is consistent with how a single-namespace search behaves in the OSS distribution. Leaving the wildcard in place
191+
// would result in no results being returned, as the wildcard is treated as a literal, and not _actually_ as a wildcard.
192+
// We had a good discussion around the tradeoffs here: https://github.com/elastic/kibana/pull/67644#discussion_r441055716
193+
const normalizeNamespaces = (namespacesToNormalize?: string[]) =>
194+
namespacesToNormalize
195+
? Array.from(
196+
new Set(namespacesToNormalize.map((x) => (x === '*' ? DEFAULT_NAMESPACE_STRING : x)))
197+
)
198+
: undefined;
199+
191200
/**
192201
* Get the "query" related keys for the search body
193202
*/
194203
export function getQueryParams({
195-
mappings,
196204
registry,
197205
namespaces,
198206
type,
@@ -206,36 +214,18 @@ export function getQueryParams({
206214
kueryNode,
207215
}: QueryParams) {
208216
const types = getTypes(
209-
mappings,
217+
registry,
210218
typeToNamespacesMap ? Array.from(typeToNamespacesMap.keys()) : type
211219
);
212220

213221
if (hasReference && !Array.isArray(hasReference)) {
214222
hasReference = [hasReference];
215223
}
216224

217-
// A de-duplicated set of namespaces makes for a more effecient query.
218-
//
219-
// Additonally, we treat the `*` namespace as the `default` namespace.
220-
// In the Default Distribution, the `*` is automatically expanded to include all available namespaces.
221-
// However, the OSS distribution (and certain configurations of the Default Distribution) can allow the `*`
222-
// to pass through to the SO Repository, and eventually to this module. When this happens, we translate to `default`,
223-
// since that is consistent with how a single-namespace search behaves in the OSS distribution. Leaving the wildcard in place
224-
// would result in no results being returned, as the wildcard is treated as a literal, and not _actually_ as a wildcard.
225-
// We had a good discussion around the tradeoffs here: https://github.com/elastic/kibana/pull/67644#discussion_r441055716
226-
const normalizeNamespaces = (namespacesToNormalize?: string[]) =>
227-
namespacesToNormalize
228-
? Array.from(
229-
new Set(namespacesToNormalize.map((x) => (x === '*' ? DEFAULT_NAMESPACE_STRING : x)))
230-
)
231-
: undefined;
232-
233225
const bool: any = {
234226
filter: [
235227
...(kueryNode != null ? [esKuery.toElasticsearchQuery(kueryNode)] : []),
236-
...(hasReference && hasReference.length
237-
? [getReferencesFilter(hasReference, hasReferenceOperator)]
238-
: []),
228+
...(hasReference?.length ? [getReferencesFilter(hasReference, hasReferenceOperator)] : []),
239229
{
240230
bool: {
241231
should: types.map((shouldType) => {
@@ -251,16 +241,133 @@ export function getQueryParams({
251241
};
252242

253243
if (search) {
254-
bool.must = [
255-
{
256-
simple_query_string: {
257-
query: search,
258-
...getFieldsForTypes(types, searchFields, rootSearchFields),
259-
...(defaultSearchOperator ? { default_operator: defaultSearchOperator } : {}),
260-
},
261-
},
262-
];
244+
const useMatchPhrasePrefix = shouldUseMatchPhrasePrefix(search);
245+
const simpleQueryStringClause = getSimpleQueryStringClause({
246+
search,
247+
types,
248+
searchFields,
249+
rootSearchFields,
250+
defaultSearchOperator,
251+
});
252+
253+
if (useMatchPhrasePrefix) {
254+
bool.should = [
255+
simpleQueryStringClause,
256+
...getMatchPhrasePrefixClauses({ search, searchFields, types, registry }),
257+
];
258+
bool.minimum_should_match = 1;
259+
} else {
260+
bool.must = [simpleQueryStringClause];
261+
}
263262
}
264263

265264
return { query: { bool } };
266265
}
266+
267+
// we only want to add match_phrase_prefix clauses
268+
// if the search is a prefix search
269+
const shouldUseMatchPhrasePrefix = (search: string): boolean => {
270+
return search.trim().endsWith('*');
271+
};
272+
273+
const getMatchPhrasePrefixClauses = ({
274+
search,
275+
searchFields,
276+
registry,
277+
types,
278+
}: {
279+
search: string;
280+
searchFields?: string[];
281+
types: string[];
282+
registry: ISavedObjectTypeRegistry;
283+
}) => {
284+
// need to remove the prefix search operator
285+
const query = search.replace(/[*]$/, '');
286+
const mppFields = getMatchPhrasePrefixFields({ searchFields, types, registry });
287+
return mppFields.map(({ field, boost }) => {
288+
return {
289+
match_phrase_prefix: {
290+
[field]: {
291+
query,
292+
boost,
293+
},
294+
},
295+
};
296+
});
297+
};
298+
299+
interface FieldWithBoost {
300+
field: string;
301+
boost?: number;
302+
}
303+
304+
const getMatchPhrasePrefixFields = ({
305+
searchFields = [],
306+
types,
307+
registry,
308+
}: {
309+
searchFields?: string[];
310+
types: string[];
311+
registry: ISavedObjectTypeRegistry;
312+
}): FieldWithBoost[] => {
313+
const output: FieldWithBoost[] = [];
314+
315+
searchFields = searchFields.filter((field) => field !== '*');
316+
let fields: string[];
317+
if (searchFields.length === 0) {
318+
fields = types.reduce((typeFields, type) => {
319+
const defaultSearchField = registry.getType(type)?.management?.defaultSearchField;
320+
if (defaultSearchField) {
321+
return [...typeFields, `${type}.${defaultSearchField}`];
322+
}
323+
return typeFields;
324+
}, [] as string[]);
325+
} else {
326+
fields = [];
327+
for (const field of searchFields) {
328+
fields = fields.concat(types.map((type) => `${type}.${field}`));
329+
}
330+
}
331+
332+
fields.forEach((rawField) => {
333+
const [field, rawBoost] = rawField.split('^');
334+
let boost: number = 1;
335+
if (rawBoost) {
336+
try {
337+
boost = parseInt(rawBoost, 10);
338+
} catch (e) {
339+
boost = 1;
340+
}
341+
}
342+
if (isNaN(boost)) {
343+
boost = 1;
344+
}
345+
output.push({
346+
field,
347+
boost,
348+
});
349+
});
350+
return output;
351+
};
352+
353+
const getSimpleQueryStringClause = ({
354+
search,
355+
types,
356+
searchFields,
357+
rootSearchFields,
358+
defaultSearchOperator,
359+
}: {
360+
search: string;
361+
types: string[];
362+
searchFields?: string[];
363+
rootSearchFields?: string[];
364+
defaultSearchOperator?: SearchOperator;
365+
}) => {
366+
return {
367+
simple_query_string: {
368+
query: search,
369+
...getSimpleQueryStringTypeFields(types, searchFields, rootSearchFields),
370+
...(defaultSearchOperator ? { default_operator: defaultSearchOperator } : {}),
371+
},
372+
};
373+
};

src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ describe('getSearchDsl', () => {
7676
getSearchDsl(mappings, registry, opts);
7777
expect(getQueryParams).toHaveBeenCalledTimes(1);
7878
expect(getQueryParams).toHaveBeenCalledWith({
79-
mappings,
8079
registry,
8180
namespaces: opts.namespaces,
8281
type: opts.type,

src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ export function getSearchDsl(
7171

7272
return {
7373
...getQueryParams({
74-
mappings,
7574
registry,
7675
namespaces,
7776
type,

src/plugins/vis_type_timeseries/common/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
*/
1919

2020
import { TypeOf } from '@kbn/config-schema';
21-
import { metricsItems, panel, seriesItems } from './vis_schema';
21+
import { metricsItems, panel, seriesItems, visPayloadSchema } from './vis_schema';
2222

2323
export type SeriesItemsSchema = TypeOf<typeof seriesItems>;
2424
export type MetricsItemsSchema = TypeOf<typeof metricsItems>;
2525
export type PanelSchema = TypeOf<typeof panel>;
26+
export type VisPayload = TypeOf<typeof visPayloadSchema>;

src/plugins/vis_type_timeseries/common/vis_schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,5 @@ export const visPayloadSchema = schema.object({
273273
min: stringRequired,
274274
max: stringRequired,
275275
}),
276+
sessionId: schema.maybe(schema.string()),
276277
});

src/plugins/vis_type_timeseries/public/request_handler.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ export const metricsRequestHandler = async ({
3232
const config = getUISettings();
3333
const timezone = getTimezone(config);
3434
const uiStateObj = uiState.get(visParams.type, {});
35-
const parsedTimeRange = getDataStart().query.timefilter.timefilter.calculateBounds(timeRange);
35+
const dataSearch = getDataStart();
36+
const parsedTimeRange = dataSearch.query.timefilter.timefilter.calculateBounds(timeRange);
3637
const scaledDataFormat = config.get('dateFormat:scaled');
3738
const dateFormat = config.get('dateFormat');
3839

@@ -53,6 +54,7 @@ export const metricsRequestHandler = async ({
5354
panels: [visParams],
5455
state: uiStateObj,
5556
savedObjectId: savedObjectId || 'unsaved',
57+
sessionId: dataSearch.search.session.getSessionId(),
5658
}),
5759
});
5860

src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('AbstractSearchStrategy', () => {
2828
beforeEach(() => {
2929
mockedFields = {};
3030
req = {
31+
payload: {},
3132
pre: {
3233
indexPatternsService: {
3334
getFieldsForWildcard: jest.fn().mockReturnValue(mockedFields),
@@ -60,6 +61,9 @@ describe('AbstractSearchStrategy', () => {
6061

6162
const responses = await abstractSearchStrategy.search(
6263
{
64+
payload: {
65+
sessionId: 1,
66+
},
6367
requestContext: {
6468
search: { search: searchFn },
6569
},
@@ -76,7 +80,9 @@ describe('AbstractSearchStrategy', () => {
7680
},
7781
indexType: undefined,
7882
},
79-
{}
83+
{
84+
sessionId: 1,
85+
}
8086
);
8187
});
8288
});

src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,28 @@ import {
2323
IUiSettingsClient,
2424
SavedObjectsClientContract,
2525
} from 'kibana/server';
26+
2627
import { Framework } from '../../../plugin';
2728
import { IndexPatternsFetcher } from '../../../../../data/server';
29+
import { VisPayload } from '../../../../common/types';
2830

2931
/**
3032
* ReqFacade is a regular KibanaRequest object extended with additional service
3133
* references to ensure backwards compatibility for existing integrations.
3234
*
3335
* This will be replaced by standard KibanaRequest and RequestContext objects in a later version.
3436
*/
35-
export type ReqFacade = FakeRequest & {
37+
export interface ReqFacade<T = unknown> extends FakeRequest {
3638
requestContext: RequestHandlerContext;
3739
framework: Framework;
38-
payload: unknown;
40+
payload: T;
3941
pre: {
4042
indexPatternsService?: IndexPatternsFetcher;
4143
};
4244
getUiSettingsService: () => IUiSettingsClient;
4345
getSavedObjectsClient: () => SavedObjectsClientContract;
4446
getEsShardTimeout: () => Promise<number>;
45-
};
47+
}
4648

4749
export class AbstractSearchStrategy {
4850
public indexType?: string;
@@ -53,8 +55,10 @@ export class AbstractSearchStrategy {
5355
this.additionalParams = additionalParams;
5456
}
5557

56-
async search(req: ReqFacade, bodies: any[], options = {}) {
58+
async search(req: ReqFacade<VisPayload>, bodies: any[], options = {}) {
5759
const requests: any[] = [];
60+
const { sessionId } = req.payload;
61+
5862
bodies.forEach((body) => {
5963
requests.push(
6064
req.requestContext
@@ -67,6 +71,7 @@ export class AbstractSearchStrategy {
6771
indexType: this.indexType,
6872
},
6973
{
74+
sessionId,
7075
...options,
7176
}
7277
)

0 commit comments

Comments
 (0)