Browse Source

fix(nocodb): grouped data list

pull/3818/head
Wing-Kam Wong 2 years ago
parent
commit
44f11f1acb
  1. 246
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts

246
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts

@ -2343,141 +2343,152 @@ class BaseModelSqlv2 {
value: Record<string, unknown>[]; value: Record<string, unknown>[];
}[] }[]
> { > {
const column = await this.model try {
.getColumns() const column = await this.model
.then((cols) => cols?.find((col) => col.id === args.groupColumnId)); .getColumns()
.then((cols) => cols?.find((col) => col.id === args.groupColumnId));
if (!column) NcError.notFound('Column not found');
if (isVirtualCol(column)) if (!column) NcError.notFound('Column not found');
NcError.notImplemented('Grouping for virtual columns not implemented'); if (isVirtualCol(column))
NcError.notImplemented('Grouping for virtual columns not implemented');
// extract distinct group column values
let groupingValues; // extract distinct group column values
if (column.uidt === UITypes.SingleSelect) { let groupingValues;
const colOptions = await column.getColOptions<SelectOption[]>(); if (column.uidt === UITypes.SingleSelect) {
groupingValues = colOptions?.map((opt) => opt.title); const colOptions = await column.getColOptions<
} else { SelectOption[] & { options }
groupingValues = ( >();
await this.dbDriver(this.model.table_name) groupingValues = colOptions.options.map((opt) => opt.title);
.select(column.column_name) } else {
.distinct() groupingValues = (
).map((row) => row[column.column_name]); await this.dbDriver(this.model.table_name)
} .select(column.column_name)
.distinct()
const qb = this.dbDriver(this.model.table_name); ).map((row) => row[column.column_name]);
}
qb.limit(+args?.limit || 20); const qb = this.dbDriver(this.model.table_name);
qb.offset(+args?.offset || 0);
await this.selectObject({ qb }); qb.limit(+args?.limit || 20);
qb.offset(+args?.offset || 0);
// todo: refactor and move to a method (applyFilterAndSort) await this.selectObject({ qb });
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) // todo: refactor and move to a method (applyFilterAndSort)
sorts = args.sortArr?.length const aliasColObjMap = await this.model.getAliasColObjMap();
? args.sortArr let sorts = extractSortsObject(args?.sort, aliasColObjMap);
: await Sort.list({ viewId: this.viewId }); 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); if (!sorts)
} else { sorts = args.sortArr?.length
await conditionV2( ? args.sortArr
[ : await Sort.list({ viewId: this.viewId });
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; 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 (sorts?.['length']) await sortV2(sorts, qb, this.dbDriver);
// 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 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( const nullListQb = qb.clone().whereNull(column.title);
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; const groupedQb = this.dbDriver.from(
}), this.dbDriver
], .unionAll(
!this.isSqlite [
) this.isSqlite
.as('__nc_grouped_list') ? 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) => { const result = (await groupedQb)?.map((d) => {
d.__proto__ = proto; d.__proto__ = proto;
return d; return d;
}); });
// todo: handle null values // todo: handle null values
const groupedResult: Record<string, Record<string, unknown>[]> = _.groupBy( const groupedResult: Record<string, Record<string, unknown>[]> =
result, _.groupBy(result, column.title);
column.title
);
const r = Object.entries(groupedResult).map(([key, value]) => ({ const r = Object.entries(groupedResult).map(([key, value]) => ({
key, key,
value, 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 const column = await this.model
.getColumns() .getColumns()
.then((cols) => cols?.find((col) => col.id === args.groupColumnId)); .then((cols) => cols?.find((col) => col.id === args.groupColumnId));
@ -2536,7 +2547,6 @@ class BaseModelSqlv2 {
); );
} }
await this.selectObject({ await this.selectObject({
qb, qb,
columns: [new Column({ ...column, title: 'key' })], columns: [new Column({ ...column, title: 'key' })],

Loading…
Cancel
Save