Skip to content

Commit

Permalink
Expand the log entries query
Browse files Browse the repository at this point in the history
  • Loading branch information
weltenwort committed Jan 7, 2021
1 parent 8854664 commit 893f2c2
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
logEntryCursorRT,
logEntryRT,
} from '../../log_entry';
import { jsonObjectRT } from '../../typed_json';
import { searchStrategyErrorRT } from '../common/errors';

export const LOG_ENTRIES_SEARCH_STRATEGY = 'infra-log-entries';
Expand All @@ -22,11 +23,12 @@ const logEntriesBaseSearchRequestParamsRT = rt.intersection([
sourceId: rt.string,
startTimestamp: rt.number,
endTimestamp: rt.number,
size: rt.number,
}),
rt.partial({
query: rt.union([rt.string, rt.null]),
size: rt.number,
query: jsonObjectRT,
columns: rt.array(logSourceColumnConfigurationRT),
highlightTerms: rt.array(rt.string),
}),
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { pick } from '@kbn/std';
import * as rt from 'io-ts';
import { concat, defer, of } from 'rxjs';
import { concatMap, filter, map, shareReplay, take } from 'rxjs/operators';
Expand Down Expand Up @@ -68,7 +69,18 @@ export const logEntriesSearchStrategyProvider = ({
sourceConfiguration$.pipe(
map(
({ configuration }): IEsSearchRequest => ({
params: createGetLogEntriesQuery(configuration.logAlias),
params: createGetLogEntriesQuery(
configuration.logAlias,
params.startTimestamp,
params.endTimestamp,
undefined, // TODO: determine cursor
params.size,
configuration.fields.timestamp,
configuration.fields.tiebreaker,
[], // TODO: determine fields list
params.query,
undefined // TODO: map over highlight terms OR reduce to one term in request
),
})
)
)
Expand Down
32 changes: 32 additions & 0 deletions x-pack/plugins/infra/server/services/log_entries/queries/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const createSortClause = (
sortDirection: 'asc' | 'desc',
timestampField: string,
tiebreakerField: string
) => ({
sort: {
[timestampField]: sortDirection,
[tiebreakerField]: sortDirection,
},
});

export const createTimeRangeFilterClauses = (
startTimestamp: number,
endTimestamp: number,
timestampField: string
) => [
{
range: {
[timestampField]: {
gte: startTimestamp,
lte: endTimestamp,
format: 'epoch_millis',
},
},
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import type { RequestParams } from '@elastic/elasticsearch';
import * as rt from 'io-ts';
import {
LogEntryAfterCursor,
logEntryAfterCursorRT,
LogEntryBeforeCursor,
LogEntryCursor,
logEntryBeforeCursorRT,
} from '../../../../common/log_entry';
import { jsonArrayRT, JsonObject } from '../../../../common/typed_json';
import {
commonHitFieldsRT,
commonSearchSuccessResponseFieldsRT,
} from '../../../utils/elasticsearch_runtime_types';
import { createSortClause, createTimeRangeFilterClauses } from './common';

export const createGetLogEntriesQuery = (
logEntriesIndex: string,
Expand All @@ -28,50 +30,98 @@ export const createGetLogEntriesQuery = (
fields: string[],
query?: JsonObject,
highlightTerm?: string
): RequestParams.AsyncSearchSubmit<Record<string, any>> => ({
index: logEntriesIndex,
terminate_after: 1,
track_scores: false,
track_total_hits: false,
body: {
size: 1,
query: {},
fields: ['*'],
// sort: [{ [timestampField]: 'desc' }, { [tiebreakerField]: 'desc' }],
_source: false,
},
});
): RequestParams.AsyncSearchSubmit<Record<string, any>> => {
const sortDirection = getSortDirection(cursor);
const highlightQuery = createHighlightQuery(highlightTerm, fields);

function createSortAndSearchAfterClause(
return {
index: logEntriesIndex,
allow_no_indices: true,
terminate_after: 1,
track_scores: false,
track_total_hits: false,
body: {
size,
query: {
bool: {
filter: [
...(query ? [query] : []),
...(highlightQuery ? [highlightQuery] : []),
...createTimeRangeFilterClauses(startTimestamp, endTimestamp, timestampField),
],
},
},
fields,
_source: false,
...createSortClause(sortDirection, timestampField, tiebreakerField),
...createSearchAfterClause(cursor),
...createHighlightClause(highlightQuery, fields),
},
};
};

const getSortDirection = (
cursor: LogEntryBeforeCursor | LogEntryAfterCursor | null | undefined
): 'asc' | 'desc' => (logEntryBeforeCursorRT.is(cursor) ? 'desc' : 'asc');

const createSearchAfterClause = (
cursor: LogEntryBeforeCursor | LogEntryAfterCursor | null | undefined
): {
sortDirection: 'asc' | 'desc';
searchAfterClause: { search_after?: readonly [number, number] };
} {
if (cursor) {
if ('before' in cursor) {
return {
sortDirection: 'desc',
searchAfterClause:
cursor.before !== 'last'
? { search_after: [cursor.before.time, cursor.before.tiebreaker] as const }
: {},
};
} else if (cursor.after !== 'first') {
return {
sortDirection: 'asc',
searchAfterClause: { search_after: [cursor.after.time, cursor.after.tiebreaker] as const },
};
}
): { search_after?: [number, number] } => {
if (logEntryBeforeCursorRT.is(cursor) && cursor.before !== 'last') {
return {
search_after: [cursor.before.time, cursor.before.tiebreaker],
};
} else if (logEntryAfterCursorRT.is(cursor) && cursor.after !== 'first') {
return {
search_after: [cursor.after.time, cursor.after.tiebreaker],
};
}

return { sortDirection: 'asc', searchAfterClause: {} };
}
return {};
};

const createHighlightClause = (highlightQuery: JsonObject | undefined, fields: string[]) =>
highlightQuery
? {
highlight: {
boundary_scanner: 'word',
fields: fields.reduce(
(highlightFieldConfigs, fieldName) => ({
...highlightFieldConfigs,
[fieldName]: {},
}),
{}
),
fragment_size: 1,
number_of_fragments: 100,
post_tags: [''],
pre_tags: [''],
highlight_query: highlightQuery,
},
}
: {};

const createHighlightQuery = (
highlightTerm: string | undefined,
fields: string[]
): JsonObject | undefined => {
if (highlightTerm) {
return {
multi_match: {
fields,
lenient: true,
query: highlightTerm,
type: 'phrase',
},
};
}
};

export const logEntryHitRT = rt.intersection([
commonHitFieldsRT,
rt.type({
fields: rt.record(rt.string, jsonArrayRT),
highlight: rt.record(rt.string, rt.array(rt.string)),
sort: rt.tuple([rt.number, rt.number]),
}),
]);
Expand Down

0 comments on commit 893f2c2

Please sign in to comment.