UNPKG

tspace-mysql

Version:

Tspace MySQL is a promise-based ORM for Node.js, designed with modern TypeScript and providing type safety for schema databases.

563 lines 19.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MongodbQueryBuilder = void 0; const __1 = require(".."); class MongodbQueryBuilder extends __1.QueryBuilder { constructor(state) { super(state); } select = () => { let PIPELINE = []; if (this.$state.get('SELECT')?.length && !this.$state.get('SELECT').some(v => v === '*')) { const parseSelect = (arr) => { const result = {}; for (const str of arr) { const match = str.match(/`[^`]+`\.`([^`]+)`/); if (!match) continue; const key = match[1]; result[key] = true; } return result; }; const project = parseSelect(this.$state.get('SELECT')); if (Object.keys(project).length) { PIPELINE.push({ $project: project }); } } if (this.$state.get('JOIN')?.length) { const parseJoin = (join) => { const type = /LEFT JOIN/i.test(join) ? "LEFT" : /RIGHT JOIN/i.test(join) ? "RIGHT" : "INNER"; const regex = /(INNER|LEFT|RIGHT)\s+JOIN\s+`(\w+)`\s+ON\s+`(\w+)`\.`(\w+)`\s*=\s*`(\w+)`\.`(\w+)`/i; const match = join.match(regex); if (!match) return null; const [, , from, localTable, localField, foreignTable, foreignField] = match; return { type, from, localField, foreignField, as: from }; }; const joins = this.$state.get('JOIN'); for (const join of joins) { const parsed = parseJoin(join); if (!parsed) continue; if (parsed.type === "RIGHT") { throw new Error("RIGHT JOIN is not supported in MongoDB aggregation pipeline."); } PIPELINE.push({ $lookup: { from: parsed.from, localField: parsed.localField, foreignField: parsed.foreignField, as: parsed.as } }); PIPELINE.push({ $unwind: { path: `$${parsed.as}`, preserveNullAndEmptyArrays: parsed.type === "INNER" ? false : true } }); } } if (this.$state.get('WHERE')?.length) { PIPELINE.push({ $match: this._parseWheres(this.$state.get('WHERE')) }); } if (this.$state.get('SELECT')?.length && !this.$state.get('SELECT').some(v => v === '*')) { const aggregate = this.$state.get('SELECT') .map(col => { const match = col.match(/\b(COUNT|AVG|SUM|MAX|MIN)\s*\((.*?)\)\s+AS\s+`?(\w+)`?/i); if (!match) return null; return { fn: match[1].toUpperCase(), field: String(match[2].match(/`[^`]+`\.`([^`]+)`/)?.[1] ?? ''), as: match[3] }; }) .find(Boolean); if (aggregate) { switch (aggregate.fn) { case 'COUNT': PIPELINE.push({ $count: aggregate.as }); break; case 'SUM': PIPELINE.push({ $group: { _id: null, [aggregate.as]: { $sum: `$${aggregate.field}` } } }); break; case 'AVG': PIPELINE.push({ $group: { _id: null, [aggregate.as]: { $avg: `$${aggregate.field}` } } }); break; case 'MAX': PIPELINE.push({ $group: { _id: null, [aggregate.as]: { $max: `$${aggregate.field}` } } }); break; case 'MIN': PIPELINE.push({ $group: { _id: null, [aggregate.as]: { $min: `$${aggregate.field}` } } }); break; } } } if (this.$state.get('ORDER_BY').length) { PIPELINE.push({ $sort: this.$state.get('ORDER_BY') }); } if (this.$state.get('OFFSET')) { PIPELINE.push({ $skip: this.$state.get('OFFSET') }); } if (this.$state.get('LIMIT')) { PIPELINE.push({ $limit: this.$state.get('LIMIT') }); } const collection = this.$state.get('TABLE_NAME')?.replace(/\`/g, ""); const pipeline = `db.${collection}.aggregate(${JSON.stringify(PIPELINE)}).toArray()`; return pipeline; }; insert() { const query = this.$state.get("INSERT"); if (!query) return ''; const parseInsert = (input) => { const normalizeColumn = (col) => col.replace(/`/g, '').split('.').pop() || ''; const splitValues = (str) => { const result = []; let current = ''; let inQuote = false; for (let i = 0; i < str.length; i++) { const char = str[i]; if (char === "'") { inQuote = !inQuote; continue; } if (char === ',' && !inQuote) { result.push(current.trim()); current = ''; continue; } current += char; } if (current) result.push(current.trim()); return result; }; const parseValue = (val) => { const v = val.trim(); if (!isNaN(Number(v))) return Number(v); if (v.toLowerCase() === 'true') return true; if (v.toLowerCase() === 'false') return false; if (v.toLowerCase() === 'null') return null; return v; }; const columns = input.columns.map(normalizeColumn); return input.values.map(row => { const values = splitValues(row).map(parseValue); const obj = { "_id": this._objectId() }; columns.forEach((col, i) => { obj[col] = values[i]; }); return obj; }); }; const collection = this.$state.get('TABLE_NAME')?.replace(/\`/g, ""); return `db.${collection}.insertMany(${JSON.stringify(parseInsert(query))})`; } update() { const query = this.$state.get("UPDATE"); if (!query || !query.length) return ''; const parseValue = (val) => { const v = val.trim().replace(/^'|'$/g, ''); if (!isNaN(Number(v))) return Number(v); if (v.toLowerCase() === 'true') return true; if (v.toLowerCase() === 'false') return false; if (v.toLowerCase() === 'null') return null; return v; }; const parseSet = (input) => { const result = {}; input.forEach(pair => { const [key, value] = pair.split('='); const k = key.replace(/`/g, '').split('.').pop()?.trim() || ''; result[k] = parseValue(value); }); return result; }; const collection = this.$state.get('TABLE_NAME')?.replace(/`/g, ""); const update = { $set: parseSet(query) }; const filter = this._parseWheres(this.$state.get('WHERE')); return `db.${collection}.updateMany(${JSON.stringify(filter)}, ${JSON.stringify(update)})`; } remove() { const isDelete = this.$state.get("DELETE"); if (!isDelete) { throw new Error("Bad query builder: DELETE state not enabled. Please check your query configuration."); } const collection = this.$state .get("TABLE_NAME") ?.replace(/`/g, ""); const filter = this._parseWheres(this.$state.get('WHERE')); return `db.${collection}.deleteMany(${JSON.stringify(filter)})`; } any() { if (this.$state.get("INSERT")) return this.insert(); if (this.$state.get("UPDATE")) return this.update(); if (this.$state.get("DELETE")) return this.remove(); return this.select(); } getColumns({ database, table }) { throw new Error("Method not implemented."); return ''; } getSchema({ database, table }) { throw new Error("Method not implemented."); return ''; } getTables(database) { throw new Error("Method not implemented."); return ''; } hasTable({ database, table }) { throw new Error("Method not implemented."); return ''; } createDatabase(database) { throw new Error("Method not implemented."); return ''; } createTable({ database, table, schema, }) { throw new Error("Method not implemented."); return ''; } addColumn({ table, column, type, attributes, after, }) { throw new Error("Method not implemented."); return ''; } changeColumn({ table, column, type, attributes, }) { throw new Error("Method not implemented."); return ''; } getChildFKs({ database, table }) { throw new Error("Method not implemented."); return ''; } getFKs({ database, table }) { throw new Error("Method not implemented."); return ''; } hasFK({ database, table, constraint, }) { throw new Error("Method not implemented."); return ''; } addFK({ table, tableRef, key, constraint, foreign, }) { throw new Error("Method not implemented."); return ''; } dropFK({ table, constraint, }) { throw new Error("Method not implemented."); return ''; } getIndexes({ database, table }) { throw new Error("Method not implemented."); return ''; } hasIndex({ database, table, name, }) { throw new Error("Method not implemented."); return ''; } addIndex({ table, name, columns, }) { throw new Error("Method not implemented."); return ''; } dropIndex({ table, name, }) { throw new Error("Method not implemented."); return ''; } hasUnique({ database, table, name, }) { throw new Error("Method not implemented."); return ''; } addUnique({ table, name, columns }) { throw new Error("Method not implemented."); return ''; } dropUnique({ table, name, }) { throw new Error("Method not implemented."); return ''; } hasPrimaryKey({ database, table, }) { throw new Error("Method not implemented."); return ''; } addPrimaryKey({ table, columns, }) { throw new Error("Method not implemented."); return ''; } dropPrimaryKey({ table }) { throw new Error("Method not implemented."); return ''; } getDatabase(database) { throw new Error("Method not implemented."); return ''; } dropDatabase(database) { throw new Error("Method not implemented."); return ''; } dropView(view) { throw new Error("Method not implemented."); return ''; } dropTable(table) { const sql = [ `${this.$constants("DROP_TABLE")}`, `\`${table.replace(/`/g, "")}\`` ].join(" "); return this.format(sql); } truncate(table) { return `db.${table}.deleteMany({})`; } sleep(second) { throw new Error("Method not implemented."); return ''; } format(pipeline) { if (typeof pipeline === "string") pipeline = [pipeline]; return pipeline .filter((s) => s !== "" || s == null) .join(" ") .replace(/\s+/g, " "); } getActiveConnections() { throw new Error("Method not implemented."); return ''; } getMaxConnections() { throw new Error("Method not implemented."); return ''; } bindJoin(values) { return ""; } bindWhere(values) { return ""; } bindOrderBy(values) { return ``; } bindGroupBy(values) { return ``; } bindSelect(values, { distinct } = {}) { return ``; } bindFrom({ from, alias, rawAlias, }) { return ``; } bindLimit(limit) { return ``; } bindOffset(offset) { return ``; } bindHaving(having) { return ``; } bindRowLevelLock(mode) { return ''; } _objectId() { const timestamp = Math.floor(Date.now() / 1000) .toString(16) .padStart(8, '0'); const random = Array.from({ length: 10 }, () => Math.floor(Math.random() * 16).toString(16)).join(''); const counter = Math.floor(Math.random() * 0xffffff) .toString(16) .padStart(6, '0'); return timestamp + random + counter; } ; _parseWheres = (wheres = []) => { const normalizeColumn = (column = '') => { const clean = column.replace(/`/g, ''); return clean.split('.').pop() || ''; }; const parseValue = (value) => { if (Array.isArray(value)) return value.map(parseValue); if (typeof value === 'string') { const trimmed = value.trim(); const unquoted = trimmed.replace(/^'+|'+$/g, ''); if (!isNaN(Number(unquoted))) return Number(unquoted); if (unquoted.toLowerCase() === 'true') return true; if (unquoted.toLowerCase() === 'false') return false; return unquoted; } return value; }; const likeToRegex = (value) => { const v = parseValue(value); return '^' + v.replace(/%/g, '.*').replace(/_/g, '.') + '$'; }; const buildCondition = (w) => { const field = normalizeColumn(w.column ?? ''); const op = (w.operator || '=').toUpperCase(); const val = w.value; const baseCondition = (() => { switch (op) { case '=': return { [field]: parseValue(val) }; case '!=': case '<>': return { [field]: { $ne: parseValue(val) } }; case '>': return { [field]: { $gt: parseValue(val) } }; case '>=': return { [field]: { $gte: parseValue(val) } }; case '<': return { [field]: { $lt: parseValue(val) } }; case '<=': return { [field]: { $lte: parseValue(val) } }; case 'IN': return { [field]: { $in: (val || []).map(parseValue) } }; case 'NOT IN': return { [field]: { $nin: (val || []).map(parseValue) } }; case 'IS NULL': return { [field]: null }; case 'IS NOT NULL': return { [field]: { $ne: null } }; case 'BETWEEN': if (!Array.isArray(val)) return {}; return { [field]: { $gte: parseValue(val[0]), $lte: parseValue(val[1]) } }; case 'LIKE': return { [field]: { $regex: likeToRegex(val), $options: 'i' } }; case 'NOT LIKE': return { [field]: { $not: { $regex: likeToRegex(val), $options: 'i' } } }; case 'EXISTS': return { [field]: { $exists: Boolean(val) } }; default: throw new Error(`Unsupported operator: ${op}`); } })(); if (w.nested && Array.isArray(w.nested)) { const nestedGroup = buildGroup(w.nested); const conditionType = (w.condition || this.$constants('AND')).toUpperCase(); if (conditionType === this.$constants('OR')) { return { $or: [baseCondition, nestedGroup] }; } return { $and: [baseCondition, nestedGroup] }; } return baseCondition; }; const buildGroup = (lists = []) => { if (!lists.length) return {}; const groups = []; for (const w of lists) { const cond = buildCondition(w); if (!cond) continue; const type = (w.condition || this.$constants('AND')).toUpperCase(); if (groups.length === 0) { groups.push(cond); continue; } if (type === this.$constants('OR')) { const last = groups.pop(); groups.push({ $or: [last, cond] }); } else { const last = groups.pop(); groups.push({ $and: [last, cond] }); } } return groups[0] || {}; }; return buildGroup(wheres); }; } exports.MongodbQueryBuilder = MongodbQueryBuilder; //# sourceMappingURL=MongodbQueryBuilder.js.map