Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 97 additions & 47 deletions backend/src/entities/table/use-cases/get-table-rows.use.case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ export class GetTableRowsUseCase extends AbstractUseCase<GetTableRowsDs, FoundTa

const configured = !!tableSettings;

const allowCsvExport = tableSettings ? tableSettings.allow_csv_export : true;
const allowCsvImport = tableSettings ? tableSettings.allow_csv_import : true;
const can_delete = tableSettings ? tableSettings.can_delete : true;
const can_update = tableSettings ? tableSettings.can_update : true;
const can_add = tableSettings ? tableSettings.can_add : true;
const allowCsvExport = tableSettings?.allow_csv_export ?? true;
const allowCsvImport = tableSettings?.allow_csv_import ?? true;
const can_delete = tableSettings?.can_delete ?? true;
const can_update = tableSettings?.can_update ?? true;
const can_add = tableSettings?.can_add ?? true;

//todo rework in daos
tableSettings = tableSettings ? tableSettings : ({} as TableSettingsEntity);
Expand Down Expand Up @@ -170,17 +170,17 @@ export class GetTableRowsUseCase extends AbstractUseCase<GetTableRowsDs, FoundTa

const foreignKeysFromWidgets: Array<ForeignKeyDSInfo> = tableWidgets
.filter((widget) => widget.widget_type === WidgetTypeEnum.Foreign_key)
.map((widget) => {
.reduce<Array<ForeignKeyDSInfo>>((acc, widget) => {
if (widget.widget_params) {
try {
const widgetParams = JSON5.parse(widget.widget_params) as ForeignKeyDSInfo;
return widgetParams;
acc.push(widgetParams);
} catch (_e) {
return null;
//
}
}
})
.filter((el) => el !== null);
return acc;
}, []);

tableForeignKeys = [...tableForeignKeys, ...foreignKeysFromWidgets];

Expand All @@ -205,23 +205,17 @@ export class GetTableRowsUseCase extends AbstractUseCase<GetTableRowsDs, FoundTa

if (tableForeignKeys && tableForeignKeys.length > 0) {
tableForeignKeys = await Promise.all(
tableForeignKeys.map((el) => {
try {
return this.attachForeignColumnNames(el, userId, connectionId, dao);
} catch (_e) {
return el;
}
}),
tableForeignKeys.map((el) => this.attachForeignColumnNames(el, userId, connectionId, dao).catch(() => el)),
);
}

const formedTableStructure = formFullTableStructure(tableStructure, tableSettings);

const largeDataset = rows.large_dataset
? true
: rows.pagination.total > Constants.LARGE_DATASET_ROW_LIMIT
? true
: false;
const largeDataset = rows.large_dataset || rows.pagination.total > Constants.LARGE_DATASET_ROW_LIMIT;

const listFields = findAvailableFields(tableSettings, tableStructure);
const actionEventsDtos = customActionEvents.map((el) => buildActionEventDto(el));
const savedFiltersRO = savedTableFilters.map((el) => buildCreatedTableFilterRO(el));

