UNPKG

@xuda.io/xuda-dbs-plugin-mysql

Version:

Xuda Database Socket for MySql

506 lines (445 loc) 13.4 kB
const _ = require("lodash"); // const dot = require("dot-object"); const crypto = require("node:crypto"); const check_unique = async function (e, docP, table_obj) { var len = docP.udfIndex.length; if (!len) { return { code: -11, data: "table must have primary index" }; } var found_unique = undefined; var ret = { code: 1, data: "ok" }; for await (var val of docP.udfIndex) { if (!table_obj.tableIndexes) { ret = { code: -14, data: "table definition missing for: " + val.fileId, }; break; } const table_index = find_item_by_key_root( table_obj.tableIndexes, "id", val.indexId ); if (!table_index) { ret = { code: -13, data: "keys definition missing for Table: " + val.fileId, }; break; } if (!table_index.data.unique) { continue; } if (!table_index?.data?.keys || _.isEmpty(table_index.data.keys)) { ret = { code: -14, data: "keys definition missing for index: " + val.indexId, }; break; } var keysArr = table_index.data.keys; var keyValue = []; for await (const [keySegment, valSegment] of Object.entries(keysArr)) { // run on key segment let _fieldType = find_item_by_key( table_obj.tableFields, "field_id", valSegment ).props.fieldType; let _val = await get_cast_val( "check_unique", valSegment, _fieldType, val.keyValue[Number(keySegment)] ); keyValue.push(_val); } var key = '["' + val.fileId + '","' + val.indexId + '",[' + keyValue.join(",") + "]]"; try { e.view = "db_index"; const json = await _this.read(params, setup_doc, resolve, reject); // var data; if (json.code > 0) { var data = json.data; if (data?.rows?.length) { for await (var val of data.rows) { if (val.id !== docP._id) { found_unique = true; } } if (found_unique) { throw "error checking unique:" + key; } } } // throw json.data; return { code: 1, data: "ok" }; } catch (msg) { console.error(msg); ret = { code: -22, data: msg }; break; } } return ret; }; const get_index_json = async function (docInP, table_obj) { try { var keysArr = []; var index_json = []; var key_val = []; if (docInP.udfData && table_obj) { // check for udf data array if (table_obj.tableIndexes) { // console.log(udfDicIndexT.rows); for await (var valIndex of table_obj.tableIndexes) { // run on index rows keysArr = valIndex.data.keys; // create array from keys string segment key_val = []; for await (var valSegment of keysArr) { // run on key segment // find keys values if (docInP.udfData.data[valSegment]) { key_val.push(docInP.udfData.data[valSegment]); continue; } // put the value // debugger; const field_obj = find_item_by_key( table_obj.tableFields, "field_id", valSegment ); if (!field_obj) { throw "field not found in key: " + valSegment; } let _val = await get_cast_val( "get_index_json", field_obj.id, field_obj.props.fieldType, null ); key_val.push(_val); } index_json.push({ fileId: docInP.udfData.udffileid, indexId: valIndex.id, keySegments: keysArr, keyValue: key_val, }); } // ); } } return { code: 1, data: index_json }; } catch (msg) { return { code: -1, data: msg }; } }; const find_item_by_key_root = function (arr, key, val) { return _.find(arr, function (e) { return e[key] === val; }); }; const find_item_by_key = function (arr, key, val) { return _.find(arr, function (e) { return e.data[key] === val; }); }; const get_cast_val = async function (source, attributeP, typeP, valP) { const module = require("@xuda.io/xu_cast"); return module.cast( typeP, valP, function (res) { var msg = `error converting ${attributeP} from ${valP} to ${typeP}`; console.error(source, msg); }, function (res) { var msg = `type mismatch auto conversion made to ${attributeP} from value ${valP} to ${typeP}`; console.warn(source, msg); } ); }; exports.read = async (params, setup_doc, resolve, reject) => { const e = params.e; const db = params.db; const app_id_reference = params.app_id_reference; const table_obj = params.table_obj; const done = async function (body) { const raw_data = async function () { var rows = []; for await (const [key, val] of Object.entries(body)) { var data = {}; for await (const val2 of table_obj.tableFields) { if (val2.props.tableExternalFieldId) { data[val2.data.field_id] = _.get( val, val2.props.tableExternalFieldId ); } else { data[val2.data.field_id] = val[val2.data.field_id]; } if (typeof data[val2.data.field_id] === "undefined") { let _val = await get_cast_val( "get view", val2.data.field_id, val2.props.fieldType, null ); data[val2.data.field_id] = _val; } } rows.push({ id: key, value: data, }); } resolve({ rows, total_rows: rows.length }); }; const count_data = async function () { var rows = []; var keys_obj = {}; rows.push({ key: "", value: body[0]["count(*)"] }); resolve({ rows: rows, total_rows: rows.length }); }; const reduce_data = async function () { var rows = []; var keys_obj = {}; const table_index = find_item_by_key_root( table_obj.tableIndexes, "id", e.indexId ); if (e.indexId && table_index) { var index_id = e.indexId; for await (const [key, val] of Object.entries(body)) { // rows.push({ // key: [e.key, index_id, [val[GROUP_BY[0]]]], // value: val[`count(${GROUP_BY[0]})`], // }); let obj = {}; for await (const [key2, val2] of Object.entries(val)) { field_id = key2; if (field_id.includes("COUNT")) { field_id = "REDUCE_VALUE"; } obj[field_id] = val2; } rows.push({ id: crypto.randomUUID(), value: obj }); } resolve({ rows, total_rows: rows.length }); } }; const totals = async function () { var total_fields_info = JSON.parse(e.total_fields_info); var totals_obj = {}; for (const [key, val] of Object.entries(total_fields_info)) { totals_obj[val.field_id] = 0; } console.log(body); for (const [row_key, row_data] of Object.entries(body)) { for (const [key, val] of Object.entries(row_data)) { totals_obj[key] = val; } } resolve(totals_obj); }; if (e.count === "true" || e.count === true) { await count_data(); } else if (e.reduce === "true") { await reduce_data(); } else { if (e.total_fields_info) { await totals(); } else { await raw_data(); } } }; var SKIP = 0; if (e.skip) { SKIP = Number(e.skip); } var fields = []; for (const [key, val] of Object.entries(table_obj.tableFields)) { if (val.props.tableExternalFieldId) { fields.push(val.props.tableExternalFieldId); } else { fields.push(val.data.field_id); } } var WHERE = []; var ORDER_BY = []; var GROUP_BY = []; var COUNT = []; var LIMIT = undefined; if (e.limit) { LIMIT = Number(e.limit); } var from = e.filter_from ? JSON.parse(e.filter_from) : {}; var to = e.filter_to ? JSON.parse(e.filter_to) : {}; for (const [key, val] of Object.entries(from)) { if (e.reduce === "true") { COUNT.push(`${key},COUNT(${key})`); GROUP_BY.push(key); } ORDER_BY.push(key); const _tableFieldsObj = find_item_by_key( table_obj.tableFields, "field_id", key ); if (val === to[key]) { if (_tableFieldsObj.props.fieldType === "string") { WHERE.push(`${key} = '${val}'`); } else { WHERE.push(`${key} = ${val}`); } } else { if (_tableFieldsObj.props.fieldType === "string") { WHERE.push(`${key} BETWEEN '${val}' AND '${to[key]}'`); } else { WHERE.push(`${key} BETWEEN ${val} AND ${to[key]}`); } } } function replaceKeysInSQLQuery(sql_query) { const keys_to_replace = table_obj.tableFields.map((e) => { return e.id; }); // Iterate over each key in the keys_to_replace array keys_to_replace.forEach((key) => { // Create a regex to find the key in the SQL query, considering word boundaries to avoid partial replacements const regex = new RegExp(`\\b${key}\\b`, "g"); // Replace the key with the desired string sql_query = sql_query.replace(regex, `udfData.data.${key}`); }); return sql_query; } if (e.filterModelSql) { WHERE.push(`AND ` + replaceKeysInSQLQuery(e.filterModelSql)); } if (e.filterModelUserSql) { WHERE.push(`AND ` + replaceKeysInSQLQuery(e.filterModelUserSql)); } if (e?.sortModel?.length) { let sortModel = JSON.parse(e.sortModel); for (const [key, val] of Object.entries(sortModel)) { var field_name = val; const _tableFieldsObj = find_item_by_key( table_obj.tableFields, "field_id", val ); if (_tableFieldsObj.props.tableExternalFieldId) { field_name = _tableFieldsObj.props.tableExternalFieldId; } sortModel[Number(key)] = field_name; if (e.sort_dir === "des") { e.desc = "true"; } } ORDER_BY = sortModel; } // if (e.grid_filter_info) { // for (const [key, val] of Object.entries(JSON.parse(e.grid_filter_info))) { // var field_name = val.field; // var value = val.value; // const _tableFieldsObj = find_item_by_key( // table_obj.tableFields, // "field_id", // val.field // ); // if (_tableFieldsObj.props.tableExternalFieldId) { // field_name = _tableFieldsObj.props.tableExternalFieldId; // } // if (_tableFieldsObj.props.fieldType === "string") { // value = // val.type === "text_filter_strict" // ? val.value + "%" // : "%" + val.value + "%"; // WHERE.push(` ${field_name} LIKE '${value}'`); // } else { // WHERE.push(`${field_name} = ${value}`); // } // } // } if (e.count === "true" || e.count === true) { COUNT.push("count(*)"); } if (e.total_fields_info) { COUNT = []; for (const [key, val] of Object.entries(JSON.parse(e.total_fields_info))) { var field_name = val.field_id; const _tableFieldsObj = find_item_by_key( table_obj.tableFields, "field_id", val.field_id ); if (_tableFieldsObj.props.tableExternalFieldId) { field_name = _tableFieldsObj.props.tableExternalFieldId; } if (val.isNumeric) { COUNT.push(`SUM(${field_name}) as ${val.field_id}`); } else { COUNT.push(`COUNT(DISTINCT ${field_name}) as ${val.field_id}`); } } } const sql = `SELECT ${ COUNT.length ? `${COUNT.join(",")}` : fields.join(",") } FROM ${table_obj.properties.menuTitle} ${ WHERE.length ? "WHERE " + WHERE.join(" AND ") : "" } ${GROUP_BY.length ? "GROUP BY " + GROUP_BY.join(",") : ""} ${ ORDER_BY.length ? "ORDER BY " + ORDER_BY.join(",") + " " + (e.desc === "true" ? "DESC" : "ASC") : "" } ${LIMIT ? ` LIMIT ${LIMIT}` : ""} ${SKIP ? ` OFFSET ${SKIP}` : ""}`; console.log(sql); try { db.query(sql, async function (error, results, fields) { if (error) throw error; await done(results); }); } catch (err) { reject(err); } }; exports.test_connection = async (params, setup_doc, resolve, reject) => { try { if (!setup_doc.con_host) throw "con_host missing"; if (!setup_doc.con_user) throw "con_user missing"; if (!setup_doc.con_password) throw "con_password missing"; if (!setup_doc.con_database) throw "con_database missing"; if (!setup_doc.con_port) throw "con_port missing"; const mysql = require(`mysql-await`); const connection = mysql.createConnection({ host: setup_doc.con_host, user: setup_doc.con_user, password: setup_doc.con_password, database: setup_doc.con_database, port: setup_doc.con_port, }); connection.on(`error`, (err) => { throw err; }); let result = await connection.awaitQuery("SELECT 1 AS test"); if (params.test_only) { connection.awaitEnd(); } resolve(connection); } catch (err) { return reject(err); } };