@mikro-orm/knex 6.3.3-dev.2 → 6.3.3-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.3.3-dev.2",
3
+ "version": "6.3.3-dev.4",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -66,7 +66,7 @@
66
66
  "@mikro-orm/core": "^6.3.2"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.3.3-dev.2",
69
+ "@mikro-orm/core": "6.3.3-dev.4",
70
70
  "better-sqlite3": "*",
71
71
  "libsql": "*",
72
72
  "mariadb": "*"
@@ -1,6 +1,6 @@
1
1
  import { inspect } from 'node:util';
2
2
  import type { Knex } from 'knex';
3
- import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, type Loaded, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryOrderMap, type QueryResult, RawQueryFragment, type RequiredEntityData, type ExpandProperty } from '@mikro-orm/core';
3
+ import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityName, type EntityProperty, type ExpandProperty, type FlushMode, type GroupOperator, type Loaded, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryOrderMap, type QueryResult, RawQueryFragment, type RequiredEntityData } from '@mikro-orm/core';
4
4
  import { JoinType, QueryType } from './enums';
5
5
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
6
6
  import { type Alias, QueryBuilderHelper } from './QueryBuilderHelper';
@@ -1155,7 +1155,7 @@ class QueryBuilder {
1155
1155
  if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
1156
1156
  return;
1157
1157
  }
