7fd89678b7
# Introduction Avoid having multiple `isDefined` definition across our pacakges Also avoid importing `isDefined` from `twenty-ui` which exposes a huge barrel for a such little util function ## In a nutshell Removed own `isDefined.ts` definition from `twenty-ui` `twenty-front` and `twenty-server` to move it to `twenty-shared`. Updated imports for each packages, and added explicit dependencies to `twenty-shared` if not already in place Related PR https://github.com/twentyhq/twenty/pull/9941
100 lines
3.4 KiB
TypeScript
100 lines
3.4 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
|
|
import { SelectQueryBuilder } from 'typeorm';
|
|
import { isDefined } from 'twenty-shared';
|
|
|
|
import { AGGREGATE_OPERATIONS } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
|
import { AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
|
import { formatColumnNamesFromCompositeFieldAndSubfields } from 'src/engine/twenty-orm/utils/format-column-names-from-composite-field-and-subfield.util';
|
|
|
|
@Injectable()
|
|
export class ProcessAggregateHelper {
|
|
public addSelectedAggregatedFieldsQueriesToQueryBuilder = ({
|
|
selectedAggregatedFields,
|
|
queryBuilder,
|
|
}: {
|
|
selectedAggregatedFields: Record<string, AggregationField>;
|
|
queryBuilder: SelectQueryBuilder<any>;
|
|
}) => {
|
|
queryBuilder.select([]);
|
|
|
|
for (const [aggregatedFieldName, aggregatedField] of Object.entries(
|
|
selectedAggregatedFields,
|
|
)) {
|
|
if (
|
|
!isDefined(aggregatedField?.fromField) ||
|
|
!isDefined(aggregatedField?.aggregateOperation)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const columnNames = formatColumnNamesFromCompositeFieldAndSubfields(
|
|
aggregatedField.fromField,
|
|
aggregatedField.fromSubFields,
|
|
);
|
|
|
|
const columnNameForNumericOperation = isDefined(
|
|
aggregatedField.subFieldForNumericOperation,
|
|
)
|
|
? formatColumnNamesFromCompositeFieldAndSubfields(
|
|
aggregatedField.fromField,
|
|
[aggregatedField.subFieldForNumericOperation],
|
|
)[0]
|
|
: columnNames[0];
|
|
|
|
if (
|
|
!Object.values(AGGREGATE_OPERATIONS).includes(
|
|
aggregatedField.aggregateOperation,
|
|
)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const concatenatedColumns = columnNames
|
|
.map((col) => `"${col}"`)
|
|
.join(',');
|
|
|
|
const columnExpression = `NULLIF(CONCAT(${concatenatedColumns}), '')`;
|
|
|
|
switch (aggregatedField.aggregateOperation) {
|
|
case AGGREGATE_OPERATIONS.countEmpty:
|
|
queryBuilder.addSelect(
|
|
`CASE WHEN COUNT(*) = 0 THEN NULL ELSE COUNT(*) - COUNT(${columnExpression}) END`,
|
|
`${aggregatedFieldName}`,
|
|
);
|
|
break;
|
|
case AGGREGATE_OPERATIONS.countNotEmpty:
|
|
queryBuilder.addSelect(
|
|
`CASE WHEN COUNT(*) = 0 THEN NULL ELSE COUNT(${columnExpression}) END`,
|
|
`${aggregatedFieldName}`,
|
|
);
|
|
break;
|
|
case AGGREGATE_OPERATIONS.countUniqueValues:
|
|
queryBuilder.addSelect(
|
|
`CASE WHEN COUNT(*) = 0 THEN NULL ELSE COUNT(DISTINCT ${columnExpression}) END`,
|
|
`${aggregatedFieldName}`,
|
|
);
|
|
break;
|
|
case AGGREGATE_OPERATIONS.percentageEmpty:
|
|
queryBuilder.addSelect(
|
|
`CASE WHEN COUNT(*) = 0 THEN NULL ELSE CAST(((COUNT(*) - COUNT(${columnExpression})::decimal) / COUNT(*)) AS DECIMAL) END`,
|
|
`${aggregatedFieldName}`,
|
|
);
|
|
break;
|
|
case AGGREGATE_OPERATIONS.percentageNotEmpty:
|
|
queryBuilder.addSelect(
|
|
`CASE WHEN COUNT(*) = 0 THEN NULL ELSE CAST((COUNT(${columnExpression})::decimal / COUNT(*)) AS DECIMAL) END`,
|
|
`${aggregatedFieldName}`,
|
|
);
|
|
break;
|
|
default: {
|
|
queryBuilder.addSelect(
|
|
`${aggregatedField.aggregateOperation}("${columnNameForNumericOperation}")`,
|
|
`${aggregatedFieldName}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|