mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 22:55:59 +00:00
perspectives: mongo join works
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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';
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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) || {});
|
||||||
|
|||||||
Reference in New Issue
Block a user