Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4a5315c

Browse files
committed
automatically merge *joinRelation calls. closes Vincit#1114
1 parent 97b72ae commit 4a5315c

File tree

4 files changed

+354
-119
lines changed

4 files changed

+354
-119
lines changed

lib/queryBuilder/QueryBuilder.js

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const Bluebird = require('bluebird');
55
const { raw } = require('./RawBuilder');
66
const { createModifier } = require('../utils/createModifier');
77
const { Type: ValidationErrorType } = require('../model/ValidationError');
8-
const { isObject, isString, isFunction, last } = require('../utils/objectUtils');
8+
const { isObject, isString, isFunction, last, groupBy } = require('../utils/objectUtils');
99
const { RelationExpression, DuplicateRelationError } = require('./RelationExpression');
1010
const { Selection } = require('./operations/select/Selection');
1111

@@ -55,6 +55,7 @@ class QueryBuilder extends QueryBuilderBase {
5555
this._eagerModifiersAtPath = [];
5656
this._allowedEagerExpression = null;
5757
this._allowedUpsertExpression = null;
58+
this._joinRelations = [];
5859

5960
this._findOperationOptions = modelClass.defaultFindOptions;
6061
this._eagerOperationOptions = modelClass.defaultEagerOptions;
@@ -383,6 +384,10 @@ class QueryBuilder extends QueryBuilderBase {
383384
return !!this._eagerExpression;
384385
}
385386

387+
hasJoinRelation() {
388+
return this._joinRelations.length > 0;
389+
}
390+
386391
isSelectAll() {
387392
if (this._operations.length === 0) {
388393
return true;
@@ -457,6 +462,7 @@ class QueryBuilder extends QueryBuilderBase {
457462

458463
builder._findOperationOptions = this._findOperationOptions;
459464
builder._eagerOperationOptions = this._eagerOperationOptions;
465+
builder._joinRelations = this._joinRelations.slice();
460466

461467
builder._findOperationFactory = this._findOperationFactory;
462468
builder._insertOperationFactory = this._insertOperationFactory;
@@ -558,6 +564,12 @@ class QueryBuilder extends QueryBuilderBase {
558564
// Take a clone so that we don't modify this instance during build.
559565
const builder = this.clone();
560566

567+
if (builder.hasJoinRelation()) {
568+
// If there are some *joinRelation calls, merge the expressions
569+
// and add the needed operations.
570+
addJoinRelationOperations(builder);
571+
}
572+
561573
if (builder.isFind()) {
562574
// If no write operations have been called at this point this query is a
563575
// find query and we need to call the custom find implementation.
@@ -980,60 +992,60 @@ class QueryBuilder extends QueryBuilderBase {
980992
return this.addOperation(new FirstOperation('first'), arguments);
981993
}
982994

983-
joinRelation() {
984-
return this.addOperation(
985-
new JoinRelationOperation('joinRelation', { joinOperation: 'join' }),
986-
arguments
987-
);
995+
joinRelation(...args) {
996+
return this.innerJoinRelation(...args);
988997
}
989998

990-
innerJoinRelation() {
991-
return this.addOperation(
992-
new JoinRelationOperation('innerJoinRelation', { joinOperation: 'innerJoin' }),
993-
arguments
994-
);
999+
innerJoinRelation(expression, options) {
1000+
this._joinRelations.push({
1001+
joinOperation: 'innerJoin',
1002+
expression,
1003+
options
1004+
});
1005+
1006+
return this;
9951007
}
9961008

997-
outerJoinRelation() {
998-
return this.addOperation(
999-
new JoinRelationOperation('outerJoinRelation', { joinOperation: 'outerJoin' }),
1000-
arguments
1001-
);
1009+
outerJoinRelation(expression, options) {
1010+
this._joinRelations.push({
1011+
joinOperation: 'outerJoin',
1012+
expression,
1013+
options
1014+
});
1015+
1016+
return this;
10021017
}
10031018

1004-
leftJoinRelation() {
1005-
return this.addOperation(
1006-
new JoinRelationOperation('leftJoinRelation', { joinOperation: 'leftJoin' }),
1007-
arguments
1008-
);
1019+
fullOuterJoinRelation(...args) {
1020+
return this.outerJoin(...args);
10091021
}
10101022

1011-
leftOuterJoinRelation() {
1012-
return this.addOperation(
1013-
new JoinRelationOperation('leftOuterJoinRelation', { joinOperation: 'leftOuterJoin' }),
1014-
arguments
1015-
);
1023+
leftJoinRelation(expression, options) {
1024+
this._joinRelations.push({
1025+
joinOperation: 'leftJoin',
1026+
expression,
1027+
options
1028+
});
1029+
1030+
return this;
10161031
}
10171032

1018-
rightJoinRelation() {
1019-
return this.addOperation(
1020-
new JoinRelationOperation('rightJoinRelation', { joinOperation: 'rightJoin' }),
1021-
arguments
1022-
);
1033+
leftOuterJoinRelation(...args) {
1034+
return this.leftJoinRelation(...args);
10231035
}
10241036

1025-
rightOuterJoinRelation() {
1026-
return this.addOperation(
1027-
new JoinRelationOperation('rightOuterJoinRelation', { joinOperation: 'rightOuterJoin' }),
1028-
arguments
1029-
);
1037+
rightJoinRelation(expression, options) {
1038+
this._joinRelations.push({
1039+
joinOperation: 'rightJoin',
1040+
expression,
1041+
options
1042+
});
1043+
1044+
return this;
10301045
}
10311046

1032-
fullOuterJoinRelation() {
1033-
return this.addOperation(
1034-
new JoinRelationOperation('fullOuterJoinRelation', { joinOperation: 'fullOuterJoin' }),
1035-
arguments
1036-
);
1047+
rightOuterJoinRelation(...args) {
1048+
return this.rightJoinRelation(...args);
10371049
}
10381050

10391051
deleteById() {
@@ -1144,6 +1156,12 @@ function findQueryExecutorOperation(builder) {
11441156
function beforeExecute(builder) {
11451157
let promise = Promise.resolve();
11461158

1159+
if (builder.hasJoinRelation()) {
1160+
// If there are some *joinRelation calls, merge the expressions
1161+
// and add the needed operations.
1162+
addJoinRelationOperations(builder);
1163+
}
1164+
11471165
if (builder.isFind()) {
11481166
// If no write operations have been called at this point this query is a
11491167
// find query and we need to call the custom find implementation.
@@ -1251,6 +1269,16 @@ function handleExecuteError(builder, err) {
12511269
return promise;
12521270
}
12531271

1272+
function addJoinRelationOperations(builder) {
1273+
if (!builder.has(JoinRelationOperation)) {
1274+
const callsGroupedByJoinOperation = groupBy(builder._joinRelations, call => call.joinOperation);
1275+
1276+
callsGroupedByJoinOperation.forEach(calls => {
1277+
builder.addOperation(new JoinRelationOperation(), calls);
1278+
});
1279+
}
1280+
}
1281+
12541282
function addFindOperation(builder) {
12551283
if (!builder.has(FindOperation)) {
12561284
const operation = builder._findOperationFactory(builder);

lib/queryBuilder/RelationExpression.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ class RelationExpression {
8383
}
8484
}
8585

86+
static getChildNames(exprObject) {
87+
return getChildNames(exprObject);
88+
}
89+
8690
get numChildren() {
8791
return this.childNames.length;
8892
}

lib/queryBuilder/operations/JoinRelationOperation.js

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,54 +6,80 @@ const { RelationExpression } = require('../RelationExpression');
66
const { isString } = require('../../utils/objectUtils');
77

88
class JoinRelationOperation extends QueryBuilderOperation {
9-
constructor(name, opt) {
10-
super(name, opt);
11-
12-
this.expression = null;
13-
this.callOpt = null;
14-
}
15-
16-
onAdd(builder, args) {
17-
this.expression = RelationExpression.create(args[0]);
18-
this.callOpt = args[1] || {};
9+
onAdd(_, calls) {
10+
this.calls = calls;
1911
return true;
2012
}
2113

2214
onBuild(builder) {
2315
const modelClass = builder.modelClass();
24-
const opt = Object.assign({}, this.callOpt);
25-
26-
opt.aliases = Object.assign({}, opt.aliases);
27-
opt.joinOperation = this.opt.joinOperation;
28-
29-
// Special case for one single relation.
30-
if (this.expression.numChildren === 1) {
31-
const relationNames = this.expression.childNames.map(it => this.expression[it].$relation);
32-
const relationName = relationNames[0];
33-
const relation = modelClass.getRelation(relationName);
34-
let alias = null;
35-
36-
if (opt.alias === false) {
37-
alias = builder.tableRefFor(relation.relatedModelClass.getTableName());
38-
} else if (opt.alias === true || !opt.alias) {
39-
alias = relation.name;
40-
} else if (isString(opt.alias)) {
41-
alias = opt.alias;
16+
const joinOperation = this.calls[0].joinOperation;
17+
let mergedExpr = RelationExpression.create();
18+
19+
for (const call of this.calls) {
20+
const expr = RelationExpression.create(call.expression).toPojo();
21+
const childNames = RelationExpression.getChildNames(expr);
22+
const options = call.options || {};
23+
24+
if (childNames.length === 1) {
25+
applyAlias(expr, modelClass, builder, options);
4226
}
4327

44-
if (alias) {
45-
opt.aliases[relationName] = alias;
28+
if (options.aliases) {
29+
applyAliases(expr, modelClass, options);
4630
}
31+
32+
mergedExpr = mergedExpr.merge(expr);
4733
}
4834

4935
const joinBuilder = new RelationJoinBuilder({
5036
modelClass,
51-
expression: this.expression
37+
expression: mergedExpr
5238
});
5339

54-
joinBuilder.setOptions(opt);
40+
joinBuilder.setOptions({ joinOperation });
5541
joinBuilder.buildJoinOnly(builder);
5642
}
5743
}
5844

45+
function applyAlias(expr, modelClass, builder, options) {
46+
const childNames = RelationExpression.getChildNames(expr);
47+
const childName = childNames[0];
48+
const childExpr = expr[childName];
49+
const relation = modelClass.getRelation(childExpr.$relation);
50+
51+
let alias = childName;
52+
53+
if (options.alias === false) {
54+
alias = builder.tableRefFor(relation.relatedModelClass.getTableName());
55+
} else if (isString(options.alias)) {
56+
alias = options.alias;
57+
}
58+
59+
if (childName !== alias) {
60+
renameRelationExpressionNode(expr, childName, alias);
61+
}
62+
}
63+
64+
function applyAliases(expr, modelClass, options) {
65+
for (const childName of RelationExpression.getChildNames(expr)) {
66+
const childExpr = expr[childName];
67+
const relation = modelClass.getRelation(childExpr.$relation);
68+
const alias = options.aliases[childExpr.$relation];
69+
70+
if (alias && alias !== childName) {
71+
renameRelationExpressionNode(expr, childName, alias);
72+
}
73+
74+
applyAliases(childExpr, relation.relatedModelClass, options);
75+
}
76+
}
77+
78+
function renameRelationExpressionNode(expr, oldName, newName) {
79+
const childExpr = expr[oldName];
80+
delete expr[oldName];
81+
expr[newName] = childExpr;
82+
childExpr.$name = newName;
83+
}
84+
5985
module.exports = JoinRelationOperation;

0 commit comments

Comments
 (0)