Permalink
Browse files

Finish eager loading of through-associations for Postgres

  • Loading branch information...
1 parent ff09665 commit 18eee709583a70c3bf20950b2d90d025b2ca8940 mde committed Mar 29, 2013
Showing with 103 additions and 54 deletions.
  1. +43 −12 lib/adapters/sql/base.js
  2. +1 −1 lib/adapters/sql/postgres.js
  3. +3 −5 lib/index.js
  4. +0 −36 test/adapters/shared.js
  5. +56 −0 test/adapters/sql/postgres.js
View
55 lib/adapters/sql/base.js
@@ -23,7 +23,7 @@ utils.mixin(Adapter.prototype, new (function () {
modelName = utils.string.getInflection(name, 'constructor', 'singular');
if (ownerName && name != ownerName) {
ownerModelName = utils.string.getInflection(ownerName, 'constructor', 'singular');
- modelName = model.getModelNameForAssociation(ownerModelName, modelName);
+ modelName = model.getAssociation(ownerModelName, modelName).model;
}
return model[modelName] || null;
@@ -46,7 +46,7 @@ utils.mixin(Adapter.prototype, new (function () {
// actual model via it's owner's associations list
else {
ownerName = utils.string.getInflection(ownerModelName, 'constructor', 'singular');
- name = model.getModelNameForAssociation(ownerName, assumedName);
+ name = model.getAssociation(ownerName, assumedName).model;
}
tableName = this._tableizeModelName(name);
@@ -63,34 +63,65 @@ utils.mixin(Adapter.prototype, new (function () {
};
this._createLeftOuterJoinStatement = function (mainModelName, assnModelName) {
- var mainInfl
+ var assn
, mainName
, mainTableName
- , assnInfl
, assnName
, assnModelName
, assnTableName
, assnModelTableName
, assnColName
+ , through
+ , throughTableName
+ , throughAssnColName
+ , sql;
- mainInfl = utils.string.getInflections(mainModelName);
- mainName = mainInfl.constructor.singular;
+ mainName = utils.string.getInflection(mainModelName, 'constructor', 'singular');
mainTableName = this._tableizeModelName(mainName);
- assnInfl = utils.string.getInflections(assnModelName);
- assnName = assnInfl.constructor.singular;
- assnModelName = model.getModelNameForAssociation(mainName, assnName);
+ assnName = utils.string.getInflection(assnModelName, 'constructor', 'singular');
+ assn = model.getAssociation(mainName, assnName);
+ assnModelName = assn.model;
assnTableName = this._tableizeModelName(assnName);
assnModelTableName = this._tableizeModelName(assnModelName);
+
+ // Normal assn
if (assnName == assnModelName) {
assnColName = mainName + 'Id';
}
+ // Named assn, namespace the id
else {
assnColName = assnName + mainName + 'Id';
}
- assnColName = this._columnizePropertyName(assnColName);
-
- return 'LEFT OUTER JOIN ' + assnModelTableName + ' ' + assnTableName +' ON (' +
+ assnColName = this._columnizePropertyName(assnColName, {useQuotes: false});
+
+ // Through assn
+ // Ex., Baz model has association Foo through Bar
+ // LEFT OUTER JOIN bars ON (bazes."id" = bars."baz_id")
+ // LEFT OUTER JOIN foos foos ON (bars."foo_id" = foos."id")
+ through = assn.through;
+ if (through) {
+ throughTableName = this._tableizeModelName(through);
+ throughAssnColName =
+ utils.string.getInflection(assnModelName, 'property', 'singular') + 'Id';
+ throughAssnColName = this._columnizePropertyName(throughAssnColName,
+ {useQuotes: false});
+
+ sql = 'LEFT OUTER JOIN ' + throughTableName + ' ON (' +
+ mainTableName + '."id" = ' + throughTableName + '."' + assnColName + '")';
+ sql += ' ';
+ sql += 'LEFT OUTER JOIN ' + assnModelTableName + ' ' + assnTableName + ' ON (' +
+ throughTableName + '."' + throughAssnColName + '" = ' +
+ assnTableName + '."id")';
+ }
+ // Normal
+ // Ex., Baz model has named association Foo {model: Bar}
+ // LEFT OUTER JOIN bars foos ON (bazes."id" = foos."baz_id")
+ else {
+ sql = 'LEFT OUTER JOIN ' + assnModelTableName + ' ' + assnTableName + ' ON (' +
mainTableName + '."id" = ' + assnTableName + '.' + assnColName + ')';
+ }
+
+ return sql;
};
this._createInsertStatement = function (item, props, useAutoIncrementId) {
View
2 lib/adapters/sql/postgres.js
@@ -54,7 +54,7 @@ utils.mixin(Adapter.prototype, new (function () {
tables.forEach(function (t) {
models[t] = {
ctor: self._modelizeTableName(t, mainTable)
- , assnType: t == mainTable ? null : model.getAssociationType(mainTable, t)
+ , assnType: t == mainTable ? null : model.getAssociation(mainTable, t).type
}
});
View
8 lib/index.js
@@ -898,19 +898,16 @@ utils.mixin(model, new (function () {
};
// FIXME: Move this into an associations lib
- this.getModelNameForAssociation = function (main, assn) {
+ this.getAssociation = function (main, assn) {
var mainName = utils.string.getInflection(main, 'constructor', 'singular')
, assnName = utils.string.getInflection(assn, 'constructor', 'singular')
, assn
, assnItem;
- if (this.descriptionRegistry[assnName]) {
- return assnName;
- }
assn = this.descriptionRegistry[mainName].associations;
for (var p in assn) {
assnItem = assn[p][assnName];
if (assnItem) {
- return assnItem.model;
+ return assnItem;
}
}
};
@@ -993,6 +990,7 @@ model.ModelDefinitionBase = function (name) {
assn[assnName] = {
model: modelName
, through: opts.through
+ , type: assnKey
};
reg[self.name].associations[assnKey] = assn;
View
36 test/adapters/shared.js
@@ -535,42 +535,6 @@ tests = {
});
}
-, 'test includes eager-fetch of hasMany association': function (next) {
- User.all({}, {includes: ['kids', 'avatars']}, function (err, data) {
- data.forEach(function (u) {
- if (u.id == currentId) {
- assert.equal(2, u.avatars.length);
- }
- });
- next();
- });
- }
-
-, 'test hasMany through': function (next) {
- User.first({login: 'asdf'}, function (err, data) {
- if (err) {
- throw err;
- }
- var u = data;
- u.addTeam(Team.create({
- name: 'foo'
- }));
- u.addTeam(Team.create({
- name: 'bar'
- }));
- u.save(function (err, data) {
- console.log('save completed');
- u.getTeams(function (err, data) {
- assert.equal(2, data.length);
- data.forEach(function (item) {
- assert.equal('Team', item.type);
- });
- next();
- });
- });
- });
- }
-
, 'test Static methods on model': function (next) {
User.findByLogin('asdf', function (err, data) {
assert.equal(data.length, 4);
View
56 test/adapters/sql/postgres.js
@@ -10,6 +10,8 @@ var utils = require('utilities')
, User = require('../../fixtures/user').User
, Profile = require('../../fixtures/profile').Profile
, Account = require('../../fixtures/account').Account
+ , Team = require('../../fixtures/team').Team
+ , Membership = require('../../fixtures/membership').Membership
, shared = require('../shared');
tests = {
@@ -109,4 +111,58 @@ for (var p in shared) {
tests[p + ' (Postgres)'] = shared[p];
}
+var eagerAssnTests = {
+ 'test includes eager-fetch of hasMany association': function (next) {
+ User.all({}, {includes: ['kids', 'avatars']}, function (err, data) {
+ data.forEach(function (u) {
+ if (u.id == currentId) {
+ assert.equal(2, u.avatars.length);
+ }
+ });
+ next();
+ });
+ }
+
+, 'test hasMany through': function (next) {
+ User.first({login: 'asdf'}, function (err, data) {
+ if (err) {
+ throw err;
+ }
+ var u = data;
+ u.addTeam(Team.create({
+ name: 'foo'
+ }));
+ u.addTeam(Team.create({
+ name: 'bar'
+ }));
+ u.save(function (err, data) {
+ currentId = u.id;
+ u.getTeams(function (err, data) {
+ assert.equal(2, data.length);
+ data.forEach(function (item) {
+ assert.equal('Team', item.type);
+ });
+ next();
+ });
+ });
+ });
+ }
+
+, 'test includes eager-fetch of hasMany/through association': function (next) {
+ User.all({login: 'asdf'}, {includes: 'teams'}, function (err, data) {
+ data.forEach(function (u) {
+ if (u.id == currentId) {
+ assert.equal(2, u.teams.length);
+ }
+ });
+ next();
+ });
+ }
+
+};
+
+for (var p in eagerAssnTests) {
+ tests[p + ' (Postgres)'] = eagerAssnTests[p];
+}
+
module.exports = tests;

0 comments on commit 18eee70

Please sign in to comment.