@mikro-orm/knex 7.0.0-dev.23 → 7.0.0-dev.25

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.
@@ -74,7 +74,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
74
74
  protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
75
75
  protected processField<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T> | undefined, field: string, ret: Field<T>[], populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T, any, any, any>): void;
76
76
  protected isPopulated<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T>, hint: PopulateOptions<T>, name?: string): boolean;
77
- protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T, any, any, any>, alias: string, options: Pick<FindOptions<T, any, any, any>, 'strategy' | 'fields' | 'exclude'>, count?: boolean): Field<T>[];
77
+ protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T, any, any, any>, alias: string, options: Pick<FindOptions<T, any, any, any>, 'strategy' | 'fields' | 'exclude'>): Field<T>[];
78
78
  }
79
79
  interface FieldsForJoinedLoadOptions<T extends object> {
80
80
  explicitFields?: readonly Field<T>[];
@@ -238,7 +238,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
238
238
  let relationPojo = {};
239
239
  meta2.props
240
240
  .filter(prop => !ref && prop.persist === false && prop.fieldNames)
241
- .filter(prop => !prop.lazy || populate.some(p => p.field === prop.name || p.all))
242
241
  .forEach(prop => {
243
242
  /* v8 ignore next 3 */
244
243
  if (prop.fieldNames.length > 1) { // composite keys
@@ -312,15 +311,20 @@ export class AbstractSqlDriver extends DatabaseDriver {
312
311
  });
313
312
  }
314
313
  async count(entityName, where, options = {}) {
315
- const meta = this.metadata.find(entityName);
316
- if (meta?.virtual) {
314
+ const meta = this.metadata.get(entityName);
315
+ if (meta.virtual) {
317
316
  return this.countVirtual(entityName, where, options);
318
317
  }
319
- const joinedProps = meta ? this.joinedProps(meta, options.populate ?? []) : [];
320
- const populateWhere = meta ? this.buildPopulateWhere(meta, joinedProps, options) : undefined;
321
- const populate = options.populate ?? [];
322
- const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging)
323
- .indexHint(options.indexHint)
318
+ options = { populate: [], ...options };
319
+ const populate = options.populate;
320
+ const joinedProps = this.joinedProps(meta, populate, options);
321
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
322
+ const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
323
+ if (meta && !Utils.isEmpty(populate)) {
324
+ this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
325
+ }
326
+ qb.__populateWhere = options._populateWhere;
327
+ qb.indexHint(options.indexHint)
324
328
  .comment(options.comments)
325
329
  .hintComment(options.hintComments)
326
330
  .groupBy(options.groupBy)
@@ -328,9 +332,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
328
332
  .populate(populate, joinedProps.length > 0 ? populateWhere : undefined, joinedProps.length > 0 ? options.populateFilter : undefined)
329
333
  .withSchema(this.getSchemaName(meta, options))
330
334
  .where(where);
331
- if (meta && !Utils.isEmpty(populate)) {
332
- this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, true);
333
- }
334
335
  if (options.em) {
335
336
  await qb.applyJoinedFilters(options.em, options.filters);
336
337
  }
@@ -941,9 +942,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
941
942
  if (ref && !hint.filter && (prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))) {
942
943
  continue;
943
944
  }
944
- if (options.count && !options?.populateFilter?.[prop.name]) {
945
- continue;
946
- }
947
945
  const meta2 = this.metadata.find(prop.type);
948
946
  const pivotRefJoin = prop.kind === ReferenceKind.MANY_TO_MANY && ref;
949
947
  const tableAlias = qb.getNextAlias(prop.name);
@@ -1254,7 +1252,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1254
1252
  }
1255
1253
  return false;
1256
1254
  }
1257
- buildFields(meta, populate, joinedProps, qb, alias, options, count = false) {
1255
+ buildFields(meta, populate, joinedProps, qb, alias, options) {
1258
1256
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => this.isPopulated(meta, prop, p)));
1259
1257
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
1260
1258
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL && p.persist !== false);
@@ -1320,7 +1318,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
1320
1318
  exclude: options.exclude,
