UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

191 lines (190 loc) 8.81 kB
import parseInt from 'lodash/parseInt'; import { ExpressionEvaluationError } from '../../parser/src/ExpressionEvaluationError'; import { handleColumnFunction, handleWhereFunction, } from './expressionFunctionUtils'; import { aggregatedScalarExpressionFunctions, } from './aggregatedScalarExpressionFunctions'; import { getTypedKeys } from '../Extensions/TypeExtensions'; const SUPPORTED_AGGREGATION_FNS = [ 'SUM', 'MIN', 'MAX', 'AVG', 'COUNT', ]; const aggregationScalarOperandMap = { SUM: ['string', 'number'], MIN: ['string', 'number'], MAX: ['string', 'number'], AVG: ['string', 'number'], COUNT: ['string', 'number'], }; // 0. optional non-capturing block of empty spaces // 1. block containing a numeric value (any digits) (required) // 2. block containing a single 'K', 'M' or 'B' letter (optional) (case insensitive) const CRITERIA_REGEX = /^(?:\s*)(\d+)(K|M|B)?$/i; export const aggregatedBooleanExpressionFunctions = { WHERE: { handler(args, context) { return handleWhereFunction(args, context); }, isHiddenFromMenu: true, description: 'Splits 2 composed queries, allowing for a main query(lhs) to have a where clause query(rhs) defined', signatures: ['<main_query> WHERE <boolean_query>'], examples: ['<main_query> WHERE <boolean_query>', '<main_query> WHERE QUERY("abc")'], hasEagerEvaluation: true, category: 'conditional', }, SUM: aggregatedScalarExpressionFunctions['SUM'], MIN: aggregatedScalarExpressionFunctions['MIN'], MAX: aggregatedScalarExpressionFunctions['MAX'], AVG: aggregatedScalarExpressionFunctions['AVG'], WEIGHT: aggregatedScalarExpressionFunctions['WEIGHT'], COUNT: aggregatedScalarExpressionFunctions['COUNT'], GROUP_BY: aggregatedScalarExpressionFunctions['GROUP_BY'], EQ: { handler(args, context) { return buildBooleanAggregationParameter(args, context, '=', (aggregatedValue, conditionValue) => aggregatedValue === conditionValue); }, isHiddenFromMenu: true, description: 'Evaluates if the aggregation result equals a numerical value defined either as a number or a string abbreviation for thousands(K), millions(M) or billions(B)', signatures: [ 'SUM() = number', `SUM() = '%number%(K|M|B)'`, 'EQ(a: SUM(), b: number)', `EQ(a: SUM(), b: '%number%(K|M|B)')`, ], examples: [`SUM([col1]) = '5M'`, 'EQ( SUM([col1]), 250000)'], category: 'comparison', }, NEQ: { handler(args, context) { return buildBooleanAggregationParameter(args, context, '!=', (aggregatedValue, conditionValue) => aggregatedValue !== conditionValue); }, isHiddenFromMenu: true, description: 'Evaluates if the aggregation result is NOT equal with a numerical value defined either as a number or a string abbreviation for thousands(K), millions(M) or billions(B)', signatures: [ 'SUM() != number', `SUM() != '%number%(K|M|B)'`, 'NEQ(a: SUM(), b: number)', `NEQ(a: SUM(), b: '%number%(K|M|B)')`, ], examples: [`SUM([col1]) != '5M'`, 'NEQ( SUM([col1]), 250000)'], category: 'comparison', }, LT: { handler(args, context) { return buildBooleanAggregationParameter(args, context, '<', (aggregatedValue, conditionValue) => aggregatedValue < conditionValue); }, isHiddenFromMenu: true, description: 'Evaluates if the aggregation result is less than a numerical value defined either as a number or a string abbreviation for thousands(K), millions(M) or billions(B)', signatures: [ 'SUM() < number', `SUM() < '%number%(K|M|B)'`, 'LT(a: SUM(), b: number)', `LT(a: SUM(), b: '%number%(K|M|B)')`, ], examples: [`SUM([col1]) < '5M'`, 'LT( SUM([col1]), 250000)'], category: 'comparison', }, LTE: { handler(args, context) { return buildBooleanAggregationParameter(args, context, '<=', (aggregatedValue, conditionValue) => aggregatedValue <= conditionValue); }, isHiddenFromMenu: true, description: 'Evaluates if the aggregation result is less than or equals a numerical value defined either as a number or a string abbreviation for thousands(K), millions(M) or billions(B)', signatures: [ 'SUM() <= number', `SUM() <= '%number%(K|M|B)'`, 'LTE(a: SUM(), b: number)', `LTE(a: SUM(), b: '%number%(K|M|B)')`, ], examples: [`SUM([col1]) <= '5M'`, 'LTE( SUM([col1]), 250000)'], category: 'comparison', }, GT: { handler(args, context) { return buildBooleanAggregationParameter(args, context, '>', (aggregatedValue, conditionValue) => aggregatedValue > conditionValue); }, isHiddenFromMenu: true, description: 'Evaluates if the aggregation result is greater than a numerical value defined either as a number or a string abbreviation for thousands(K), millions(M) or billions(B)', signatures: [ 'SUM() > number', `SUM() > '%number%(K|M|B)'`, 'GT(a: SUM(), b: number)', `GT(a: SUM(), b: '%number%(K|M|B)')`, ], examples: [`SUM([col1]) > '5M'`, 'GT( SUM([col1]), 250000)'], category: 'comparison', }, GTE: { handler(args, context) { return buildBooleanAggregationParameter(args, context, '>=', (aggregatedValue, conditionValue) => aggregatedValue >= conditionValue); }, isHiddenFromMenu: true, description: 'Evaluates if the aggregation result is greater than or equals a numerical value defined either as a number or a string abbreviation for thousands(K), millions(M) or billions(B)', signatures: [ 'SUM() >= number', `SUM() >= '%number%(K|M|B)'`, 'GTE(a: SUM(), b: number)', `GTE(a: SUM(), b: '%number%(K|M|B)')`, ], examples: [`SUM([col1]) >= '5M'`, 'GTE( SUM([col1]), 250000)'], category: 'comparison', }, COL: { handler(args, context) { return handleColumnFunction(args, context); }, description: 'References a column by its unique identifier', signatures: ['[colName]', 'COL(name: string)'], examples: ['[col1]', "COL('col1')"], category: 'special', }, }; const buildBooleanAggregationParameter = (args, context, comparisonOperator, conditionFn) => { // LHS const aggregationOperand = extractScalarAggregationOperand(comparisonOperator, args[0]); // RHS const scalarOperand = extractScalarOperand(comparisonOperator, args[1], aggregationScalarOperandMap[aggregationOperand.name]); return { type: 'aggregationBoolean', name: aggregationOperand.name, scalarAggregation: aggregationOperand, conditionValue: scalarOperand, conditionFn, }; }; const extractScalarAggregationOperand = (consumingFunctionName, argument) => { const result = argument; if (result == undefined || result.type !== 'aggregationScalar' || !SUPPORTED_AGGREGATION_FNS.includes(result.name)) { throw new ExpressionEvaluationError(consumingFunctionName, `comparison operator expects as a left-hand operand an aggregation function of type '${SUPPORTED_AGGREGATION_FNS.join(' | ')}'`); } return result; }; const extractScalarOperand = (consumingFunctionName, value, allowedTypes) => { if (value == undefined || !allowedTypes.find((allowedType) => typeof value === allowedType)) { throw new ExpressionEvaluationError(consumingFunctionName, `comparison operator expects as a right-hand operand a scalar value of type '${allowedTypes.join(' | ')}'`); } if (typeof value === 'number') { return value; } // assertion is safe, we checked in the IF above const stringValue = value; const matchingResult = stringValue.match(CRITERIA_REGEX); const numericString = matchingResult?.[1]; const largeNumberUnit = matchingResult?.[2] ?? 'number'; if (!numericString) { throw new ExpressionEvaluationError('Numeric abbreviation', `"${stringValue}" is invalid, expecting a number with a suffix for thousands(K), millions(M) or billions(B)"`); } // unit -> number const numberUnitRatios = { number: 1, k: 1000, m: 1000000, b: 1000000000, }; // numeric value return parseInt(numericString) * numberUnitRatios[largeNumberUnit.toLowerCase()]; }; export const aggregatedBooleanExpressionFunctionNames = getTypedKeys(aggregatedBooleanExpressionFunctions);