Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Instantiate objects for result from LEFT JOIN recordsets
  • Loading branch information
mde committed Mar 27, 2013
1 parent 7ba54ca commit 32d78a6
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 13 deletions.
27 changes: 21 additions & 6 deletions lib/adapters/sql/base.js
Expand Up @@ -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]) {
Expand All @@ -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)
Expand All @@ -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);
Expand All @@ -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(', ');
Expand All @@ -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);

Expand Down
72 changes: 67 additions & 5 deletions lib/adapters/sql/postgres.js
Expand Up @@ -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) {
Expand All @@ -58,6 +120,7 @@ utils.mixin(Adapter.prototype, new (function () {
inst.id = row[tableName + '#id'];
inst._saved = true;
res.push(inst);
*/
});
callback(null, res);
}
Expand Down Expand Up @@ -86,7 +149,6 @@ utils.mixin(Adapter.prototype, new (function () {
};

this.exec = function (query, callback) {
console.log(query);
this.client.query(query, callback);
};

Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
8 changes: 6 additions & 2 deletions test/adapters/shared.js
Expand Up @@ -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();
});
}
Expand Down

0 comments on commit 32d78a6

Please sign in to comment.