Aggregated queries #1 (#8345)

First step of https://github.com/twentyhq/twenty/issues/6868

Adds min.., max.. queries for DATETIME fields
adds min.., max.., avg.., sum.. queries for NUMBER fields 

(count distinct operation and composite fields such as CURRENCY handling
will be dealt with in a future PR)

<img width="1422" alt="Capture d’écran 2024-11-06 à 15 48 46"
src="https://github.com/user-attachments/assets/4bcdece0-ad3e-4536-9720-fe4044a36719">

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Marie
2024-11-14 18:05:05 +01:00
committed by GitHub
parent c966533f26
commit a799370483
93 changed files with 1590 additions and 1178 deletions

View File

@ -30,12 +30,10 @@ export class CreateManyResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.createMany(args, options);

View File

@ -30,12 +30,10 @@ export class CreateOneResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.createOne(args, options);

View File

@ -30,12 +30,10 @@ export class DeleteManyResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.deleteMany(args, options);

View File

@ -30,12 +30,10 @@ export class DeleteOneResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.deleteOne(args, options);

View File

@ -30,12 +30,10 @@ export class DestroyManyResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.destroyMany(args, options);

View File

@ -30,12 +30,10 @@ export class DestroyOneResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphQLQueryRunnerService.destroyOne(args, options);

View File

@ -30,12 +30,10 @@ export class FindDuplicatesResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.findDuplicates(

View File

@ -30,12 +30,10 @@ export class FindManyResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.findMany(args, options);

View File

@ -30,12 +30,10 @@ export class FindOneResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.findOne(args, options);

View File

@ -30,12 +30,10 @@ export class RestoreManyResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.restoreMany(args, options);

View File

@ -28,12 +28,10 @@ export class SearchResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.search(args, options);

View File

@ -30,12 +30,10 @@ export class UpdateManyResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.updateMany(args, options);

View File

@ -30,12 +30,10 @@ export class UpdateOneResolverFactory
try {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
objectMetadataItem: internalContext.objectMetadataItem,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
objectMetadataMap: internalContext.objectMetadataMap,
objectMetadataMapItem: internalContext.objectMetadataMapItem,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.updateOne(args, options);

View File

@ -1,14 +0,0 @@
import { Record as IRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
export interface PGGraphQLResponse<Data = any> {
resolve: {
data: Data;
};
}
export type PGGraphQLResult<Data = any> = [PGGraphQLResponse<Data>];
export interface PGGraphQLMutation<Record = IRecord> {
affectedRows: number;
records: Record[];
}

View File

@ -1,10 +1,10 @@
import { GraphQLFieldResolver } from 'graphql';
import {
Record,
RecordFilter,
RecordOrderBy,
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
ObjectRecord,
ObjectRecordFilter,
ObjectRecordOrderBy,
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
import { workspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/factories/factories';
@ -26,8 +26,8 @@ export enum ResolverArgsType {
}
export interface FindManyResolverArgs<
Filter extends RecordFilter = RecordFilter,
OrderBy extends RecordOrderBy = RecordOrderBy,
Filter extends ObjectRecordFilter = ObjectRecordFilter,
OrderBy extends ObjectRecordOrderBy = ObjectRecordOrderBy,
> {
first?: number;
last?: number;
@ -42,14 +42,14 @@ export interface FindOneResolverArgs<Filter = any> {
}
export interface FindDuplicatesResolverArgs<
Data extends Partial<Record> = Partial<Record>,
Data extends Partial<ObjectRecord> = Partial<ObjectRecord>,
> {
ids?: string[];
data?: Data[];
}
export interface SearchResolverArgs<
Filter extends RecordFilter = RecordFilter,
Filter extends ObjectRecordFilter = ObjectRecordFilter,
> {
searchInput?: string;
filter?: Filter;
@ -57,28 +57,28 @@ export interface SearchResolverArgs<
}
export interface CreateOneResolverArgs<
Data extends Partial<Record> = Partial<Record>,
Data extends Partial<ObjectRecord> = Partial<ObjectRecord>,
> {
data: Data;
upsert?: boolean;
}
export interface CreateManyResolverArgs<
Data extends Partial<Record> = Partial<Record>,
Data extends Partial<ObjectRecord> = Partial<ObjectRecord>,
> {
data: Data[];
upsert?: boolean;
}
export interface UpdateOneResolverArgs<
Data extends Partial<Record> = Partial<Record>,
Data extends Partial<ObjectRecord> = Partial<ObjectRecord>,
> {
id: string;
data: Data;
}
export interface UpdateManyResolverArgs<
Data extends Partial<Record> = Partial<Record>,
Data extends Partial<ObjectRecord> = Partial<ObjectRecord>,
Filter = any,
> {
filter: Filter;

View File

@ -2,8 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
import { IResolvers } from '@graphql-tools/utils';
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
import { DeleteManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory';
import { DestroyManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory';
import { DestroyOneResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory';
@ -11,7 +9,7 @@ import { RestoreManyResolverFactory } from 'src/engine/api/graphql/workspace-res
import { SearchResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory';
import { UpdateManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { ObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
import { getResolverName } from 'src/engine/utils/get-resolver-name.util';
import { CreateManyResolverFactory } from './factories/create-many-resolver.factory';
@ -49,8 +47,7 @@ export class WorkspaceResolverFactory {
async create(
authContext: AuthContext,
objectMetadataCollection: ObjectMetadataInterface[],
objectMetadataMap: ObjectMetadataMap,
objectMetadataMaps: ObjectMetadataMaps,
workspaceResolverBuilderMethods: WorkspaceResolverBuilderMethods,
): Promise<IResolvers> {
const factories = new Map<
@ -76,7 +73,7 @@ export class WorkspaceResolverFactory {
Mutation: {},
};
for (const objectMetadata of objectMetadataCollection) {
for (const objectMetadata of Object.values(objectMetadataMaps.byId)) {
// Generate query resolvers
for (const methodName of workspaceResolverBuilderMethods.queries) {
const resolverName = getResolverName(objectMetadata, methodName);
@ -94,11 +91,8 @@ export class WorkspaceResolverFactory {
resolvers.Query[resolverName] = resolverFactory.create({
authContext,
objectMetadataItem: objectMetadata,
fieldMetadataCollection: objectMetadata.fields,
objectMetadataCollection,
objectMetadataMap,
objectMetadataMapItem: objectMetadataMap[objectMetadata.nameSingular],
objectMetadataMaps,
objectMetadataItemWithFieldMaps: objectMetadata,
});
}
@ -119,11 +113,8 @@ export class WorkspaceResolverFactory {
resolvers.Mutation[resolverName] = resolverFactory.create({
authContext,
objectMetadataItem: objectMetadata,
fieldMetadataCollection: objectMetadata.fields,
objectMetadataCollection,
objectMetadataMap,
objectMetadataMapItem: objectMetadataMap[objectMetadata.nameSingular],
objectMetadataMaps,
objectMetadataItemWithFieldMaps: objectMetadata,
});
}
}