@mikro-orm/knex 6.4.17-dev.65 → 6.4.17-dev.67

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.
@@ -758,80 +758,39 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
758
758
  }
759
759
  }
760
760
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options, pivotJoin) {
761
+ if (owners.length === 0) {
762
+ return {};
763
+ }
761
764
  const pivotMeta = this.metadata.find(prop.pivotEntity);
762
765
  const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
763
766
  const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
764
767
  const ownerMeta = this.metadata.find(pivotProp2.type);
765
- options = { ...options };
766
- const qb = this.createQueryBuilder(prop.pivotEntity, ctx, options.connectionType, undefined, options?.logging)
767
- .withSchema(this.getSchemaName(pivotMeta, options))
768
- .indexHint(options.indexHint)
769
- .comment(options.comments)
770
- .hintComment(options.hintComments);
771
- const pivotAlias = qb.alias;
772
- const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(core_1.Utils.PK_SEPARATOR);
773
768
  const cond = {
774
- [pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
769
+ [pivotProp2.name]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
775
770
  };
776
- /* istanbul ignore if */
777
- if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
778
- where = cond;
779
- }
780
- else {
781
- where = { ...where, ...cond };
782
- }
783
- orderBy = this.getPivotOrderBy(prop, pivotProp1, pivotAlias, orderBy);
784
- const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
785
- const fields = [];
786
- const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
787
- const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
788
- const cols = [
789
- ...prop[k1].map(col => `${pivotAlias}.${col} as fk__${col}`),
790
- ...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
791
- ];
792
- fields.push(...cols);
793
- if (!pivotJoin) {
794
- const targetAlias = qb.getNextAlias(prop.targetMeta.tableName);
795
- const targetSchema = this.getSchemaName(prop.targetMeta, options) ?? this.platform.getDefaultSchemaName();
796
- qb.innerJoin(pivotProp1.name, targetAlias, {}, targetSchema);
797
- const targetFields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, targetAlias, options);
798
- const additionalFields = [];
799
- for (const field of targetFields) {
800
- const f = field.toString();
801
- additionalFields.push(f.includes('.') ? field : `${targetAlias}.${f}`);
802
- if (core_1.RawQueryFragment.isKnownFragment(field)) {
803
- qb.rawFragments.add(f);
804
- }
805
- }
806
- fields.unshift(...additionalFields);
807
- // we need to handle 1:1 owner auto-joins explicitly, as the QB type is the pivot table, not the target
808
- populate.forEach(hint => {
809
- const alias = qb.getNextAlias(prop.targetMeta.tableName);
810
- qb.leftJoin(`${targetAlias}.${hint.field}`, alias);
811
- // eslint-disable-next-line dot-notation
812
- for (const join of Object.values(qb['_joins'])) {
813
- const [propName] = hint.field.split(':', 2);
814
- if (join.alias === alias && join.prop.name === propName) {
815
- fields.push(...qb.helper.mapJoinColumns(qb.type, join));
816
- }
817
- }
818
- });
819
- }
820
- qb.select(fields)
821
- .where({ [pivotProp1.name]: where })
822
- .orderBy(orderBy)
823
- .setLockMode(options.lockMode, options.lockTableAliases);
824
- if (owners.length === 1 && (options.offset != null || options.limit != null)) {
825
- qb.limit(options.limit, options.offset);
826
- }
827
- const res = owners.length ? await this.rethrow(qb.execute('all', { mergeResults: false, mapResults: false })) : [];
828
- const tmp = {};
829
- const items = res.map((row) => {
830
- const root = super.mapResult(row, prop.targetMeta);
831
- this.mapJoinedProps(root, prop.targetMeta, populate, qb, root, tmp, pivotMeta.className + '.' + pivotProp1.name);
832
- return root;
771
+ if (!core_1.Utils.isEmpty(where)) {
772
+ cond[pivotProp1.name] = { ...where };
773
+ }
774
+ where = cond;
775
+ const populateField = pivotJoin ? `${pivotProp1.name}:ref` : pivotProp1.name;
776
+ const populate = this.autoJoinOneToOneOwner(prop.targetMeta, options?.populate ?? [], options?.fields);
777
+ const childFields = !core_1.Utils.isEmpty(options?.fields) ? options.fields.map(f => `${pivotProp1.name}.${f}`) : [];
778
+ const childExclude = !core_1.Utils.isEmpty(options?.exclude) ? options.exclude.map(f => `${pivotProp1.name}.${f}`) : [];
779
+ const fields = pivotJoin
780
+ ? [pivotProp1.name, pivotProp2.name]
781
+ : [pivotProp1.name, pivotProp2.name, ...childFields];
782
+ const res = await this.find(pivotMeta.className, where, {
783
+ ctx,
784
+ ...options,
785
+ fields,
786
+ exclude: childExclude,
787
+ orderBy: this.getPivotOrderBy(prop, pivotProp1, orderBy, options?.orderBy),
788
+ populate: [{ field: populateField, strategy: core_1.LoadStrategy.JOINED, joinType: query_1.JoinType.innerJoin, children: populate }],
789
+ populateWhere: undefined,
790
+ // @ts-ignore
791
+ _populateWhere: 'infer',
792
+ populateFilter: !core_1.Utils.isEmpty(options?.populateFilter) ? { [pivotProp2.name]: options?.populateFilter } : undefined,
833
793
  });
834
- qb.clearRawFragmentsCache();
835
794
  const map = {};
836
795
  const pkProps = ownerMeta.getPrimaryProps();
837
796
  for (const owner of owners) {
@@ -841,29 +800,26 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
841
800
  }));
