Browse Source

feat: add nested link/unlik (WIP)

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5901/head
Pranav C 1 year ago
parent
commit
3c48500604
  1. 42
      packages/nocodb/src/controllers/data-table.controller.ts
  2. 191
      packages/nocodb/src/db/BaseModelSqlv2.ts
  3. 73
      packages/nocodb/src/services/data-table.service.ts

42
packages/nocodb/src/controllers/data-table.controller.ts

@ -136,15 +136,55 @@ export class DataTableController {
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@Param('rowId') rowId: string,
@Body() refRowIds: string | string[] | number | number[],
) {
return await this.dataTableService.nestedDataList({
modelId,
rowId: rowId,
query: req.query,
viewId,
columnId,
});
}
@Get(['/api/v1/tables/:modelId/links/:columnId/row/:rowId'])
@Post('nestedDataLink')
async nestedLink(
@Request() req,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@Param('rowId') rowId: string,
@Body() refRowIds: string | string[] | number | number[],
) {
return await this.dataTableService.nestedLink({
modelId,
rowId: rowId,
query: req.query,
viewId,
columnId,
refRowIds,
});
}
@Delete(['/api/v1/tables/:modelId/links/:columnId/row/:rowId'])
@Acl('nestedDataUnlink')
async nestedUnlink(
@Request() req,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@Param('rowId') rowId: string,
@Body() refRowIds: string | string[] | number | number[],
) {
return await this.dataTableService.nestedUnlink({
modelId,
rowId: rowId,
query: req.query,
viewId,
columnId,
refRowIds,
});
}
}

