diff --git a/lib/queryBuilder/QueryBuilderOperationSupport.js b/lib/queryBuilder/QueryBuilderOperationSupport.js index 4c7159bdf..c1593f550 100644 --- a/lib/queryBuilder/QueryBuilderOperationSupport.js +++ b/lib/queryBuilder/QueryBuilderOperationSupport.js @@ -1,6 +1,6 @@ 'use strict'; -const { isString, isFunction, isRegExp, last } = require('../utils/objectUtils'); +const { isString, isFunction, isRegExp, mergeMaps, last } = require('../utils/objectUtils'); const { QueryBuilderContextBase } = require('./QueryBuilderContextBase'); const { QueryBuilderUserContext } = require('./QueryBuilderUserContext'); const { deprecate } = require('../utils/deprecate'); @@ -177,6 +177,14 @@ class QueryBuilderOperationSupport { subqueryOf(query) { if (query) { + if (this._isPartialQuery) { + // Merge alias and table name maps for "partial" subqueries. + const ctx = this.internalContext(); + + ctx.aliasMap = mergeMaps(query.internalContext().aliasMap, ctx.aliasMap); + ctx.tableMap = mergeMaps(query.internalContext().tableMap, ctx.tableMap); + } + this._parentQuery = query; if (this.unsafeKnex() === null) { diff --git a/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js b/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js index 8144407e3..7cded1f98 100644 --- a/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +++ b/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js @@ -131,7 +131,7 @@ function convertFunction(func, builder) { function convertQueryBuilderFunction(knexQueryBuilder, func, builder) { const convertedQueryBuilder = builder.constructor.forClass(builder.modelClass()); - convertedQueryBuilder.subqueryOf(builder).isPartial(true); + convertedQueryBuilder.isPartial(true).subqueryOf(builder); func.call(convertedQueryBuilder, convertedQueryBuilder); convertedQueryBuilder.toKnexQuery(knexQueryBuilder); @@ -141,7 +141,7 @@ function convertJoinBuilderFunction(knexJoinBuilder, func, builder) { const JoinBuilder = getJoinBuilder(); const joinClauseBuilder = JoinBuilder.forClass(builder.modelClass()); - joinClauseBuilder.subqueryOf(builder).isPartial(true); + joinClauseBuilder.isPartial(true).subqueryOf(builder); func.call(joinClauseBuilder, joinClauseBuilder); joinClauseBuilder.toKnexQuery(knexJoinBuilder); diff --git a/lib/utils/objectUtils.js b/lib/utils/objectUtils.js index e3b703f7c..4de3c2891 100644 --- a/lib/utils/objectUtils.js +++ b/lib/utils/objectUtils.js @@ -373,6 +373,18 @@ function isSafeKey(key) { return isNumber(key) || (isString(key) && key !== '__proto__'); } +function mergeMaps(map1, map2) { + const map = new Map(map1); + + if (map2) { + for (const key of map2.keys()) { + map.set(key, map2.get(key)); + } + } + + return map; +} + module.exports = { isEmpty, isString, @@ -385,6 +397,7 @@ module.exports = { difference, upperFirst, zipObject, + mergeMaps, cloneDeep, asSingle, asArray, diff --git a/tests/integration/find.js b/tests/integration/find.js index 3eb0a0638..335203dde 100644 --- a/tests/integration/find.js +++ b/tests/integration/find.js @@ -301,6 +301,27 @@ module.exports = (session) => { }); }); + it('.where() with a subquery and aliases (using tableRefFor in subquery)', () => { + return Model2.query() + .alias('m1') + .where((query) => + query.where( + 'id_col', + Model2.query() + .select('m2.id_col') + .alias('m2') + .where( + 'm2.model2_prop2', + ref(`${query.tableRefFor(query.modelClass())}.model2_prop2`) + ) + ) + ) + .orderBy('m1.model2_prop2') + .then((models) => { + expect(_.map(models, 'model2Prop2').sort()).to.eql([10, 20, 30]); + }); + }); + it('.where() with an object and knex query builder', () => { return Model2.query() .where({