import { ServerQueryParam, ServerQueryParamsJoin, ServerQueryParamsJoinMatchObject, ServerQueryQueryObject, } from "../../../types"; import sqlGenOperatorGen from "./sql-gen-operator-gen"; type Param = { genObject?: ServerQueryParam; tableName: string; dbFullName?: string; count?: boolean; }; type Return = { string: string; values: string[]; }; /** * # SQL Query Generator * @description Generates an SQL Query for node module `mysql` or `serverless-mysql` */ export default function sqlGenerator< T extends { [key: string]: any } = { [key: string]: any } >({ tableName, genObject, dbFullName, count }: Param): Return { const finalQuery = genObject?.query ? genObject.query : undefined; const queryKeys = finalQuery ? Object.keys(finalQuery) : undefined; const sqlSearhValues: string[] = []; const finalDbName = dbFullName ? `${dbFullName}.` : ""; /** * # Generate Query */ function genSqlSrchStr({ queryObj, join, field, }: { queryObj: ServerQueryQueryObject[string]; join?: ServerQueryParamsJoin[]; field?: string; }) { const finalFieldName = (() => { if (queryObj?.tableName) { return `${finalDbName}${queryObj.tableName}.${field}`; } if (join) { return `${finalDbName}${tableName}.${field}`; } return field; })(); let str = `${finalFieldName}=?`; if (Array.isArray(queryObj.value)) { const strArray: string[] = []; queryObj.value.forEach((val) => { const valueParsed = val; const valueString = typeof valueParsed == "string" ? valueParsed : valueParsed.value?.toString(); const valueEquality = typeof valueParsed == "object" ? valueParsed.equality || queryObj.equality : queryObj.equality; const operatorStrParam = sqlGenOperatorGen({ equality: valueEquality, fieldName: finalFieldName || "", value: valueString?.toString() || "", }); if (operatorStrParam.str && operatorStrParam.param) { strArray.push(operatorStrParam.str); sqlSearhValues.push(operatorStrParam.param); } else if (operatorStrParam.str) { strArray.push(operatorStrParam.str); } }); str = "(" + strArray.join(` ${queryObj.operator || "AND"} `) + ")"; } else { const valueParsed = String(queryObj.value); const operatorStrParam = sqlGenOperatorGen({ equality: queryObj.equality, fieldName: finalFieldName || "", value: valueParsed, }); if (operatorStrParam.str && operatorStrParam.param) { str = operatorStrParam.str; sqlSearhValues.push(operatorStrParam.param); } else if (operatorStrParam.str) { str = operatorStrParam.str; } // if (queryObj.equality == "LIKE") { // str = `LOWER(${finalFieldName}) LIKE LOWER('%${valueParsed}%')`; // } else if (queryObj.equality == "LIKE_RAW") { // str = `LOWER(${finalFieldName}) LIKE LOWER(?)`; // sqlSearhValues.push(valueParsed); // } else if (queryObj.equality == "NOT LIKE") { // str = `LOWER(${finalFieldName}) NOT LIKE LOWER('%${valueParsed}%')`; // } else if (queryObj.equality == "NOT LIKE_RAW") { // str = `LOWER(${finalFieldName}) NOT LIKE LOWER(?)`; // sqlSearhValues.push(valueParsed); // } else if (queryObj.equality == "REGEXP") { // str = `LOWER(${finalFieldName}) REGEXP LOWER(?)`; // sqlSearhValues.push(valueParsed); // } else if (queryObj.equality == "FULLTEXT") { // str = `MATCH(${finalFieldName}) AGAINST(? IN BOOLEAN MODE)`; // sqlSearhValues.push(valueParsed); // } else if (queryObj.equality == "NOT EQUAL") { // str = `${finalFieldName} != ?`; // sqlSearhValues.push(valueParsed); // } else if (queryObj.equality) { // str = `${finalFieldName} ${operator} ?`; // sqlSearhValues.push(valueParsed); // } else { // sqlSearhValues.push(valueParsed); // } } return str; } const sqlSearhString = queryKeys?.map((field) => { const queryObj = finalQuery?.[field]; if (!queryObj) return; if (queryObj.__query) { const subQueryGroup = queryObj.__query; const subSearchKeys = Object.keys(subQueryGroup); const subSearchString = subSearchKeys.map((_field) => { const newSubQueryObj = subQueryGroup?.[_field]; return genSqlSrchStr({ queryObj: newSubQueryObj, field: _field, join: genObject?.join, }); }); return ( "(" + subSearchString.join(` ${queryObj.operator || "AND"} `) + ")" ); } return genSqlSrchStr({ queryObj, field, join: genObject?.join }); }); function generateJoinStr( mtch: ServerQueryParamsJoinMatchObject, join: ServerQueryParamsJoin ) { return `${finalDbName}${ typeof mtch.source == "object" ? mtch.source.tableName : tableName }.${ typeof mtch.source == "object" ? mtch.source.fieldName : mtch.source }=${(() => { if (mtch.targetLiteral) { return `'${mtch.targetLiteral}'`; } if (join.alias) { return `${finalDbName}${ typeof mtch.target == "object" ? mtch.target.tableName : join.alias }.${ typeof mtch.target == "object" ? mtch.target.fieldName : mtch.target }`; } return `${finalDbName}${ typeof mtch.target == "object" ? mtch.target.tableName : join.tableName }.${ typeof mtch.target == "object" ? mtch.target.fieldName : mtch.target }`; })()}`; } let queryString = (() => { let str = "SELECT"; if (count) { str += ` COUNT(*)`; } else if (genObject?.selectFields?.[0]) { if (genObject.join) { str += ` ${genObject.selectFields ?.map((fld) => `${finalDbName}${tableName}.${String(fld)}`) .join(",")}`; } else { str += ` ${genObject.selectFields?.join(",")}`; } } else { if (genObject?.join) { str += ` ${finalDbName}${tableName}.*`; } else { str += " *"; } } if (genObject?.join && !count) { const existingJoinTableNames: string[] = [tableName]; str += "," + genObject.join .map((joinObj) => { const joinTableName = joinObj.alias ? joinObj.alias : joinObj.tableName; if (existingJoinTableNames.includes(joinTableName)) return null; existingJoinTableNames.push(joinTableName); if (joinObj.selectFields) { return joinObj.selectFields .map((selectField) => { if (typeof selectField == "string") { return `${finalDbName}${joinTableName}.${selectField}`; } else if (typeof selectField == "object") { let aliasSelectField = selectField.count ? `COUNT(${finalDbName}${joinTableName}.${selectField.field})` : `${finalDbName}${joinTableName}.${selectField.field}`; if (selectField.alias) aliasSelectField += ` AS ${selectField.alias}`; return aliasSelectField; } }) .join(","); } else { return `${finalDbName}${joinTableName}.*`; } }) .filter((_) => Boolean(_)) .join(","); } str += ` FROM ${finalDbName}${tableName}`; if (genObject?.join) { str += " " + genObject.join .map((join) => { return ( join.joinType + " " + (join.alias ? `${finalDbName}${join.tableName}` + " " + join.alias : `${finalDbName}${join.tableName}`) + " ON " + (() => { if (Array.isArray(join.match)) { return ( "(" + join.match .map((mtch) => generateJoinStr(mtch, join) ) .join( join.operator ? ` ${join.operator} ` : " AND " ) + ")" ); } else if (typeof join.match == "object") { return generateJoinStr(join.match, join); } })() ); }) .join(" "); } return str; })(); if (sqlSearhString?.[0] && sqlSearhString.find((str) => str)) { const stringOperator = genObject?.searchOperator || "AND"; queryString += ` WHERE ${sqlSearhString.join(` ${stringOperator} `)}`; } if (genObject?.group?.[0]) { queryString += ` GROUP BY ${genObject.group .map((g) => `\`${g.toString()}\``) .join(",")}`; } if (genObject?.order && !count) { queryString += ` ORDER BY ${ genObject.join ? `${finalDbName}${tableName}.${String(genObject.order.field)}` : String(genObject.order.field) } ${genObject.order.strategy}`; } if (genObject?.limit && !count) queryString += ` LIMIT ${genObject.limit}`; if (genObject?.offset && !count) queryString += ` OFFSET ${genObject.offset}`; return { string: queryString, values: sqlSearhValues, }; }