perspectives: mongo join works

This commit is contained in:
Jan Prochazka
2022-10-02 09:44:52 +02:00
parent da5dd7ac62
commit f60e1190c8
5 changed files with 196 additions and 117 deletions

View File

@@ -57,8 +57,8 @@ export class PerspectiveDataLoader {
return conditions.length == 1 ? conditions[0] : conditions.length > 0 ? { $and: conditions } : null; return conditions.length == 1 ? conditions[0] : conditions.length > 0 ? { $and: conditions } : null;
} }
async loadGrouping(props: PerspectiveDataLoadProps) { async loadGroupingSqlDb(props: PerspectiveDataLoadProps) {
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns } = props; const { schemaName, pureName, bindingColumns } = props;
const bindingColumnExpressions = bindingColumns.map( const bindingColumnExpressions = bindingColumns.map(
columnName => columnName =>
@@ -96,7 +96,7 @@ export class PerspectiveDataLoader {
select.groupBy = bindingColumnExpressions; select.groupBy = bindingColumnExpressions;
if (dbg?.enabled) { if (dbg?.enabled) {
dbg(`LOAD COUNTS, table=${props.pureName}, columns=${props.dataColumns?.join(',')}`); dbg(`LOAD COUNTS, table=${props.pureName}, columns=${bindingColumns?.join(',')}`);
} }
const response = await this.apiCall('database-connections/sql-select', { const response = await this.apiCall('database-connections/sql-select', {
@@ -112,6 +112,52 @@ export class PerspectiveDataLoader {
})); }));
} }
async loadGroupingDocDb(props: PerspectiveDataLoadProps) {
const { schemaName, pureName, bindingColumns } = props;
const aggregate = [
{ $match: this.buildMongoCondition(props) },
{
$group: {
_id: _zipObject(
bindingColumns,
bindingColumns.map(col => '$' + col)
),
count: { $sum: 1 },
},
},
];
if (dbg?.enabled) {
dbg(`LOAD COUNTS, table=${props.pureName}, columns=${bindingColumns?.join(',')}`);
}
const response = await this.apiCall('database-connections/collection-data', {
conid: props.databaseConfig.conid,
database: props.databaseConfig.database,
options: {
pureName,
aggregate,
},
});
if (response.errorMessage) return response;
return response.rows.map(row => ({
...row._id,
_perspective_group_size_: parseInt(row.count),
}));
}
async loadGrouping(props: PerspectiveDataLoadProps) {
const { engineType } = props;
switch (engineType) {
case 'sqldb':
return this.loadGroupingSqlDb(props);
case 'docdb':
return this.loadGroupingDocDb(props);
}
}
async loadDataSqlDb(props: PerspectiveDataLoadProps) { async loadDataSqlDb(props: PerspectiveDataLoadProps) {
const { const {
schemaName, schemaName,

View File

@@ -8,7 +8,7 @@ import {
TableInfo, TableInfo,
ViewInfo, ViewInfo,
} from 'dbgate-types'; } from 'dbgate-types';
import { equalFullName } from 'dbgate-tools'; import { equalFullName, isCollectionInfo, isTableInfo, isViewInfo } from 'dbgate-tools';
import { import {
ChangePerspectiveConfigFunc, ChangePerspectiveConfigFunc,
createPerspectiveNodeConfig, createPerspectiveNodeConfig,
@@ -879,7 +879,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode {
export class PerspectiveTableNode extends PerspectiveTreeNode { export class PerspectiveTableNode extends PerspectiveTreeNode {
constructor( constructor(
public table: TableInfo | ViewInfo, public table: TableInfo | ViewInfo | CollectionInfo,
dbs: MultipleDatabaseInfo, dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
@@ -892,14 +892,16 @@ export class PerspectiveTableNode extends PerspectiveTreeNode {
} }
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
const isMongo = isCollectionInfo(this.table);
return { return {
schemaName: this.table.schemaName, schemaName: this.table.schemaName,
pureName: this.table.pureName, pureName: this.table.pureName,
dataColumns: this.getDataLoadColumns(), dataColumns: this.getDataLoadColumns(),
databaseConfig: this.databaseConfig, databaseConfig: this.databaseConfig,
orderBy: this.getOrderBy(this.table), orderBy: this.getOrderBy(this.table),
sqlCondition: this.getChildrenSqlCondition(), sqlCondition: isMongo ? null : this.getChildrenSqlCondition(),
engineType: 'sqldb', mongoCondition: isMongo ? this.getChildrenMongoCondition() : null,
engineType: isMongo ? 'docdb' : 'sqldb',
}; };
} }
@@ -956,87 +958,87 @@ export class PerspectiveTableNode extends PerspectiveTreeNode {
} }
} }
export class PerspectiveCollectionNode extends PerspectiveTreeNode { // export class PerspectiveCollectionNode extends PerspectiveTreeNode {
constructor( // constructor(
public collection: CollectionInfo, // public collection: CollectionInfo,
dbs: MultipleDatabaseInfo, // dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, // config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, // setConfig: ChangePerspectiveConfigFunc,
public dataProvider: PerspectiveDataProvider, // public dataProvider: PerspectiveDataProvider,
databaseConfig: PerspectiveDatabaseConfig, // databaseConfig: PerspectiveDatabaseConfig,
parentNode: PerspectiveTreeNode, // parentNode: PerspectiveTreeNode,
designerId: string // designerId: string
) { // ) {
super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); // super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId);
} // }
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { // getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
return { // return {
schemaName: this.collection.schemaName, // schemaName: this.collection.schemaName,
pureName: this.collection.pureName, // pureName: this.collection.pureName,
dataColumns: this.getDataLoadColumns(), // dataColumns: this.getDataLoadColumns(),
databaseConfig: this.databaseConfig, // databaseConfig: this.databaseConfig,
orderBy: this.getOrderBy(this.collection), // orderBy: this.getOrderBy(this.collection),
mongoCondition: this.getChildrenMongoCondition(), // mongoCondition: this.getChildrenMongoCondition(),
engineType: 'docdb', // engineType: 'docdb',
}; // };
} // }
get codeName() { // get codeName() {
return this.collection.schemaName // return this.collection.schemaName
? `${this.collection.schemaName}:${this.collection.pureName}` // ? `${this.collection.schemaName}:${this.collection.pureName}`
: this.collection.pureName; // : this.collection.pureName;
} // }
get title() { // get title() {
return this.nodeConfig?.alias || this.collection.pureName; // return this.nodeConfig?.alias || this.collection.pureName;
} // }
get isExpandable() { // get isExpandable() {
return true; // return true;
} // }
generateChildNodes(): PerspectiveTreeNode[] { // generateChildNodes(): PerspectiveTreeNode[] {
return getCollectionChildPerspectiveNodes( // return getCollectionChildPerspectiveNodes(
this.designerId, // this.designerId,
this.collection, // this.collection,
this.dbs, // this.dbs,
this.config, // this.config,
this.setConfig, // this.setConfig,
this.dataProvider, // this.dataProvider,
this.databaseConfig, // this.databaseConfig,
this // this
); // );
} // }
get icon() { // get icon() {
return 'img collection'; // return 'img collection';
} // }
getBaseTableFromThis() { // getBaseTableFromThis() {
return this.collection; // return this.collection;
} // }
get headerTableAttributes() { // get headerTableAttributes() {
return { // return {
schemaName: this.collection.schemaName, // schemaName: this.collection.schemaName,
pureName: this.collection.pureName, // pureName: this.collection.pureName,
conid: this.databaseConfig.conid, // conid: this.databaseConfig.conid,
database: this.databaseConfig.database, // database: this.databaseConfig.database,
}; // };
} // }
get tableCode() { // get tableCode() {
return `${this.collection.schemaName}|${this.collection.pureName}`; // return `${this.collection.schemaName}|${this.collection.pureName}`;
} // }
get namedObject(): NamedObjectInfo { // get namedObject(): NamedObjectInfo {
return { // return {
schemaName: this.collection.schemaName, // schemaName: this.collection.schemaName,
pureName: this.collection.pureName, // pureName: this.collection.pureName,
}; // };
} // }
} // }
// export class PerspectiveViewNode extends PerspectiveTreeNode { // export class PerspectiveViewNode extends PerspectiveTreeNode {
// constructor( // constructor(
@@ -1202,7 +1204,7 @@ export class PerspectiveTableReferenceNode extends PerspectiveTableNode {
export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode { export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode {
constructor( constructor(
public customJoin: PerspectiveCustomJoinConfig, public customJoin: PerspectiveCustomJoinConfig,
table: TableInfo | ViewInfo, table: TableInfo | ViewInfo | CollectionInfo,
dbs: MultipleDatabaseInfo, dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
@@ -1234,6 +1236,8 @@ export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode {
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
// console.log('CUSTOM JOIN', this.customJoin); // console.log('CUSTOM JOIN', this.customJoin);
// console.log('this.getDataLoadColumns()', this.getDataLoadColumns()); // console.log('this.getDataLoadColumns()', this.getDataLoadColumns());
const isMongo = isCollectionInfo(this.table);
return { return {
schemaName: this.table.schemaName, schemaName: this.table.schemaName,
pureName: this.table.pureName, pureName: this.table.pureName,
@@ -1245,8 +1249,9 @@ export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode {
dataColumns: this.getDataLoadColumns(), dataColumns: this.getDataLoadColumns(),
databaseConfig: this.databaseConfig, databaseConfig: this.databaseConfig,
orderBy: this.getOrderBy(this.table), orderBy: this.getOrderBy(this.table),
sqlCondition: this.getChildrenSqlCondition(), sqlCondition: isMongo ? null : this.getChildrenSqlCondition(),
engineType: 'sqldb', mongoCondition: isMongo ? this.getChildrenMongoCondition() : null,
engineType: isMongo ? 'docdb' : 'sqldb',
}; };
} }
@@ -1389,7 +1394,7 @@ export function getCollectionChildPerspectiveNodes(
} }
export function getTableChildPerspectiveNodes( export function getTableChildPerspectiveNodes(
table: TableInfo | ViewInfo, table: TableInfo | ViewInfo | CollectionInfo,
dbs: MultipleDatabaseInfo, dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
@@ -1400,14 +1405,19 @@ export function getTableChildPerspectiveNodes(
if (!table) return []; if (!table) return [];
const db = parentNode.db; const db = parentNode.db;
const columnNodes = table.columns.map(col => const pattern = dataProvider.dataPatterns[parentNode.designerId];
const tableOrView = isTableInfo(table) || isViewInfo(table) ? table : null;
const columnNodes =
tableOrView?.columns?.map(col =>
findDesignerIdForNode( findDesignerIdForNode(
config, config,
parentNode, parentNode,
designerId => designerId =>
new PerspectiveTableColumnNode( new PerspectiveTableColumnNode(
col, col,
table, tableOrView,
dbs, dbs,
config, config,
setConfig, setConfig,
@@ -1417,8 +1427,26 @@ export function getTableChildPerspectiveNodes(
designerId designerId
) )
) )
); ) ||
pattern?.columns?.map(col =>
findDesignerIdForNode(
config,
parentNode,
designerId =>
new PerspectivePatternColumnNode(
table,
col,
dbs,
config,
setConfig,
dataProvider,
databaseConfig,
parentNode,
designerId
)
)
) ||
[];
// if (!columnNodes.find(x => x.isChecked)) { // if (!columnNodes.find(x => x.isChecked)) {
// const circularColumns = columnNodes.filter(x => x.isCircular).map(x => x.columnName); // const circularColumns = columnNodes.filter(x => x.isCircular).map(x => x.columnName);
// const defaultColumns = getPerspectiveDefaultColumns(table, db, circularColumns); // const defaultColumns = getPerspectiveDefaultColumns(table, db, circularColumns);
@@ -1480,6 +1508,7 @@ export function getTableChildPerspectiveNodes(
const db = dbs?.[newConfig.conid]?.[newConfig.database]; const db = dbs?.[newConfig.conid]?.[newConfig.database];
const table = db?.tables?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); const table = db?.tables?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
const view = db?.views?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); const view = db?.views?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
const collection = db?.collections?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
const join: PerspectiveCustomJoinConfig = { const join: PerspectiveCustomJoinConfig = {
refNodeDesignerId: node.designerId, refNodeDesignerId: node.designerId,
@@ -1496,11 +1525,11 @@ export function getTableChildPerspectiveNodes(
: ref.columns.map(col => ({ baseColumnName: col.target, refColumnName: col.source })), : ref.columns.map(col => ({ baseColumnName: col.target, refColumnName: col.source })),
}; };
if (table || view) { if (table || view || collection) {
customs.push( customs.push(
new PerspectiveCustomJoinTreeNode( new PerspectiveCustomJoinTreeNode(
join, join,
table || view, table || view || collection,
dbs, dbs,
config, config,
setConfig, setConfig,

View File

@@ -1,4 +1,4 @@
import { DatabaseInfo, TableInfo, ApplicationDefinition } from 'dbgate-types'; import { DatabaseInfo, TableInfo, ApplicationDefinition, ViewInfo, CollectionInfo } from 'dbgate-types';
import _flatten from 'lodash/flatten'; import _flatten from 'lodash/flatten';
export function addTableDependencies(db: DatabaseInfo): DatabaseInfo { export function addTableDependencies(db: DatabaseInfo): DatabaseInfo {
@@ -118,3 +118,15 @@ export function isTableColumnUnique(table: TableInfo, column: string) {
} }
return false; return false;
} }
export function isTableInfo(obj: { objectTypeField?: string }): obj is TableInfo {
return obj.objectTypeField == 'tables';
}
export function isViewInfo(obj: { objectTypeField?: string }): obj is ViewInfo {
return obj.objectTypeField == 'views';
}
export function isCollectionInfo(obj: { objectTypeField?: string }): obj is CollectionInfo {
return obj.objectTypeField == 'collections';
}

View File

@@ -28,7 +28,6 @@
import { import {
ChangePerspectiveConfigFunc, ChangePerspectiveConfigFunc,
extractPerspectiveDatabases, extractPerspectiveDatabases,
PerspectiveCollectionNode,
PerspectiveConfig, PerspectiveConfig,
PerspectiveDataProvider, PerspectiveDataProvider,
PerspectiveTableNode, PerspectiveTableNode,
@@ -142,20 +141,9 @@
$: dataProvider = new PerspectiveDataProvider(cache, loader, $dataPatterns); $: dataProvider = new PerspectiveDataProvider(cache, loader, $dataPatterns);
$: root = $: root =
tableInfo || viewInfo tableInfo || viewInfo || collectionInfo
? new PerspectiveTableNode( ? new PerspectiveTableNode(
tableInfo || viewInfo, tableInfo || viewInfo || collectionInfo,
$dbInfos,
config,
setConfig,
dataProvider,
{ conid, database },
null,
config.rootDesignerId
)
: collectionInfo
? new PerspectiveCollectionNode(
collectionInfo,
$dbInfos, $dbInfos,
config, config,
setConfig, setConfig,

View File

@@ -207,6 +207,10 @@ const driver = {
if (options.countDocuments) { if (options.countDocuments) {
const count = await collection.countDocuments(convertObjectId(options.condition) || {}); const count = await collection.countDocuments(convertObjectId(options.condition) || {});
return { count }; return { count };
} else if (options.aggregate) {
let cursor = await collection.aggregate(options.aggregate);
const rows = await cursor.toArray();
return { rows: rows.map(transformMongoData) };
} else { } else {
// console.log('options.condition', JSON.stringify(options.condition, undefined, 2)); // console.log('options.condition', JSON.stringify(options.condition, undefined, 2));
let cursor = await collection.find(convertObjectId(options.condition) || {}); let cursor = await collection.find(convertObjectId(options.condition) || {});