@mikro-orm/knex 6.0.0-dev.110 → 6.0.0-dev.112

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.
@@ -29,7 +29,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
29
29
  nativeUpdateMany<T extends object>(entityName: string, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T> & UpsertManyOptions<T>): Promise<QueryResult<T>>;
30
30
  nativeDelete<T extends object>(entityName: string, where: FilterQuery<T> | string | any, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
31
31
  syncCollection<T extends object, O extends object>(coll: Collection<T, O>, options?: DriverMethodOptions): Promise<void>;
32
- loadFromPivotTable<T extends object, O extends object>(prop: EntityProperty, owners: Primary<O>[][], where?: FilterQuery<any>, orderBy?: OrderDefinition<T>, ctx?: Transaction, options?: FindOptions<T, any, any>): Promise<Dictionary<T[]>>;
32
+ loadFromPivotTable<T extends object, O extends object>(prop: EntityProperty, owners: Primary<O>[][], where?: FilterQuery<any>, orderBy?: OrderDefinition<T>, ctx?: Transaction, options?: FindOptions<T, any, any>, pivotJoin?: boolean): Promise<Dictionary<T[]>>;
33
33
  private getPivotOrderBy;
34
34
  execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(queryOrKnex: string | Knex.QueryBuilder | Knex.Raw, params?: any[], method?: 'all' | 'get' | 'run', ctx?: Transaction): Promise<T>;
35
35
  /**
@@ -61,5 +61,5 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
61
61
  protected buildJoinedPropsOrderBy<T extends object>(entityName: string, qb: QueryBuilder<T>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], parentPath?: string): QueryOrderMap<T>[];
62
62
  protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
63
63
  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>): void;
64
- protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>, fields?: Field<T>[]): Field<T>[];
64
+ protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>, alias: string, fields?: Field<T>[]): Field<T>[];
65
65
  }
@@ -30,7 +30,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
30
30
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
31
31
  const joinedProps = this.joinedProps(meta, populate);
32
32
  const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
33
- const fields = this.buildFields(meta, populate, joinedProps, qb, options.fields);
33
+ const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options.fields);
34
34
  const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(entityName, qb, meta, joinedProps);
35
35
  const orderBy = [...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy];
36
36
  if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
@@ -290,7 +290,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
290
290
  async nativeInsertMany(entityName, data, options = {}) {
291
291
  options.processCollections ??= true;
292
292
  options.convertCustomTypes ??= true;
293
- const meta = this.metadata.find(entityName);
293
+ const meta = this.metadata.find(entityName)?.root;
294
294
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
295
295
  const pks = this.getPrimaryKeyFields(entityName);
296
296
  const set = new Set();
@@ -322,24 +322,31 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
322
322
  if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
323
323
  sql += data.map(row => {
324
324
  const keys = [];
325
+ const usedDups = [];
325
326
  props.forEach(prop => {
326
327
  if (prop.fieldNames.length > 1) {
327
328
  const param = core_1.Utils.flatten([...row[prop.name] ?? prop.fieldNames.map(() => null)]);
328
329
  const key = param.map(() => '?');
329
330
  prop.fieldNames.forEach((field, idx) => {
330
- if (!duplicates.includes(field)) {
331
+ if (!duplicates.includes(field) || !usedDups.includes(field)) {
331
332
  params.push(param[idx]);
332
333
  keys.push(key[idx]);
334
+ usedDups.push(field);
333
335
  }
334
336
  });
335
337
  }
336
- else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
337
- keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
338
- addParams(prop, row);
339
- }
340
338
  else {
341
- addParams(prop, row);
342
- keys.push('?');
339
+ const field = prop.fieldNames[0];
340
+ if (!duplicates.includes(field) || !usedDups.includes(field)) {
341
+ if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
342
+ keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
343
+ }
344
+ else {
345
+ keys.push('?');
346
+ }
347
+ addParams(prop, row);
348
+ usedDups.push(field);
349
+ }
343
350
  }
344
351
  });
345
352
  return '(' + (keys.join(', ') || 'default') + ')';
@@ -568,18 +575,18 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
568
575
  }
569
576
  return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
570
577
  }
571
- async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
572
- const pivotProp2 = this.getPivotInverseProperty(prop);
573
- const prop2 = prop.targetMeta.properties[prop.mappedBy ?? prop.inversedBy];
574
- const ownerMeta = this.metadata.find(pivotProp2.type);
578
+ async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options, pivotJoin) {
575
579
  const pivotMeta = this.metadata.find(prop.pivotEntity);
580
+ const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
581
+ const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
582
+ const ownerMeta = this.metadata.find(pivotProp2.type);
576
583
  options = { ...options };
577
- const qb = this.createQueryBuilder(prop.type, ctx, options.connectionType)
578
- .withSchema(this.getSchemaName(prop.targetMeta, options))
584
+ const qb = this.createQueryBuilder(prop.pivotEntity, ctx, options.connectionType)
585
+ .withSchema(this.getSchemaName(pivotMeta, options))
579
586
  .indexHint(options.indexHint)
580
587
  .comment(options.comments)
581
588
  .hintComment(options.hintComments);
582
- const pivotAlias = qb.getNextAlias(pivotMeta.tableName);
589
+ const pivotAlias = qb.alias;
583
590
  const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(core_1.Utils.PK_SEPARATOR);
584
591
  const cond = {
585
592
  [pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
@@ -591,9 +598,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
591
598
  else {
592
599
  where = { ...where, ...cond };
593
600
  }
594
- orderBy = this.getPivotOrderBy(prop, pivotAlias, orderBy);
601
+ orderBy = this.getPivotOrderBy(prop, pivotProp1, pivotAlias, orderBy);
595
602
  const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
596
- const fields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, options.fields);
603
+ const fields = [];
597
604
  const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
598
605
  const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
599
606
  const cols = [
@@ -601,25 +608,36 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
601
608
  ...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
602
609
  ];
603
610
  fields.push(...cols);
611
+ if (!pivotJoin) {
612
+ const targetAlias = qb.getNextAlias(prop.targetMeta.tableName);
613
+ const targetSchema = this.getSchemaName(prop.targetMeta, options) ?? this.platform.getDefaultSchemaName();
614
+ qb.innerJoin(pivotProp1.name, targetAlias, {}, targetSchema);
615
+ const targetFields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, targetAlias, options.fields);
616
+ for (const field of targetFields) {
617
+ const f = field.toString();
618
+ fields.unshift(f.includes('.') ? field : `${targetAlias}.${f}`);
619
+ }
620
+ // we need to handle 1:1 owner auto-joins explicitly, as the QB type is the pivot table, not the target
621
+ populate.forEach(hint => {
622
+ const alias = qb.getNextAlias(prop.targetMeta.tableName);
623
+ qb.leftJoin(`${targetAlias}.${hint.field}`, alias);
624
+ // eslint-disable-next-line dot-notation
625
+ Object.values(qb['_joins']).forEach(join => {
626
+ if (join.alias === alias && join.prop.name === hint.field) {
627
+ fields.push(...qb.helper.mapJoinColumns(qb.type, join));
628
+ }
629
+ });
630
+ });
631
+ }
604
632
  qb.select(fields)
605
- .join(prop2.name, pivotAlias, {}, 'pivotJoin', undefined, options.schema)
606
- .populate(populate)
607
- .where(where)
633
+ .where({ [pivotProp1.name]: where })
608
634
  .orderBy(orderBy)
609
635
  .setLockMode(options.lockMode, options.lockTableAliases);
610
636
  if (owners.length === 1 && (options.offset != null || options.limit != null)) {
611
637
  qb.limit(options.limit, options.offset);
612
638
  }
613
- const schema = this.config.get('schema');
614
- if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options.schema) {
615
- // eslint-disable-next-line dot-notation
616
- qb['finalize']();
617
- // eslint-disable-next-line dot-notation
618
- Object.values(qb['_joins']).forEach(join => {
619
- join.schema = schema;
620
- });
621
- }
622
- const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
639
+ const res = owners.length ? await this.rethrow(qb.execute('all', false)) : [];
640
+ const items = res.map((row) => super.mapResult(row, prop.targetMeta));
623
641
  const map = {};
624
642
  const pkProps = ownerMeta.getPrimaryProps();
625
643
  owners.forEach(owner => {
@@ -636,16 +654,19 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
636
654
  }));
637
655
  map[key].push(item);
638
656
  prop.joinColumns.forEach(col => delete item[`fk__${col}`]);
639
- prop.inverseJoinColumns.forEach(col => delete item[`fk__${col}`]);
657
+ prop.inverseJoinColumns.forEach((col, idx) => {
658
+ core_1.Utils.renameKey(item, `fk__${col}`, prop.targetMeta.primaryKeys[idx]);
659
+ });
640
660
  });
641
661
  return map;
642
662
  }
643
- getPivotOrderBy(prop, pivotAlias, orderBy) {
663
+ getPivotOrderBy(prop, pivotProp, pivotAlias, orderBy) {
664
+ // FIXME this is ignoring the rest of the array items
644
665
  if (!core_1.Utils.isEmpty(orderBy)) {
645
- return orderBy;
666
+ return [{ [pivotProp.name]: core_1.Utils.asArray(orderBy)[0] }];
646
667
  }
647
668
  if (!core_1.Utils.isEmpty(prop.orderBy)) {
648
- return core_1.Utils.asArray(prop.orderBy);
669
+ return [{ [pivotProp.name]: core_1.Utils.asArray(prop.orderBy)[0] }];
649
670
  }
650
671
  if (prop.fixedOrder) {
651
672
  return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC }];
@@ -721,7 +742,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
721
742
  .filter(prop => shouldHaveColumn(prop, populate, explicitFields))
722
743
  .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, parentTableAlias)));
723
744
  joinedProps.forEach(relation => {
724
- const prop = meta.properties[relation.field];
745
+ const [propName] = relation.field.split(':', 2);
746
+ const prop = meta.properties[propName];
725
747
  const meta2 = this.metadata.find(prop.type);
726
748
  const tableAlias = qb.getNextAlias(prop.name);
727
749
  const field = parentTableAlias ? `${parentTableAlias}.${prop.name}` : prop.name;
@@ -851,7 +873,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
851
873
  const orderBy = [];
852
874
  const joinedProps = this.joinedProps(meta, populate);
853
875
  joinedProps.forEach(relation => {
854
- const prop = meta.properties[relation.field];
876
+ const [propName] = relation.field.split(':', 2);
877
+ const prop = meta.properties[propName];
855
878
  const propOrderBy = prop.orderBy;
856
879
  const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
857
880
  const propAlias = qb.getAliasForJoinPath(path);
@@ -902,7 +925,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
902
925
  }
903
926
  ret.push(prop.name);
904
927
  }
905
- buildFields(meta, populate, joinedProps, qb, fields) {
928
+ buildFields(meta, populate, joinedProps, qb, alias, fields) {
906
929
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
907
930
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
908
931
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
@@ -944,9 +967,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
944
967
  meta.props
945
968
  .filter(prop => prop.formula && !lazyProps.includes(prop))
946
969
  .forEach(prop => {
947
- const alias = this.connection.getKnex().ref(qb.alias).toString();
970
+ const a = this.connection.getKnex().ref(alias).toString();
948
971
  const aliased = this.connection.getKnex().ref(prop.fieldNames[0]).toString();
949
- ret.push((0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`));
972
+ ret.push((0, core_1.raw)(`${prop.formula(a)} as ${aliased}`));
950
973
  });
951
974
  meta.props
952
975
  .filter(prop => prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.0.0-dev.110",
3
+ "version": "6.0.0-dev.112",
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,6 +66,6 @@
66
66
  "@mikro-orm/core": "^5.8.5"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.0.0-dev.110"
69
+ "@mikro-orm/core": "6.0.0-dev.112"
70
70
  }
71
71
  }
@@ -274,6 +274,7 @@ class QueryBuilder {
274
274
  aliasMap: this.getAliasMap(),
275
275
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
276
276
  convertCustomTypes: false,
277
+ type: 'orderBy',
277
278
  });
278
279
  this._orderBy.push(CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processed).process(this));
279
280
  });
@@ -26,7 +26,7 @@ export declare class QueryBuilderHelper {
26
26
  private processJoinClause;
27
27
  private wrapQueryGroup;
28
28
  mapJoinColumns(type: QueryType, join: JoinOptions): (string | Knex.Raw)[];
29
- isOneToOneInverse(field: string): boolean;
29
+ isOneToOneInverse(field: string, meta?: EntityMetadata): boolean;
30
30
  getTableName(entityName: string): string;
31
31
  /**
32
32
  * Checks whether the RE can be rewritten to simple LIKE query
@@ -157,6 +157,8 @@ class QueryBuilderHelper {
157
157
  const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
158
158
  ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, cond, schema);
159
159
  ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
160
+ const tmp = prop2.referencedTableName.split('.');
161
+ ret[`${pivotAlias}.${prop2.name}#${alias}`].schema ??= tmp.length > 1 ? tmp[0] : undefined;
160
162
  return ret;
161
163
  }
162
164
  processJoins(qb, joins, schema) {
@@ -177,7 +179,7 @@ class QueryBuilderHelper {
177
179
  conditions.push(`${left} = ${this.knex.ref(right)}`);
178
180
  return;
179
181
  }
180
- const left = join.prop.kind === core_1.ReferenceKind.SCALAR && join.prop.persist === false ? primaryKey : `${join.ownerAlias}.${primaryKey}`;
182
+ const left = `${join.ownerAlias}.${primaryKey}`;
181
183
  conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
182
184
  });
183
185
  }
@@ -282,8 +284,8 @@ class QueryBuilderHelper {
282
284
  ...join.inverseJoinColumns.map(col => this.mapper(`${join.alias}.${col}`, type, undefined, `fk__${col}`)),
283
285
  ];
284
286
  }
285
- isOneToOneInverse(field) {
286
- const meta = this.metadata.find(this.entityName);
287
+ isOneToOneInverse(field, meta) {
288
+ meta ??= this.metadata.find(this.entityName);
287
289
  const prop = meta.properties[field];
288
290
  return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
289
291
  }
@@ -474,7 +476,12 @@ class QueryBuilderHelper {
474
476
  if (core_1.Utils.isRawSql(colPart)) {
475
477
  colPart = this.platform.formatQuery(colPart.sql, colPart.params);
476
478
  }
477
- ret.push(`${colPart} ${order.toLowerCase()}`);
479
+ if (Array.isArray(order)) {
480
+ order.forEach(part => ret.push(this.getQueryOrderFromObject(type, part, populate)));
481
+ }
482
+ else {
483
+ ret.push(`${colPart} ${order.toLowerCase()}`);
484
+ }
478
485
  });
479
486
  });
480
487
  return ret.join(', ');
@@ -33,7 +33,7 @@ class DatabaseTable {
33
33
  delete this.columns[name];
34
34
  }
35
35
  getIndexes() {
36
- return this.indexes;
36
+ return core_1.Utils.removeDuplicates(this.indexes);
37
37
  }
38
38
  getChecks() {
39
39
  return this.checks;