const rowsRO = {
rows: rows.data,
Expand All @@ -237,13 +231,13 @@ export class GetTableRowsUseCase extends AbstractUseCase<GetTableRowsDs, FoundTa
widgets: tableWidgets,
identity_column: tableSettings.identity_column ? tableSettings.identity_column : null,
table_permissions: userTablePermissions,
list_fields: findAvailableFields(tableSettings, tableStructure),
action_events: customActionEvents.map((el) => buildActionEventDto(el)),
table_actions: customActionEvents.map((el) => buildActionEventDto(el)),
list_fields: listFields,
action_events: actionEventsDtos,
table_actions: actionEventsDtos,
large_dataset: largeDataset,
allow_csv_export: allowCsvExport,
allow_csv_import: allowCsvImport,
saved_filters: savedTableFilters.map((el) => buildCreatedTableFilterRO(el)),
saved_filters: savedFiltersRO,
can_delete: can_delete,
can_update: can_update,
can_add: can_add,
Expand All @@ -263,24 +257,53 @@ export class GetTableRowsUseCase extends AbstractUseCase<GetTableRowsDs, FoundTa
const identitiesMap = new Map<string, any[]>();

if (tableForeignKeys?.length > 0) {
const uniqueReferencedTables = [...new Set(tableForeignKeys.map((fk) => fk.referenced_table_name))];
const foreignTableSettingsPromises = uniqueReferencedTables.map((tableName) =>
this._dbContext.tableSettingsRepository.findTableSettings(connectionId, tableName).then((settings) => ({
tableName,
settings,
})),
);
const foreignTableSettingsResults = await Promise.all(foreignTableSettingsPromises);
const foreignTableSettingsMap = new Map(
foreignTableSettingsResults.map((result) => [result.tableName, result.settings]),
);

const foreignKeyDataMap = new Map<string, { foreignKey: ForeignKeyDS; values: Set<string | number> }>();

for (const foreignKey of tableForeignKeys) {
const foreignKeysValuesCollection = rowsRO.rows
.map((row) => row[foreignKey.column_name])
.filter((value) => value !== undefined) as (string | number)[];
const valuesSet = new Set<string | number>();
for (const row of rowsRO.rows) {
const value = row[foreignKey.column_name];
if (value !== undefined && value !== null) {
valuesSet.add(value as string | number);
}
}

const foreignTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings(
connectionId,
foreignKey.referenced_table_name,
);
const key = `${foreignKey.referenced_table_name}:${foreignKey.referenced_column_name}`;
if (foreignKeyDataMap.has(key)) {
const existing = foreignKeyDataMap.get(key);
valuesSet.forEach((v) => existing!.values.add(v));
} else {
foreignKeyDataMap.set(key, { foreignKey, values: valuesSet });
}
}

const identityPromises = Array.from(foreignKeyDataMap.values()).map(async ({ foreignKey, values }) => {
const foreignTableSettings = foreignTableSettingsMap.get(foreignKey.referenced_table_name);
const identityColumns = await this.getBatchedIdentityColumns(
foreignKeysValuesCollection,
Array.from(values),
foreignKey,
dao,
foreignTableSettings,
userEmail,
);
return { foreignKey, identityColumns };
});

const identityResults = await Promise.all(identityPromises);

for (const { foreignKey, identityColumns } of identityResults) {
if (!identitiesMap.has(foreignKey.referenced_table_name)) {
identitiesMap.set(foreignKey.referenced_table_name, []);
}
Expand All @@ -299,22 +322,34 @@ export class GetTableRowsUseCase extends AbstractUseCase<GetTableRowsDs, FoundTa
referencedTableName: key.referenced_table_name,
}));

const identityLookupMaps = new Map<string, Map<any, any>>();
for (const element of foreignKeysConformity) {
const identityForCurrentTable = identities.find(
(el) => el.referenced_table_name === element.referencedTableName,
);

if (!identityForCurrentTable) continue;

const identityColumnsMap = new Map(
identityForCurrentTable.identity_columns.map((col) => [col[element.realFKeyName], col]),
);
const lookupKey = `${element.referencedTableName}:${element.realFKeyName}`;
if (!identityLookupMaps.has(lookupKey)) {
const identityColumnsMap = new Map(
identityForCurrentTable.identity_columns.map((col) => [col[element.realFKeyName], col]),
);
identityLookupMaps.set(lookupKey, identityColumnsMap);
}
}

for (const row of rowsRO.rows) {
for (const element of foreignKeysConformity) {
const lookupKey = `${element.referencedTableName}:${element.realFKeyName}`;
const identityColumnsMap = identityLookupMaps.get(lookupKey);

if (!identityColumnsMap) continue;

for (const row of rowsRO.rows) {
const identityForCurrentValue = identityColumnsMap.get(row[element.currentFKeyName]);
row[element.currentFKeyName] =
typeof identityForCurrentValue === 'object' && identityForCurrentValue !== null
? { ...identityForCurrentValue }
? identityForCurrentValue
: {};
}
}
Expand Down Expand Up @@ -395,22 +430,37 @@ export class GetTableRowsUseCase extends AbstractUseCase<GetTableRowsDs, FoundTa
foreignTableSettings: TableSettingsDS,
userEmail: string,
): Promise<Array<Record<string, unknown>>> {
foreignKeysValuesCollection = [...new Set(foreignKeysValuesCollection)];
const uniqueValues = [...new Set(foreignKeysValuesCollection)];

if (uniqueValues.length === 0) {
return [];
}

const batchSize = 50;
const chunkedValues = this.chunkArray(foreignKeysValuesCollection, batchSize);
let identityColumns = [];

for (const chunk of chunkedValues) {
const result = await dao.getIdentityColumns(
if (uniqueValues.length <= batchSize) {
return await dao.getIdentityColumns(
foreignKey.referenced_table_name,
foreignKey.referenced_column_name,
foreignTableSettings?.identity_column,
chunk,
uniqueValues,
userEmail,
);
identityColumns = identityColumns.concat(result);
}

return identityColumns;
const chunkedValues = this.chunkArray(uniqueValues, batchSize);
const results = await Promise.all(
chunkedValues.map((chunk) =>
dao.getIdentityColumns(
foreignKey.referenced_table_name,
foreignKey.referenced_column_name,
foreignTableSettings?.identity_column,
chunk,
userEmail,
),
),
);

return results.flat();
}
}
Loading