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>[];
}[]
> {
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[]>();
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<string, Record<string, unknown>[]> = _.groupBy(
result,
column.title
);
// todo: handle null values
const groupedResult: Record<string, Record<string, unknown>[]> =
_.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' })],

Loading…
Cancel
Save