Skip to content

Commit

Permalink
build optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
alexwizp committed Sep 2, 2021
1 parent ca072c7 commit fb2e83a
Show file tree
Hide file tree
Showing 19 changed files with 362 additions and 277 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
buildResultColumns,
getBucketIdentifier,
} from '../../../../../../src/plugins/expressions/common';
import type { CounterRateExpressionFunction } from './types';

export const counterRateFn: CounterRateExpressionFunction['fn'] = (
input,
{ by, inputColumnId, outputColumnId, outputColumnName }
) => {
const resultColumns = buildResultColumns(input, outputColumnId, inputColumnId, outputColumnName);

if (!resultColumns) {
return input;
}
const previousValues: Partial<Record<string, number>> = {};

return {
...input,
columns: resultColumns,
rows: input.rows.map((row) => {
const newRow = { ...row };

const bucketIdentifier = getBucketIdentifier(row, by);
const previousValue = previousValues[bucketIdentifier];
const currentValue = newRow[inputColumnId];
if (currentValue != null && previousValue != null) {
const currentValueAsNumber = Number(currentValue);
if (currentValueAsNumber >= previousValue) {
newRow[outputColumnId] = currentValueAsNumber - previousValue;
} else {
newRow[outputColumnId] = currentValueAsNumber;
}
} else {
newRow[outputColumnId] = undefined;
}

if (currentValue != null) {
previousValues[bucketIdentifier] = Number(currentValue);
} else {
previousValues[bucketIdentifier] = undefined;
}

return newRow;
}),
};
};
64 changes: 7 additions & 57 deletions x-pack/plugins/lens/common/expressions/counter_rate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,8 @@
*/

import { i18n } from '@kbn/i18n';
import {
getBucketIdentifier,
buildResultColumns,
} from '../../../../../../src/plugins/expressions/common';
import type {
ExpressionFunctionDefinition,
Datatable,
} from '../../../../../../src/plugins/expressions/common';

import type { CounterRateExpressionFunction } from './types';

