Files
dbgate/packages/datalib/src/PerspectiveDataLoader.ts
2022-10-01 19:18:18 +02:00

309 lines
7.4 KiB
TypeScript

import { Condition, Expression, Select } from 'dbgate-sqltree';
import { PerspectiveDataLoadProps } from './PerspectiveDataProvider';
import debug from 'debug';
import _zipObject from 'lodash/zipObject';
const dbg = debug('dbgate:PerspectiveDataLoader');
export class PerspectiveDataLoader {
constructor(public apiCall) {}
buildSqlCondition(props: PerspectiveDataLoadProps): Condition {
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, sqlCondition } = props;
const conditions = [];
if (sqlCondition) {
conditions.push(sqlCondition);
}
if (bindingColumns?.length == 1) {
conditions.push({
conditionType: 'in',
expr: {
exprType: 'column',
columnName: bindingColumns[0],
source: {
name: { schemaName, pureName },
},
},
values: bindingValues.map(x => x[0]),
});
}
return conditions.length > 0
? {
conditionType: 'and',
conditions,
}
: null;
}
buildMongoCondition(props: PerspectiveDataLoadProps): {} {
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, mongoCondition } = props;
const conditions = [];
if (mongoCondition) {
conditions.push(mongoCondition);
}
if (bindingColumns?.length == 1) {
conditions.push({
[bindingColumns[0]]: { $in: bindingValues.map(x => x[0]) },
});
}
return conditions.length == 1 ? conditions[0] : conditions.length > 0 ? { $and: conditions } : null;
}
async loadGrouping(props: PerspectiveDataLoadProps) {
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns } = props;
const bindingColumnExpressions = bindingColumns.map(
columnName =>
({
exprType: 'column',
columnName,
source: {
name: { schemaName, pureName },
},
} as Expression)
);
const select: Select = {
commandType: 'select',
from: {
name: { schemaName, pureName },
},
columns: [
{
exprType: 'call',
func: 'COUNT',
args: [
{
exprType: 'raw',
sql: '*',
},
],
alias: '_perspective_group_size_',
},
...bindingColumnExpressions,
],
where: this.buildSqlCondition(props),
};
select.groupBy = bindingColumnExpressions;
if (dbg?.enabled) {
dbg(`LOAD COUNTS, table=${props.pureName}, columns=${props.dataColumns?.join(',')}`);
}
const response = await this.apiCall('database-connections/sql-select', {
conid: props.databaseConfig.conid,
database: props.databaseConfig.database,
select,
});
if (response.errorMessage) return response;
return response.rows.map(row => ({
...row,
_perspective_group_size_: parseInt(row._perspective_group_size_),
}));
}
async loadDataSqlDb(props: PerspectiveDataLoadProps) {
const {
schemaName,
pureName,
bindingColumns,
bindingValues,
dataColumns,
orderBy,
sqlCondition: condition,
engineType,
} = props;
if (dataColumns?.length == 0) {
return [];
}
const select: Select = {
commandType: 'select',
from: {
name: { schemaName, pureName },
},
columns: dataColumns?.map(columnName => ({
exprType: 'column',
columnName,
source: {
name: { schemaName, pureName },
},
})),
selectAll: !dataColumns,
orderBy: orderBy?.map(({ columnName, order }) => ({
exprType: 'column',
columnName,
direction: order,
source: {
name: { schemaName, pureName },
},
})),
range: props.range,
where: this.buildSqlCondition(props),
};
if (dbg?.enabled) {
dbg(
`LOAD DATA, table=${props.pureName}, columns=${props.dataColumns?.join(',')}, range=${props.range?.offset},${
props.range?.limit
}`
);
}
const response = await this.apiCall('database-connections/sql-select', {
conid: props.databaseConfig.conid,
database: props.databaseConfig.database,
select,
});
if (response.errorMessage) return response;
return response.rows;
}
getDocDbLoadOptions(props: PerspectiveDataLoadProps, useSort: boolean) {
const { pureName } = props;
const res: any = {
pureName,
condition: this.buildMongoCondition(props),
skip: props.range?.offset,
limit: props.range?.limit,
};
if (useSort && props.orderBy?.length > 0) {
res.sort = _zipObject(
props.orderBy.map(col => col.columnName),
props.orderBy.map(col => (col.order == 'DESC' ? -1 : 1))
);
}
return res;
}
async loadDataDocDb(props: PerspectiveDataLoadProps) {
const {
schemaName,
pureName,
bindingColumns,
bindingValues,
dataColumns,
orderBy,
sqlCondition: condition,
engineType,
} = props;
if (dataColumns?.length == 0) {
return [];
}
if (dbg?.enabled) {
dbg(
`LOAD DATA, collection=${props.pureName}, columns=${props.dataColumns?.join(',')}, range=${
props.range?.offset
},${props.range?.limit}`
);
}
const options = this.getDocDbLoadOptions(props, true);
const response = await this.apiCall('database-connections/collection-data', {
conid: props.databaseConfig.conid,
database: props.databaseConfig.database,
options,
});
if (response.errorMessage) return response;
return response.rows;
}
async loadData(props: PerspectiveDataLoadProps) {
const { engineType } = props;
switch (engineType) {
case 'sqldb':
return this.loadDataSqlDb(props);
case 'docdb':
return this.loadDataDocDb(props);
}
}
async loadRowCountSqlDb(props: PerspectiveDataLoadProps) {
const {
schemaName,
pureName,
bindingColumns,
bindingValues,
dataColumns,
orderBy,
sqlCondition: condition,
} = props;
const select: Select = {
commandType: 'select',
from: {
name: { schemaName, pureName },
},
columns: [
{
exprType: 'raw',
sql: 'COUNT(*)',
alias: 'count',
},
],
where: this.buildSqlCondition(props),
};
const response = await this.apiCall('database-connections/sql-select', {
conid: props.databaseConfig.conid,
database: props.databaseConfig.database,
select,
});
if (response.errorMessage) return response;
return response.rows[0];
}
async loadRowCountDocDb(props: PerspectiveDataLoadProps) {
const {
schemaName,
pureName,
bindingColumns,
bindingValues,
dataColumns,
orderBy,
sqlCondition: condition,
} = props;
const options = {
...this.getDocDbLoadOptions(props, false),
countDocuments: true,
};
const response = await this.apiCall('database-connections/collection-data', {
conid: props.databaseConfig.conid,
database: props.databaseConfig.database,
options,
});
return response;
}
async loadRowCount(props: PerspectiveDataLoadProps) {
const { engineType } = props;
switch (engineType) {
case 'sqldb':
return this.loadRowCountSqlDb(props);
case 'docdb':
return this.loadRowCountDocDb(props);
}
}
}