1321
1319
  populate,
1322
1320
  parentTableAlias: alias,
1323
- count,
1324
1321
  ...options,
1325
1322
  }));
1326
1323
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "7.0.0-dev.23",
3
+ "version": "7.0.0-dev.25",
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
  "type": "module",
6
6
  "exports": {
@@ -54,9 +54,9 @@
54
54
  "sqlstring": "2.3.3"
55
55
  },
56
56
  "devDependencies": {
57
- "@mikro-orm/core": "^6.5.1"
57
+ "@mikro-orm/core": "^6.5.3"
58
58
  },
59
59
  "peerDependencies": {
60
- "@mikro-orm/core": "7.0.0-dev.23"
60
+ "@mikro-orm/core": "7.0.0-dev.25"
61
61
  }
62
62
  }
@@ -20,7 +20,7 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
20
20
  shouldInline(payload: any): boolean;
21
21
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
22
22
  shouldRename(payload: any): boolean;
23
- renameFieldToPK<T>(qb: IQueryBuilder<T>): string;
23
+ renameFieldToPK<T>(qb: IQueryBuilder<T>, ownerAlias?: string): string;
24
24
  getPath(addIndex?: boolean): string;
25
25
  private isPivotJoin;
26
26
  getPivotPath(path: string): string;
@@ -65,13 +65,13 @@ export class CriteriaNode {
65
65
  default: return false;
66
66
  }
67
67
  }