191
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -3483,6 +3483,197 @@ class BaseModelSqlv2 {
}
return data;
}
async addLinks({
cookie,
childIds,
colId,
rowId,
}: {
cookie: any;
childIds: string | string[] | number | number[];
colId: string;
rowId: string;
}) {
const columns = await this.model.getColumns();
const column = columns.find((c) => c.id === colId);
if (!column || column.uidt !== UITypes.LinkToAnotherRecord)
NcError.notFound('Column not found');
const colOptions = await column.getColOptions<LinkToAnotherRecordColumn>();
const childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn();
const parentTable = await parentColumn.getModel();
const childTable = await childColumn.getModel();
await childTable.getColumns();
await parentTable.getColumns();
const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable);
switch (colOptions.type) {
case RelationTypes.MANY_TO_MANY:
{
const vChildCol = await colOptions.getMMChildColumn();
const vParentCol = await colOptions.getMMParentColumn();
const vTable = await colOptions.getMMModel();
const vTn = this.getTnPath(vTable);
if (this.isSnowflake) {
const parentPK = this.dbDriver(parentTn)
.select(parentColumn.column_name)
.where(_wherePk(parentTable.primaryKeys, childId))
.first();
const childPK = this.dbDriver(childTn)
.select(childColumn.column_name)
.where(_wherePk(childTable.primaryKeys, rowId))
.first();
await this.dbDriver.raw(
`INSERT INTO ?? (??, ??) SELECT (${parentPK.toQuery()}), (${childPK.toQuery()})`,
[vTn, vParentCol.column_name, vChildCol.column_name],
);
} else {
await this.dbDriver(vTn).insert({
[vParentCol.column_name]: this.dbDriver(parentTn)
.select(parentColumn.column_name)
.where(_wherePk(parentTable.primaryKeys, childId))
.first(),
[vChildCol.column_name]: this.dbDriver(childTn)
.select(childColumn.column_name)
.where(_wherePk(childTable.primaryKeys, rowId))
.first(),
});
}
}
break;
case RelationTypes.HAS_MANY:
{
await this.dbDriver(childTn)
.update({
[childColumn.column_name]: this.dbDriver.from(
this.dbDriver(parentTn)
.select(parentColumn.column_name)
.where(_wherePk(parentTable.primaryKeys, rowId))
.first()
.as('___cn_alias'),
),
})
.where(_wherePk(childTable.primaryKeys, childId));
}
break;
case RelationTypes.BELONGS_TO:
{
await this.dbDriver(childTn)
.update({
[childColumn.column_name]: this.dbDriver.from(
this.dbDriver(parentTn)
.select(parentColumn.column_name)
.where(_wherePk(parentTable.primaryKeys, childId))
.first()
.as('___cn_alias'),
),
})
.where(_wherePk(childTable.primaryKeys, rowId));
}
break;
}
const response = await this.readByPk(rowId);
await this.afterInsert(response, this.dbDriver, cookie);
await this.afterAddChild(rowId, childId, cookie);
}
async removeLinks({
cookie,
childIds,
colId,
rowId,
}: {
cookie: any;
childIds: string | string[] | number | number[];
colId: string;
rowId: string;
}) {
const columns = await this.model.getColumns();
const column = columns.find((c) => c.id === colId);
if (!column || column.uidt !== UITypes.LinkToAnotherRecord)
NcError.notFound('Column not found');
const colOptions = await column.getColOptions<LinkToAnotherRecordColumn>();
const childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn();
const parentTable = await parentColumn.getModel();
const childTable = await childColumn.getModel();
await childTable.getColumns();
await parentTable.getColumns();
const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable);
const prevData = await this.readByPk(rowId);
switch (colOptions.type) {
case RelationTypes.MANY_TO_MANY:
{
const vChildCol = await colOptions.getMMChildColumn();
const vParentCol = await colOptions.getMMParentColumn();
const vTable = await colOptions.getMMModel();
const vTn = this.getTnPath(vTable);
await this.dbDriver(vTn)
.where({
[vParentCol.column_name]: this.dbDriver(parentTn)
.select(parentColumn.column_name)
.where(_wherePk(parentTable.primaryKeys, childId))
.first(),
[vChildCol.column_name]: this.dbDriver(childTn)
.select(childColumn.column_name)
.where(_wherePk(childTable.primaryKeys, rowId))
.first(),
})
.delete();
}
break;
case RelationTypes.HAS_MANY:
{
await this.dbDriver(childTn)
// .where({
// [childColumn.cn]: this.dbDriver(parentTable.tn)
// .select(parentColumn.cn)
// .where(parentTable.primaryKey.cn, rowId)
// .first()
// })
.where(_wherePk(childTable.primaryKeys, childId))
.update({ [childColumn.column_name]: null });
}
break;
case RelationTypes.BELONGS_TO:
{
await this.dbDriver(childTn)
// .where({
// [childColumn.cn]: this.dbDriver(parentTable.tn)
// .select(parentColumn.cn)
// .where(parentTable.primaryKey.cn, childId)
// .first()
// })
.where(_wherePk(childTable.primaryKeys, rowId))
.update({ [childColumn.column_name]: null });
}
break;
}
const newData = await this.readByPk(rowId);
await this.afterUpdate(prevData, newData, this.dbDriver, cookie);
await this.afterRemoveChild(rowId, childId, cookie);
}
}
function extractSortsObject(

73
packages/nocodb/src/services/data-table.service.ts

@ -3,7 +3,7 @@ import { RelationTypes, UITypes } from 'nocodb-sdk'
import { NcError } from '../helpers/catchError';
import { PagedResponseImpl } from '../helpers/PagedResponse'
import { Base, Column, LinkToAnotherRecordColumn, Model, View } from '../models'
import { getColumnByIdOrName } from '../modules/datas/helpers'
import { getColumnByIdOrName, getViewAndModelByAliasOrId, PathParams } from '../modules/datas/helpers'
import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2';
import { DatasService } from './datas.service';
@ -250,16 +250,7 @@ export class DataTableService {
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await Column.get({colId: param.columnId})
if(!column) NcError.badRequest('Column not found'
if(column.fk_model_id !== model.id)
NcError.badRequest('Column not belong to model')
if (column.uidt !== UITypes.LinkToAnotherRecord)
NcError.badRequest('Column is not LTAR');
const column = await this.getColumn(param)
const colOptions = await column.getColOptions<LinkToAnotherRecordColumn>(
)
@ -296,4 +287,64 @@ export class DataTableService {
...param.query,
});
}
private async getColumn(param: {modelId: string; columnId: string }) {
const column = await Column.get({ colId: param.columnId })
if (!column) NcError.badRequest('Column not found'
if (column.fk_model_id !== param.modelId)
NcError.badRequest('Column not belong to model')
if (column.uidt !== UITypes.LinkToAnotherRecord)
NcError.badRequest('Column is not LTAR')
return column
}
async nestedLink(param: { cookie:any;viewId: string; modelId: string; columnId: string; query: any; refRowIds: string | string[] | number | number[]; rowId: string }) {
const { model, view } = await this.getModelAndView(param);
if (!model) NcError.notFound('Table not found');
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await this.getColumn(param);
await baseModel.addLinks({
colId: column.id,
childIds: param.refRowIds,
rowId: param.rowId,
cookie: param.cookie,
});
return true; }
async nestedUnlink(param: { cookie:any;viewId: string; modelId: string; columnId: string; query: any; refRowIds: string | string[] | number | number[]; rowId: string }) {
const { model, view } = await this.getModelAndView(param);
if (!model) NcError.notFound('Table not found');
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await this.getColumn(param);
await baseModel.removeLinks({
colId: column.id,
childIds: param.refRowIds,
rowId: param.rowId,
cookie: param.cookie,
});
return true;
}
}

Loading…
Cancel
Save