@mikro-orm/knex 6.0.0-dev.200 → 6.0.0-dev.202

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.
@@ -15,6 +15,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
15
15
  createEntityManager<D extends IDatabaseDriver = IDatabaseDriver>(useContext?: boolean): D[typeof EntityManagerType];
16
16
  find<T extends object, P extends string = never, F extends string = '*'>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P, F>): Promise<EntityData<T>[]>;
17
17
  findOne<T extends object, P extends string = never, F extends string = '*'>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P, F>): Promise<EntityData<T> | null>;
18
+ protected hasToManyJoins<T extends object>(hint: PopulateOptions<T>, meta: EntityMetadata<T>): boolean;
18
19
  findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any, any>): Promise<EntityData<T>[]>;
19
20
  countVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: CountOptions<T, any>): Promise<number>;
20
21
  protected findFromVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any> | CountOptions<T, any>, type: QueryType): Promise<EntityData<T>[] | number>;
@@ -74,11 +74,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
74
74
  const meta = this.metadata.find(entityName);
75
75
  const populate = this.autoJoinOneToOneOwner(meta, opts.populate, opts.fields);
76
76
  const joinedProps = this.joinedProps(meta, populate, options);
77
- const hasToManyJoins = joinedProps.some(hint => {
78
- const [propName] = hint.field.split(':', 2);
79
- const prop = meta.properties[propName];
80
- return prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind);
81
- });
77
+ const hasToManyJoins = joinedProps.some(hint => this.hasToManyJoins(hint, meta));
82
78
  if (joinedProps.length === 0 || !hasToManyJoins) {
83
79
  opts.limit = 1;
84
80
  }
@@ -89,6 +85,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
89
85
  const res = await this.find(entityName, where, opts);
90
86
  return res[0] || null;
91
87
  }
88
+ hasToManyJoins(hint, meta) {
89
+ const [propName] = hint.field.split(':', 2);
90
+ const prop = meta.properties[propName];
91
+ if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
92
+ return true;
93
+ }
94
+ if (hint.children && prop.targetMeta) {
95
+ return hint.children.some(hint => this.hasToManyJoins(hint, prop.targetMeta));
96
+ }
97
+ return false;
98
+ }
92
99
  async findVirtual(entityName, where, options) {
93
100
  return this.findFromVirtual(entityName, where, options, query_1.QueryType.SELECT);
94
101
  }
@@ -161,16 +168,16 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
161
168
  }
162
169
  mapJoinedProps(result, meta, populate, qb, root, map, parentJoinPath) {
163
170
  const joinedProps = this.joinedProps(meta, populate);
164
- joinedProps.forEach(p => {
165
- const [propName, ref] = p.field.split(':', 2);
166
- const relation = meta.properties[propName];
171
+ joinedProps.forEach(hint => {
172
+ const [propName, ref] = hint.field.split(':', 2);
173
+ const prop = meta.properties[propName];
167
174
  /* istanbul ignore next */
168
- if (!relation) {
175
+ if (!prop) {
169
176
  return;
170
177
  }
171
- const pivotRefJoin = relation.kind === core_1.ReferenceKind.MANY_TO_MANY && ref;
172
- const meta2 = this.metadata.find(relation.type);
173
- let path = parentJoinPath ? `${parentJoinPath}.${relation.name}` : `${meta.name}.${relation.name}`;
178
+ const pivotRefJoin = prop.kind === core_1.ReferenceKind.MANY_TO_MANY && ref;
179
+ const meta2 = this.metadata.find(prop.type);
180
+ let path = parentJoinPath ? `${parentJoinPath}.${prop.name}` : `${meta.name}.${prop.name}`;
174
181
  if (!parentJoinPath) {
175
182
  path = '[populate]' + path;
176
183
  }
@@ -181,18 +188,18 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
181
188
  // pivot ref joins via joined strategy need to be handled separately here, as they dont join the target entity
182
189
  if (pivotRefJoin) {
183
190
  let item;
184
- if (relation.inverseJoinColumns.length > 1) { // composite keys
185
- item = relation.inverseJoinColumns.map(name => root[`${relationAlias}__${name}`]);
191
+ if (prop.inverseJoinColumns.length > 1) { // composite keys
192
+ item = prop.inverseJoinColumns.map(name => root[`${relationAlias}__${name}`]);
186
193
  }
187
194
  else {
188
- const alias = `${relationAlias}__${relation.inverseJoinColumns[0]}`;
195
+ const alias = `${relationAlias}__${prop.inverseJoinColumns[0]}`;
189
196
  item = root[alias];
190
197
  }
191
- relation.joinColumns.forEach(name => delete root[`${relationAlias}__${name}`]);
192
- relation.inverseJoinColumns.forEach(name => delete root[`${relationAlias}__${name}`]);
193
- result[relation.name] ??= [];
198
+ prop.joinColumns.forEach(name => delete root[`${relationAlias}__${name}`]);
199
+ prop.inverseJoinColumns.forEach(name => delete root[`${relationAlias}__${name}`]);
200
+ result[prop.name] ??= [];
194
201
  if (item) {
195
- result[relation.name].push(item);
202
+ result[prop.name].push(item);
196
203
  }
197
204
  return;
198
205
  }
@@ -202,18 +209,20 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
202
209
  return root[`${relationAlias}__${name}`] != null;
203
210
  }));
204
211
  if (!hasPK) {
205
- if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
206
- result[relation.name] ??= [];
212
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(prop.kind)) {
213
+ result[prop.name] ??= [];
207
214
  }
208
- if ([core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(relation.kind)) {
209
- result[relation.name] ??= null;
215
+ if ([core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
216
+ result[prop.name] ??= null;
210
217
  }
211
218
  return;
212
219
  }
213
220
  const relationPojo = {};
214
221
  meta2.props
215
222
  .filter(prop => prop.persist === false && prop.fieldNames)
223
+ .filter(prop => !prop.lazy || populate.some(p => p.field === prop.name || p.all))
216
224
  .forEach(prop => {
225
+ /* istanbul ignore if */
217
226
  if (prop.fieldNames.length > 1) { // composite keys
218
227
  relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
219
228
  }
@@ -223,11 +232,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
223
232
  }
224
233
  });
225
234
  meta2.props
226
- .filter(prop => this.platform.shouldHaveColumn(prop, p.children || []))
235
+ .filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []))
227
236
  .forEach(prop => {
228
237
  if (prop.fieldNames.length > 1) { // composite keys
229
- relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
238
+ const fk = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
230
239
  prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
240
+ relationPojo[prop.name] = core_1.Utils.mapFlatCompositePrimaryKey(fk, prop);
231
241
  }
232
242
  else if (prop.runtimeType === 'Date') {
233
243
  const alias = `${relationAlias}__${prop.fieldNames[0]}`;
@@ -240,14 +250,14 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
240
250
  delete root[alias];
241
251
  }
242
252
  });
243
- if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
244
- result[relation.name] ??= [];
245
- result[relation.name].push(relationPojo);
253
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(prop.kind)) {
254
+ result[prop.name] ??= [];
255
+ result[prop.name].push(relationPojo);
246
256
  }
247
257
  else {
248
- result[relation.name] = relationPojo;
258
+ result[prop.name] = relationPojo;
249
259
  }
250
- const populateChildren = p.children || [];
260
+ const populateChildren = hint.children || [];
251
261
  this.mapJoinedProps(relationPojo, meta2, populateChildren, qb, root, map, path);
252
262
  });
