Aggregate count variations (#9304)

Closes https://github.com/twentyhq/private-issues/issues/222

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Marie
2025-01-02 17:35:05 +01:00
committed by GitHub
parent 0f1458cbe9
commit 5d857fbfb5
43 changed files with 650 additions and 203 deletions

View File

@ -1,6 +1,10 @@
import { GraphQLISODateTime } from '@nestjs/graphql';
import { GraphQLFloat, GraphQLInt, GraphQLScalarType } from 'graphql';
import {
getColumnNameForAggregateOperation,
getSubfieldForAggregateOperation,
} from 'twenty-shared';
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
@ -12,6 +16,7 @@ export type AggregationField = {
type: GraphQLScalarType;
description: string;
fromField: string;
fromFieldType: FieldMetadataType;
fromSubField?: string;
aggregateOperation: AGGREGATE_OPERATIONS;
};
@ -19,94 +24,164 @@ export type AggregationField = {
export const getAvailableAggregationsFromObjectFields = (
fields: FieldMetadataInterface[],
): Record<string, AggregationField> => {
return fields.reduce<Record<string, AggregationField>>((acc, field) => {
acc['totalCount'] = {
type: GraphQLInt,
description: `Total number of records in the connection`,
fromField: 'id',
aggregateOperation: AGGREGATE_OPERATIONS.count,
};
return fields.reduce<Record<string, AggregationField>>(
(acc, field) => {
if (field.type === FieldMetadataType.RELATION) {
return acc;
}
if (field.type === FieldMetadataType.DATE_TIME) {
acc[`min${capitalize(field.name)}`] = {
type: GraphQLISODateTime,
description: `Oldest date contained in the field ${field.name}`,
const columnName = getColumnNameForAggregateOperation(
field.name,
field.type,
);
const fromSubField = getSubfieldForAggregateOperation(field.type);
acc[`countUniqueValues${capitalize(columnName)}`] = {
type: GraphQLInt,
description: `Number of unique values for ${field.name}`,
fromField: field.name,
aggregateOperation: AGGREGATE_OPERATIONS.min,
fromFieldType: field.type,
fromSubField,
aggregateOperation: AGGREGATE_OPERATIONS.countUniqueValues,
};
acc[`max${capitalize(field.name)}`] = {
type: GraphQLISODateTime,
description: `Most recent date contained in the field ${field.name}`,
acc[`countEmpty${capitalize(columnName)}`] = {
type: GraphQLInt,
description: `Number of empty values for ${field.name}`,
fromField: field.name,
aggregateOperation: AGGREGATE_OPERATIONS.max,
fromFieldType: field.type,
fromSubField,
aggregateOperation: AGGREGATE_OPERATIONS.countEmpty,
};
}
if (field.type === FieldMetadataType.NUMBER) {
acc[`min${capitalize(field.name)}`] = {
acc[`countNotEmpty${capitalize(columnName)}`] = {
type: GraphQLInt,
description: `Number of non-empty values for ${field.name}`,
fromField: field.name,
fromFieldType: field.type,
fromSubField,
aggregateOperation: AGGREGATE_OPERATIONS.countNotEmpty,
};
acc[`percentageEmpty${capitalize(columnName)}`] = {
type: GraphQLFloat,
description: `Minimum amount contained in the field ${field.name}`,
description: `Percentage of empty values for ${field.name}`,
fromField: field.name,
aggregateOperation: AGGREGATE_OPERATIONS.min,
fromFieldType: field.type,
fromSubField,
aggregateOperation: AGGREGATE_OPERATIONS.percentageEmpty,
};
acc[`max${capitalize(field.name)}`] = {
acc[`percentageNotEmpty${capitalize(columnName)}`] = {
type: GraphQLFloat,
description: `Maximum amount contained in the field ${field.name}`,
description: `Percentage of non-empty values for ${field.name}`,
fromField: field.name,
aggregateOperation: AGGREGATE_OPERATIONS.max,
fromFieldType: field.type,
fromSubField,
aggregateOperation: AGGREGATE_OPERATIONS.percentageNotEmpty,
};
acc[`avg${capitalize(field.name)}`] = {
type: GraphQLFloat,
description: `Average amount contained in the field ${field.name}`,
fromField: field.name,
aggregateOperation: AGGREGATE_OPERATIONS.avg,
};
switch (field.type) {
case FieldMetadataType.DATE_TIME:
acc[`min${capitalize(field.name)}`] = {
type: GraphQLISODateTime,
description: `Oldest date contained in the field ${field.name}`,
fromField: field.name,
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.min,
};
acc[`sum${capitalize(field.name)}`] = {
type: GraphQLFloat,
description: `Sum of amounts contained in the field ${field.name}`,
fromField: field.name,
aggregateOperation: AGGREGATE_OPERATIONS.sum,
};
}
acc[`max${capitalize(field.name)}`] = {
type: GraphQLISODateTime,
description: `Most recent date contained in the field ${field.name}`,
fromField: field.name,
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.max,
};
break;
case FieldMetadataType.NUMBER:
acc[`min${capitalize(field.name)}`] = {
type: GraphQLFloat,
description: `Minimum amount contained in the field ${field.name}`,
fromField: field.name,
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.min,
};
if (field.type === FieldMetadataType.CURRENCY) {
acc[`min${capitalize(field.name)}AmountMicros`] = {
type: GraphQLFloat,
description: `Minimum amount contained in the field ${field.name}`,
fromField: field.name,
fromSubField: 'amountMicros',
aggregateOperation: AGGREGATE_OPERATIONS.min,
};
acc[`max${capitalize(field.name)}`] = {
type: GraphQLFloat,
description: `Maximum amount contained in the field ${field.name}`,
fromField: field.name,
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.max,
};
acc[`max${capitalize(field.name)}AmountMicros`] = {
type: GraphQLFloat,
description: `Maximal amount contained in the field ${field.name}`,
fromField: field.name,
fromSubField: 'amountMicros',
aggregateOperation: AGGREGATE_OPERATIONS.max,
};
acc[`avg${capitalize(field.name)}`] = {
type: GraphQLFloat,
description: `Average amount contained in the field ${field.name}`,
fromField: field.name,
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.avg,
};
acc[`sum${capitalize(field.name)}AmountMicros`] = {
type: GraphQLFloat,
description: `Sum of amounts contained in the field ${field.name}`,
fromField: field.name,
fromSubField: 'amountMicros',
aggregateOperation: AGGREGATE_OPERATIONS.sum,
};
acc[`sum${capitalize(field.name)}`] = {
type: GraphQLFloat,
description: `Sum of amounts contained in the field ${field.name}`,
fromField: field.name,
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.sum,
};
break;
case FieldMetadataType.CURRENCY:
acc[`min${capitalize(field.name)}AmountMicros`] = {
type: GraphQLFloat,
description: `Minimum amount contained in the field ${field.name}`,
fromField: field.name,
fromSubField: 'amountMicros',
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.min,
};
acc[`avg${capitalize(field.name)}AmountMicros`] = {
type: GraphQLFloat,
description: `Average amount contained in the field ${field.name}`,
fromField: field.name,
fromSubField: 'amountMicros',
aggregateOperation: AGGREGATE_OPERATIONS.avg,
};
}
acc[`max${capitalize(field.name)}AmountMicros`] = {
type: GraphQLFloat,
description: `Maximal amount contained in the field ${field.name}`,
fromField: field.name,
fromSubField: 'amountMicros',
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.max,
};
return acc;
}, {});
acc[`sum${capitalize(field.name)}AmountMicros`] = {
type: GraphQLFloat,
description: `Sum of amounts contained in the field ${field.name}`,
fromField: field.name,
fromSubField: 'amountMicros',
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.sum,
};
acc[`avg${capitalize(field.name)}AmountMicros`] = {
type: GraphQLFloat,
description: `Average amount contained in the field ${field.name}`,
fromField: field.name,
fromSubField: 'amountMicros',
fromFieldType: field.type,
aggregateOperation: AGGREGATE_OPERATIONS.avg,
};
break;
}
return acc;
},
{
totalCount: {
type: GraphQLInt,
description: `Total number of records in the connection`,
fromField: 'id',
fromFieldType: FieldMetadataType.UUID,
aggregateOperation: AGGREGATE_OPERATIONS.count,
},
},
);
};