export interface CounterRateArgs {
by?: string[];
Expand All @@ -22,13 +16,6 @@ export interface CounterRateArgs {
outputColumnName?: string;
}

export type ExpressionFunctionCounterRate = ExpressionFunctionDefinition<
'lens_counter_rate',
Datatable,
CounterRateArgs,
Datatable
>;

/**
* Calculates the counter rate of a specified column in the data table.
*
Expand Down Expand Up @@ -59,7 +46,7 @@ export type ExpressionFunctionCounterRate = ExpressionFunctionDefinition<
* before comparison. If the values are objects, the return value of their `toString` method will be used for comparison.
* Missing values (`null` and `undefined`) will be treated as empty strings.
*/
export const counterRate: ExpressionFunctionCounterRate = {
export const counterRate: CounterRateExpressionFunction = {
name: 'lens_counter_rate',
type: 'datatable',

Expand Down Expand Up @@ -101,46 +88,9 @@ export const counterRate: ExpressionFunctionCounterRate = {
},
},

fn(input, { by, inputColumnId, outputColumnId, outputColumnName }) {
const resultColumns = buildResultColumns(
input,
outputColumnId,
inputColumnId,
outputColumnName
);

if (!resultColumns) {
return input;
}
const previousValues: Partial<Record<string, number>> = {};
return {
...input,
columns: resultColumns,
rows: input.rows.map((row) => {
const newRow = { ...row };

const bucketIdentifier = getBucketIdentifier(row, by);
const previousValue = previousValues[bucketIdentifier];
const currentValue = newRow[inputColumnId];
if (currentValue != null && previousValue != null) {
const currentValueAsNumber = Number(currentValue);
if (currentValueAsNumber >= previousValue) {
newRow[outputColumnId] = currentValueAsNumber - previousValue;
} else {
newRow[outputColumnId] = currentValueAsNumber;
}
} else {
newRow[outputColumnId] = undefined;
}

if (currentValue != null) {
previousValues[bucketIdentifier] = Number(currentValue);
} else {
previousValues[bucketIdentifier] = undefined;
}

return newRow;
}),
};
async fn(...args) {
/** Build optimization: prevent adding extra code into initial bundle **/
const { counterRateFn } = await import('./counter_rate_fn');
return counterRateFn(...args);
},
};
16 changes: 16 additions & 0 deletions x-pack/plugins/lens/common/expressions/counter_rate/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { Datatable, ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions';
import { CounterRateArgs } from './index';

export type CounterRateExpressionFunction = ExpressionFunctionDefinition<
'lens_counter_rate',
Datatable,
CounterRateArgs,
Datatable | Promise<Datatable>
>;
94 changes: 7 additions & 87 deletions x-pack/plugins/lens/common/expressions/datatable/datatable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,10 @@
*/

import { i18n } from '@kbn/i18n';
import { cloneDeep } from 'lodash';
import type {
ExecutionContext,
DatatableColumnMeta,
ExpressionFunctionDefinition,
} from '../../../../../../src/plugins/expressions/common';
import type { ExecutionContext } from '../../../../../../src/plugins/expressions/common';
import type { FormatFactory, LensMultiTable } from '../../types';
import type { ColumnConfigArg } from './datatable_column';
import { getSortingCriteria } from './sorting';
import { computeSummaryRowForColumn } from './summary';
import { transposeTable } from './transpose_helpers';
import type { DatatableExpressionFunction } from './types';

export interface SortingState {
columnId: string | undefined;
Expand All @@ -43,18 +36,9 @@ export interface DatatableArgs {
sortingDirection: SortingState['direction'];
}

function isRange(meta: { params?: { id?: string } } | undefined) {
return meta?.params?.id === 'range';
}

export const getDatatable = (
getFormatFactory: (context: ExecutionContext) => FormatFactory | Promise<FormatFactory>
): ExpressionFunctionDefinition<
'lens_datatable',
LensMultiTable,
DatatableArgs,
Promise<DatatableRender>
> => ({
): DatatableExpressionFunction => ({
name: 'lens_datatable',
type: 'render',
inputTypes: ['lens_multitable'],
Expand Down Expand Up @@ -86,73 +70,9 @@ export const getDatatable = (
help: '',
},
},
async fn(data, args, context) {
let untransposedData: LensMultiTable | undefined;
// do the sorting at this level to propagate it also at CSV download
const [firstTable] = Object.values(data.tables);
const [layerId] = Object.keys(context.inspectorAdapters.tables || {});
const formatters: Record<string, ReturnType<FormatFactory>> = {};
const formatFactory = await getFormatFactory(context);

firstTable.columns.forEach((column) => {
formatters[column.id] = formatFactory(column.meta?.params);
});

const hasTransposedColumns = args.columns.some((c) => c.isTransposed);
if (hasTransposedColumns) {
// store original shape of data separately
untransposedData = cloneDeep(data);
// transposes table and args inplace
transposeTable(args, firstTable, formatters);
}

const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args;

const columnsReverseLookup = firstTable.columns.reduce<
Record<string, { name: string; index: number; meta?: DatatableColumnMeta }>
>((memo, { id, name, meta }, i) => {
memo[id] = { name, index: i, meta };
return memo;
}, {});

const columnsWithSummary = args.columns.filter((c) => c.summaryRow);
for (const column of columnsWithSummary) {
column.summaryRowValue = computeSummaryRowForColumn(
column,
firstTable,
formatters,
formatFactory({ id: 'number' })
);
}

if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') {
// Sort on raw values for these types, while use the formatted value for the rest
const sortingCriteria = getSortingCriteria(
isRange(columnsReverseLookup[sortBy]?.meta)
? 'range'
: columnsReverseLookup[sortBy]?.meta?.type,
sortBy,
formatters[sortBy],
sortDirection
);
// replace the table here
context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || [])
.slice()
.sort(sortingCriteria);
// replace also the local copy
firstTable.rows = context.inspectorAdapters.tables[layerId].rows;
} else {
args.sortingColumnId = undefined;
args.sortingDirection = 'none';
}
return {
type: 'render',
as: 'lens_datatable_renderer',
value: {
data,
untransposedData,
args,
},
};
async fn(...args) {
/** Build optimization: prevent adding extra code into initial bundle **/
const { datatableFn } = await import('./datatable_fn');
return datatableFn(getFormatFactory)(...args);
},
});
94 changes: 94 additions & 0 deletions x-pack/plugins/lens/common/expressions/datatable/datatable_fn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { cloneDeep } from 'lodash';
import { FormatFactory, LensMultiTable } from '../../types';
import { transposeTable } from './transpose_helpers';
import { computeSummaryRowForColumn } from './summary';
import { getSortingCriteria } from './sorting';
import type {
DatatableColumnMeta,
ExecutionContext,
} from '../../../../../../src/plugins/expressions';
import type { DatatableExpressionFunction } from './types';

function isRange(meta: { params?: { id?: string } } | undefined) {
return meta?.params?.id === 'range';
}

export const datatableFn = (
getFormatFactory: (context: ExecutionContext) => FormatFactory | Promise<FormatFactory>
) =>
(async (data, args, context) => {
let untransposedData: LensMultiTable | undefined;
// do the sorting at this level to propagate it also at CSV download
const [firstTable] = Object.values(data.tables);
const [layerId] = Object.keys(context.inspectorAdapters.tables || {});
const formatters: Record<string, ReturnType<FormatFactory>> = {};
const formatFactory = await getFormatFactory(context);

firstTable.columns.forEach((column) => {
formatters[column.id] = formatFactory(column.meta?.params);
});

const hasTransposedColumns = args.columns.some((c) => c.isTransposed);
if (hasTransposedColumns) {
// store original shape of data separately
untransposedData = cloneDeep(data);
// transposes table and args inplace
transposeTable(args, firstTable, formatters);
}

const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args;

const columnsReverseLookup = firstTable.columns.reduce<
Record<string, { name: string; index: number; meta?: DatatableColumnMeta }>
>((memo, { id, name, meta }, i) => {
memo[id] = { name, index: i, meta };
return memo;
}, {});

const columnsWithSummary = args.columns.filter((c) => c.summaryRow);
for (const column of columnsWithSummary) {
column.summaryRowValue = computeSummaryRowForColumn(
column,
firstTable,
formatters,
formatFactory({ id: 'number' })
);
}

if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') {
// Sort on raw values for these types, while use the formatted value for the rest
const sortingCriteria = getSortingCriteria(
isRange(columnsReverseLookup[sortBy]?.meta)
? 'range'
: columnsReverseLookup[sortBy]?.meta?.type,
sortBy,
formatters[sortBy],
sortDirection
);
// replace the table here
context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || [])
.slice()
.sort(sortingCriteria);
// replace also the local copy
firstTable.rows = context.inspectorAdapters.tables[layerId].rows;
} else {
args.sortingColumnId = undefined;
args.sortingDirection = 'none';
}
return {
type: 'render',
as: 'lens_datatable_renderer',
value: {
data,
untransposedData,
args,
},
};
}) as DatatableExpressionFunction['fn'];
17 changes: 17 additions & 0 deletions x-pack/plugins/lens/common/expressions/datatable/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions';
import type { LensMultiTable } from '../../types';
import type { DatatableArgs, DatatableRender } from './datatable';

export type DatatableExpressionFunction = ExpressionFunctionDefinition<
'lens_datatable',
LensMultiTable,
DatatableArgs,
Promise<DatatableRender>
>;
Loading

0 comments on commit fb2e83a

Please sign in to comment.