253
263
  }
@@ -744,7 +754,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
744
754
  return populate.filter(hint => {
745
755
  const [propName, ref] = hint.field.split(':', 2);
746
756
  const prop = meta.properties[propName] || {};
747
- return (options?.strategy || hint.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && ![core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.EMBEDDED].includes(prop.kind);
757
+ if (ref) {
758
+ // keep only pivot ref joins here, as that only triggers actual join
759
+ return prop.kind === core_1.ReferenceKind.MANY_TO_MANY;
760
+ }
761
+ return (prop.strategy || options?.strategy || hint.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && ![core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.EMBEDDED].includes(prop.kind);
748
762
  });
749
763
  }
750
764
  /**
@@ -842,7 +856,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
842
856
  mapPropToFieldNames(qb, prop, tableAlias) {
843
857
  const knex = this.connection.getKnex();
844
858
  const aliased = knex.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
845
- if (prop.customType?.convertToJSValueSQL && tableAlias) {
859
+ if (tableAlias && prop.customTypes?.some(type => type.convertToJSValueSQL)) {
860
+ return prop.fieldNames.map((col, idx) => {
861
+ if (!prop.customTypes[idx].convertToJSValueSQL) {
862
+ return col;
863
+ }
864
+ const prefixed = knex.ref(col).withSchema(tableAlias).toString();
865
+ const aliased = knex.ref(`${tableAlias}__${col}`).toString();
866
+ return (0, core_1.raw)(`${prop.customTypes[idx].convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`);
867
+ });
868
+ }
869
+ if (tableAlias && prop.customType?.convertToJSValueSQL) {
846
870
  const prefixed = knex.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
847
871
  return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
848
872
  }
@@ -973,7 +997,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
973
997
  const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true });
974
998
  const meta2 = this.metadata.find(prop.type);
975
999
  if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && prop.fixedOrder && join) {
976
- orderBy.push({ [`${(join.ownerAlias)}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC });
1000
+ const alias = ref ? propAlias : join.ownerAlias;
1001
+ orderBy.push({ [`${alias}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC });
977
1002
  }
978
1003
  if (propOrderBy) {
979
1004
  core_1.Utils.keys(propOrderBy).forEach(field => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.0.0-dev.200",
3
+ "version": "6.0.0-dev.202",
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.9.2"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.0.0-dev.200"
69
+ "@mikro-orm/core": "6.0.0-dev.202"
70
70
  }
71
71
  }