diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts index 3d6fca0777..d06e3a460c 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts @@ -2343,141 +2343,152 @@ class BaseModelSqlv2 { value: Record[]; }[] > { - const column = await this.model - .getColumns() - .then((cols) => cols?.find((col) => col.id === args.groupColumnId)); - - if (!column) NcError.notFound('Column not found'); - if (isVirtualCol(column)) - NcError.notImplemented('Grouping for virtual columns not implemented'); - - // extract distinct group column values - let groupingValues; - if (column.uidt === UITypes.SingleSelect) { - const colOptions = await column.getColOptions(); - groupingValues = colOptions?.map((opt) => opt.title); - } else { - groupingValues = ( - await this.dbDriver(this.model.table_name) - .select(column.column_name) - .distinct() - ).map((row) => row[column.column_name]); - } - - const qb = this.dbDriver(this.model.table_name); + try { + const column = await this.model + .getColumns() + .then((cols) => cols?.find((col) => col.id === args.groupColumnId)); + + if (!column) NcError.notFound('Column not found'); + if (isVirtualCol(column)) + NcError.notImplemented('Grouping for virtual columns not implemented'); + + // extract distinct group column values + let groupingValues; + if (column.uidt === UITypes.SingleSelect) { + const colOptions = await column.getColOptions< + SelectOption[] & { options } + >(); + groupingValues = colOptions.options.map((opt) => opt.title); + } else { + groupingValues = ( + await this.dbDriver(this.model.table_name) + .select(column.column_name) + .distinct() + ).map((row) => row[column.column_name]); + } - qb.limit(+args?.limit || 20); - qb.offset(+args?.offset || 0); + const qb = this.dbDriver(this.model.table_name); - await this.selectObject({ qb }); + qb.limit(+args?.limit || 20); + qb.offset(+args?.offset || 0); - // todo: refactor and move to a method (applyFilterAndSort) - const aliasColObjMap = await this.model.getAliasColObjMap(); - let sorts = extractSortsObject(args?.sort, aliasColObjMap); - const filterObj = extractFilterFromXwhere(args.where, aliasColObjMap); - // todo: replace with view id - if (!args.ignoreFilterSort && this.viewId) { - await conditionV2( - [ - new Filter({ - children: - (await Filter.rootFilterList({ viewId: this.viewId })) || [], - is_group: true, - }), - new Filter({ - children: args.filterArr || [], - is_group: true, - logical_op: 'and', - }), - new Filter({ - children: filterObj, - is_group: true, - logical_op: 'and', - }), - ], - qb, - this.dbDriver - ); + await this.selectObject({ qb }); - if (!sorts) - sorts = args.sortArr?.length - ? args.sortArr - : await Sort.list({ viewId: this.viewId }); + // todo: refactor and move to a method (applyFilterAndSort) + const aliasColObjMap = await this.model.getAliasColObjMap(); + let sorts = extractSortsObject(args?.sort, aliasColObjMap); + const filterObj = extractFilterFromXwhere(args.where, aliasColObjMap); + // todo: replace with view id + if (!args.ignoreFilterSort && this.viewId) { + await conditionV2( + [ + new Filter({ + children: + (await Filter.rootFilterList({ viewId: this.viewId })) || [], + is_group: true, + }), + new Filter({ + children: args.filterArr || [], + is_group: true, + logical_op: 'and', + }), + new Filter({ + children: filterObj, + is_group: true, + logical_op: 'and', + }), + ], + qb, + this.dbDriver + ); - if (sorts?.['length']) await sortV2(sorts, qb, this.dbDriver); - } else { - await conditionV2( - [ - new Filter({ - children: args.filterArr || [], - is_group: true, - logical_op: 'and', - }), - new Filter({ - children: filterObj, - is_group: true, - logical_op: 'and', - }), - ], - qb, - this.dbDriver - ); + if (!sorts) + sorts = args.sortArr?.length + ? args.sortArr + : await Sort.list({ viewId: this.viewId }); - if (!sorts) sorts = args.sortArr; + if (sorts?.['length']) await sortV2(sorts, qb, this.dbDriver); + } else { + await conditionV2( + [ + new Filter({ + children: args.filterArr || [], + is_group: true, + logical_op: 'and', + }), + new Filter({ + children: filterObj, + is_group: true, + logical_op: 'and', + }), + ], + qb, + this.dbDriver + ); - if (sorts?.['length']) await sortV2(sorts, qb, this.dbDriver); - } + if (!sorts) sorts = args.sortArr; - // sort by primary key if not autogenerated string - // if autogenerated string sort by created_at column if present - if (this.model.primaryKey && this.model.primaryKey.ai) { - qb.orderBy(this.model.primaryKey.column_name); - } else if (this.model.columns.find((c) => c.column_name === 'created_at')) { - qb.orderBy('created_at'); - } + if (sorts?.['length']) await sortV2(sorts, qb, this.dbDriver); + } - const nullListQb = qb.clone().whereNull(column.title); + // sort by primary key if not autogenerated string + // if autogenerated string sort by created_at column if present + if (this.model.primaryKey && this.model.primaryKey.ai) { + qb.orderBy(this.model.primaryKey.column_name); + } else if ( + this.model.columns.find((c) => c.column_name === 'created_at') + ) { + qb.orderBy('created_at'); + } - const groupedQb = this.dbDriver.from( - this.dbDriver - .unionAll( - [ - this.isSqlite - ? this.dbDriver.select().from(nullListQb) - : nullListQb, - ...groupingValues.map((r) => { - const query = qb.clone().where(column.title, r); + const nullListQb = qb.clone().whereNull(column.title); - return this.isSqlite ? this.dbDriver.select().from(query) : query; - }), - ], - !this.isSqlite - ) - .as('__nc_grouped_list') - ); + const groupedQb = this.dbDriver.from( + this.dbDriver + .unionAll( + [ + this.isSqlite + ? this.dbDriver.select().from(nullListQb) + : nullListQb, + ...groupingValues.map((r) => { + const query = qb.clone().where(column.title, r); + + return this.isSqlite + ? this.dbDriver.select().from(query) + : query; + }), + ], + !this.isSqlite + ) + .as('__nc_grouped_list') + ); - const proto = await this.getProto(); + const proto = await this.getProto(); - const result = (await groupedQb)?.map((d) => { - d.__proto__ = proto; - return d; - }); + const result = (await groupedQb)?.map((d) => { + d.__proto__ = proto; + return d; + }); - // todo: handle null values - const groupedResult: Record[]> = _.groupBy( - result, - column.title - ); + // todo: handle null values + const groupedResult: Record[]> = + _.groupBy(result, column.title); - const r = Object.entries(groupedResult).map(([key, value]) => ({ - key, - value, - })); + const r = Object.entries(groupedResult).map(([key, value]) => ({ + key, + value, + })); - return r; + return r; + } catch (e) { + console.log(e); + throw e; + } } - public async groupedListCount(args: { groupColumnId: string ; ignoreFilterSort?:boolean} & XcFilter) { + public async groupedListCount( + args: { groupColumnId: string; ignoreFilterSort?: boolean } & XcFilter + ) { const column = await this.model .getColumns() .then((cols) => cols?.find((col) => col.id === args.groupColumnId)); @@ -2536,7 +2547,6 @@ class BaseModelSqlv2 { ); } - await this.selectObject({ qb, columns: [new Column({ ...column, title: 'key' })],