Browse files

Instantiate objects for result from LEFT JOIN recordsets

  • Loading branch information...
1 parent 7ba54ca commit 32d78a6d8ce8da44c656c9db0dd4e0338c5e3c39 mde committed Mar 26, 2013
Showing with 94 additions and 13 deletions.
  1. +21 −6 lib/adapters/sql/base.js
  2. +67 −5 lib/adapters/sql/postgres.js
  3. +6 −2 test/adapters/shared.js
View
27 lib/adapters/sql/base.js
@@ -13,7 +13,7 @@ Adapter = function () {
Adapter.prototype = new BaseAdapter();
utils.mixin(Adapter.prototype, new (function () {
- var _getModelNameForAssociation = function (mainName, assnName) {
+ this._getModelNameForAssociation = function (mainName, assnName) {
var assn
, assnItem;
if (model.descriptionRegistry[assnName]) {
@@ -34,6 +34,21 @@ utils.mixin(Adapter.prototype, new (function () {
return tableName;
};
+ this._modelizeTableName = function (name, ownerName) {
+ var infl
+ , modelName
+ , ownerModelName
+ infl = utils.string.getInflections(name);
+ modelName = infl.constructor.singular;
+ if (ownerName && name != ownerName) {
+ infl = utils.string.getInflections(ownerName);
+ ownerModelName = infl.constructor.singular;
+ modelName = this._getModelNameForAssociation(ownerModelName, modelName);
+ }
+
+ return model[modelName] || null;
+ };
+
this._createSelectStatement = function (modelName, ownerModelName) {
var infl = utils.string.getInflections(modelName)
@@ -54,7 +69,7 @@ utils.mixin(Adapter.prototype, new (function () {
else {
infl = utils.string.getInflections(ownerModelName);
ownerName = infl.constructor.singular;
- name = _getModelNameForAssociation(ownerName, assumedName);
+ name = this._getModelNameForAssociation(ownerName, assumedName);
}
tableName = this._tableizeModelName(name);
@@ -64,7 +79,7 @@ utils.mixin(Adapter.prototype, new (function () {
propArr.push(assumedTableName + '."id" AS "' + assumedTableName + '#id"');
for (var p in props) {
- propArr.push(tableName + '.' + this._columnizePropertyName(p) + ' AS "' +
+ propArr.push(assumedTableName + '.' + this._columnizePropertyName(p) + ' AS "' +
assumedTableName + '#' + this._columnizePropertyName(p, {useQuotes: false}) + '"');
}
return propArr.join(', ');
@@ -86,14 +101,14 @@ utils.mixin(Adapter.prototype, new (function () {
mainTableName = this._tableizeModelName(mainName);
assnInfl = utils.string.getInflections(assnModelName);
assnName = assnInfl.constructor.singular;
- assnModelName = _getModelNameForAssociation(mainName, assnName);
+ assnModelName = this._getModelNameForAssociation(mainName, assnName);
assnTableName = this._tableizeModelName(assnName);
assnModelTableName = this._tableizeModelName(assnModelName);
if (assnName == assnModelName) {
- assnColName = assnName + 'Id';
+ assnColName = mainName + 'Id';
}
else {
- assnColName = assnName + assnModelName + 'Id';
+ assnColName = assnName + mainName + 'Id';
}
assnColName = this._columnizePropertyName(assnColName);
View
72 lib/adapters/sql/postgres.js
@@ -34,19 +34,81 @@ Adapter.prototype.constructor = Adapter;
utils.mixin(Adapter.prototype, new (function () {
- var _itemsWithSQL = function (sql, model, callback) {
+ var _itemsWithSQL = function (sql, tables, mainTable, callback) {
var self = this;
this.exec(sql, function (err, data) {
var res
, rows
- , tableName = self._tableizeModelName(model.modelName);
+ , inst
+ , models = {}
+ , mainId;
+
if (err) {
callback(err, null);
}
else {
+ // Make a map of the constructors for each table-name
+ // Note that the table-name may be an alias for an association
+ // First item in this list should be the owner table for
+ // any subsequent associations, e.g., ['users', 'profiles']
+ tables.forEach(function (t) {
+ models[t] = self._modelizeTableName(t, mainTable);
+ });
+
rows = data.rows;
res = [];
+
rows.forEach(function (row) {
+ var obj = {}
+ , colArr
+ , table
+ , key
+ , params;
+
+
+ // Create a column object with mapped params
+ // users#login: 'foo', users#name: 'adsf', profiles#nickname: 'qwer'
+ //{ users: {login: 'foo', name: 'asdf'}
+ //, profile: {nickname: 'qewr'}
+ //}
+ for (var p in row) {
+ colArr = p.split('#');
+ table = colArr[0];
+ key = colArr[1];
+ obj[table] = obj[table] || {}
+ obj[table][key] = row[p];
+ }
+
+ // Iterate over the list of table names, create a base owner object
+ // when a new id shows up. (Owner object record is repeated for multiple
+ // associations.) For each subsquent record, instantiate the association
+ // and append it to an array in the named property for that association
+ tables.forEach(function (p) {
+ params = obj[p];
+ // Owner table-name
+ if (p == mainTable) {
+ // This is a new id -- create an owner object
+ if (params.id != mainId) {
+ inst = models[p].create(params);
+ inst._saved = true;
+ res.push(inst);
+ }
+ // Update to check against next record
+ mainId = params.id;
+ }
+ // Association table-name
+ else {
+ // Ignore empty records
+ if (params.id) {
+ // Create an array to hold the items if necessary
+ // FIXME: hasOne associations should not be an array
+ inst[p] = inst[p] || [];
+ inst[p].push(models[p].create(params));
+ }
+ }
+ });
+
+ /*
var item = {}
, key;
for (var p in row) {
@@ -58,6 +120,7 @@ utils.mixin(Adapter.prototype, new (function () {
inst.id = row[tableName + '#id'];
inst._saved = true;
res.push(inst);
+ */
});
callback(null, res);
}
@@ -86,7 +149,6 @@ utils.mixin(Adapter.prototype, new (function () {
};
this.exec = function (query, callback) {
- console.log(query);
this.client.query(query, callback);
};
@@ -139,7 +201,7 @@ utils.mixin(Adapter.prototype, new (function () {
}
sql += ';'
- _itemsWithSQL.apply(this, [sql, query.model, function (err, res) {
+ _itemsWithSQL.apply(this, [sql, selects, tableName, function (err, res) {
// If explicitly limited to one, just return the single instance
// This is also used by the `first` method
if (res && query.opts.limit == 1) {
@@ -154,7 +216,7 @@ utils.mixin(Adapter.prototype, new (function () {
var name = this._tableizeModelName(model.modelName)
, sql = 'SELECT ' + name + '.* FROM ' + name;
sql += ' ' + query;
- _itemsWithSQL.apply(this, [sql, model, callback]);
+ _itemsWithSQL.apply(this, [sql, [name], name, callback]);
};
this.update = function (data, query, callback) {
View
8 test/adapters/shared.js
@@ -534,8 +534,12 @@ tests = {
}
, 'test includes eager-fetch of hasMany association': function (next) {
- User.all({}, {includes: ['kids']}, function (err, data) {
- throw new Error();
+ User.all({}, {includes: ['kids', 'avatars']}, function (err, data) {
+ data.forEach(function (u) {
+ if (u.id == currentId) {
+ assert.equal(2, u.avatars.length);
+ }
+ });
next();
});
}

0 comments on commit 32d78a6

Please sign in to comment.