UNPKG

orm

Version:

NodeJS Object-relational mapping

348 lines (294 loc) 9.63 kB
var _ = require('lodash') /** * Order should be a String (with the property name assumed ascending) * or an Array or property String names. * * Examples: * * 1. 'property1' (ORDER BY property1 ASC) * 2. '-property1' (ORDER BY property1 DESC) * 3. [ 'property1' ] (ORDER BY property1 ASC) * 4. [ '-property1' ] (ORDER BY property1 DESC) * 5. [ 'property1', 'A' ] (ORDER BY property1 ASC) * 6. [ 'property1', 'Z' ] (ORDER BY property1 DESC) * 7. [ '-property1', 'A' ] (ORDER BY property1 ASC) * 8. [ 'property1', 'property2' ] (ORDER BY property1 ASC, property2 ASC) * 9. [ 'property1', '-property2' ] (ORDER BY property1 ASC, property2 DESC) * ... */ exports.standardizeOrder = function (order) { if (typeof order === "string") { if (order[0] === "-") { return [ [ order.substr(1), "Z" ] ]; } return [ [ order, "A" ] ]; } var new_order = [], minus; for (var i = 0; i < order.length; i++) { minus = (order[i][0] === "-"); if (i < order.length - 1 && [ "A", "Z" ].indexOf(order[i + 1].toUpperCase()) >= 0) { new_order.push([ (minus ? order[i].substr(1) : order[i]), order[i + 1] ]); i += 1; } else if (minus) { new_order.push([ order[i].substr(1), "Z" ]); } else { new_order.push([ order[i], "A" ]); } } return new_order; }; /** * Operations * A) Build an index of associations, with their name as the key * B) Check for any conditions with a key in the association index * C) Ensure that our condition supports array values * D) Remove original condition (not DB compatible) * E) Convert our association fields into an array, indexes are the same as model.id * F) Itterate through values for the condition, only accept instances of the same type as the association */ exports.checkConditions = function (conditions, one_associations) { var k, i, j; // A) var associations = {}; for (i = 0; i < one_associations.length; i++) { associations[one_associations[i].name] = one_associations[i]; } for (k in conditions) { // B) if (!associations.hasOwnProperty(k)) continue; // C) var values = conditions[k]; if (!Array.isArray(values)) values = [values]; // D) delete conditions[k]; // E) var association_fields = Object.keys(associations[k].field); var model = associations[k].model; // F) for (i = 0; i < values.length; i++) { if (values[i].isInstance && values[i].model().uid === model.uid) { if (association_fields.length === 1) { if (typeof conditions[association_fields[0]] === 'undefined') { conditions[association_fields[0]] = values[i][model.id[0]]; } else if(Array.isArray(conditions[association_fields[0]])) { conditions[association_fields[0]].push(values[i][model.id[0]]); } else { conditions[association_fields[0]] = [conditions[association_fields[0]], values[i][model.id[0]]]; } } else { var _conds = {}; for (j = 0; j < association_fields.length; i++) { _conds[association_fields[j]] = values[i][model.id[j]]; } conditions.or = conditions.or || []; conditions.or.push(_conds); } } } } return conditions; }; /** * Gets all the values within an object or array, optionally * using a keys array to get only specific values */ exports.values = function (obj, keys) { var i, k, vals = []; if (keys) { for (i = 0; i < keys.length; i++) { vals.push(obj[keys[i]]); } } else if (Array.isArray(obj)) { for (i = 0; i < obj.length; i++) { vals.push(obj[i]); } } else { for (k in obj) { if (!/[0-9]+/.test(k)) { vals.push(obj[k]); } } } return vals; }; // Qn: is Zero a valid value for a FK column? // Why? Well I've got a pre-existing database that started all its 'serial' IDs at zero... // Answer: hasValues() is only used in hasOne association, so it's probably ok... exports.hasValues = function (obj, keys) { for (var i = 0; i < keys.length; i++) { if (!obj[keys[i]] && obj[keys[i]] !== 0) return false; // 0 is also a good value... } return true; }; exports.populateConditions = function (model, fields, source, target, overwrite) { for (var i = 0; i < model.id.length; i++) { if (typeof target[fields[i]] === 'undefined' || overwrite !== false) { target[fields[i]] = source[model.id[i]]; } else if (Array.isArray(target[fields[i]])) { target[fields[i]].push(source[model.id[i]]); } else { target[fields[i]] = [target[fields[i]], source[model.id[i]]]; } } }; exports.getConditions = function (model, fields, from) { var conditions = {}; exports.populateConditions(model, fields, from, conditions); return conditions; }; exports.wrapFieldObject = function (params) { if (!params.field) { var assoc_key = params.model.settings.get("properties.association_key"); if (typeof assoc_key === "function") { params.field = assoc_key(params.altName.toLowerCase(), params.model.id[0]); } else { params.field = assoc_key.replace("{name}", params.altName.toLowerCase()) .replace("{field}", params.model.id[0]); } } if (typeof params.field == 'object') { for (var k in params.field) { if (!/[0-9]+/.test(k) && params.field.hasOwnProperty(k)) { return params.field; } } } var newObj = {}, newProp, propPreDefined, propFromKey; propPreDefined = params.model.properties[params.field]; propFromKey = params.model.properties[params.model.id[0]]; newProp = { type: 'integer' }; var prop = _.cloneDeep(propPreDefined || propFromKey || newProp); if (!propPreDefined) { _.extend(prop, { name: params.field, mapsTo: params.mapsTo || params.field }); } newObj[params.field] = prop; return newObj; }; exports.formatField = function (model, name, required, reversed) { var fields = {}, field_opts, field_name; var keys = model.id; var assoc_key = model.settings.get("properties.association_key"); for (var i = 0; i < keys.length; i++) { if (reversed) { field_name = keys[i]; } else if (typeof assoc_key === "function") { field_name = assoc_key(name.toLowerCase(), keys[i]); } else { field_name = assoc_key.replace("{name}", name.toLowerCase()) .replace("{field}", keys[i]); } if (model.properties.hasOwnProperty(keys[i])) { var p = model.properties[keys[i]]; field_opts = { type : p.type || "integer", size : p.size || 4, unsigned : p.unsigned || true, time : p.time || false, big : p.big || false, values : p.values || null, required : required, name : field_name, mapsTo : field_name }; } else { field_opts = { type : "integer", unsigned : true, size : 4, required : required, name : field_name, mapsTo : field_name }; } fields[field_name] = field_opts; } return fields; }; // If the parent associations key is `serial`, the join tables // key should be changed to `integer`. exports.convertPropToJoinKeyProp = function (props, opts) { var prop; for (var k in props) { prop = props[k]; prop.required = opts.required; if (prop.type == 'serial') { prop.type = 'integer'; } if (opts.makeKey) { prop.key = true; } else { delete prop.key; } } return props; } exports.getRealPath = function (path_str, stack_index) { var path = require("path"); // for now, load here (only when needed) var cwd = process.cwd(); var err = new Error(); var tmp = err.stack.split(/\r?\n/)[typeof stack_index !== "undefined" ? stack_index : 3], m; if ((m = tmp.match(/^\s*at\s+(.+):\d+:\d+$/)) !== null) { cwd = path.dirname(m[1]); } else if ((m = tmp.match(/^\s*at\s+module\.exports\s+\((.+?)\)/)) !== null) { cwd = path.dirname(m[1]); } else if ((m = tmp.match(/^\s*at\s+.+\s+\((.+):\d+:\d+\)$/)) !== null) { cwd = path.dirname(m[1]); } var pathIsAbsolute = path.isAbsolute || require('path-is-absolute'); if (!pathIsAbsolute(path_str)) { path_str = path.join(cwd, path_str); } if (path_str.substr(-1) === path.sep) { path_str += "index"; } return path_str; }; exports.transformPropertyNames = function (dataIn, properties) { var k, prop; var dataOut = {}; for (k in dataIn) { prop = properties[k]; if (prop) { dataOut[prop.mapsTo] = dataIn[k]; } else { dataOut[k] = dataIn[k]; } } return dataOut; }; exports.transformOrderPropertyNames = function (order, properties) { if (!order) return order; var i, item; var newOrder = JSON.parse(JSON.stringify(order)); // Rename order properties according to mapsTo for (var i = 0; i < newOrder.length; i++) { item = newOrder[i]; // orderRaw if (Array.isArray(item[1])) continue; if (Array.isArray(item[0])) { // order on a hasMany // [ ['modelName', 'propName'], 'Z'] item[0][1] = properties[item[0][1]].mapsTo; } else { // normal order item[0] = properties[item[0]].mapsTo; } } return newOrder; } exports.renameDatastoreFieldsToPropertyNames = function (data, fieldToPropertyMap) { var k, prop; for (k in data) { prop = fieldToPropertyMap[k]; if (prop && prop.name != k) { data[prop.name] = data[k]; delete data[k]; } } return data; }