mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Timelion] Support of Runtime Fields (#96700)
* [Timelion] Support of Runtime Fields * Replace call of getScriptedFields() with getComputedFields().runtimeFields, refactor buildAggBody and es.test.js * Refactor index.js and agg_body.js Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
71145a6778
commit
fe48ae396b
6 changed files with 50 additions and 72 deletions
|
@ -10,13 +10,7 @@ import { get } from 'lodash';
|
|||
import { getIndexPatterns } from './plugin_services';
|
||||
import { TimelionFunctionArgs } from '../../common/types';
|
||||
import { TimelionExpressionFunction, TimelionExpressionArgument } from '../../common/parser';
|
||||
import {
|
||||
IndexPatternField,
|
||||
indexPatterns as indexPatternsUtils,
|
||||
KBN_FIELD_TYPES,
|
||||
} from '../../../data/public';
|
||||
|
||||
const isRuntimeField = (field: IndexPatternField) => Boolean(field.runtimeField);
|
||||
import { indexPatterns as indexPatternsUtils, KBN_FIELD_TYPES } from '../../../data/public';
|
||||
|
||||
export function getArgValueSuggestions() {
|
||||
const indexPatterns = getIndexPatterns();
|
||||
|
@ -77,7 +71,6 @@ export function getArgValueSuggestions() {
|
|||
.getByType(KBN_FIELD_TYPES.NUMBER)
|
||||
.filter(
|
||||
(field) =>
|
||||
!isRuntimeField(field) &&
|
||||
field.aggregatable &&
|
||||
containsFieldName(valueSplit[1], field) &&
|
||||
!indexPatternsUtils.isNestedField(field)
|
||||
|
@ -101,7 +94,6 @@ export function getArgValueSuggestions() {
|
|||
.getAll()
|
||||
.filter(
|
||||
(field) =>
|
||||
!isRuntimeField(field) &&
|
||||
field.aggregatable &&
|
||||
[
|
||||
KBN_FIELD_TYPES.NUMBER,
|
||||
|
@ -124,10 +116,7 @@ export function getArgValueSuggestions() {
|
|||
return indexPattern.fields
|
||||
.getByType(KBN_FIELD_TYPES.DATE)
|
||||
.filter(
|
||||
(field) =>
|
||||
!isRuntimeField(field) &&
|
||||
containsFieldName(partial, field) &&
|
||||
!indexPatternsUtils.isNestedField(field)
|
||||
(field) => containsFieldName(partial, field) && !indexPatternsUtils.isNestedField(field)
|
||||
)
|
||||
.map((field) => ({ name: field.name, insertText: field.name }));
|
||||
},
|
||||
|
|
|
@ -120,7 +120,7 @@ describe('es', () => {
|
|||
});
|
||||
|
||||
describe('metric aggs', () => {
|
||||
const emptyScriptedFields = [];
|
||||
const emptyScriptFields = {};
|
||||
|
||||
test('adds a metric agg for each metric', () => {
|
||||
config.metric = [
|
||||
|
@ -133,7 +133,7 @@ describe('es', () => {
|
|||
'percentiles:\\:bytes\\:123:20.0,50.0,100.0',
|
||||
'percentiles:a:2',
|
||||
];
|
||||
agg = createDateAgg(config, tlConfig, emptyScriptedFields);
|
||||
agg = createDateAgg(config, tlConfig, emptyScriptFields);
|
||||
expect(agg.time_buckets.aggs['sum(beer)']).toEqual({ sum: { field: 'beer' } });
|
||||
expect(agg.time_buckets.aggs['avg(bytes)']).toEqual({ avg: { field: 'bytes' } });
|
||||
expect(agg.time_buckets.aggs['percentiles(bytes)']).toEqual({
|
||||
|
@ -156,14 +156,15 @@ describe('es', () => {
|
|||
|
||||
test('adds a scripted metric agg for each scripted metric', () => {
|
||||
config.metric = ['avg:scriptedBytes'];
|
||||
const scriptedFields = [
|
||||
{
|
||||
name: 'scriptedBytes',
|
||||
script: 'doc["bytes"].value',
|
||||
lang: 'painless',
|
||||
const scriptFields = {
|
||||
scriptedBytes: {
|
||||
script: {
|
||||
source: 'doc["bytes"].value',
|
||||
lang: 'painless',
|
||||
},
|
||||
},
|
||||
];
|
||||
agg = createDateAgg(config, tlConfig, scriptedFields);
|
||||
};
|
||||
agg = createDateAgg(config, tlConfig, scriptFields);
|
||||
expect(agg.time_buckets.aggs['avg(scriptedBytes)']).toEqual({
|
||||
avg: {
|
||||
script: {
|
||||
|
@ -176,14 +177,14 @@ describe('es', () => {
|
|||
|
||||
test('has a special `count` metric that uses a script', () => {
|
||||
config.metric = ['count'];
|
||||
agg = createDateAgg(config, tlConfig, emptyScriptedFields);
|
||||
agg = createDateAgg(config, tlConfig, emptyScriptFields);
|
||||
expect(typeof agg.time_buckets.aggs.count.bucket_script).toBe('object');
|
||||
expect(agg.time_buckets.aggs.count.bucket_script.buckets_path).toEqual('_count');
|
||||
});
|
||||
|
||||
test('has a special `count` metric with redundant field which use a script', () => {
|
||||
config.metric = ['count:beer'];
|
||||
agg = createDateAgg(config, tlConfig, emptyScriptedFields);
|
||||
agg = createDateAgg(config, tlConfig, emptyScriptFields);
|
||||
expect(typeof agg.time_buckets.aggs.count.bucket_script).toBe('object');
|
||||
expect(agg.time_buckets.aggs.count.bucket_script.buckets_path).toEqual('_count');
|
||||
});
|
||||
|
@ -192,7 +193,7 @@ describe('es', () => {
|
|||
|
||||
describe('buildRequest', () => {
|
||||
const fn = buildRequest;
|
||||
const emptyScriptedFields = [];
|
||||
const emptyScriptFields = {};
|
||||
let tlConfig;
|
||||
let config;
|
||||
beforeEach(() => {
|
||||
|
@ -206,20 +207,20 @@ describe('es', () => {
|
|||
|
||||
test('sets the index on the request', () => {
|
||||
config.index = 'beer';
|
||||
const request = fn(config, tlConfig, emptyScriptedFields);
|
||||
const request = fn(config, tlConfig, emptyScriptFields);
|
||||
|
||||
expect(request.params.index).toEqual('beer');
|
||||
});
|
||||
|
||||
test('always sets body.size to 0', () => {
|
||||
const request = fn(config, tlConfig, emptyScriptedFields);
|
||||
const request = fn(config, tlConfig, emptyScriptFields);
|
||||
|
||||
expect(request.params.body.size).toEqual(0);
|
||||
});
|
||||
|
||||
test('creates a filters agg that contains each of the queries passed', () => {
|
||||
config.q = ['foo', 'bar'];
|
||||
const request = fn(config, tlConfig, emptyScriptedFields);
|
||||
const request = fn(config, tlConfig, emptyScriptFields);
|
||||
|
||||
expect(request.params.body.aggs.q.meta.type).toEqual('split');
|
||||
|
||||
|
@ -231,14 +232,14 @@ describe('es', () => {
|
|||
describe('timeouts', () => {
|
||||
test('sets the timeout on the request', () => {
|
||||
config.index = 'beer';
|
||||
const request = fn(config, tlConfig, emptyScriptedFields, 30000);
|
||||
const request = fn(config, tlConfig, emptyScriptFields, {}, 30000);
|
||||
|
||||
expect(request.params.timeout).toEqual('30000ms');
|
||||
});
|
||||
|
||||
test('sets no timeout if elasticsearch.shardTimeout is set to 0', () => {
|
||||
config.index = 'beer';
|
||||
const request = fn(config, tlConfig, emptyScriptedFields, 0);
|
||||
const request = fn(config, tlConfig, emptyScriptFields, {}, 0);
|
||||
|
||||
expect(request.params).not.toHaveProperty('timeout');
|
||||
});
|
||||
|
@ -258,7 +259,7 @@ describe('es', () => {
|
|||
test('sets ignore_throttled=true on the request', () => {
|
||||
config.index = 'beer';
|
||||
tlConfig.settings[UI_SETTINGS.SEARCH_INCLUDE_FROZEN] = false;
|
||||
const request = fn(config, tlConfig, emptyScriptedFields);
|
||||
const request = fn(config, tlConfig, emptyScriptFields);
|
||||
|
||||
expect(request.params.ignore_throttled).toEqual(true);
|
||||
});
|
||||
|
@ -266,7 +267,7 @@ describe('es', () => {
|
|||
test('sets no timeout if elasticsearch.shardTimeout is set to 0', () => {
|
||||
tlConfig.settings[UI_SETTINGS.SEARCH_INCLUDE_FROZEN] = true;
|
||||
config.index = 'beer';
|
||||
const request = fn(config, tlConfig, emptyScriptedFields);
|
||||
const request = fn(config, tlConfig, emptyScriptFields);
|
||||
|
||||
expect(request.params.ignore_throttled).toEqual(false);
|
||||
});
|
||||
|
@ -301,7 +302,7 @@ describe('es', () => {
|
|||
|
||||
test('adds the contents of body.extended.es.filter to a filter clause of the bool', () => {
|
||||
config.kibana = true;
|
||||
const request = fn(config, tlConfig, emptyScriptedFields);
|
||||
const request = fn(config, tlConfig, emptyScriptFields);
|
||||
const filter = request.params.body.query.bool.filter.bool;
|
||||
expect(filter.must.length).toEqual(1);
|
||||
expect(filter.must_not.length).toEqual(2);
|
||||
|
@ -309,12 +310,12 @@ describe('es', () => {
|
|||
|
||||
test('does not include filters if config.kibana = false', () => {
|
||||
config.kibana = false;
|
||||
const request = fn(config, tlConfig, emptyScriptedFields);
|
||||
const request = fn(config, tlConfig, emptyScriptFields);
|
||||
expect(request.params.body.query.bool.filter).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('adds a time filter to the bool querys must clause', () => {
|
||||
let request = fn(config, tlConfig, emptyScriptedFields);
|
||||
let request = fn(config, tlConfig, emptyScriptFields);
|
||||
expect(request.params.body.query.bool.must.length).toEqual(1);
|
||||
expect(request.params.body.query.bool.must[0]).toEqual({
|
||||
range: {
|
||||
|
@ -327,7 +328,7 @@ describe('es', () => {
|
|||
});
|
||||
|
||||
config.kibana = true;
|
||||
request = fn(config, tlConfig, emptyScriptedFields);
|
||||
request = fn(config, tlConfig, emptyScriptFields);
|
||||
expect(request.params.body.query.bool.must.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
@ -335,7 +336,7 @@ describe('es', () => {
|
|||
describe('config.split', () => {
|
||||
test('adds terms aggs, in order, under the filters agg', () => {
|
||||
config.split = ['beer:5', 'wine:10', ':lemo:nade::15', ':jui:ce:723::45'];
|
||||
const request = fn(config, tlConfig, emptyScriptedFields);
|
||||
const request = fn(config, tlConfig, {});
|
||||
|
||||
let aggs = request.params.body.aggs.q.aggs;
|
||||
|
||||
|
@ -362,19 +363,21 @@ describe('es', () => {
|
|||
|
||||
test('adds scripted terms aggs, in order, under the filters agg', () => {
|
||||
config.split = ['scriptedBeer:5', 'scriptedWine:10'];
|
||||
const scriptedFields = [
|
||||
{
|
||||
name: 'scriptedBeer',
|
||||
script: 'doc["beer"].value',
|
||||
lang: 'painless',
|
||||
const scriptFields = {
|
||||
scriptedBeer: {
|
||||
script: {
|
||||
source: 'doc["beer"].value',
|
||||
lang: 'painless',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'scriptedWine',
|
||||
script: 'doc["wine"].value',
|
||||
lang: 'painless',
|
||||
scriptedWine: {
|
||||
script: {
|
||||
source: 'doc["wine"].value',
|
||||
lang: 'painless',
|
||||
},
|
||||
},
|
||||
];
|
||||
const request = fn(config, tlConfig, scriptedFields);
|
||||
};
|
||||
const request = fn(config, tlConfig, scriptFields);
|
||||
|
||||
const aggs = request.params.body.aggs.q.aggs;
|
||||
|
||||
|
|
|
@ -101,11 +101,10 @@ export default new Datasource('es', {
|
|||
(index) => index.title === config.index
|
||||
);
|
||||
|
||||
const scriptedFields = indexPatternSpec?.getScriptedFields() ?? [];
|
||||
|
||||
const { scriptFields = {}, runtimeFields = {} } = indexPatternSpec?.getComputedFields() ?? {};
|
||||
const esShardTimeout = tlConfig.esShardTimeout;
|
||||
|
||||
const body = buildRequest(config, tlConfig, scriptedFields, esShardTimeout);
|
||||
const body = buildRequest(config, tlConfig, scriptFields, runtimeFields, esShardTimeout);
|
||||
|
||||
const resp = await tlConfig.context.search
|
||||
.search(
|
||||
|
|
|
@ -6,21 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export function buildAggBody(fieldName, scriptedFields) {
|
||||
const scriptedField = scriptedFields.find((field) => {
|
||||
return field.name === fieldName;
|
||||
});
|
||||
|
||||
if (scriptedField) {
|
||||
return {
|
||||
script: {
|
||||
source: scriptedField.script,
|
||||
lang: scriptedField.lang,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
export const buildAggBody = (fieldName, scriptFields) =>
|
||||
scriptFields[fieldName] ?? {
|
||||
field: fieldName,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { buildAggBody } from './agg_body';
|
|||
import createDateAgg from './create_date_agg';
|
||||
import { UI_SETTINGS } from '../../../../../data/server';
|
||||
|
||||
export default function buildRequest(config, tlConfig, scriptedFields, timeout) {
|
||||
export default function buildRequest(config, tlConfig, scriptFields, runtimeFields, timeout) {
|
||||
const bool = { must: [] };
|
||||
|
||||
const timeFilter = {
|
||||
|
@ -51,7 +51,7 @@ export default function buildRequest(config, tlConfig, scriptedFields, timeout)
|
|||
(config.split || []).forEach((clause) => {
|
||||
const [field, arg] = clause.split(/:(\d+$)/);
|
||||
if (field && arg) {
|
||||
const termsAgg = buildAggBody(field, scriptedFields);
|
||||
const termsAgg = buildAggBody(field, scriptFields);
|
||||
termsAgg.size = parseInt(arg, 10);
|
||||
aggCursor[field] = {
|
||||
meta: { type: 'split' },
|
||||
|
@ -64,7 +64,7 @@ export default function buildRequest(config, tlConfig, scriptedFields, timeout)
|
|||
}
|
||||
});
|
||||
|
||||
_.assign(aggCursor, createDateAgg(config, tlConfig, scriptedFields));
|
||||
_.assign(aggCursor, createDateAgg(config, tlConfig, scriptFields));
|
||||
|
||||
const request = {
|
||||
index: config.index,
|
||||
|
@ -75,6 +75,7 @@ export default function buildRequest(config, tlConfig, scriptedFields, timeout)
|
|||
},
|
||||
aggs: aggs,
|
||||
size: 0,
|
||||
runtime_mappings: runtimeFields,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import { search, METRIC_TYPES } from '../../../../../data/server';
|
|||
|
||||
const { dateHistogramInterval } = search.aggs;
|
||||
|
||||
export default function createDateAgg(config, tlConfig, scriptedFields) {
|
||||
export default function createDateAgg(config, tlConfig, scriptFields) {
|
||||
const dateAgg = {
|
||||
time_buckets: {
|
||||
meta: { type: 'time_buckets' },
|
||||
|
@ -47,7 +47,7 @@ export default function createDateAgg(config, tlConfig, scriptedFields) {
|
|||
const percentArgs = splittedArgs[1];
|
||||
const metricKey = metricName + '(' + field + ')';
|
||||
|
||||
metricBody[metricKey] = { [metricName]: buildAggBody(field, scriptedFields) };
|
||||
metricBody[metricKey] = { [metricName]: buildAggBody(field, scriptFields) };
|
||||
|
||||
if (metricName === METRIC_TYPES.PERCENTILES && percentArgs) {
|
||||
let percentList = percentArgs.split(',');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue