UNPKG

@aggregion/mongo-like-query-to-sql

Version:
298 lines (297 loc) 11 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.objToSQL = exports.convertASTToSQL = exports.parseObjToAST = void 0; /* eslint-disable @typescript-eslint/no-use-before-define */ var isEmpty = function (arg) { if (arg === null || arg === undefined) { return true; } if (typeof arg === 'string') { return !arg.length; } if (Array.isArray(arg)) { return !arg.length; } if (typeof arg === 'object') { return !Object.keys(arg).length; } return false; }; var castValue = function (v) { if (typeof v === 'number') { return v; } if (typeof v === 'string') { return "'" + v + "'"; } if (v === null) { return 'NULL'; } return v; }; var isOperator = function (v) { return v.startsWith('$'); }; // https://clickhouse.yandex/docs/ru/query_language/functions/array_functions/ var isFunction = function (v) { return ['$has', '$hasAll', '$hasAny', '$notHasAll', '$notHasAny', '$notHas'].includes(v); }; var isScalar = function (v) { return ['number', 'string'].includes(typeof v) || v === null; }; var castOp = function (v) { switch (v) { case '$and': return 'AND'; case '$or': return 'OR'; case '$lt': return '<'; case '$gt': return '>'; case '$lte': return '<='; case '$gte': return '>='; case '$eq': return '='; case '$ne': return '!='; case '$in': return 'IN'; case '$nin': return 'NOT IN'; case '$not': return 'NOT'; case '$is': return 'IS'; default: throw new Error("Unknown operator " + v); } }; exports.parseObjToAST = function (_a) { var _b; var where = _a.where, parentField = _a.parentField, parentOp = _a.parentOp, _c = _a.parentIsArray, parentIsArray = _c === void 0 ? false : _c; if (isScalar(where) || (parentOp && ['$in', '$nin'].includes(parentOp))) { return { ast: where }; } var ast = []; var foreigns = {}; for (var field in where) { if (isOperator(field)) { if (isFunction(field)) { // const parsedAst = parseObjToAST({where: where[field], parentField: field, parentOp: field}); // if (parsedAst.foreigns) { // Object.assign(foreigns, parsedAst.foreigns); // } var tmp = { value: where[field], func: field.substring(1), }; if (parentField) { tmp.field = parentField; } ast.push(tmp); } else if (['$inSubquery', '$ninSubquery'].includes(field)) { var subquery = where[field]; if ('having' in subquery && typeof subquery.having !== 'string') { throw new Error(field + " having clause should be of type string"); } var parsedAST = exports.parseObjToAST({ where: subquery.where }); var tmp = (_b = {}, _b[field.substr(1)] = { whereAST: parsedAST.ast, from: subquery.from, field: subquery.field, foreigns: parsedAST.foreigns, groupBy: subquery.groupBy, having: subquery.having, }, _b.field = parentField, _b); ast.push(tmp); } else { var parsedAst = exports.parseObjToAST({ where: where[field], parentField: field, parentOp: field, }); if (parsedAst.foreigns) { Object.assign(foreigns, parsedAst.foreigns); } var tmp = { value: parsedAst.ast, op: castOp(field), }; if (parentField && !parentIsArray) { tmp.field = parentField; } if (tmp.field && tmp.field.indexOf('.') > 0) { foreigns[tmp.field.split('.')[0]] = true; } ast.push(tmp); } } else { var parsedAst = exports.parseObjToAST({ where: where[field], parentField: field, parentOp: parentOp, parentIsArray: Array.isArray(where), }); if (parsedAst.foreigns) { Object.assign(foreigns, parsedAst.foreigns); } if (isScalar(parsedAst.ast)) { return { ast: { field: field, value: parsedAst.ast, op: castOp('$eq'), }, foreigns: foreigns, }; } ast.push({ values: parsedAst.ast, op: 'AND', }); } } return { ast: ast, foreigns: foreigns }; }; // eslint-disable-next-line complexity var castToConditionPiece = function (_a) { var _b = _a === void 0 ? {} : _a, op = _b.op, field = _b.field, inSubquery = _b.inSubquery, ninSubquery = _b.ninSubquery, value = _b.value, func = _b.func; if (op) { switch (op) { case 'IN': case 'NOT IN': if (!Array.isArray(value)) { throw new TypeError('Incorrect type of value for "IN". Value must be an Array.'); } return field + " " + op + " (" + value.map(castValue).join(', ') + ")"; case 'OR': case 'AND': if (!Array.isArray(value)) { throw new TypeError("Incorrect type of value for \"" + op + "\". Value must be an Array."); } return "(" + value.map(exports.convertASTToSQL).join(" " + op + " ") + ")"; case 'NOT': if (Array.isArray(value)) { return "NOT (" + value.map(exports.convertASTToSQL) + ")"; } if (isScalar(value)) { return "NOT " + castValue(value); } return "NOT (" + castToConditionPiece(value) + ")"; // Maybe incorrect. Not used. case 'IS': if (isScalar(value)) { return field + " IS " + castValue(value); } return field + " IS " + value.map(castToConditionPiece); default: return field + " " + op + " " + castValue(value); } } else if (func) { switch (func) { case 'hasAny': case 'hasAll': if (Array.isArray(value)) { return func + "(" + field + ", [" + value.map(castValue).join(', ') + "])"; } if (isScalar(value)) { return func + "(" + field + ", [" + castValue(value) + "])"; } throw new Error("Incorrect argument for \"" + func + "\" func"); case 'notHasAll': if (Array.isArray(value)) { return "NOT hasAll(" + field + ", [" + value.map(castValue).join(', ') + "])"; } if (isScalar(value)) { return "NOT hasAll(" + field + ", [" + castValue(value) + "])"; } throw new Error("Incorrect argument for \"" + func + "\" func"); case 'notHasAny': if (Array.isArray(value)) { return "NOT hasAny(" + field + ", [" + value.map(castValue).join(', ') + "])"; } if (isScalar(value)) { return "NOT hasAny(" + field + ", [" + castValue(value) + "])"; } throw new Error("Incorrect argument for \"" + func + "\" func"); case 'has': if (!isScalar(value)) { throw new Error("Incorrect argument for \"" + func + "\" func"); } return func + "(" + field + ", " + castValue(value) + ")"; case 'notHas': if (!isScalar(value)) { throw new Error("Incorrect argument for \"" + func + "\" func"); } return "NOT has(" + field + ", " + castValue(value) + ")"; default: throw new Error('Unknown func'); } } else if (inSubquery) { var sql = field + " IN (SELECT " + inSubquery.field + " FROM " + inSubquery.from; if (inSubquery.whereAST && !isEmpty(inSubquery.whereAST)) { sql += " WHERE " + exports.convertASTToSQL(inSubquery.whereAST); } if (inSubquery.groupBy) { sql += " GROUP BY " + inSubquery.groupBy; } if (inSubquery.having) { sql += " HAVING " + inSubquery.having; } sql += ')'; return sql; } else if (ninSubquery) { var sql = field + " NOT IN (SELECT " + ninSubquery.field + " FROM " + ninSubquery.from; if (ninSubquery.whereAST && !isEmpty(ninSubquery.whereAST)) { sql += " WHERE " + exports.convertASTToSQL(ninSubquery.whereAST); } if (ninSubquery.groupBy) { sql += " GROUP BY " + ninSubquery.groupBy; } if (ninSubquery.having) { sql += " HAVING " + ninSubquery.having; } sql += ')'; return sql; } else { throw new Error('AST item must contain "op" or "func"'); } }; exports.convertASTToSQL = function (ast) { var sqlArr = []; if (Array.isArray(ast)) { for (var _i = 0, ast_1 = ast; _i < ast_1.length; _i++) { var value = ast_1[_i]; if (Array.isArray(value.values)) { sqlArr.push("(" + value.values.map(exports.convertASTToSQL).join(" " + value.op + " ") + ")"); } else { sqlArr.push(castToConditionPiece(value)); } } } else if (Array.isArray(ast.values)) { sqlArr.push("(" + ast.values.map(exports.convertASTToSQL).join(" " + ast.op + " ") + ")"); } else { sqlArr.push(castToConditionPiece(ast)); } return sqlArr.join(' AND '); }; exports.objToSQL = function (where) { var result = exports.parseObjToAST({ where: where }); if (result.ast && Array.isArray(result.ast) && result.ast.length > 0 && Array.isArray(result.ast[0].value) && result.ast[0].value.length === 0) { return '(1=1)'; } return exports.convertASTToSQL(result.ast); };