@mikro-orm/sql 7.3.0-dev.1 → 7.3.0-dev.2

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/sql",
3
- "version": "7.3.0-dev.1",
3
+ "version": "7.3.0-dev.2",
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
  "keywords": [
6
6
  "data-mapper",
@@ -53,7 +53,7 @@
53
53
  "@mikro-orm/core": "^7.2.1"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.3.0-dev.1"
56
+ "@mikro-orm/core": "7.3.0-dev.2"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -17,6 +17,8 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
17
17
  index?: number;
18
18
  constructor(metadata: MetadataStorage, entityName: EntityName<T>, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | RawQueryFragmentSymbol | undefined, validate?: boolean, strict?: boolean);
19
19
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
20
+ /** Whether this node holds the condition for a single target of a polymorphic relation (e.g. `imageable[Article]`). */
21
+ isPolymorphicBranch(): boolean;
20
22
  unwrap(): any;
21
23
  shouldInline(payload: any): boolean;
22
24
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
@@ -1,4 +1,4 @@
1
- import { RawQueryFragment, ReferenceKind, Utils, inspect, } from '@mikro-orm/core';
1
+ import { QueryHelper, RawQueryFragment, ReferenceKind, Utils, inspect, } from '@mikro-orm/core';
2
2
  /**
3
3
  * Helper for working with deeply nested where/orderBy/having criteria. Uses composite pattern to build tree from the payload.
4
4
  * Auto-joins relations and converts payload from { books: { publisher: { name: '...' } } } to { 'publisher_alias.name': '...' }
@@ -28,7 +28,9 @@ export class CriteriaNode {
28
28
  return;
29
29
  }
30
30
  for (const k of pks) {
31
- this.prop = meta.props.find(prop => prop.name === k || (prop.fieldNames?.length === 1 && prop.fieldNames[0] === k && prop.persist !== false));
31
+ const [name, target] = QueryHelper.splitPolymorphicKey(k);
32
+ this.prop = meta.props.find(prop => (prop.name === name && (!target || !!prop.polymorphTargets?.some(t => t.className === target))) ||
33
+ (prop.fieldNames?.length === 1 && prop.fieldNames[0] === k && prop.persist !== false));
32
34
  const isProp = this.prop || meta.props.find(prop => (prop.fieldNames || []).includes(k));
33
35
  // do not validate if the key is prefixed or type casted (e.g. `k::text`)
34
36
  if (validate && !isProp && !k.includes('.') && !k.includes('::') && !Utils.isOperator(k)) {
@@ -40,6 +42,10 @@ export class CriteriaNode {
40
42
  process(qb, options) {
41
43
  return this.payload;
42
44
  }
45
+ /** Whether this node holds the condition for a single target of a polymorphic relation (e.g. `imageable[Article]`). */
46
+ isPolymorphicBranch() {
47
+ return !!this.prop?.polymorphic && typeof this.key === 'string' && this.key.endsWith(']');
48
+ }
43
49
  unwrap() {
44
50
  return this.payload;
45
51
  }
@@ -1,4 +1,4 @@
1
- import { GroupOperator, isRaw, JsonType, RawQueryFragment, ReferenceKind, Utils, ValidationError, } from '@mikro-orm/core';
1
+ import { GroupOperator, isRaw, JsonType, QueryHelper, RawQueryFragment, ReferenceKind, Utils, ValidationError, } from '@mikro-orm/core';
2
2
  import { ObjectCriteriaNode } from './ObjectCriteriaNode.js';
3
3
  import { ArrayCriteriaNode } from './ArrayCriteriaNode.js';
4
4
  import { ScalarCriteriaNode } from './ScalarCriteriaNode.js';
@@ -50,8 +50,9 @@ export class CriteriaNodeFactory {
50
50
  }
51
51
  static createObjectItemNode(metadata, entityName, node, payload, key, meta, validate = true) {
52
52
  const rawField = RawQueryFragment.isKnownFragmentSymbol(key);
53
- const prop = rawField ? null : meta?.properties[key];
54
- const childEntity = prop && prop.kind !== ReferenceKind.SCALAR ? prop.targetMeta.class : entityName;
53
+ const prop = rawField ? null : meta?.properties[QueryHelper.splitPolymorphicKey(key)[0]];
54
+ const target = rawField ? undefined : QueryHelper.findTargetMeta(prop, key);
55
+ const childEntity = prop && prop.kind !== ReferenceKind.SCALAR ? (target ?? prop.targetMeta).class : entityName;
55
56
  const isNotEmbedded = rawField || prop?.kind !== ReferenceKind.EMBEDDED;
56
57
  const val = payload[key];
57
58
  if (isNotEmbedded && prop?.customType instanceof JsonType) {
@@ -99,7 +99,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
99
99
  // with an exclusive condition on the join columns:
100
100
  // - if the owning column is null, the row is missing, we don't apply the filter
101
101
  // - if the target column is not null, the row is matched, we apply the filter
102
- if (toOneProperty && this.prop.nullable && this.isStrict()) {
102
+ if (toOneProperty && this.prop.nullable && this.isStrict() && !this.isPolymorphicBranch()) {
103
103
  const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
104
104
  qb.andWhere({
105
105
  $or: [
@@ -109,7 +109,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
109
109
  });
110
110
  }
111
111
  }
112
- return keys.reduce((o, field) => {
112
+ const ret = keys.reduce((o, field) => {
113
113
  const childNode = this.payload[field];
114
114
  const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
115
115
  const operator = Utils.isOperator(field);
@@ -149,6 +149,12 @@ export class ObjectCriteriaNode extends CriteriaNode {
149
149
  }
150
150
  return o;
151
151
  }, {});
152
+ // the target is left joined, so its condition must only match rows that point to it
153
+ if (this.isPolymorphicBranch() && alias) {
154
+ const pk = this.metadata.find(this.entityName).primaryKeys[0];
155
+ this.inlineCondition(`${alias}.${pk}`, ret, { $ne: null });
156
+ }
157
+ return ret;
152
158
  }
153
159
  isStrict() {
154
160
  return (this.strict ||
@@ -211,7 +217,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
211
217
  this.inlineCondition(key, obj, value);
212
218
  }
213
219
  inlineChildPayload(o, payload, field, alias, childAlias) {
214
- const prop = this.metadata.find(this.entityName).properties[field];
220
+ const prop = this.payload[field].prop;
215
221
  for (const k of Utils.getObjectQueryKeys(payload)) {
216
222
  if (RawQueryFragment.isKnownFragmentSymbol(k)) {
217
223
  o[k] = payload[k];
@@ -273,6 +279,10 @@ export class ObjectCriteriaNode extends CriteriaNode {
273
279
  if (keys.some(k => this.isCollectionOperator(k))) {
274
280
  return true;
275
281
  }
282
+ // the target's discriminator is checked in the join condition, so it is needed even for PK conditions
283
+ if (this.isPolymorphicBranch()) {
284
+ return !nestedAlias;
285
+ }
276
286
  const meta = this.metadata.find(this.entityName);
277
287
  const embeddable = this.prop.kind === ReferenceKind.EMBEDDED;
278
288
  const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
@@ -317,6 +327,10 @@ export class ObjectCriteriaNode extends CriteriaNode {
317
327
  if (this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator)) {
318
328
  qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
319
329
  }
330
+ else if (this.isPolymorphicBranch()) {
331
+ const targetMeta = this.metadata.find(this.entityName);
332
+ qb.addPolymorphicJoin(this.prop, targetMeta, alias, nestedAlias, JoinType.leftJoin, path);
333
+ }
320
334
  else {
321
335
  const prev = qb.state.fields?.slice();
322
336
  const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
package/typings.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Generated, Kysely } from 'kysely';
2
- import type { CheckCallback, DeferMode, Dictionary, EntityName, EntityProperty, EntitySchemaWithMeta, ExtractDefineEntityProperties, FilterQuery, GroupOperator, IndexColumnOptions, InferEntityName, Opt, Primary, PrimaryProperty, QueryFlag, QueryOrderMap, RawQueryFragment, RoutineIgnoreField, Scalar, Type } from '@mikro-orm/core';
2
+ import type { CheckCallback, DeferMode, Dictionary, EntityName, EntityMetadata, EntityProperty, EntitySchemaWithMeta, ExtractDefineEntityProperties, FilterQuery, GroupOperator, IndexColumnOptions, InferEntityName, Opt, Primary, PrimaryProperty, QueryFlag, QueryOrderMap, RawQueryFragment, RoutineIgnoreField, Scalar, Type } from '@mikro-orm/core';
3
3
  import type { JoinType, QueryType } from './query/enums.js';
4
4
  import type { DatabaseSchema } from './schema/DatabaseSchema.js';
5
5
  import type { DatabaseTable } from './schema/DatabaseTable.js';
@@ -295,6 +295,8 @@ export interface IQueryBuilder<T> {
295
295
  scheduleFilterCheck(path: string): void;
296
296
  /** @internal */
297
297
  ensureTPTJoins(): void;
298
+ /** @internal */
299
+ addPolymorphicJoin(prop: EntityProperty, targetMeta: EntityMetadata, ownerAlias: string, alias: string, type: JoinType, path: string, schema?: string): void;
298
300
  withSchema(schema: string): this;
299
301
  }
300
302
  export interface ICriteriaNodeProcessOptions {