diff --git a/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue b/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue index aaa297c6f6..f1b87bd0fd 100644 --- a/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue +++ b/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue @@ -744,21 +744,7 @@ export default { return o }, {}) - let insertedData = await this.api.insert(insertObj) - - // todo: optimize - if (this.meta.v && this.meta.v.length) { - try { - const where = this.meta.columns.filter(c => c.pk).map(c => `(${c._cn},eq,${insertedData[c._cn]})`).join('~and') - if (where) { - const { childs, parents, many } = this.queryParams - const data = (await this.api.list({ where, childs, parents, many }) || [insertedData]) - insertedData = data.length ? data[0] : insertedData - } - } catch (e) { - // ignore - } - } + const insertedData = await this.api.insert(insertObj) this.data.splice(row, 1, { row: insertedData, diff --git a/packages/nocodb/src/lib/dataMapper/lib/BaseModel.ts b/packages/nocodb/src/lib/dataMapper/lib/BaseModel.ts index 9f488958ea..2d4ad519f7 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/BaseModel.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/BaseModel.ts @@ -62,6 +62,7 @@ abstract class BaseModel { protected hasManyRelations: any; protected belongsToRelations: any; protected manyToManyRelations: any; + protected virtualColumns: any; protected config: any; protected clientType: string; public readonly type: string; diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts index d919e9686d..ad16bfb540 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts @@ -41,6 +41,7 @@ class BaseModelSql extends BaseModel { hasMany = [], belongsTo = [], manyToMany = [], + v, type, dbModels }: { @@ -66,6 +67,7 @@ class BaseModelSql extends BaseModel { this.hasManyRelations = hasMany; this.belongsToRelations = belongsTo; this.manyToManyRelations = manyToMany; + this.virtualColumns = v; this.config = { limitDefault: process.env.DB_QUERY_LIMIT_DEFAULT || 10, limitMax: process.env.DB_QUERY_LIMIT_MAX || 500, @@ -157,7 +159,9 @@ class BaseModelSql extends BaseModel { * @returns {Object} Copy of the object excluding primary keys * @private */ - _extractPks(obj) { + _extractPks(obj): { + [key: string]: any + } { const objCopy = this.mapAliasToColumn(obj); for (const key in objCopy) { if (this.pks.filter(pk => pk._cn === key).length === 0) { @@ -173,7 +177,7 @@ class BaseModelSql extends BaseModel { * @returns {Object} Copy of the object excluding primary keys * @private */ - _extractPksValues(obj) { + _extractPksValues(obj): string { const objCopy = this.mapAliasToColumn(obj); for (const key in objCopy) { if (this.pks.filter(pk => pk._cn === key).length === 0) { @@ -238,6 +242,7 @@ class BaseModelSql extends BaseModel { * @param {Object} [trx] - knex transaction object * @returns {Promise|Promise} */ + // todo: optimize async insert(data, trx = null, cookie?: any) { try { @@ -260,6 +265,7 @@ class BaseModelSql extends BaseModel { response = await this._run(query); } + const ai = this.columns.find(c => c.ai); if (!response || (typeof response?.[0] !== 'object' && response?.[0] !== null)) { let id; if (response?.length) { @@ -268,12 +274,14 @@ class BaseModelSql extends BaseModel { id = (await this._run(query))[0]; } - const ai = this.columns.find(c => c.ai); if (ai) { - response = await this.readByPk(id) + // response = await this.readByPk(id) + response = await this.nestedRead(id, this.defaultNestedBtQueryParams) } else { response = data; } + } else if (ai) { + response = await this.nestedRead(Array.isArray(response) ? response?.[0]?.[ai._cn] : response?.[ai._cn], this.defaultNestedBtQueryParams) } if (Array.isArray(response)) { @@ -1765,6 +1773,75 @@ class BaseModelSql extends BaseModel { return obj; } + protected get defaultNestedBtQueryParams(): any { + return Object.entries(this.defaultNestedQueryParams || {}).reduce((paramsObj, [key, val]) => { + if (key.startsWith('bfield') || key.startsWith('bf') || key === 'bt') { + return {...paramsObj, [key]: val} + } + return paramsObj; + }, {}) + } + + protected get defaultNestedQueryParams(): any { + // generate default nested fields args based on virtual column list + try { + const nestedFields: { + [key: string]: string[] + } = (this.virtualColumns || []).reduce((obj, vc) => { + if (vc.hm) { + obj.hm.push(vc.hm.tn) + } else if (vc.bt) { + obj.bt.push(vc.bt.rtn) + } else if (vc.mm) { + obj.mm.push(vc.mm.rtn) + } + return obj + }, {hm: [], bt: [], mm: []}) + + // todo: handle if virtual column missing + // construct fields args based on lookup columns + const fieldsObj = (this.virtualColumns || []).reduce((obj, vc) => { + if (!vc.lk) { + return obj + } + + let key + let index + let column + + switch (vc.lk.type) { + case 'mm': + index = nestedFields.mm.indexOf(vc.lk.ltn) + 1 + key = `mfields${index}` + column = vc.lk.lcn + break + case 'hm': + index = nestedFields.hm.indexOf(vc.lk.ltn) + 1 + key = `hfields${index}` + column = vc.lk.lcn + break + case 'bt': + index = nestedFields.bt.indexOf(vc.lk.ltn) + 1 + key = `bfields${index}` + column = vc.lk.lcn + break + } + + if (index && column) { + obj[key] = `${obj[key] ? `${obj[key]},` : ''}${column}` + } + + return obj + }, {}) + return { + ...Object.entries(nestedFields).reduce((ro, [k, a]) => ({...ro, [k]: a.join(',')}), {}), + ...fieldsObj + } + } catch (e) { + return {} + } + } + } diff --git a/packages/nocodb/src/lib/noco/common/BaseModel.ts b/packages/nocodb/src/lib/noco/common/BaseModel.ts index 78bdbf2ecc..ef5c9c77f1 100644 --- a/packages/nocodb/src/lib/noco/common/BaseModel.ts +++ b/packages/nocodb/src/lib/noco/common/BaseModel.ts @@ -76,12 +76,25 @@ class BaseModel> extends BaseModelSql { await this.handleHooks('after.delete', data, req) } - private async handleHooks(hookName, data, req): Promise { + private async handleHooks(hookName, _data, req): Promise { + let data = _data; + + try { if (this.tn in this.builder.hooks && hookName in this.builder.hooks[this.tn] && this.builder.hooks[this.tn][hookName] ) { + + if (hookName === 'after.update') { + try { + data = await this.nestedRead(req.params.id, this.defaultNestedQueryParams) + } catch (_) { + /* ignore */ + } + } + + for (const hook of this.builder.hooks[this.tn][hookName]) { if (!hook.active) { continue