Add support to n:m relations

This commit is contained in:
Diogo Doreto 2015-11-22 21:44:14 -02:00
parent aa396620fa
commit 7b305b4920
2 changed files with 65 additions and 19 deletions

View File

@ -1073,33 +1073,51 @@ SQLConnector.prototype.buildJoins = function(model, where) {
var relations = modelDef.model.relations;
var stmt = new ParameterizedSQL('', []);
var buildOneToMany = function buildOneToMany(modelFrom, keyFrom, modelTo, keyTo, filter) {
var modelToEscaped = this.tableEscaped(modelTo);
var innerFilter = assign({}, filter);
var innerIdField = {};
innerIdField[keyTo] = true;
innerFilter.fields = assign({}, innerFilter.fields, innerIdField);
var condition = this.columnEscaped(modelFrom, keyFrom) + '=' +
this.columnEscaped(modelTo, keyTo);
var innerSelect = this.buildSelect(modelTo, innerFilter, {
skipParameterize: true
});
return new ParameterizedSQL('INNER JOIN (', [])
.merge(innerSelect)
.merge(') AS ' + modelToEscaped)
.merge('ON ' + condition);
}.bind(this);
for (var key in where) {
if (!(key in relations)) continue;
var rel = relations[key];
var keyFrom = rel.keyFrom;
var modelTo = rel.modelTo.definition.name;
var modelToEscaped = this.tableEscaped(modelTo);
var keyTo = rel.keyTo;
var innerWhere = assign({}, where[key]);
var innerIdField = {};
innerIdField[keyTo] = true;
innerWhere.fields = assign({}, innerWhere.fields, innerIdField);
var condition = this.columnEscaped(model, keyFrom) + '=' +
this.columnEscaped(modelTo, keyTo);
var innerSelect = this.buildSelect(modelTo, innerWhere, {
skipParameterize: true
});
stmt
.merge('INNER JOIN (')
.merge(innerSelect)
.merge(') AS ' + modelToEscaped)
.merge('ON ' + condition);
var join;
if (!rel.modelThrough) {
// 1:n relation
join = buildOneToMany(model, keyFrom, modelTo, keyTo, where[key]);
} else {
// n:m relation
var modelThrough = rel.modelThrough.definition.name;
var keyThrough = rel.keyThrough;
var modelToKey = rel.modelTo.definition._ids[0].name;
var innerFilter = {fields: {}};
innerFilter.fields[keyThrough] = true;
var joinInner = buildOneToMany(model, keyFrom, modelThrough, keyTo, innerFilter);
join = buildOneToMany(modelThrough, keyThrough, modelTo, modelToKey, where[key]);
join = joinInner.merge(join);
}
stmt.merge(join);
}
return stmt;

View File

@ -57,6 +57,12 @@ describe('sql connector', function() {
Order.belongsTo(Customer, {as: 'customer', foreignKey: 'customer_name'});
Order.belongsTo(Store, {as: 'store', foreignKey: 'store_id'});
Store.hasMany(Order, {as: 'orders', foreignKey: 'store_id'});
Store.hasMany(Customer, {
as: 'customers',
through: Order,
foreignKey: 'store_id',
keyThrough: 'customer_name'
});
Customer.belongsTo(Store, {as: 'favorite_store', foreignKey: 'favorite_store'});
Store.hasMany(Customer, {as: 'customers_fav', foreignKey: 'favorite_store'});
});
@ -343,7 +349,7 @@ describe('sql connector', function() {
});
});
it('builds SELECT with INNER JOIN', function () {
it('builds SELECT with INNER JOIN (1:n relation)', function () {
var sql = connector.buildSelect('customer', {
where: {
orders: {
@ -364,6 +370,28 @@ describe('sql connector', function() {
});
});
it('builds SELECT with INNER JOIN (n:n relation)', function () {
var sql = connector.buildSelect('store', {
where: {
customers: {
where: {
vip: true
}
}
}
});
expect(sql.toJSON()).to.eql({
sql: 'SELECT DISTINCT `STORE`.`ID`,`STORE`.`STATE` FROM `STORE` INNER JOIN' +
' ( SELECT `ORDER`.`CUSTOMER_NAME`,`ORDER`.`STORE_ID` FROM `ORDER` ' +
'ORDER BY `ORDER`.`ID` ) AS `ORDER` ON `STORE`.`ID`=`ORDER`.`STORE_ID` ' +
'INNER JOIN ( SELECT `CUSTOMER`.`NAME` FROM `CUSTOMER` WHERE ' +
'`CUSTOMER`.`VIP`=$1 ORDER BY `CUSTOMER`.`NAME` ) AS `CUSTOMER` ON ' +
'`ORDER`.`CUSTOMER_NAME`=`CUSTOMER`.`NAME` ORDER BY `STORE`.`ID`',
params: [true]
});
});
it('builds SELECT with INNER JOIN and order by relation columns', function () {
var sql = connector.buildSelect('order', {
where: {