Improve object metadata maps size (#8635)
## Context The object metadata map is becoming quite large and its structure could be simplified. We are removing byNameSingular/byNamePlural keys, the former can be retrieved through a new helper and the latter is not used in the code base currently.
This commit is contained in:
@ -19,6 +19,7 @@ import {
|
|||||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
|
import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
|
||||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||||
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
|
|
||||||
export class GraphqlQueryParser {
|
export class GraphqlQueryParser {
|
||||||
private fieldMetadataMapByName: FieldMetadataMap;
|
private fieldMetadataMapByName: FieldMetadataMap;
|
||||||
@ -108,9 +109,10 @@ export class GraphqlQueryParser {
|
|||||||
parentObjectMetadata: ObjectMetadataItemWithFieldMaps,
|
parentObjectMetadata: ObjectMetadataItemWithFieldMaps,
|
||||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||||
): GraphqlQuerySelectedFieldsResult {
|
): GraphqlQuerySelectedFieldsResult {
|
||||||
const parentFields =
|
const parentFields = getObjectMetadataMapItemByNameSingular(
|
||||||
this.objectMetadataMaps.byNameSingular[parentObjectMetadata.nameSingular]
|
this.objectMetadataMaps,
|
||||||
?.fieldsByName;
|
parentObjectMetadata.nameSingular,
|
||||||
|
)?.fieldsByName;
|
||||||
|
|
||||||
if (!parentFields) {
|
if (!parentFields) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-meta
|
|||||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||||
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||||
import { isPlainObject } from 'src/utils/is-plain-object';
|
import { isPlainObject } from 'src/utils/is-plain-object';
|
||||||
@ -143,7 +144,10 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const objectMetadata = this.objectMetadataMaps.byNameSingular[objectName];
|
const objectMetadata = getObjectMetadataMapItemByNameSingular(
|
||||||
|
this.objectMetadataMaps,
|
||||||
|
objectName,
|
||||||
|
);
|
||||||
|
|
||||||
if (!objectMetadata) {
|
if (!objectMetadata) {
|
||||||
throw new GraphqlQueryRunnerException(
|
throw new GraphqlQueryRunnerException(
|
||||||
|
|||||||
@ -7,6 +7,10 @@ import {
|
|||||||
|
|
||||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||||
|
|
||||||
|
import {
|
||||||
|
GraphqlQueryRunnerException,
|
||||||
|
GraphqlQueryRunnerExceptionCode,
|
||||||
|
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||||
import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper';
|
import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper';
|
||||||
import {
|
import {
|
||||||
getRelationMetadata,
|
getRelationMetadata,
|
||||||
@ -15,6 +19,7 @@ import {
|
|||||||
import { AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
import { AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||||
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||||
import { deduceRelationDirection } from 'src/engine/utils/deduce-relation-direction.util';
|
import { deduceRelationDirection } from 'src/engine/utils/deduce-relation-direction.util';
|
||||||
|
|
||||||
@ -177,13 +182,23 @@ export class ProcessNestedRelationsHelper {
|
|||||||
joinField: `${inverseRelationName}Id`,
|
joinField: `${inverseRelationName}Id`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const referenceObjectMetadataItemWithFieldsMaps =
|
||||||
|
getObjectMetadataMapItemByNameSingular(
|
||||||
|
objectMetadataMaps,
|
||||||
|
referenceObjectMetadata.nameSingular,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!referenceObjectMetadataItemWithFieldsMaps) {
|
||||||
|
throw new GraphqlQueryRunnerException(
|
||||||
|
`Object ${referenceObjectMetadata.nameSingular} not found`,
|
||||||
|
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(nestedRelations).length > 0) {
|
if (Object.keys(nestedRelations).length > 0) {
|
||||||
await this.processNestedRelations({
|
await this.processNestedRelations({
|
||||||
objectMetadataMaps,
|
objectMetadataMaps,
|
||||||
parentObjectMetadataItem:
|
parentObjectMetadataItem: referenceObjectMetadataItemWithFieldsMaps,
|
||||||
objectMetadataMaps.byNameSingular[
|
|
||||||
referenceObjectMetadata.nameSingular
|
|
||||||
],
|
|
||||||
parentObjectRecords: relationResults as ObjectRecord[],
|
parentObjectRecords: relationResults as ObjectRecord[],
|
||||||
parentObjectRecordsAggregatedValues: relationAggregatedFieldsResult,
|
parentObjectRecordsAggregatedValues: relationAggregatedFieldsResult,
|
||||||
relations: nestedRelations as Record<
|
relations: nestedRelations as Record<
|
||||||
@ -258,13 +273,23 @@ export class ProcessNestedRelationsHelper {
|
|||||||
relationName,
|
relationName,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const referenceObjectMetadataItemWithFieldsMaps =
|
||||||
|
getObjectMetadataMapItemByNameSingular(
|
||||||
|
objectMetadataMaps,
|
||||||
|
referenceObjectMetadata.nameSingular,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!referenceObjectMetadataItemWithFieldsMaps) {
|
||||||
|
throw new GraphqlQueryRunnerException(
|
||||||
|
`Object ${referenceObjectMetadata.nameSingular} not found`,
|
||||||
|
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(nestedRelations).length > 0) {
|
if (Object.keys(nestedRelations).length > 0) {
|
||||||
await this.processNestedRelations({
|
await this.processNestedRelations({
|
||||||
objectMetadataMaps,
|
objectMetadataMaps,
|
||||||
parentObjectMetadataItem:
|
parentObjectMetadataItem: referenceObjectMetadataItemWithFieldsMaps,
|
||||||
objectMetadataMaps.byNameSingular[
|
|
||||||
referenceObjectMetadata.nameSingular
|
|
||||||
],
|
|
||||||
parentObjectRecords: relationResults as ObjectRecord[],
|
parentObjectRecords: relationResults as ObjectRecord[],
|
||||||
parentObjectRecordsAggregatedValues: relationAggregatedFieldsResult,
|
parentObjectRecordsAggregatedValues: relationAggregatedFieldsResult,
|
||||||
relations: nestedRelations as Record<
|
relations: nestedRelations as Record<
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/g
|
|||||||
import { settings } from 'src/engine/constants/settings';
|
import { settings } from 'src/engine/constants/settings';
|
||||||
import { DUPLICATE_CRITERIA_COLLECTION } from 'src/engine/core-modules/duplicate/constants/duplicate-criteria.constants';
|
import { DUPLICATE_CRITERIA_COLLECTION } from 'src/engine/core-modules/duplicate/constants/duplicate-criteria.constants';
|
||||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||||
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
||||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||||
@ -56,10 +57,21 @@ export class GraphqlQueryFindDuplicatesResolverService
|
|||||||
objectMetadataItemWithFieldMaps.nameSingular,
|
objectMetadataItemWithFieldMaps.nameSingular,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const objectMetadataItemWithFieldsMaps =
|
||||||
|
getObjectMetadataMapItemByNameSingular(
|
||||||
|
objectMetadataMaps,
|
||||||
|
objectMetadataItemWithFieldMaps.nameSingular,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!objectMetadataItemWithFieldsMaps) {
|
||||||
|
throw new GraphqlQueryRunnerException(
|
||||||
|
`Object ${objectMetadataItemWithFieldMaps.nameSingular} not found`,
|
||||||
|
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const graphqlQueryParser = new GraphqlQueryParser(
|
const graphqlQueryParser = new GraphqlQueryParser(
|
||||||
objectMetadataMaps.byNameSingular[
|
objectMetadataItemWithFieldsMaps?.fieldsByName,
|
||||||
objectMetadataItemWithFieldMaps.nameSingular
|
|
||||||
].fieldsByName,
|
|
||||||
objectMetadataMaps,
|
objectMetadataMaps,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,5 @@ import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/typ
|
|||||||
|
|
||||||
export type ObjectMetadataMaps = {
|
export type ObjectMetadataMaps = {
|
||||||
byId: Record<string, ObjectMetadataItemWithFieldMaps>;
|
byId: Record<string, ObjectMetadataItemWithFieldMaps>;
|
||||||
byNameSingular: Record<string, ObjectMetadataItemWithFieldMaps>;
|
idByNameSingular: Record<string, string>;
|
||||||
byNamePlural: Record<string, ObjectMetadataItemWithFieldMaps>;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||||
|
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||||
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
|
|
||||||
|
describe('getObjectMetadataMapItemByNameSingular', () => {
|
||||||
|
it('should return the correct metadata item when given a valid singular name', () => {
|
||||||
|
const mockMetadataItem = {
|
||||||
|
id: 'test-id',
|
||||||
|
nameSingular: 'company',
|
||||||
|
} as ObjectMetadataItemWithFieldMaps;
|
||||||
|
|
||||||
|
const mockObjectMetadataMaps: ObjectMetadataMaps = {
|
||||||
|
byId: {
|
||||||
|
'test-id': mockMetadataItem,
|
||||||
|
},
|
||||||
|
idByNameSingular: {
|
||||||
|
company: 'test-id',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getObjectMetadataMapItemByNameSingular(
|
||||||
|
mockObjectMetadataMaps,
|
||||||
|
'company',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(mockMetadataItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined when the singular name does not exist', () => {
|
||||||
|
const mockObjectMetadataMaps: ObjectMetadataMaps = {
|
||||||
|
byId: {},
|
||||||
|
idByNameSingular: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getObjectMetadataMapItemByNameSingular(
|
||||||
|
mockObjectMetadataMaps,
|
||||||
|
'nonexistent',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -9,8 +9,7 @@ export const generateObjectMetadataMaps = (
|
|||||||
): ObjectMetadataMaps => {
|
): ObjectMetadataMaps => {
|
||||||
const objectMetadataMaps: ObjectMetadataMaps = {
|
const objectMetadataMaps: ObjectMetadataMaps = {
|
||||||
byId: {},
|
byId: {},
|
||||||
byNameSingular: {},
|
idByNameSingular: {},
|
||||||
byNamePlural: {},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const objectMetadata of objectMetadataCollection) {
|
for (const objectMetadata of objectMetadataCollection) {
|
||||||
@ -29,10 +28,8 @@ export const generateObjectMetadataMaps = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
objectMetadataMaps.byId[objectMetadata.id] = processedObjectMetadata;
|
objectMetadataMaps.byId[objectMetadata.id] = processedObjectMetadata;
|
||||||
objectMetadataMaps.byNameSingular[objectMetadata.nameSingular] =
|
objectMetadataMaps.idByNameSingular[objectMetadata.nameSingular] =
|
||||||
processedObjectMetadata;
|
objectMetadata.id;
|
||||||
objectMetadataMaps.byNamePlural[objectMetadata.namePlural] =
|
|
||||||
processedObjectMetadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return objectMetadataMaps;
|
return objectMetadataMaps;
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||||
|
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||||
|
|
||||||
|
export const getObjectMetadataMapItemByNameSingular = (
|
||||||
|
objectMetadataMaps: ObjectMetadataMaps,
|
||||||
|
nameSingular: string,
|
||||||
|
): ObjectMetadataItemWithFieldMaps | undefined => {
|
||||||
|
return objectMetadataMaps.byId[
|
||||||
|
objectMetadataMaps.idByNameSingular[nameSingular]
|
||||||
|
];
|
||||||
|
};
|
||||||
@ -23,6 +23,7 @@ import { UpsertOptions } from 'typeorm/repository/UpsertOptions';
|
|||||||
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||||
|
|
||||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||||
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
import { WorkspaceEntitiesStorage } from 'src/engine/twenty-orm/storage/workspace-entities.storage';
|
import { WorkspaceEntitiesStorage } from 'src/engine/twenty-orm/storage/workspace-entities.storage';
|
||||||
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
||||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||||
@ -630,16 +631,20 @@ export class WorkspaceRepository<
|
|||||||
throw new Error('Object metadata name is missing');
|
throw new Error('Object metadata name is missing');
|
||||||
}
|
}
|
||||||
|
|
||||||
const objectMetadata =
|
const objectMetadata = getObjectMetadataMapItemByNameSingular(
|
||||||
this.internalContext.objectMetadataMaps.byNameSingular[
|
this.internalContext.objectMetadataMaps,
|
||||||
objectMetadataName
|
objectMetadataName,
|
||||||
];
|
);
|
||||||
|
|
||||||
if (!objectMetadata) {
|
if (!objectMetadata) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Object metadata for object "${objectMetadataName}" is missing ` +
|
`Object metadata for object "${objectMetadataName}" is missing ` +
|
||||||
`in workspace "${this.internalContext.workspaceId}" ` +
|
`in workspace "${this.internalContext.workspaceId}" ` +
|
||||||
`with object metadata collection length: ${this.internalContext.objectMetadataMaps.byNameSingular.length}`,
|
`with object metadata collection length: ${
|
||||||
|
Object.keys(
|
||||||
|
this.internalContext.objectMetadataMaps.idByNameSingular,
|
||||||
|
).length
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/c
|
|||||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||||
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||||
@ -179,7 +180,10 @@ export class RecordCRUDWorkflowAction implements WorkflowAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const objectMetadataItemWithFieldsMaps =
|
const objectMetadataItemWithFieldsMaps =
|
||||||
objectMetadataMaps.byNameSingular[workflowActionInput.objectName];
|
getObjectMetadataMapItemByNameSingular(
|
||||||
|
objectMetadataMaps,
|
||||||
|
workflowActionInput.objectName,
|
||||||
|
);
|
||||||
|
|
||||||
if (!objectMetadataItemWithFieldsMaps) {
|
if (!objectMetadataItemWithFieldsMaps) {
|
||||||
throw new RecordCRUDActionException(
|
throw new RecordCRUDActionException(
|
||||||
|
|||||||
Reference in New Issue
Block a user