1158
- const joins = Object.values(this._joins);
1158
+ let joins = Object.values(this._joins);
1159
1159
  joins.forEach(join => {
1160
1160
  join.cond_ = join.cond;
1161
1161
  join.cond = {};
@@ -1172,6 +1172,15 @@ class QueryBuilder {
1172
1172
  const [alias] = this.helper.splitField(k);
1173
1173
  const join = joins.find(j => j.alias === alias);
1174
1174
  if (join) {
1175
+ const parentJoin = joins.find(j => j.alias === join.ownerAlias);
1176
+ // https://stackoverflow.com/a/56815807/3665878
1177
+ if (parentJoin) {
1178
+ const nested = (parentJoin.nested ??= new Set());
1179
+ join.type = join.type === enums_1.JoinType.innerJoin || [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(parentJoin.prop.kind)
1180
+ ? enums_1.JoinType.nestedInnerJoin
1181
+ : enums_1.JoinType.nestedLeftJoin;
1182
+ nested.add(join);
1183
+ }
1175
1184
  if (join.cond[k]) {
1176
1185
  join.cond = { [op ?? '$and']: [join.cond, { [k]: cond[k] }] };
1177
1186
  }
@@ -1189,6 +1198,7 @@ class QueryBuilder {
1189
1198
  const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
1190
1199
  .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
1191
1200
  .process(this, { matchPopulateJoins: true, ignoreBranching: true, preferNoBranch: true });
1201
+ joins = Object.values(this._joins); // there might be new joins created by processing the `populateWhere` object
1192
1202
  replaceOnConditions(cond);
1193
1203
  }
1194
1204
  }
@@ -23,6 +23,10 @@ export declare class QueryBuilderHelper {
23
23
  joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: JoinType, cond?: Dictionary, schema?: string): JoinOptions;
24
24
  joinManyToManyReference(prop: EntityProperty, ownerAlias: string, alias: string, pivotAlias: string, type: JoinType, cond: Dictionary, path: string, schema?: string): Dictionary<JoinOptions>;
25
25
  processJoins(qb: Knex.QueryBuilder, joins: Dictionary<JoinOptions>, schema?: string): void;
26
+ createJoinExpression(join: JoinOptions, joins: Dictionary<JoinOptions>, schema?: string): {
27
+ sql: string;
28
+ params: Knex.Value[];
29
+ };
26
30
  private processJoinClause;
27
31
  private wrapQueryGroup;
28
32
  mapJoinColumns(type: QueryType, join: JoinOptions): (string | Knex.Raw)[];
@@ -183,56 +183,75 @@ class QueryBuilderHelper {
183
183
  }
184
184
  processJoins(qb, joins, schema) {
185
185
  Object.values(joins).forEach(join => {
186
- let table = join.table;
187
- const method = join.type === enums_1.JoinType.pivotJoin ? 'left join' : join.type;
188
- const conditions = [];
189
- const params = [];
190
- schema = join.schema && join.schema !== '*' ? join.schema : schema;
191
- if (schema) {
192
- table = `${schema}.${table}`;
186
+ if ([enums_1.JoinType.nestedInnerJoin, enums_1.JoinType.nestedLeftJoin].includes(join.type)) {
187
+ return;
193
188
  }
194
- if (join.prop.name !== '__subquery__') {
195
- join.primaryKeys.forEach((primaryKey, idx) => {
196
- const right = `${join.alias}.${join.joinColumns[idx]}`;
197
- if (join.prop.formula) {
198
- const alias = this.platform.quoteIdentifier(join.ownerAlias);
199
- const left = join.prop.formula(alias);
200
- conditions.push(`${left} = ${this.knex.ref(right)}`);
201
- return;
202
- }
203
- const left = join.prop.object && join.prop.fieldNameRaw
204
- ? join.prop.fieldNameRaw.replaceAll(core_1.ALIAS_REPLACEMENT, join.ownerAlias)
205
- : this.knex.ref(`${join.ownerAlias}.${primaryKey}`);
189
+ const { sql, params } = this.createJoinExpression(join, joins, schema);
190
+ qb.joinRaw(sql, params);
191
+ });
192
+ }
193
+ createJoinExpression(join, joins, schema) {
194
+ let table = join.table;
195
+ const method = {
196
+ [enums_1.JoinType.nestedInnerJoin]: 'inner join',
197
+ [enums_1.JoinType.nestedLeftJoin]: 'left join',
198
+ [enums_1.JoinType.pivotJoin]: 'left join',
199
+ }[join.type] ?? join.type;
200
+ const conditions = [];
201
+ const params = [];
202
+ schema = join.schema && join.schema !== '*' ? join.schema : schema;
203
+ if (schema) {
204
+ table = `${schema}.${table}`;
205
+ }
206
+ if (join.prop.name !== '__subquery__') {
207
+ join.primaryKeys.forEach((primaryKey, idx) => {
208
+ const right = `${join.alias}.${join.joinColumns[idx]}`;
209
+ if (join.prop.formula) {
210
+ const alias = this.platform.quoteIdentifier(join.ownerAlias);
211
+ const left = join.prop.formula(alias);
206
212
  conditions.push(`${left} = ${this.knex.ref(right)}`);
207
- });
208
- }
209
- if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
210
- const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
211
- const alias = join.inverseAlias ?? join.alias;
212
- join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
213
- }
214
- for (const key of Object.keys(join.cond)) {
215
- const hasPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
216
- const newKey = hasPrefix ? key : `${join.alias}.${key}`;
217
- const clause = this.processJoinClause(newKey, join.cond[key], join.alias, params);
218
- /* istanbul ignore else */
219
- if (clause !== '()') {
220
- conditions.push(clause);
213
+ return;
221
214
  }
215
+ const left = join.prop.object && join.prop.fieldNameRaw
216
+ ? join.prop.fieldNameRaw.replaceAll(core_1.ALIAS_REPLACEMENT, join.ownerAlias)
217
+ : this.knex.ref(`${join.ownerAlias}.${primaryKey}`);
218
+ conditions.push(`${left} = ${this.knex.ref(right)}`);
219
+ });
220
+ }
221
+ if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
222
+ const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
223
+ const alias = join.inverseAlias ?? join.alias;
224
+ join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
225
+ }
226
+ for (const key of Object.keys(join.cond)) {
227
+ const hasPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
228
+ const newKey = hasPrefix ? key : `${join.alias}.${key}`;
229
+ const clause = this.processJoinClause(newKey, join.cond[key], join.alias, params);
230
+ /* istanbul ignore else */
231
+ if (clause !== '()') {
232
+ conditions.push(clause);
222
233
  }
223
- let sql = method + ' ';
224
- if (join.subquery) {
225
- sql += `(${join.subquery})`;
226
- }
227
- else {
228
- sql += this.knex.ref(table);
229
- }
230
- sql += ` as ${this.knex.ref(join.alias)}`;
231
- if (conditions.length > 0) {
232
- sql += ` on ${conditions.join(' and ')}`;
234
+ }
235
+ let sql = method + ' ';
236
+ if (join.nested) {
237
+ sql += `(${this.knex.ref(table)} as ${this.knex.ref(join.alias)}`;
238
+ for (const nested of join.nested) {
239
+ const { sql: nestedSql, params: nestedParams } = this.createJoinExpression(nested, joins, schema);
240
+ sql += ' ' + nestedSql;
241
+ params.unshift(...nestedParams);
233
242
  }
234
- return qb.joinRaw(sql, params);
235
- });
243
+ sql += `)`;
244
+ }
245
+ else if (join.subquery) {
246
+ sql += `(${join.subquery}) as ${this.knex.ref(join.alias)}`;
247
+ }
248
+ else {
249
+ sql += `${this.knex.ref(table)} as ${this.knex.ref(join.alias)}`;
250
+ }
251
+ if (conditions.length > 0) {
252
+ sql += ` on ${conditions.join(' and ')}`;
253
+ }
254
+ return { sql, params };
236
255
  }
237
256
  processJoinClause(key, value, alias, params, operator = '$eq') {
238
257
  if (core_1.Utils.isGroupOperator(key) && Array.isArray(value)) {
package/query/enums.d.ts CHANGED
@@ -9,6 +9,8 @@ export declare enum QueryType {
9
9
  export declare enum JoinType {
10
10
  leftJoin = "left join",
11
11
  innerJoin = "inner join",
12
+ nestedLeftJoin = "nested left join",
13
+ nestedInnerJoin = "nested inner join",
12
14
  pivotJoin = "pivot join",
13
15
  innerJoinLateral = "inner join lateral",
14
16
  leftJoinLateral = "left join lateral"
package/query/enums.js CHANGED
@@ -14,6 +14,8 @@ var JoinType;
14
14
  (function (JoinType) {
15
15
  JoinType["leftJoin"] = "left join";
16
16
  JoinType["innerJoin"] = "inner join";
17
+ JoinType["nestedLeftJoin"] = "nested left join";
18
+ JoinType["nestedInnerJoin"] = "nested inner join";
17
19
  JoinType["pivotJoin"] = "pivot join";
18
20
  JoinType["innerJoinLateral"] = "inner join lateral";
19
21
  JoinType["leftJoinLateral"] = "left join lateral";
package/typings.d.ts CHANGED
@@ -27,6 +27,7 @@ export interface JoinOptions {
27
27
  cond: Dictionary;
28
28
  cond_?: Dictionary;
29
29
  subquery?: string;
30
+ nested?: Set<JoinOptions>;
30
31
  }
31
32
  export interface Column {
32
33
  name: string;