Skip to content

Commit 5f155bb

Browse files
committed
Begin performance improvements
1 parent 8009d41 commit 5f155bb

File tree

7 files changed

+98
-59
lines changed

7 files changed

+98
-59
lines changed

packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/find-duplicates-query.factory.ts

+50-36
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
33
import isEmpty from 'lodash.isempty';
44

55
import { WorkspaceQueryBuilderOptions } from 'src/engine/api/graphql/workspace-query-builder/interfaces/workspace-query-builder-options.interface';
6-
import { RecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
6+
import { Record } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
77
import { FindDuplicatesResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
88

99
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
@@ -23,57 +23,71 @@ export class FindDuplicatesQueryFactory {
2323
private readonly duplicateService: DuplicateService,
2424
) {}
2525

26-
async create<Filter extends RecordFilter = RecordFilter>(
27-
args: FindDuplicatesResolverArgs<Filter>,
26+
async create(
27+
args: FindDuplicatesResolverArgs,
2828
options: WorkspaceQueryBuilderOptions,
29-
currentRecord?: Record<string, unknown>,
29+
currentRecord?: Record,
3030
) {
3131
const fieldsString = await this.fieldsStringFactory.create(
3232
options.info,
3333
options.fieldMetadataCollection,
3434
options.objectMetadataCollection,
3535
);
3636

37-
const argsData = this.getFindDuplicateBy<Filter>(
38-
args,
39-
options,
40-
currentRecord,
41-
);
37+
if (currentRecord) {
38+
return `query {
39+
${this.buildQuery(fieldsString, options, undefined, currentRecord)}
40+
}`;
41+
}
4242

43-
const duplicateCondition =
44-
this.duplicateService.buildDuplicateConditionForGraphQL(
45-
options.objectMetadataItem,
46-
argsData,
47-
args.id,
43+
const query = args.data?.reduce((acc, dataItem, index) => {
44+
const argsData = this.argsAliasFactory.create(
45+
dataItem ?? {},
46+
options.fieldMetadataCollection,
4847
);
4948

50-
const filters = stringifyWithoutKeyQuote(duplicateCondition);
49+
return (
50+
acc +
51+
this.buildQuery(
52+
fieldsString,
53+
options,
54+
argsData as Record,
55+
undefined,
56+
index,
57+
)
58+
);
59+
}, '');
5160

52-
return `
53-
query {
54-
${computeObjectTargetTable(options.objectMetadataItem)}Collection${
55-
isEmpty(duplicateCondition?.or)
56-
? '(first: 0)'
57-
: `(filter: ${filters})`
58-
} {
59-
${fieldsString}
60-
}
61-
}
62-
`;
61+
return `query {
62+
${query}
63+
}`;
6364
}
6465

65-
getFindDuplicateBy<Filter extends RecordFilter = RecordFilter>(
66-
args: FindDuplicatesResolverArgs<Filter>,
66+
buildQuery(
67+
fieldsString: string,
6768
options: WorkspaceQueryBuilderOptions,
68-
currentRecord?: Record<string, unknown>,
69+
data?: Record,
70+
currentRecord?: Record,
71+
index?: number,
6972
) {
70-
if (currentRecord) {
71-
return currentRecord;
72-
}
73+
const duplicateCondition =
74+
this.duplicateService.buildDuplicateConditionForGraphQL(
75+
options.objectMetadataItem,
76+
data ?? currentRecord,
77+
currentRecord?.id,
78+
);
7379

74-
return this.argsAliasFactory.create(
75-
args.data ?? {},
76-
options.fieldMetadataCollection,
77-
);
80+
const filters = stringifyWithoutKeyQuote(duplicateCondition);
81+
82+
return `${computeObjectTargetTable(
83+
options.objectMetadataItem,
84+
)}Collection${index}: ${computeObjectTargetTable(
85+
options.objectMetadataItem,
86+
)}Collection${
87+
isEmpty(duplicateCondition?.or) ? '(first: 0)' : `(filter: ${filters})`
88+
} {
89+
${fieldsString}
90+
}
91+
`;
7892
}
7993
}

packages/twenty-server/src/engine/api/graphql/workspace-query-builder/workspace-query-builder.factory.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ export class WorkspaceQueryBuilderFactory {
6464
return this.findOneQueryFactory.create<Filter>(args, options);
6565
}
6666

67-
findDuplicates<Filter extends RecordFilter = RecordFilter>(
68-
args: FindDuplicatesResolverArgs<Filter>,
67+
findDuplicates(
68+
args: FindDuplicatesResolverArgs,
6969
options: WorkspaceQueryBuilderOptions,
70-
existingRecord?: Record<string, unknown>,
70+
existingRecord?: IRecord,
7171
): Promise<string> {
72-
return this.findDuplicatesQueryFactory.create<Filter>(
72+
return this.findDuplicatesQueryFactory.create(
7373
args,
7474
options,
7575
existingRecord,

packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ export class QueryRunnerArgsFactory {
4747
return {
4848
...args,
4949
data: await Promise.all(
50-
(args as CreateManyResolverArgs).data.map((arg, index) =>
50+
(args as CreateManyResolverArgs).data?.map((arg, index) =>
5151
this.overrideDataByFieldMetadata(arg, options, fieldMetadataMap, {
5252
argIndex: index,
5353
shouldBackfillPosition,
5454
}),
55-
),
55+
) ?? [],
5656
),
5757
} satisfies CreateManyResolverArgs;
5858
case ResolverArgsType.FindOne:
@@ -80,11 +80,13 @@ export class QueryRunnerArgsFactory {
8080
(args as FindDuplicatesResolverArgs).id,
8181
fieldMetadataMap,
8282
),
83-
data: await this.overrideDataByFieldMetadata(
84-
(args as FindDuplicatesResolverArgs).data,
85-
options,
86-
fieldMetadataMap,
87-
{ shouldBackfillPosition: false },
83+
data: await Promise.all(
84+
(args as FindDuplicatesResolverArgs).data?.map((arg, index) =>
85+
this.overrideDataByFieldMetadata(arg, options, fieldMetadataMap, {
86+
argIndex: index,
87+
shouldBackfillPosition,
88+
}),
89+
) ?? [],
8890
),
8991
};
9092
default:

packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -192,24 +192,24 @@ export class WorkspaceQueryRunnerService {
192192
ResolverArgsType.FindDuplicates,
193193
)) as FindDuplicatesResolverArgs<TRecord>;
194194

195-
let existingRecord: Record<string, unknown> | undefined;
195+
let existingRecord: Record<string, any> | null = null;
196196

197197
if (computedArgs.id) {
198-
existingRecord = await this.duplicateService.findExistingRecord(
199-
computedArgs.id,
198+
existingRecord = await this.duplicateService.findExistingRecords(
199+
[computedArgs.id],
200200
objectMetadataItem,
201201
workspaceId,
202202
);
203203

204-
if (!existingRecord) {
204+
if (existingRecord && existingRecord.length != 1) {
205205
throw new NotFoundError(`Object with id ${args.id} not found`);
206206
}
207207
}
208208

209209
const query = await this.workspaceQueryBuilderFactory.findDuplicates(
210210
computedArgs,
211211
options,
212-
existingRecord,
212+
existingRecord ? existingRecord[0] : null,
213213
);
214214

215215
await this.workspacePreQueryHookService.executePreHooks(
@@ -226,6 +226,7 @@ export class WorkspaceQueryRunnerService {
226226
result,
227227
objectMetadataItem,
228228
'',
229+
true,
229230
);
230231
}
231232

@@ -652,11 +653,28 @@ export class WorkspaceQueryRunnerService {
652653
graphqlResult: PGGraphQLResult | undefined,
653654
objectMetadataItem: ObjectMetadataInterface,
654655
command: string,
656+
isMultiQuery = false,
655657
): Promise<Result> {
656658
const entityKey = `${command}${computeObjectTargetTable(
657659
objectMetadataItem,
658660
)}Collection`;
659-
const result = graphqlResult?.[0]?.resolve?.data?.[entityKey];
661+
const result = !isMultiQuery
662+
? graphqlResult?.[0]?.resolve?.data?.[entityKey]
663+
: {
664+
edges: Object.keys(graphqlResult?.[0]?.resolve?.data).reduce(
665+
(acc: IRecord[], dataItem, index) => {
666+
acc.push(
667+
graphqlResult?.[0]?.resolve?.data[`${entityKey}${index}`].edges,
668+
);
669+
670+
return acc;
671+
},
672+
[],
673+
)[0],
674+
};
675+
676+
console.log('result', result);
677+
660678
const errors = graphqlResult?.[0]?.resolve?.errors;
661679

662680
if (
@@ -687,6 +705,8 @@ export class WorkspaceQueryRunnerService {
687705
objectMetadataItem,
688706
);
689707

708+
console.log(resultWithGetters);
709+
690710
return parseResult(resultWithGetters);
691711
}
692712

packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export interface FindOneResolverArgs<Filter = any> {
4141

4242
export interface FindDuplicatesResolverArgs<Data extends Record = Record> {
4343
id?: string;
44-
data?: Data;
44+
data?: Data[];
4545
}
4646

4747
export interface CreateOneResolverArgs<Data extends Record = Record> {

packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export const getResolverArgs = (
9494
data: {
9595
kind: InputTypeDefinitionKind.Create,
9696
isNullable: true,
97+
isArray: true,
9798
},
9899
};
99100
case 'deleteOne':

packages/twenty-server/src/engine/core-modules/duplicate/duplicate.service.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ export class DuplicateService {
1313
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
1414
) {}
1515

16-
async findExistingRecord(
17-
recordId: string | number,
16+
async findExistingRecords(
17+
recordIds: (string | number)[],
1818
objectMetadata: ObjectMetadataInterface,
1919
workspaceId: string,
2020
) {
@@ -30,13 +30,15 @@ export class DuplicateService {
3030
objectMetadata,
3131
)}" p
3232
WHERE
33-
p."id" = $1
33+
p."id" IN (${recordIds
34+
.map((_, index) => `$${index + 1}`)
35+
.join(', ')})
3436
`,
35-
[recordId],
37+
recordIds,
3638
workspaceId,
3739
);
3840

39-
return results.length > 0 ? results[0] : null;
41+
return results as Record<string, any>[] | null;
4042
}
4143

4244
async findDuplicate(

0 commit comments

Comments
 (0)