68
- renameFieldToPK(qb) {
69
- let joinAlias = qb.getAliasForJoinPath(this.getPath(), { matchPopulateJoins: true });
68
+ renameFieldToPK(qb, ownerAlias) {
69
+ const joinAlias = qb.getAliasForJoinPath(this.getPath(), { matchPopulateJoins: true });
70
70
  if (!joinAlias && this.parent && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind) && this.prop.owner) {
71
- joinAlias = qb.getAliasForJoinPath(this.parent.getPath());
72
- return Utils.getPrimaryKeyHash(this.prop.joinColumns.map(col => `${joinAlias ?? qb.alias}.${col}`));
71
+ const alias = qb.getAliasForJoinPath(this.parent.getPath()) ?? ownerAlias ?? qb.alias;
72
+ return Utils.getPrimaryKeyHash(this.prop.joinColumns.map(col => `${alias}.${col}`));
73
73
  }
74
- const alias = joinAlias ?? qb.alias;
74
+ const alias = joinAlias ?? ownerAlias ?? qb.alias;
75
75
  if (this.prop.kind === ReferenceKind.MANY_TO_MANY) {
76
76
  return Utils.getPrimaryKeyHash(this.prop.inverseJoinColumns.map(col => `${alias}.${col}`));
77
77
  }
@@ -67,13 +67,14 @@ export class ObjectCriteriaNode extends CriteriaNode {
67
67
  const virtual = childNode.prop?.persist === false && !childNode.prop?.formula;
68
68
  // if key is missing, we are inside group operator and we need to prefix with alias
69
69
  const primaryKey = this.key && this.metadata.find(this.entityName)?.primaryKeys.includes(field);
70
+ const isToOne = childNode.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(childNode.prop.kind);
70
71
  if (childNode.shouldInline(payload)) {
71
- const childAlias = qb.getAliasForJoinPath(childNode.getPath(), options);
72
+ const childAlias = qb.getAliasForJoinPath(childNode.getPath(), { preferNoBranch: isToOne, ...options });
72
73
  const a = qb.helper.isTableNameAliasRequired(qb.type) ? alias : undefined;
73
74
  this.inlineChildPayload(o, payload, field, a, childAlias);
74
75
  }
75
76
  else if (childNode.shouldRename(payload)) {
76
- this.inlineCondition(childNode.renameFieldToPK(qb), o, payload);
77
+ this.inlineCondition(childNode.renameFieldToPK(qb, alias), o, payload);
77
78
  }
78
79
  else if (isRawField) {
79
80
  const rawField = RawQueryFragment.getKnownFragment(field);
@@ -285,7 +285,7 @@ export class QueryBuilder {
285
285
  }
286
286
  }
287
287
  if (Utils.hasObjectKeys(join.cond)) {
288
- /* istanbul ignore next */
288
+ /* v8 ignore next */
289
289
  join.cond = { $and: [join.cond, cond] };
290
290
  }
291
291
  else {
@@ -1249,17 +1249,27 @@ export class QueryBuilder {
1249
1249
  return;
1250
1250
  }
1251
1251
  const joins = Object.values(this._joins);
1252
+ const lookupParentGroup = (j) => {
1253
+ return j.nested ?? (j.parent ? lookupParentGroup(j.parent) : undefined);
1254
+ };
1252
1255
  for (const join of joins) {
1253
1256
  if (join.type === JoinType.innerJoin) {
1254
- const parentJoin = joins.find(j => j.alias === join.ownerAlias);
1257
+ join.parent = joins.find(j => j.alias === join.ownerAlias);
1255
1258
  // https://stackoverflow.com/a/56815807/3665878
1256
- if (parentJoin?.type === JoinType.leftJoin || parentJoin?.type === JoinType.nestedLeftJoin) {
1257
- const nested = (parentJoin.nested ??= new Set());
1259
+ if (join.parent?.type === JoinType.leftJoin || join.parent?.type === JoinType.nestedLeftJoin) {
1260
+ const nested = ((join.parent).nested ??= new Set());
1258
1261
  join.type = join.type === JoinType.innerJoin
1259
1262
  ? JoinType.nestedInnerJoin
1260
1263
  : JoinType.nestedLeftJoin;
1261
1264
  nested.add(join);
1262
1265
  }
1266
+ else if (join.parent?.type === JoinType.nestedInnerJoin) {
1267
+ const group = lookupParentGroup(join.parent);
1268
+ join.type = join.type === JoinType.innerJoin
1269
+ ? JoinType.nestedInnerJoin
1270
+ : JoinType.nestedLeftJoin;
1271
+ group?.add(join);
1272
+ }
1263
1273
  }
1264
1274
  }
1265
1275
  }
@@ -251,105 +251,6 @@ export class QueryBuilderHelper {
251
251
  }
252
252
  return { sql, params };
253
253
  }
254
- // <<<<<<< HEAD
255
- // private processJoinClause(key: string, value: unknown, alias: string, params: Knex.Value[], operator = '$eq'): string {
256
- // if (Utils.isGroupOperator(key) && Array.isArray(value)) {
257
- // const parts = value.map(sub => {
258
- // return this.wrapQueryGroup(Object.keys(sub).map(k => this.processJoinClause(k, sub[k], alias, params)));
259
- // });
260
- // return this.wrapQueryGroup(parts, key);
261
- // }
262
- //
263
- // const rawField = RawQueryFragment.getKnownFragment(key);
264
- //
265
- // if (!rawField && !Utils.isOperator(key, false) && !this.isPrefixed(key)) {
266
- // key = `${alias}.${key}`;
267
- // }
268
- //
269
- // if (this.isSimpleRegExp(value)) {
270
- // params.push(this.getRegExpParam(value));
271
- // return `${this.knex.ref(this.mapper(key))} like ?`;
272
- // }
273
- //
274
- // if (value instanceof RegExp) {
275
- // value = this.platform.getRegExpValue(value);
276
- // }
277
- //
278
- // if (Utils.isOperator(key, false) && Utils.isPlainObject(value)) {
279
- // const parts = Object.keys(value).map(k => this.processJoinClause(k, (value as Dictionary)[k], alias, params, key));
280
- //
281
- // return key === '$not' ? `not ${this.wrapQueryGroup(parts)}` : this.wrapQueryGroup(parts);
282
- // }
283
- //
284
- // if (Utils.isPlainObject(value) && Object.keys(value).every(k => Utils.isOperator(k, false))) {
285
- // const parts = Object.keys(value).map(op => this.processJoinClause(key, (value as Dictionary)[op], alias, params, op));
286
- //
287
- // return this.wrapQueryGroup(parts);
288
- // }
289
- //
290
- // const [fromAlias, fromField] = this.splitField(key as EntityKey);
291
- // const prop = this.getProperty(fromField, fromAlias);
292
- // operator = operator === '$not' ? '$eq' : operator;
293
- // const column = this.mapper(key, undefined, undefined, null);
294
- //
295
- // if (value === null) {
296
- // return `${this.knex.ref(column)} is ${operator === '$ne' ? 'not ' : ''}null`;
297
- // }
298
- //
299
- // if (operator === '$fulltext' && prop) {
300
- // const query = this.knex.raw(this.platform.getFullTextWhereClause(prop), {
301
- // column,
302
- // query: this.knex.raw('?'),
303
- // }).toSQL().toNative();
304
- // params.push(value as Knex.Value);
305
- //
306
- // return query.sql;
307
- // }
308
- //
309
- // const replacement = this.getOperatorReplacement(operator, { [operator]: value });
310
- //
311
- // if (['$in', '$nin'].includes(operator) && Array.isArray(value)) {
312
- // params.push(...value as Knex.Value[]);
313
- // return `${this.knex.ref(column)} ${replacement} (${value.map(() => '?').join(', ')})`;
314
- // }
315
- //
316
- // if (operator === '$exists') {
317
- // value = null;
318
- // }
319
- //
320
- // if (rawField) {
321
- // let sql = rawField.sql.replaceAll(ALIAS_REPLACEMENT, alias);
322
- // params.push(...rawField.params as Knex.Value[]);
323
- // params.push(...Utils.asArray(value) as Knex.Value[]);
324
- //
325
- // if ((Utils.asArray(value) as Knex.Value[]).length > 0) {
326
- // sql += ' = ?';
327
- // }
328
- //
329
- // return sql;
330
- // }
331
- //
332
- // if (value !== null) {
333
- // if (prop?.customType) {
334
- // value = prop.customType.convertToDatabaseValue(value, this.platform, { fromQuery: true, key, mode: 'query' });
335
- // }
336
- //
337
- // params.push(value as Knex.Value);
338
- // }
339
- //
340
- // return `${this.knex.ref(column)} ${replacement} ${value === null ? 'null' : '?'}`;
341
- // }
342
- //
343
- // private wrapQueryGroup(parts: string[], operator = '$and') {
344
- // if (parts.length === 1) {
345
- // return parts[0];
346
- // }
347
- //
348
- // return `(${parts.join(` ${GroupOperator[operator as keyof typeof GroupOperator]} `)})`;
349
- // }
350
- //
351
- // mapJoinColumns(type: QueryType, join: JoinOptions): (string | Knex.Raw)[] {
352
- // =======
353
254
  mapJoinColumns(type, join) {
354
255
  if (join.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(join.prop.kind)) {
355
256
  return join.prop.fieldNames.map((_fieldName, idx) => {
package/typings.d.ts CHANGED
@@ -27,6 +27,7 @@ export interface JoinOptions {
27
27
  cond_?: Dictionary;
28
28
  subquery?: string;
29
29
  nested?: Set<JoinOptions>;
30
+ parent?: JoinOptions;
30
31
  }
31
32
  export interface Column {
32
33
  name: string;
@@ -185,7 +186,7 @@ export interface ICriteriaNode<T extends object> {
185
186
  shouldInline(payload: any): boolean;
186
187
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
187
188
  shouldRename(payload: any): boolean;
188
- renameFieldToPK<T>(qb: IQueryBuilder<T>): string;
189
+ renameFieldToPK<T>(qb: IQueryBuilder<T>, ownerAlias?: string): string;
189
190
  getPath(addIndex?: boolean): string;
190
191
  getPivotPath(path: string): string;
191
192
  }