diff --git a/packages/tools/src/filterBehaviours.ts b/packages/tools/src/filterBehaviours.ts index 1a1e07100..619639012 100644 --- a/packages/tools/src/filterBehaviours.ts +++ b/packages/tools/src/filterBehaviours.ts @@ -39,13 +39,15 @@ export const datetimeFilterBehaviour: FilterBehaviour = { }; export const mongoFilterBehaviour: FilterBehaviour = { - compilerType: 'mongoCondition', + compilerType: 'sqlTree', supportEquals: true, supportArrayTesting: true, supportNumberLikeComparison: true, supportStringInclusion: true, supportBooleanValues: true, supportExistsTesting: true, + + allowStringToken: true, }; export const evalFilterBehaviour: FilterBehaviour = { diff --git a/packages/web/src/datagrid/CollectionDataGridCore.svelte b/packages/web/src/datagrid/CollectionDataGridCore.svelte index a4e72e838..d1699e9b9 100644 --- a/packages/web/src/datagrid/CollectionDataGridCore.svelte +++ b/packages/web/src/datagrid/CollectionDataGridCore.svelte @@ -33,11 +33,18 @@ const ast = parseFilter(filters[uniqueName], filterBehaviour); // console.log('AST', ast); const cond = _.cloneDeepWith(ast, expr => { - if (expr.__placeholder__) { + if (expr.exprType == 'placeholder') { return { - [uniqueName]: expr.__placeholder__, + exprType: 'column', + columnName: uniqueName, }; } + + // if (expr.__placeholder__) { + // return { + // [uniqueName]: expr.__placeholder__, + // }; + // } }); conditions.push(cond); } catch (err) { @@ -47,7 +54,8 @@ return conditions.length > 0 ? { - $and: conditions, + conditionType: 'and', + conditions, } : undefined; } diff --git a/plugins/dbgate-plugin-mongo/src/backend/driver.js b/plugins/dbgate-plugin-mongo/src/backend/driver.js index 64762e051..e9ecbede4 100644 --- a/plugins/dbgate-plugin-mongo/src/backend/driver.js +++ b/plugins/dbgate-plugin-mongo/src/backend/driver.js @@ -7,6 +7,7 @@ const MongoClient = require('mongodb').MongoClient; const ObjectId = require('mongodb').ObjectId; const AbstractCursor = require('mongodb').AbstractCursor; const createBulkInsertStream = require('./createBulkInsertStream'); +const { convertToMongoCondition } = require('../frontend/convertToMongoCondition'); function transformMongoData(row) { return _.cloneDeepWith(row, (x) => { @@ -269,10 +270,13 @@ const driver = { return res.databases; }, async readCollection(pool, options) { + const mongoCondition = convertToMongoCondition(options.condition); + console.log('******************* mongoCondition *****************') + console.log(JSON.stringify(mongoCondition, undefined, 2)); try { const collection = pool.__getDatabase().collection(options.pureName); if (options.countDocuments) { - const count = await collection.countDocuments(convertObjectId(options.condition) || {}); + const count = await collection.countDocuments(convertObjectId(mongoCondition) || {}); return { count }; } else if (options.aggregate) { let cursor = await collection.aggregate(convertObjectId(options.aggregate)); @@ -280,7 +284,7 @@ const driver = { return { rows: rows.map(transformMongoData) }; } else { // console.log('options.condition', JSON.stringify(options.condition, undefined, 2)); - let cursor = await collection.find(convertObjectId(options.condition) || {}); + let cursor = await collection.find(convertObjectId(mongoCondition) || {}); if (options.sort) cursor = cursor.sort(options.sort); if (options.skip) cursor = cursor.skip(options.skip); if (options.limit) cursor = cursor.limit(options.limit); diff --git a/plugins/dbgate-plugin-mongo/src/frontend/convertToMongoCondition.js b/plugins/dbgate-plugin-mongo/src/frontend/convertToMongoCondition.js new file mode 100644 index 000000000..536f538a1 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/src/frontend/convertToMongoCondition.js @@ -0,0 +1,99 @@ +function convertLeftOperandToMongoColumn(left) { + if (left.exprType == 'placeholder') return '__placeholder__'; + if (left.exprType == 'column') return left.columnName; + throw new Error(`Unknown left operand type ${left.exprType}`); +} + +function convertRightOperandToMongoValue(right) { + if (right.exprType == 'value') return right.value; + throw new Error(`Unknown right operand type ${right.exprType}`); +} + +function convertToMongoCondition(filter) { + if (!filter) { + return null; + } + switch (filter.conditionType) { + case 'and': + return { + $and: filter.conditions.map((x) => convertToMongoCondition(x)), + }; + case 'or': + return { + $or: filter.conditions.map((x) => convertToMongoCondition(x)), + }; + case 'binary': + switch (filter.operator) { + case '=': + return { + [convertLeftOperandToMongoColumn(filter.left)]: { + $eq: convertRightOperandToMongoValue(filter.right), + }, + }; + case '!=': + case '<>': + return { + [convertLeftOperandToMongoColumn(filter.left)]: { + $ne: convertRightOperandToMongoValue(filter.right), + }, + }; + case '<': + return { + [convertLeftOperandToMongoColumn(filter.left)]: { + $lt: convertRightOperandToMongoValue(filter.right), + }, + }; + case '<=': + return { + [convertLeftOperandToMongoColumn(filter.left)]: { + $lte: convertRightOperandToMongoValue(filter.right), + }, + }; + case '>': + return { + [convertLeftOperandToMongoColumn(filter.left)]: { + $gt: convertRightOperandToMongoValue(filter.right), + }, + }; + case '>=': + return { + [convertLeftOperandToMongoColumn(filter.left)]: { + $gte: convertRightOperandToMongoValue(filter.right), + }, + }; + } + break; + + case 'isNull': + return { + [convertLeftOperandToMongoColumn(filter.expr)]: { + $exists: false, + }, + }; + + case 'isNotNull': + return { + [convertLeftOperandToMongoColumn(filter.expr)]: { + $exists: true, + }, + }; + + case 'not': + return { + $not: convertToMongoCondition(filter.condition), + }; + case 'like': + return { + [convertLeftOperandToMongoColumn(filter.left)]: { + $regex: `${convertRightOperandToMongoValue(filter.right)}`.replace(/%/g, '.*'), + $options: 'i', + }, + }; + default: + throw new Error(`Unknown condition type ${filter.conditionType}`); + } +} + +module.exports = { + convertToMongoCondition, +};