842
801
  map[key] = [];
843
802
  }
844
- for (const item of items) {
845
- const key = core_1.Utils.getPrimaryKeyHash(prop.joinColumns.map((col, idx) => {
846
- const pkProp = pkProps[idx];
847
- return pkProp.customType ? pkProp.customType.convertToJSValue(item[`fk__${col}`], this.platform) : item[`fk__${col}`];
848
- }));
849
- map[key].push(item);
850
- prop.joinColumns.forEach(col => delete item[`fk__${col}`]);
851
- prop.inverseJoinColumns.forEach((col, idx) => {
852
- core_1.Utils.renameKey(item, `fk__${col}`, prop.targetMeta.primaryKeys[idx]);
853
- });
803
+ for (const item of res) {
804
+ const key = core_1.Utils.getPrimaryKeyHash(core_1.Utils.asArray(item[pivotProp2.name]));
805
+ map[key].push(item[pivotProp1.name]);
854
806
  }
855
807
  return map;
856
808
  }
857
- getPivotOrderBy(prop, pivotProp, pivotAlias, orderBy) {
858
- // FIXME this is ignoring the rest of the array items
809
+ getPivotOrderBy(prop, pivotProp, orderBy, parentOrderBy) {
859
810
  if (!core_1.Utils.isEmpty(orderBy)) {
860
- return [{ [pivotProp.name]: core_1.Utils.asArray(orderBy)[0] }];
811
+ return core_1.Utils.asArray(orderBy).map(o => ({ [pivotProp.name]: o }));
812
+ }
813
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && core_1.Utils.asArray(parentOrderBy).some(o => o[prop.name])) {
814
+ return core_1.Utils.asArray(parentOrderBy)
815
+ .filter(o => o[prop.name])
816
+ .map(o => ({ [pivotProp.name]: o[prop.name] }));
861
817
  }
862
818
  if (!core_1.Utils.isEmpty(prop.orderBy)) {
863
- return [{ [pivotProp.name]: core_1.Utils.asArray(prop.orderBy)[0] }];
819
+ return core_1.Utils.asArray(prop.orderBy).map(o => ({ [pivotProp.name]: o }));
864
820
  }
865
821
  if (prop.fixedOrder) {
866
- return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC }];
822
+ return [{ [prop.fixedOrderColumn]: core_1.QueryOrder.ASC }];
867
823
  }
868
824
  return [];
869
825
  }
@@ -881,7 +837,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
881
837
  const toPopulate = meta.relations
882
838
  .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !prop.lazy && !relationsToPopulate.includes(prop.name))
883
839
  .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
884
- .map(prop => ({ field: `${prop.name}:ref`, strategy: prop.strategy }));
840
+ .map(prop => ({ field: `${prop.name}:ref`, strategy: prop.strategy ?? core_1.LoadStrategy.JOINED }));
885
841
  return [...populate, ...toPopulate];
886
842
  }
887
843
  /**
@@ -889,16 +845,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
889
845
  */
