Skip to content

Commit 569bcf1

Browse files
committed
Merge pull request brianc#68 from brianc/auto-join
Auto join
2 parents b4d257f + 2b010dd commit 569bcf1

4 files changed

Lines changed: 144 additions & 0 deletions

File tree

lib/joiner.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
var getPrimaryKeyColumn = function(table) {
2+
for(var i = 0; i < table.columns.length; i++) {
3+
var col = table.columns[i];
4+
if(col.primaryKey) {
5+
return col;
6+
}
7+
}
8+
};
9+
10+
var findReference = function(left, right) {
11+
//find reference
12+
for(var i = 0; i < right.columns.length; i++) {
13+
var col = right.columns[i];
14+
if(col.references) {
15+
var leftName = left.getName();
16+
if(col.references == leftName || col.references.table == leftName) {
17+
var leftCol = left[col.references.column] || getPrimaryKeyColumn(left);
18+
return {
19+
left: leftCol,
20+
right: col
21+
};
22+
}
23+
}
24+
}
25+
};
26+
27+
module.exports = {
28+
//auto-join two tables based on column properties
29+
//requires one column to have { references: {table: 'foreignTableName', column: 'foreignColumnName'}}
30+
//or to have { references: 'foreignTableName'} -- in which case the foreign table's primary key is assumed
31+
leftJoin: function(left, right) {
32+
var leftCol, rightCol;
33+
var ref = findReference(left, right);
34+
if(!ref) {
35+
ref = findReference(right, left);
36+
}
37+
return left.join(right).on(ref.left.equals(ref.right));
38+
}
39+
}

lib/table.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
'use strict';
22

3+
var util = require('util');
4+
35
var Query = require(__dirname + '/node/query');
46
var Column = require(__dirname + '/column');
57
var TableNode = require(__dirname + '/node/table');
68
var JoinNode = require(__dirname + '/node/join');
9+
var Joiner = require(__dirname + '/joiner');
710

811
var Table = function(config) {
912
this._schema = config.schema;
@@ -15,6 +18,18 @@ var Table = function(config) {
1518

1619
Table.define = function(config) {
1720
var table = new Table(config);
21+
22+
//allow hash of columns as well as array
23+
if(config.columns && !util.isArray(config.columns)) {
24+
var cols = []
25+
for(var key in config.columns) {
26+
var col = config.columns[key];
27+
col.name = key;
28+
cols.push(col);
29+
}
30+
config.columns = cols;
31+
}
32+
1833
for (var i = 0; i < config.columns.length; i++) {
1934
table.addColumn(config.columns[i]);
2035
}
@@ -156,6 +171,11 @@ Table.prototype.leftJoin = function(other) {
156171
return new JoinNode('LEFT', this.toNode(), other.toNode());
157172
};
158173

174+
//auto-join tables based on column intropsection
175+
Table.prototype.joinTo = function(other) {
176+
return Joiner.leftJoin(this, other);
177+
};
178+
159179
Table.prototype.as = function(alias) {
160180
//TODO could this be cleaner?
161181
var t = Table.define(this._initialConfig);

test/dialects/join-to-tests.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use strict';
2+
var sql = require(__dirname + '/../../lib');
3+
4+
var Harness = require('./support');
5+
6+
var user = sql.define({
7+
name: 'user',
8+
columns: {
9+
id: { primaryKey: true }
10+
}
11+
});
12+
13+
var photo = sql.define({
14+
name: 'photo',
15+
columns: {
16+
ownerId: {
17+
references: 'user'
18+
}
19+
}
20+
});
21+
22+
var post = sql.define({
23+
name: 'post',
24+
columns: {
25+
id: { primaryKey: true },
26+
ownerId: {
27+
references: {
28+
table: 'user',
29+
column: 'id'
30+
}
31+
}
32+
}
33+
});
34+
35+
Harness.test({
36+
query : user.joinTo(post),
37+
pg : '"user" INNER JOIN "post" ON ("user"."id" = "post"."ownerId")',
38+
sqlite: '"user" INNER JOIN "post" ON ("user"."id" = "post"."ownerId")',
39+
mysql : '`user` INNER JOIN `post` ON (`user`.`id` = `post`.`ownerId`)'
40+
});
41+
42+
Harness.test({
43+
query : post.joinTo(user),
44+
pg : '"post" INNER JOIN "user" ON ("user"."id" = "post"."ownerId")',
45+
sqlite: '"post" INNER JOIN "user" ON ("user"."id" = "post"."ownerId")',
46+
mysql : '`post` INNER JOIN `user` ON (`user`.`id` = `post`.`ownerId`)'
47+
});
48+
49+
Harness.test({
50+
query : user.joinTo(photo),
51+
pg : '"user" INNER JOIN "photo" ON ("user"."id" = "photo"."ownerId")',
52+
sqlite: '"user" INNER JOIN "photo" ON ("user"."id" = "photo"."ownerId")',
53+
mysql : '`user` INNER JOIN `photo` ON (`user`.`id` = `photo`.`ownerId`)'
54+
});

test/table-tests.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,37 @@ test('table with fancier column definitions', function() {
8282
assert.equal(email.anythingYouWant, 'awesome');
8383
});
8484

85+
test('table with object structured column definitions', function() {
86+
var table = Table.define({
87+
name: 'blah',
88+
columns: {
89+
id: {
90+
type: 'serial',
91+
notNull: true,
92+
primaryKey: true
93+
},
94+
email: {
95+
type: 'text',
96+
notNull: true,
97+
unique: true,
98+
anythingYouWant: 'awesome'
99+
}
100+
}
101+
});
102+
var cols = table.columns;
103+
assert.equal(cols.length, 2);
104+
var id = cols[0];
105+
assert.equal(id.name, 'id');
106+
assert.equal(id.type, 'serial');
107+
assert.equal(id.notNull, true);
108+
assert.equal(id.primaryKey, true);
109+
var email = cols[1];
110+
assert.equal(email.name, 'email');
111+
assert.equal(email.type, 'text');
112+
assert.equal(email.notNull, true);
113+
assert.equal(email.unique, true);
114+
assert.equal(email.anythingYouWant, 'awesome');
115+
});
85116
test('table with dynamic column definition', function() {
86117
var table = Table.define({ name: 'foo', columns: [] });
87118
assert.equal(table.columns.length, 0);

0 commit comments

Comments
 (0)