From 3a92328fc3afbbfa304e2d30aa98af8628ff8a0f Mon Sep 17 00:00:00 2001 From: Amir Jafarian Date: Fri, 4 Dec 2015 13:51:07 -0500 Subject: [PATCH] Implement replaceAttributes --- lib/sql.js | 173 +++++++++++++++++++++++++++++++++++++++-------- test/sql.test.js | 2 +- 2 files changed, 146 insertions(+), 29 deletions(-) diff --git a/lib/sql.js b/lib/sql.js index d9848ec..74e6b63 100644 --- a/lib/sql.js +++ b/lib/sql.js @@ -554,13 +554,10 @@ SQLConnector.prototype.buildDelete = function(model, where, options) { * @param {Function} cb The callback function */ SQLConnector.prototype.destroyAll = function(model, where, options, cb) { - var self = this; var stmt = this.buildDelete(model, where, options); - this.execute(stmt.sql, stmt.params, options, function(err, info) { - var affectedRows = self.getCountForAffectedRows(model, info); - if (cb) { - cb(err, {count: affectedRows}); - } + this._executeAlteringQuery(model, stmt.sql, stmt.params, options, function(err, data) { + if (cb) + cb(err, data); }); }; @@ -582,14 +579,40 @@ Connector.defineAliases(SQLConnector.prototype, 'destroyAll', ['deleteAll']); * @private */ SQLConnector.prototype.updateAttributes = function(model, id, data, options, cb) { - if (!isIdValuePresent(id, cb)) { - return; - } + if (!isIdValuePresent(id, cb)) return; + var where = this._buildWhereObjById(model, id, data); + this.updateAll(model, where, data, options, cb); +}; + +/** + * Replace attributes for a given model instance + * @param {String} model The model name + * @param {*} id The id value + * @param {Object} data The model data instance containing all properties to + * be replaced + * @param {Object} options Options object + * @param {Function} cb The callback function + * @private + */ +SQLConnector.prototype.replace = function(model, id, data, options, cb) { + if (!isIdValuePresent(id, cb)) return; + var where = this._buildWhereObjById(model, id, data); + this._replace(model, where, data, options, cb); +}; + +/* + * @param model The model name. + * @param id The instance ID. + * @param {Object} data The data Object. + * @returns {Object} where The where object for a spcific instance. + * @private + */ +SQLConnector.prototype._buildWhereObjById = function(model, id, data) { var idName = this.idName(model); delete data[idName]; var where = {}; where[idName] = id; - this.updateAll(model, where, data, options, cb); + return where; }; /** @@ -601,8 +624,33 @@ SQLConnector.prototype.updateAttributes = function(model, id, data, options, cb) * @param {Function} cb The callback function * @returns {ParameterizedSQL} The UPDATE SQL statement */ -SQLConnector.prototype.buildUpdate = function(model, where, data, options) { +SQLConnector.prototype._buildUpdate = function(model, where, data, options) { var fields = this.buildFieldsForUpdate(model, data); + return this._constructUpdateQuery(model, where, fields); +}; + +/** + * Build the UPDATE statement for replacing + * @param {String} model The model name + * @param {Object} where The where object + * @param {Object} data The data to be changed + * @param {Object} options The options object + * @param {Function} cb The callback function + * @returns {ParameterizedSQL} The UPDATE SQL statement for replacing fields + */ +SQLConnector.prototype._buildReplace = function(model, where, data, options) { + var fields = this.buildFieldsForReplace(model, data); + return this._constructUpdateQuery(model, where, fields); +}; + +/* + * @param model The model name. + * @param {} where The where object. + * @param {Object} field The parameterizedSQL fileds. + * @returns {Object} update query Constructed update query. + * @private + */ +SQLConnector.prototype._constructUpdateQuery = function(model, where, fields) { var updateClause = new ParameterizedSQL('UPDATE ' + this.tableEscaped(model)); var whereClause = this.buildWhere(model, where); updateClause.merge([fields, whereClause]); @@ -619,18 +667,38 @@ SQLConnector.prototype.buildUpdate = function(model, where, data, options) { * @param {Function} cb The callback function */ SQLConnector.prototype.update = function(model, where, data, options, cb) { - var self = this; - var stmt = this.buildUpdate(model, where, data, options); - this.execute(stmt.sql, stmt.params, options, function(err, info) { - var affectedRows = self.getCountForAffectedRows(model, info); - if (cb) { - cb(err, {count: affectedRows}); - } + var stmt = this._buildUpdate(model, where, data, options); + this._executeAlteringQuery(model, stmt.sql, stmt.params, options, function(err, data) { + if (cb) + cb(err, data); }); }; -// Alias to `update`. Juggler checks `update` only. +/** + * Replace all instances that match the where clause with the given data + * @param {String} model The model name + * @param {Object} where The where object + * @param {Object} data The property/value object representing changes + * to be made + * @param {Object} options The options object + * @param {Function} cb The callback function + */ +SQLConnector.prototype._replace = function(model, where, data, options, cb) { + var stmt = this._buildReplace(model, where, data, options); + this._executeAlteringQuery(model, stmt.sql, stmt.params, options, cb); +}; + +SQLConnector.prototype._executeAlteringQuery = function(model, sql, params, options, cb) { + var self = this; + this.execute(sql, params, options, function(err, info) { + var affectedRows = self.getCountForAffectedRows(model, info); + cb(err, {count: affectedRows}); + }); +}; + +// Alias to `update` and `replace`. Juggler checks `update` and `replace` only. Connector.defineAliases(SQLConnector.prototype, 'update', ['updateAll']); +Connector.defineAliases(SQLConnector.prototype, 'replace', ['replaceAll']); /** * Build the SQL WHERE clause for the where object @@ -877,13 +945,38 @@ SQLConnector.prototype.buildOrderBy = function(model, order) { * @returns {{names: Array, values: Array, properties: Array}} */ SQLConnector.prototype.buildFields = function(model, data, excludeIds) { +// var props = this.getModelDefinition(model).properties; + var keys = Object.keys(data); + return this._createFields(model, data, keys, excludeIds); +}; + +/** + * Build an array of fields for the replace database operation + * @param {String} model Model name + * @param {Object} data Model data object + * @param {Boolean} excludeIds Exclude id properties or not, default to false + * @returns {{names: Array, values: Array, properties: Array}} + */ +SQLConnector.prototype._buildReplaceFields = function(model, data, excludeIds) { + var props = this.getModelDefinition(model).properties; + var keys = Object.keys(props); + return this._createFields(model, data, keys, excludeIds); +}; + +/* + * @param {String} model The model name. + * @returns {Object} data The model data object. + * @returns {Array} keys The key fields for which need to be built. + * @param {Boolean} excludeIds Exclude id properties or not, default to false + * @private + */ +SQLConnector.prototype._createFields = function(model, data, keys, excludeIds) { + var props = this.getModelDefinition(model).properties; var fields = { names: [], // field names columnValues: [], // an array of ParameterizedSQL properties: [] // model properties - }; - var props = this.getModelDefinition(model).properties; - var keys = Object.keys(data); + }; for (var i = 0, n = keys.length; i < n; i++) { var key = keys[i]; var p = props[key]; @@ -892,10 +985,10 @@ SQLConnector.prototype.buildFields = function(model, data, excludeIds) { debug('Unknown property %s is skipped for model %s', key, model); continue; } + if (excludeIds && p.id) { continue; } - var k = this.columnEscaped(model, key); var v = this.toColumnValue(p, data[key]); if (v !== undefined) { @@ -912,17 +1005,41 @@ SQLConnector.prototype.buildFields = function(model, data, excludeIds) { }; /** - * Build the SET clause for database update - * @param {String} model Model na - * @param {Object} data The model data object - * @param {Boolean} excludeIds Exclude id properties or not, default to true - * @returns {string} The list of fields for update + * Build the SET clause for database update. + * @param {String} model Model name. + * @param {Object} data The model data object. + * @param {Boolean} excludeIds Exclude id properties or not, default to true. + * @returns {string} The list of fields for update query. */ SQLConnector.prototype.buildFieldsForUpdate = function(model, data, excludeIds) { if (excludeIds === undefined) { excludeIds = true; } var fields = this.buildFields(model, data, excludeIds); + return this._constructUpdateParameterizedSQL(fields); +}; + +/** + * Build the SET clause for database replace through update query. + * @param {String} model Model name. + * @param {Object} data The model data object. + * @param {Boolean} excludeIds Exclude id properties or not, default to true. + * @returns {string} The list of fields for update query. + */ +SQLConnector.prototype.buildFieldsForReplace = function(model, data, excludeIds) { + if (excludeIds === undefined) { + excludeIds = true; + } + var fields = this._buildReplaceFields(model, data, excludeIds); + return this._constructUpdateParameterizedSQL(fields); +}; + +/* + * @param {Object} field The fileds. + * @returns {Object} parameterizedSQL. + * @private + */ +SQLConnector.prototype._constructUpdateParameterizedSQL = function(fields) { var columns = new ParameterizedSQL(''); for (var i = 0, n = fields.names.length; i < n; i++) { var clause = ParameterizedSQL.append(fields.names[i], diff --git a/test/sql.test.js b/test/sql.test.js index dd89e1e..0d84392 100644 --- a/test/sql.test.js +++ b/test/sql.test.js @@ -281,7 +281,7 @@ describe('sql connector', function() { }); it('builds UPDATE', function() { - var sql = connector.buildUpdate('customer', {name: 'John'}, {vip: false}); + var sql = connector._buildUpdate('customer', {name: 'John'}, {vip: false}); expect(sql.toJSON()).to.eql({ sql: 'UPDATE `CUSTOMER` SET `VIP`=$1 WHERE `NAME`=$2', params: [false, 'John']