From d422b729e0a5ec75510bc81dab7924d787de66eb Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 16 Nov 2023 12:24:06 +0000 Subject: [PATCH] feat: lookup support - mm/hm lookup --- .../smartsheet/toolbar/GroupByMenu.vue | 12 +- .../src/db/generateBTLookupSelectQuery.ts | 11 +- .../src/db/generateMMLookupSelectQuery.ts | 204 ++++++++++++++++++ 3 files changed, 216 insertions(+), 11 deletions(-) create mode 100644 packages/nocodb/src/db/generateMMLookupSelectQuery.ts diff --git a/packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue b/packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue index 3febd97e8e..1b62c363bc 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue @@ -68,11 +68,11 @@ const fieldsToGroupBy = computed(() => { const fields = meta.value?.columns || [] return fields.filter((field) => { - if (!groupingUidt.includes(field.uidt as UITypes)) return false - - if (field.uidt === UITypes.Lookup) { - return btLookups.value.includes(field.id) - } + // if (!groupingUidt.includes(field.uidt as UITypes)) return false + // + // if (field.uidt === UITypes.Lookup) { + // return btLookups.value.includes(field.id) + // } return true }) @@ -243,7 +243,7 @@ onMounted(async () => { v-model="group.fk_column_id" class="caption nc-sort-field-select" :columns=" - fieldsToGroupBy.filter((f) => (f.id && !groupedByColumnIds.includes(f.id)) || f.id === group.fk_column_id) + fieldsToGroupBy " :allow-empty="true" @change="saveGroupBy" diff --git a/packages/nocodb/src/db/generateBTLookupSelectQuery.ts b/packages/nocodb/src/db/generateBTLookupSelectQuery.ts index 62835a1a44..5883fd0b56 100644 --- a/packages/nocodb/src/db/generateBTLookupSelectQuery.ts +++ b/packages/nocodb/src/db/generateBTLookupSelectQuery.ts @@ -38,9 +38,9 @@ export default async function generateBTLookupSelectQuery({ await relationCol.getColOptions(); // if not belongs to then throw error as we don't support - if (relation.type !== RelationTypes.BELONGS_TO) - NcError.badRequest('HasMany/ManyToMany lookup is not supported'); - + if (relation.type !== RelationTypes.BELONGS_TO) { + // NcError.badRequest('HasMany/ManyToMany lookup is not supported'); + } const childColumn = await relation.getChildColumn(); const parentColumn = await relation.getParentColumn(); const childModel = await childColumn.getModel(); @@ -70,8 +70,9 @@ export default async function generateBTLookupSelectQuery({ // if any of the relation in nested lookup is // not belongs to then throw error as we don't support - if (relation.type !== RelationTypes.BELONGS_TO) - NcError.badRequest('HasMany/ManyToMany lookup is not supported'); + if (relation.type !== RelationTypes.BELONGS_TO) { + // NcError.badRequest('HasMany/ManyToMany lookup is not supported'); + } const childColumn = await relation.getChildColumn(); const parentColumn = await relation.getParentColumn(); diff --git a/packages/nocodb/src/db/generateMMLookupSelectQuery.ts b/packages/nocodb/src/db/generateMMLookupSelectQuery.ts new file mode 100644 index 0000000000..baf5a703ae --- /dev/null +++ b/packages/nocodb/src/db/generateMMLookupSelectQuery.ts @@ -0,0 +1,204 @@ +import { RelationTypes, UITypes } from 'nocodb-sdk'; +import type LookupColumn from '../models/LookupColumn'; +import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2'; +import type { + Column, + FormulaColumn, + LinkToAnotherRecordColumn, + Model, + RollupColumn, +} from '~/models'; +import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2'; +import genRollupSelectv2 from '~/db/genRollupSelectv2'; +import { NcError } from '~/helpers/catchError'; + +export default async function generateBTLookupSelectQuery({ + column, + baseModelSqlv2, + alias, + model, +}: { + column: Column; + baseModelSqlv2: BaseModelSqlv2; + alias: string; + model: Model; +}): Promise { + const knex = baseModelSqlv2.dbDriver; + + const rootAlias = alias; + + { + let aliasCount = 0, + selectQb; + const alias = `__nc_lk_${aliasCount++}`; + const lookup = await column.getColOptions(); + { + const relationCol = await lookup.getRelationColumn(); + const relation = + await relationCol.getColOptions(); + + // if not belongs to then throw error as we don't support + if (relation.type !== RelationTypes.BELONGS_TO) { + // NcError.badRequest('HasMany/ManyToMany lookup is not supported'); + } + const childColumn = await relation.getChildColumn(); + const parentColumn = await relation.getParentColumn(); + const childModel = await childColumn.getModel(); + await childModel.getColumns(); + const parentModel = await parentColumn.getModel(); + await parentModel.getColumns(); + + `${baseModelSqlv2.getTnPath(parentModel.table_name)} as ${alias}`, + ).where( + selectQb = knex( + `${alias}.${parentColumn.column_name}`, + knex.raw(`??`, [ + `${rootAlias || baseModelSqlv2.getTnPath(childModel.table_name)}.${ + childColumn.column_name + }`, + ]), + ); + } + let lookupColumn = await lookup.getLookupColumn(); + let prevAlias = alias; + while (lookupColumn.uidt === UITypes.Lookup) { + const nestedAlias = `__nc_lk_nested_${aliasCount++}`; + const nestedLookup = await lookupColumn.getColOptions(); + const relationCol = await nestedLookup.getRelationColumn(); + const relation = + await relationCol.getColOptions(); + + // if any of the relation in nested lookup is + // not belongs to then throw error as we don't support + if (relation.type !== RelationTypes.BELONGS_TO) { + // NcError.badRequest('HasMany/ManyToMany lookup is not supported'); + } + + const childColumn = await relation.getChildColumn(); + const parentColumn = await relation.getParentColumn(); + const childModel = await childColumn.getModel(); + await childModel.getColumns(); + const parentModel = await parentColumn.getModel(); + await parentModel.getColumns(); + + selectQb.join( + `${baseModelSqlv2.getTnPath(parentModel.table_name)} as ${nestedAlias}`, + `${nestedAlias}.${parentColumn.column_name}`, + `${prevAlias}.${childColumn.column_name}`, + ); + + + + + + const mmModel = await relationColumnOption.getMMModel(); + const mmChildCol = await relationColumnOption.getMMChildColumn(); + const mmParentCol = await relationColumnOption.getMMParentColumn(); + + knex( + `${baseModelSqlv2.getTnPath( + parentModel?.table_name, + )} as ${refTableAlias}`, + ) + [columnOptions.rollup_function as string]?.( + knex.ref(`${refTableAlias}.${rollupColumn.column_name}`), + ) + .innerJoin( + baseModelSqlv2.getTnPath(mmModel.table_name), + knex.ref( + `${baseModelSqlv2.getTnPath(mmModel.table_name)}.${ + mmParentCol.column_name + }`, + ), + '=', + knex.ref(`${refTableAlias}.${parentCol.column_name}`), + ) + .where( + knex.ref( + `${baseModelSqlv2.getTnPath(mmModel.table_name)}.${ + mmChildCol.column_name + }`, + ), + '=', + knex.ref( + `${alias || baseModelSqlv2.getTnPath(childModel.table_name)}.${ + childCol.column_name + }`, + ), + ) + + lookupColumn = await nestedLookup.getLookupColumn(); + prevAlias = nestedAlias; + } + + switch (lookupColumn.uidt) { + case UITypes.Links: + case UITypes.Rollup: + { + const builder = ( + await genRollupSelectv2({ + baseModelSqlv2, + knex, + columnOptions: + (await lookupColumn.getColOptions()) as RollupColumn, + alias: prevAlias, + }) + ).builder; + selectQb.select(builder); + } + break; + case UITypes.LinkToAnotherRecord: + { + const nestedAlias = `__nc_sort${aliasCount++}`; + const relation = + await lookupColumn.getColOptions(); + if (relation.type !== 'bt') return; + + const colOptions = + (await column.getColOptions()) as LinkToAnotherRecordColumn; + const childColumn = await colOptions.getChildColumn(); + const parentColumn = await colOptions.getParentColumn(); + const childModel = await childColumn.getModel(); + await childModel.getColumns(); + const parentModel = await parentColumn.getModel(); + await parentModel.getColumns(); + + selectQb + .join( + `${baseModelSqlv2.getTnPath( + parentModel.table_name, + )} as ${nestedAlias}`, + `${nestedAlias}.${parentColumn.column_name}`, + `${prevAlias}.${childColumn.column_name}`, + ) + .select(parentModel?.displayValue?.column_name); + } + break; + case UITypes.Formula: + { + const builder = ( + await formulaQueryBuilderv2( + baseModelSqlv2, + ( + await column.getColOptions() + ).formula, + null, + model, + column, + ) + ).builder; + + selectQb.select(builder); + } + break; + default: + { + selectQb.select(`${prevAlias}.${lookupColumn.column_name}`); + } + + break; + } + + return { builder: selectQb }; + } +}