Deprecate object metadata maps in favor of flat entities (#16080)
## Context Deprecating the old objectMetadataMap type in favour of split flat entities to match with our new caching. In the long run, trying to achieve: - Better performance through caching - Consistent data access patterns across the codebase - Reduced database queries Now that everything is based on flat entities, which are cached, we can finish the refactoring of workspace context cache which should already improve performances. Then the last step will be to consume that new cache in the new global datasource to get rid of the many workspace datasources stored in the server
This commit is contained in:
+23
-17
@@ -1,7 +1,10 @@
|
||||
import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { encodeCursorData } from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util';
|
||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||
import {
|
||||
mockFlatFieldMetadataMaps,
|
||||
mockFlatObjectMetadatas,
|
||||
} from 'src/engine/core-modules/__mocks__/mockFlatObjectMetadatas';
|
||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
@@ -30,53 +33,53 @@ describe('SearchService', () => {
|
||||
describe('filterObjectMetadataItems', () => {
|
||||
it('should return searchable object metadata items', () => {
|
||||
const objectMetadataItems = service.filterObjectMetadataItems({
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemsWithFieldMaps,
|
||||
flatObjectMetadatas: mockFlatObjectMetadatas,
|
||||
includedObjectNameSingulars: [],
|
||||
excludedObjectNameSingulars: [],
|
||||
});
|
||||
|
||||
expect(objectMetadataItems).toEqual([
|
||||
mockObjectMetadataItemsWithFieldMaps[0],
|
||||
mockObjectMetadataItemsWithFieldMaps[1],
|
||||
mockObjectMetadataItemsWithFieldMaps[2],
|
||||
mockFlatObjectMetadatas[0],
|
||||
mockFlatObjectMetadatas[1],
|
||||
mockFlatObjectMetadatas[2],
|
||||
]);
|
||||
});
|
||||
it('should return searchable object metadata items without excluded ones', () => {
|
||||
const objectMetadataItems = service.filterObjectMetadataItems({
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemsWithFieldMaps,
|
||||
flatObjectMetadatas: mockFlatObjectMetadatas,
|
||||
includedObjectNameSingulars: [],
|
||||
excludedObjectNameSingulars: ['company'],
|
||||
});
|
||||
|
||||
expect(objectMetadataItems).toEqual([
|
||||
mockObjectMetadataItemsWithFieldMaps[0],
|
||||
mockObjectMetadataItemsWithFieldMaps[2],
|
||||
mockFlatObjectMetadatas[0],
|
||||
mockFlatObjectMetadatas[2],
|
||||
]);
|
||||
});
|
||||
it('should return searchable object metadata items with included ones only', () => {
|
||||
const objectMetadataItems = service.filterObjectMetadataItems({
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemsWithFieldMaps,
|
||||
flatObjectMetadatas: mockFlatObjectMetadatas,
|
||||
includedObjectNameSingulars: ['company'],
|
||||
excludedObjectNameSingulars: [],
|
||||
});
|
||||
|
||||
expect(objectMetadataItems).toEqual([
|
||||
mockObjectMetadataItemsWithFieldMaps[1],
|
||||
]);
|
||||
expect(objectMetadataItems).toEqual([mockFlatObjectMetadatas[1]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLabelIdentifierColumns', () => {
|
||||
it('should return the two label identifier columns for a person object metadata item', () => {
|
||||
const labelIdentifierColumns = service.getLabelIdentifierColumns(
|
||||
mockObjectMetadataItemsWithFieldMaps[0],
|
||||
mockFlatObjectMetadatas[0],
|
||||
mockFlatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(labelIdentifierColumns).toEqual(['nameFirstName', 'nameLastName']);
|
||||
});
|
||||
it('should return the label identifier column for a regular object metadata item', () => {
|
||||
const labelIdentifierColumns = service.getLabelIdentifierColumns(
|
||||
mockObjectMetadataItemsWithFieldMaps[1],
|
||||
mockFlatObjectMetadatas[1],
|
||||
mockFlatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(labelIdentifierColumns).toEqual(['name']);
|
||||
@@ -86,14 +89,16 @@ describe('SearchService', () => {
|
||||
describe('getImageIdentifierColumn', () => {
|
||||
it('should return null if the object metadata item does not have an image identifier', () => {
|
||||
const imageIdentifierColumn = service.getImageIdentifierColumn(
|
||||
mockObjectMetadataItemsWithFieldMaps[0],
|
||||
mockFlatObjectMetadatas[0],
|
||||
mockFlatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(imageIdentifierColumn).toBeNull();
|
||||
});
|
||||
it('should return `domainNamePrimaryLinkUrl` column for a company object metadata item', () => {
|
||||
const imageIdentifierColumn = service.getImageIdentifierColumn(
|
||||
mockObjectMetadataItemsWithFieldMaps[1],
|
||||
mockFlatObjectMetadatas[1],
|
||||
mockFlatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(imageIdentifierColumn).toEqual('domainNamePrimaryLinkUrl');
|
||||
@@ -101,7 +106,8 @@ describe('SearchService', () => {
|
||||
|
||||
it('should return the image identifier column', () => {
|
||||
const imageIdentifierColumn = service.getImageIdentifierColumn(
|
||||
mockObjectMetadataItemsWithFieldMaps[2],
|
||||
mockFlatObjectMetadatas[2],
|
||||
mockFlatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(imageIdentifierColumn).toEqual('imageIdentifierFieldName');
|
||||
|
||||
@@ -4,4 +4,5 @@ export class SearchException extends CustomException<SearchExceptionCode> {}
|
||||
|
||||
export enum SearchExceptionCode {
|
||||
LABEL_IDENTIFIER_FIELD_NOT_FOUND = 'LABEL_IDENTIFIER_FIELD_NOT_FOUND',
|
||||
OBJECT_METADATA_NOT_FOUND = 'OBJECT_METADATA_NOT_FOUND',
|
||||
}
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ export class SearchApiExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: SearchException) {
|
||||
switch (exception.code) {
|
||||
case SearchExceptionCode.LABEL_IDENTIFIER_FIELD_NOT_FOUND:
|
||||
case SearchExceptionCode.OBJECT_METADATA_NOT_FOUND:
|
||||
throw exception;
|
||||
default: {
|
||||
assertUnreachable(exception.code);
|
||||
|
||||
@@ -14,9 +14,6 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
|
||||
import { CustomPermissionGuard } from 'src/engine/guards/custom-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
|
||||
import { buildObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-metadata-item-with-field-maps.util';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Resolver()
|
||||
@UseFilters(SearchApiExceptionFilter, PreventNestToAutoLogGraphqlErrorsFilter)
|
||||
@@ -41,48 +38,29 @@ export class SearchResolver {
|
||||
after,
|
||||
}: SearchArgs,
|
||||
) {
|
||||
const { flatObjectMetadataMaps, flatFieldMetadataMaps, flatIndexMaps } =
|
||||
const { flatObjectMetadataMaps, flatFieldMetadataMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId: workspace.id,
|
||||
flatMapsKeys: [
|
||||
'flatObjectMetadataMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatIndexMaps',
|
||||
],
|
||||
flatMapsKeys: ['flatObjectMetadataMaps', 'flatFieldMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const { idByNameSingular } = buildObjectIdByNameMaps(
|
||||
flatObjectMetadataMaps,
|
||||
);
|
||||
const objectMetadataMaps: ObjectMetadataMaps = {
|
||||
byId: {},
|
||||
idByNameSingular,
|
||||
};
|
||||
|
||||
for (const [id, flatObj] of Object.entries(flatObjectMetadataMaps.byId)) {
|
||||
if (isDefined(flatObj)) {
|
||||
objectMetadataMaps.byId[id] = buildObjectMetadataItemWithFieldMaps(
|
||||
flatObj,
|
||||
flatFieldMetadataMaps,
|
||||
flatIndexMaps,
|
||||
);
|
||||
}
|
||||
}
|
||||
const flatObjectMetadatas = Object.values(
|
||||
flatObjectMetadataMaps.byId,
|
||||
).filter(isDefined);
|
||||
|
||||
const filteredObjectMetadataItems =
|
||||
this.searchService.filterObjectMetadataItems({
|
||||
objectMetadataItemWithFieldMaps: Object.values(
|
||||
objectMetadataMaps.byId,
|
||||
).filter(isDefined),
|
||||
flatObjectMetadatas,
|
||||
includedObjectNameSingulars: includedObjectNameSingulars ?? [],
|
||||
excludedObjectNameSingulars: excludedObjectNameSingulars ?? [],
|
||||
});
|
||||
|
||||
const allRecordsWithObjectMetadataItems =
|
||||
await this.searchService.getAllRecordsWithObjectMetadataItems({
|
||||
objectMetadataItemWithFieldMaps: filteredObjectMetadataItems,
|
||||
flatObjectMetadatas: filteredObjectMetadataItems,
|
||||
flatFieldMetadataMaps,
|
||||
searchInput,
|
||||
limit,
|
||||
filter,
|
||||
@@ -93,6 +71,7 @@ export class SearchResolver {
|
||||
|
||||
return this.searchService.computeSearchObjectResults({
|
||||
recordsWithObjectMetadataItems: allRecordsWithObjectMetadataItems,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceId: workspace.id,
|
||||
limit,
|
||||
after,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import chunk from 'lodash.chunk';
|
||||
import { FieldMetadataType, ObjectRecord } from 'twenty-shared/types';
|
||||
import { getLogoUrlFromDomainName } from 'twenty-shared/utils';
|
||||
import { getLogoUrlFromDomainName, isDefined } from 'twenty-shared/utils';
|
||||
import { Brackets, type ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { type ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
@@ -26,9 +26,10 @@ import {
|
||||
} from 'src/engine/core-modules/search/exceptions/search.exception';
|
||||
import { type RecordsWithObjectMetadataItem } from 'src/engine/core-modules/search/types/records-with-object-metadata-item';
|
||||
import { formatSearchTerms } from 'src/engine/core-modules/search/utils/format-search-terms';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { generateObjectMetadataMaps } from 'src/engine/metadata-modules/utils/generate-object-metadata-maps.util';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
|
||||
@@ -49,7 +50,8 @@ export class SearchService {
|
||||
) {}
|
||||
|
||||
async getAllRecordsWithObjectMetadataItems({
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadatas,
|
||||
flatFieldMetadataMaps,
|
||||
includedObjectNameSingulars,
|
||||
excludedObjectNameSingulars,
|
||||
searchInput,
|
||||
@@ -57,10 +59,11 @@ export class SearchService {
|
||||
filter,
|
||||
after,
|
||||
}: {
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps[];
|
||||
flatObjectMetadatas: FlatObjectMetadata[];
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
} & SearchArgs) {
|
||||
const filteredObjectMetadataItems = this.filterObjectMetadataItems({
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadatas,
|
||||
includedObjectNameSingulars: includedObjectNameSingulars ?? [],
|
||||
excludedObjectNameSingulars: excludedObjectNameSingulars ?? [],
|
||||
});
|
||||
@@ -75,17 +78,18 @@ export class SearchService {
|
||||
|
||||
for (const objectMetadataItemChunk of filteredObjectMetadataItemsChunks) {
|
||||
const recordsWithObjectMetadataItems = await Promise.all(
|
||||
objectMetadataItemChunk.map(async (objectMetadataItem) => {
|
||||
objectMetadataItemChunk.map(async (flatObjectMetadata) => {
|
||||
const repository =
|
||||
await this.twentyORMManager.getRepository<ObjectRecord>(
|
||||
objectMetadataItem.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
return {
|
||||
objectMetadataItem,
|
||||
objectMetadataItem: flatObjectMetadata,
|
||||
records: await this.buildSearchQueryAndGetRecords({
|
||||
entityManager: repository,
|
||||
objectMetadataItem,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
searchTerms: formatSearchTerms(searchInput, 'and'),
|
||||
searchTermsOr: formatSearchTerms(searchInput, 'or'),
|
||||
limit: limit as number,
|
||||
@@ -103,34 +107,33 @@ export class SearchService {
|
||||
}
|
||||
|
||||
filterObjectMetadataItems({
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadatas,
|
||||
includedObjectNameSingulars,
|
||||
excludedObjectNameSingulars,
|
||||
}: {
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps[];
|
||||
flatObjectMetadatas: FlatObjectMetadata[];
|
||||
includedObjectNameSingulars: string[];
|
||||
excludedObjectNameSingulars: string[];
|
||||
}) {
|
||||
return objectMetadataItemWithFieldMaps.filter(
|
||||
({ nameSingular, isSearchable }) => {
|
||||
if (!isSearchable) {
|
||||
return false;
|
||||
}
|
||||
if (excludedObjectNameSingulars.includes(nameSingular)) {
|
||||
return false;
|
||||
}
|
||||
if (includedObjectNameSingulars.length > 0) {
|
||||
return includedObjectNameSingulars.includes(nameSingular);
|
||||
}
|
||||
return flatObjectMetadatas.filter(({ nameSingular, isSearchable }) => {
|
||||
if (!isSearchable) {
|
||||
return false;
|
||||
}
|
||||
if (excludedObjectNameSingulars.includes(nameSingular)) {
|
||||
return false;
|
||||
}
|
||||
if (includedObjectNameSingulars.length > 0) {
|
||||
return includedObjectNameSingulars.includes(nameSingular);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
async buildSearchQueryAndGetRecords<Entity extends ObjectLiteral>({
|
||||
entityManager,
|
||||
objectMetadataItem,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
searchTerms,
|
||||
searchTermsOr,
|
||||
limit,
|
||||
@@ -138,7 +141,8 @@ export class SearchService {
|
||||
after,
|
||||
}: {
|
||||
entityManager: WorkspaceRepository<Entity>;
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
searchTerms: string;
|
||||
searchTermsOr: string;
|
||||
limit: number;
|
||||
@@ -147,30 +151,33 @@ export class SearchService {
|
||||
}) {
|
||||
const queryBuilder = entityManager.createQueryBuilder();
|
||||
|
||||
const { flatObjectMetadataMaps } = entityManager.internalContext;
|
||||
|
||||
const queryParser = new GraphqlQueryParser(
|
||||
objectMetadataItem,
|
||||
generateObjectMetadataMaps([
|
||||
{
|
||||
...objectMetadataItem,
|
||||
fields: Object.values(objectMetadataItem.fieldsById),
|
||||
},
|
||||
]),
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
queryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItem.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
filter,
|
||||
);
|
||||
|
||||
queryParser.applyDeletedAtToBuilder(queryBuilder, filter);
|
||||
|
||||
const imageIdentifierField =
|
||||
this.getImageIdentifierColumn(objectMetadataItem);
|
||||
const imageIdentifierField = this.getImageIdentifierColumn(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
const fieldsToSelect = [
|
||||
'id',
|
||||
...this.getLabelIdentifierColumns(objectMetadataItem),
|
||||
...this.getLabelIdentifierColumns(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
...(imageIdentifierField ? [imageIdentifierField] : []),
|
||||
].map((field) => `"${field}"`);
|
||||
|
||||
@@ -180,7 +187,7 @@ export class SearchService {
|
||||
|
||||
const cursorWhereCondition = this.computeCursorWhereCondition({
|
||||
after,
|
||||
objectMetadataNameSingular: objectMetadataItem.nameSingular,
|
||||
objectMetadataNameSingular: flatObjectMetadata.nameSingular,
|
||||
tsRankExpr,
|
||||
tsRankCDExpr,
|
||||
});
|
||||
@@ -273,9 +280,10 @@ export class SearchService {
|
||||
}
|
||||
|
||||
getLabelIdentifierColumns(
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
if (!objectMetadataItem.labelIdentifierFieldMetadataId) {
|
||||
if (!flatObjectMetadata.labelIdentifierFieldMetadataId) {
|
||||
throw new SearchException(
|
||||
'Label identifier field not found',
|
||||
SearchExceptionCode.LABEL_IDENTIFIER_FIELD_NOT_FOUND,
|
||||
@@ -283,10 +291,17 @@ export class SearchService {
|
||||
}
|
||||
|
||||
const labelIdentifierField =
|
||||
objectMetadataItem.fieldsById[
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId
|
||||
flatFieldMetadataMaps.byId[
|
||||
flatObjectMetadata.labelIdentifierFieldMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(labelIdentifierField)) {
|
||||
throw new SearchException(
|
||||
'Label identifier field not found',
|
||||
SearchExceptionCode.LABEL_IDENTIFIER_FIELD_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (labelIdentifierField.type === FieldMetadataType.FULL_NAME) {
|
||||
return [
|
||||
`${labelIdentifierField.name}FirstName`,
|
||||
@@ -294,37 +309,44 @@ export class SearchService {
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
objectMetadataItem.fieldsById[
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId
|
||||
].name,
|
||||
];
|
||||
return [labelIdentifierField.name];
|
||||
}
|
||||
|
||||
getLabelIdentifierValue(
|
||||
record: ObjectRecord,
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): string {
|
||||
const labelIdentifierFields =
|
||||
this.getLabelIdentifierColumns(objectMetadataItem);
|
||||
const labelIdentifierFields = this.getLabelIdentifierColumns(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
return labelIdentifierFields.map((field) => record[field]).join(' ');
|
||||
}
|
||||
|
||||
getImageIdentifierColumn(
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
if (objectMetadataItem.nameSingular === 'company') {
|
||||
if (flatObjectMetadata.nameSingular === 'company') {
|
||||
return 'domainNamePrimaryLinkUrl';
|
||||
}
|
||||
|
||||
if (!objectMetadataItem.imageIdentifierFieldMetadataId) {
|
||||
if (!flatObjectMetadata.imageIdentifierFieldMetadataId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return objectMetadataItem.fieldsById[
|
||||
objectMetadataItem.imageIdentifierFieldMetadataId
|
||||
].name;
|
||||
const imageIdentifierField =
|
||||
flatFieldMetadataMaps.byId[
|
||||
flatObjectMetadata.imageIdentifierFieldMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(imageIdentifierField)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return imageIdentifierField.name;
|
||||
}
|
||||
|
||||
private getImageUrlWithToken(avatarUrl: string, workspaceId: string): string {
|
||||
@@ -336,13 +358,16 @@ export class SearchService {
|
||||
|
||||
getImageIdentifierValue(
|
||||
record: ObjectRecord,
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
workspaceId: string,
|
||||
): string {
|
||||
const imageIdentifierField =
|
||||
this.getImageIdentifierColumn(objectMetadataItem);
|
||||
const imageIdentifierField = this.getImageIdentifierColumn(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
if (objectMetadataItem.nameSingular === 'company') {
|
||||
if (flatObjectMetadata.nameSingular === 'company') {
|
||||
return getLogoUrlFromDomainName(record.domainNamePrimaryLinkUrl) || '';
|
||||
}
|
||||
|
||||
@@ -391,11 +416,13 @@ export class SearchService {
|
||||
|
||||
computeSearchObjectResults({
|
||||
recordsWithObjectMetadataItems,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
limit,
|
||||
after,
|
||||
}: {
|
||||
recordsWithObjectMetadataItems: RecordsWithObjectMetadataItem[];
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
workspaceId: string;
|
||||
limit: number;
|
||||
after?: string;
|
||||
@@ -406,10 +433,15 @@ export class SearchService {
|
||||
return {
|
||||
recordId: record.id,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
label: this.getLabelIdentifierValue(record, objectMetadataItem),
|
||||
label: this.getLabelIdentifierValue(
|
||||
record,
|
||||
objectMetadataItem,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
imageUrl: this.getImageIdentifierValue(
|
||||
record,
|
||||
objectMetadataItem,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
),
|
||||
tsRankCD: record.tsRankCD,
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export type RecordsWithObjectMetadataItem = {
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataItem: FlatObjectMetadata;
|
||||
records: ObjectRecord[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user