Skip to content

Commit

Permalink
Add support for custom column mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
Raymond Feng committed Feb 20, 2015
1 parent d5090cb commit 460e07e
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 31 deletions.
74 changes: 43 additions & 31 deletions lib/mssql.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ MsSQL.prototype.create = function (model, data, callback) {

var columns = fieldsAndData.fields ? " (" + fieldsAndData.fields + ")" : "";
var sql = "INSERT INTO " + tblName + columns + MsSQL.newline;
sql += "OUTPUT INSERTED." + modelPKID + " AS insertId" + MsSQL.newline;
sql += "OUTPUT INSERTED." + this.columnEscaped(model, modelPKID)
+ " AS insertId" + MsSQL.newline;
sql += (fieldsAndData.paramPlaceholders ?
"VALUES (" + fieldsAndData.paramPlaceholders + ");" :
"DEFAULT VALUES;");
Expand Down Expand Up @@ -187,10 +188,11 @@ MsSQL.prototype.updateOrCreate = function (model, data, callback) {
if (props[key]) {
//check for the "id" key also, for backwards compatibility with the jugglingdb hardcoded id system
if (key !== "id" && key !== modelPKID) {
fieldNames.push("[" + key + "]");
var columnName = self.columnEscaped(model, key);
fieldNames.push(columnName);
fieldValues.push(self.toDatabase(props[key], data[key], true));
fieldValuesPlaceholders.push("(?)");
combined.push(key + "=(?)");
combined.push(columnName + "=(?)");
}
}
});
Expand All @@ -204,14 +206,14 @@ MsSQL.prototype.updateOrCreate = function (model, data, callback) {
//update
sql = "UPDATE " + tblName + MsSQL.newline;
sql += "SET " + combined.join() + MsSQL.newline;
sql += "WHERE [" + modelPKID + "] = (?);" + MsSQL.newline;
sql += "WHERE " + self.columnEscaped(model, modelPKID) + " = (?);" + MsSQL.newline;
sql += "SELECT " + id + " AS pkid;";
fieldValues.push(id);
} else {
//insert with identity_insert
sql = "SET IDENTITY_INSERT " + tblName + " ON;" + MsSQL.newline;
var columns = fieldNames.length ? "," + fieldNames.join() + ")" : ")";
sql += "INSERT INTO " + tblName + " ([" + modelPKID + "]"
sql += "INSERT INTO " + tblName + " (" + self.columnEscaped(model, modelPKID)
+ columns + MsSQL.newline;
sql += "VALUES (" + id + "," + fieldValuesPlaceholders.join() + ");" + MsSQL.newline;
sql += "SET IDENTITY_INSERT " + tblName + " OFF;" + MsSQL.newline;
Expand Down Expand Up @@ -349,7 +351,7 @@ MsSQL.prototype.update = MsSQL.prototype.updateAll = function (model, where, dat
if (props[key]) {
//check for the "id" key also, for backwards compatibility with the jugglingdb hardcoded id system
if (key !== "id" && key !== modelPKID) {
fieldNames.push("[" + key + "]");
fieldNames.push(self.columnEscaped(model, key));
fieldValues.push(self.toDatabase(props[key], data[key], true));
fieldValuesPlaceholders.push("(?)");
combined.push(key + "=(?)");
Expand Down Expand Up @@ -396,7 +398,7 @@ MsSQL.prototype.buildInsert = function (model, data) {
delete data.id;
Object.keys(data).forEach(function (key) {
if (props[key]) {
insertIntoFields.push("[" + key + "]");
insertIntoFields.push(self.columnEscaped(model, key));
paramPlaceholders.push("(?)");
params.push(self.toDatabase(props[key], data[key], true));
}
Expand Down Expand Up @@ -560,28 +562,33 @@ MsSQL.prototype.toDatabase = function (prop, val, wrap) {
};

MsSQL.prototype.fromDatabase = function (model, data) {
var self = this;
if (!data) {
return null;
}
// create an "id" property in the data for backwards compatibility with juggling-db
// data.id = data[this.idName(model)];
var props = this._models[model].properties;
var result = {};
//look for date values in the data, convert them from the database to a javascript date object
Object.keys(data).forEach(function (key) {
Object.keys(props).forEach(function (p) {
var key = self.column(model, p);
var val = data[key];
if (props[key]) {
if (props[key].type.name === 'Boolean' && val !== null) {
if (val !== undefined) {
if (props[p].type.name === 'Boolean' && val !== null) {
val = (true && val); //convert to a boolean type from number
}
if (props[key].type.name === 'Date' && val !== null) {
if (props[p].type.name === 'Date' && val !== null) {
if(!(val instanceof Date)) {
val = new Date(val.toString());
}
}
}
data[key] = val;
if (val !== undefined) {
result[p] = val;
}
});
return data;
return result;
};

MsSQL.prototype.escapeName = function (name) {
Expand Down Expand Up @@ -710,15 +717,17 @@ MsSQL.prototype.all = function (model, filter, callback) {
if (err) return callback(err);

//convert database types to js types
data = self.fromDatabase(model, data);
data = data.map(function (obj) {
return self.fromDatabase(model, obj);
});

//check for eager loading relationships
if (filter && filter.include) {
this._models[model].model.include(data, filter.include, callback);
self._models[model].model.include(data, filter.include, callback);
} else {
callback(null, data);
}
}.bind(this));
});

return sql;
};
Expand Down Expand Up @@ -997,7 +1006,8 @@ MsSQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
if (found) {
actualize(propName, found);
} else {
columnsToAdd.push('[' + propName + '] ' + self.propertySettingsSQL(model, propName));
columnsToAdd.push(self.columnEscaped(model, propName) +
' ' + self.propertySettingsSQL(model, propName));
}
});

Expand All @@ -1006,7 +1016,7 @@ MsSQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
var notFound = !~propNames.indexOf(f.Field);
if (f.Field === idName) return;
if (notFound || !m.properties[f.Field]) {
columnsToDrop.push('[' + f.Field + ']');
columnsToDrop.push(self.columnEscaped(model, f.Field));
}
});

Expand All @@ -1017,7 +1027,7 @@ MsSQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
}
if (indexNames.indexOf(indexName) === -1 && !m.properties[indexName]
|| m.properties[indexName] && !m.properties[indexName].index) {
sql.push('DROP INDEX [' + indexName + ']');
sql.push('DROP INDEX ' + self.columnEscaped(model, indexName));
} else {
// first: check single (only type and kind)
if (m.properties[indexName] && !m.properties[indexName].index) {
Expand All @@ -1034,7 +1044,7 @@ MsSQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
});
}
if (!orderMatched) {
sql.push('DROP INDEX [' + indexName + ']');
sql.push('DROP INDEX [' + self.columnEscaped(model, indexName) + ']');
delete ai[indexName];
}
}
Expand All @@ -1047,6 +1057,7 @@ MsSQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
return;
}
var found = ai[propName] && ai[propName].info;
var columnName = self.columnEscaped(model, propName);
if (!found) {
var type = '';
var kind = '';
Expand All @@ -1057,8 +1068,8 @@ MsSQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
// kind = i.kind;
}
if (kind && type) {
sql.push('ADD ' + kind + ' INDEX [' + propName
+ '] ([' + propName + ']) ' + type);
sql.push('ADD ' + kind + ' INDEX ' + columnName
+ ' (' + columnName + ') ' + type);
} else {
sql.push('ADD ' + kind + ' INDEX [' + propName + '] '
+ type + ' ([' + propName + ']) ');
Expand All @@ -1083,8 +1094,8 @@ MsSQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
sql.push('ADD ' + kind + ' INDEX [' + indexName + '] ('
+ i.columns + ') ' + type);
} else {
sql.push('ADD ' + kind + ' INDEX ' + type + ' [' + indexName
+ '] (' + i.columns + ')');
sql.push('ADD ' + kind + ' INDEX [' + indexName + '] ('
+ i.columns + ') ');
}
}
});
Expand Down Expand Up @@ -1121,7 +1132,7 @@ MsSQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
function actualize(propName, oldSettings) {
var newSettings = m.properties[propName];
if (newSettings && changed(newSettings, oldSettings)) {
columnsToAlter.push('[' + propName + '] '
columnsToAlter.push(self.columnEscaped(model, propName) + ' '
+ self.propertySettingsSQL(model, propName));
}
}
Expand Down Expand Up @@ -1155,24 +1166,25 @@ MsSQL.prototype.propertiesSQL = function(model) {
if (prop === modelPKID) {
var idProp = objModel.properties[modelPKID];
if (idProp.type === Number) {
sql.push("[" + modelPKID + "] [int] IDENTITY(1,1) NOT NULL");
sql.push(self.columnEscaped(model, modelPKID) +
" [int] IDENTITY(1,1) NOT NULL");
continue;
} else if (idProp.type === String) {
if(idProp.generated) {
sql.push("[" + modelPKID
+ "] [uniqueidentifier] DEFAULT newid() NOT NULL");
sql.push(self.columnEscaped(model, modelPKID) +
" [uniqueidentifier] DEFAULT newid() NOT NULL");
} else {
sql.push("[" + modelPKID + "] " +
sql.push(self.columnEscaped(model, modelPKID) + " " +
self.propertySettingsSQL(model, prop) + " DEFAULT newid()");
}
continue;
}
}
sql.push("[" + prop + "] " + self.propertySettingsSQL(model, prop));
sql.push(self.columnEscaped(model, prop) + " " + self.propertySettingsSQL(model, prop));
}
var joinedSql = sql.join("," + MsSQL.newline + " ");
var cmd = "PRIMARY KEY CLUSTERED" + MsSQL.newline + "(" + MsSQL.newline;
cmd += " [" + modelPKID + "] ASC" + MsSQL.newline;
cmd += " " + self.columnEscaped(model, modelPKID) + " ASC" + MsSQL.newline;
cmd += ") WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, " +
"IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)"

Expand Down
108 changes: 108 additions & 0 deletions test/mapping.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
process.env.NODE_ENV = 'test';
require('should');
require('./init');

var async = require('async');

var db;

before(function() {
db = getDataSource();
});

describe('Mapping models', function() {
it('should honor the mssql settings for table/column', function(done) {

var schema =
{
'name': 'TestInventory',
'options': {
'idInjection': false,
'mssql': {
'schema': 'dbo', 'table': 'INVENTORYTEST'
}
},
'properties': {
'productId': {
'type': 'Number', 'id': true, generated: true,
'mssql': {
'columnName': 'PRODUCT_ID',
'dataType': 'uniqueidentifier',
'nullable': 'N'
}
},
'locationId': {
'type': 'String', 'required': true, 'length': 20,
'mssql': {
'columnName': 'LOCATION_ID', 'dataType': 'nvarchar',
'nullable': 'N'
}
},
'available': {
'type': 'Number', 'required': false,
'mssql': {
'columnName': 'AVAILABLE', 'dataType': 'int', 'nullable': 'Y'
}
},
'total': {
'type': 'Number', 'required': false,
'mssql': {
'columnName': 'TOTAL', 'dataType': 'int', 'nullable': 'Y'
}
}
}
};
var models = db.modelBuilder.buildModels(schema);
// console.log(models);
var Model = models['TestInventory'];
Model.attachTo(db);

db.automigrate(function(err, data) {
async.series([
function(callback) {
Model.destroyAll(callback);
},
function(callback) {
Model.create({locationId: 'l001',
available: 10, total: 50}, callback);
},
function(callback) {
Model.create({locationId: 'l002',
available: 30, total: 40}, callback);
},
function(callback) {
Model.create({locationId: 'l001',
available: 15, total: 30}, callback);
},
function(callback) {
Model.find({fields: ['productId', 'locationId', 'available']},
function(err, results) {
// console.log(results);
results.should.have.lengthOf(3);
results.forEach(function(r) {
r.should.have.property('productId');
r.should.have.property('locationId');
r.should.have.property('available');
r.should.not.have.property('total');
});
callback(null, results);
});
},
function(callback) {
Model.find({fields: {'total': false}}, function(err, results) {
// console.log(results);
results.should.have.lengthOf(3);
results.forEach(function(r) {
r.should.have.property('productId');
r.should.have.property('locationId');
r.should.have.property('available');
r.should.not.have.property('total');
});
callback(null, results);
});
}
], done);
});

});
});

0 comments on commit 460e07e

Please sign in to comment.