890
846
  joinedProps(meta, populate, options) {
891
847
  return populate.filter(hint => {
892
- const [propName, ref] = hint.field.split(':', 2);
848
+ const [propName] = hint.field.split(':', 2);
893
849
  const prop = meta.properties[propName] || {};
894
- if (hint.filter && hint.strategy === core_1.LoadStrategy.JOINED) {
850
+ const strategy = (0, core_1.getLoadingStrategy)(hint.strategy || options?.strategy || prop.strategy || this.config.get('loadStrategy'), prop.kind);
851
+ if (hint.filter && (0, core_1.getLoadingStrategy)(hint.strategy, prop.kind) === core_1.LoadStrategy.JOINED) {
895
852
  return true;
896
853
  }
897
854
  // skip redundant joins for 1:1 owner population hints when using `mapToPk`
898
855
  if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && prop.mapToPk && prop.owner) {
899
856
  return false;
900
857
  }
901
- if ((options?.strategy || hint.strategy || prop.strategy || this.config.get('loadStrategy')) !== core_1.LoadStrategy.JOINED) {
858
+ if (strategy !== core_1.LoadStrategy.JOINED) {
902
859
  // force joined strategy for explicit 1:1 owner populate hint as it would require a join anyway
903
860
  return prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
904
861
  }
@@ -967,7 +924,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
967
924
  const [propName, ref] = hint.field.split(':', 2);
968
925
  const prop = meta.properties[propName];
969
926
  // ignore ref joins of known FKs unless it's a filter hint
970
- if (ref && !hint.filter && (prop.kind === core_1.ReferenceKind.MANY_TO_ONE || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner))) {
927
+ if (ref && !hint.filter && (prop.kind === core_1.ReferenceKind.MANY_TO_ONE || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && prop.owner))) {
971
928
  continue;
972
929
  }
973
930
  if (count && (!options?.populateFilter || !options.populateFilter[prop.name])) {
@@ -983,9 +940,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
983
940
  }
984
941
  const joinType = pivotRefJoin
985
942
  ? query_1.JoinType.pivotJoin
986
- : hint.filter && !prop.nullable
987
- ? query_1.JoinType.innerJoin
988
- : query_1.JoinType.leftJoin;
943
+ : hint.joinType
944
+ ? hint.joinType
945
+ : hint.filter && !prop.nullable
946
+ ? query_1.JoinType.innerJoin
947
+ : query_1.JoinType.leftJoin;
989
948
  qb.join(field, tableAlias, {}, joinType, path);
990
949
  if (pivotRefJoin) {
991
950
  fields.push(...prop.joinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)), ...prop.inverseJoinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
@@ -1003,7 +962,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
1003
962
  if (!ref && !prop.mapToPk) {
1004
963
  fields.push(...this.getFieldsForJoinedLoad(qb, meta2, childExplicitFields.length === 0 ? undefined : childExplicitFields, childExclude, hint.children, options, tableAlias, path, count));
1005
964
  }
1006
- else if (hint.filter || prop.mapToPk) {
965
+ else if (hint.filter || prop.mapToPk || (ref && [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind))) {
1007
966
  fields.push(...prop.referencedColumnNames.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
1008
967
  }
1009
968
  }
@@ -1148,7 +1107,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
1148
1107
  let path = parentPath;
1149
1108
  const meta2 = this.metadata.find(prop.type);
1150
1109
  const childOrder = orderHint[prop.name];
1151
- if (![core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || core_1.Utils.isPlainObject(childOrder)) {
1110
+ if (prop.kind !== core_1.ReferenceKind.SCALAR && (![core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || core_1.Utils.isPlainObject(childOrder))) {
1152
1111
  path += `.${propName}`;
1153
1112
  }
1154
1113
  if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
@@ -1156,7 +1115,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
1156
1115
  }
1157
1116
  const join = qb.getJoinForPath(path, { matchPopulateJoins: true });
1158
1117
  const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true }) ?? parentAlias;
1159
- if (!join && parentAlias === qb.alias) {
1118
+ if (!join) {
1160
1119
  continue;
1161
1120
  }
1162
1121
  if (join && ![core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.EMBEDDED].includes(prop.kind) && typeof childOrder === 'object') {
package/index.mjs CHANGED
@@ -221,6 +221,8 @@ export const createSqlFunction = mod.createSqlFunction;
221
221
  export const defineConfig = mod.defineConfig;
222
222
  export const defineEntity = mod.defineEntity;
223
223
  export const equals = mod.equals;
224
+ export const expandDotPaths = mod.expandDotPaths;
225
+ export const getLoadingStrategy = mod.getLoadingStrategy;
224
226
  export const getOnConflictFields = mod.getOnConflictFields;
225
227
  export const getOnConflictReturningFields = mod.getOnConflictReturningFields;
226
228
  export const helper = mod.helper;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.4.17-dev.65",
3
+ "version": "6.4.17-dev.67",
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.4.16"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.4.17-dev.65",
69
+ "@mikro-orm/core": "6.4.17-dev.67",
70
70
  "better-sqlite3": "*",
71
71
  "libsql": "*",
72
72
  "mariadb": "*"
@@ -69,7 +69,7 @@ class CriteriaNode {
69
69
  }
70
70
  }
71
71
  renameFieldToPK(qb) {
72
- let joinAlias = qb.getAliasForJoinPath(this.getPath());
72
+ let joinAlias = qb.getAliasForJoinPath(this.getPath(), { matchPopulateJoins: true });
73
73
  if (!joinAlias && this.parent && [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(this.prop.kind) && this.prop.owner) {
74
74
  joinAlias = qb.getAliasForJoinPath(this.parent.getPath());
75
75
  return core_1.Utils.getPrimaryKeyHash(this.prop.joinColumns.map(col => `${joinAlias ?? qb.alias}.${col}`));
@@ -9,7 +9,8 @@ const enums_1 = require("./enums");
9
9
  */
10
10
  class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
11
11
  process(qb, options) {
12
- const nestedAlias = qb.getAliasForJoinPath(this.getPath(), options);
12
+ const matchPopulateJoins = options?.matchPopulateJoins || (this.prop && [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
13
+ const nestedAlias = qb.getAliasForJoinPath(this.getPath(), { ...options, matchPopulateJoins });
13
14
  const ownerAlias = options?.alias || qb.alias;
14
15
  const keys = Object.keys(this.payload);
15
16
  let alias = options?.alias;
@@ -1,10 +1,10 @@
1
1
  import { CriteriaNode } from './CriteriaNode';
2
- import type { IQueryBuilder, ICriteriaNodeProcessOptions } from '../typings';
2
+ import type { ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings';
3
3
  /**
4
4
  * @internal
5
5
  */
6
6
  export declare class ScalarCriteriaNode<T extends object> extends CriteriaNode<T> {
7
7
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
8
- willAutoJoin<T>(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
9
- shouldJoin(): boolean;
8
+ willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
9
+ private shouldJoin;
10
10
  }
@@ -9,7 +9,9 @@ const enums_1 = require("./enums");
9
9
  */
10
10
  class ScalarCriteriaNode extends CriteriaNode_1.CriteriaNode {
11
11
  process(qb, options) {
12
- if (this.shouldJoin()) {
12
+ const matchPopulateJoins = options?.matchPopulateJoins || (this.prop && [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
13
+ const nestedAlias = qb.getAliasForJoinPath(this.getPath(), { ...options, matchPopulateJoins });
14
+ if (this.shouldJoin(qb, nestedAlias)) {
13
15
  const path = this.getPath();
14
16
  const parentPath = this.parent.getPath(); // the parent is always there, otherwise `shouldJoin` would return `false`
15
17
  const nestedAlias = qb.getAliasForJoinPath(path) || qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
@@ -30,10 +32,10 @@ class ScalarCriteriaNode extends CriteriaNode_1.CriteriaNode {
30
32
  return this.payload;
31
33
  }
32
34
  willAutoJoin(qb, alias, options) {
33
- return this.shouldJoin();
35
+ return this.shouldJoin(qb, alias);
34
36
  }
35
- shouldJoin() {
36
- if (!this.parent || !this.prop) {
37
+ shouldJoin(qb, nestedAlias) {
38
+ if (!this.parent || !this.prop || (nestedAlias && [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(qb.type ?? enums_1.QueryType.SELECT))) {
37
39
  return false;
38
40
  }
39
41
  switch (this.prop.kind) {