import { ARRAY_OPERATORS, ReferenceKind } from '@mikro-orm/core'; import { CriteriaNode } from './CriteriaNode.js'; import { JoinType, QueryType } from './enums.js'; import { QueryBuilder } from './QueryBuilder.js'; /** * @internal */ export class ScalarCriteriaNode extends CriteriaNode { process(qb, options) { const matchPopulateJoins = options?.matchPopulateJoins || (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind)); const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), { ...options, matchPopulateJoins }); if (this.shouldJoin(qb, nestedAlias)) { const path = this.getPath(); const parentPath = this.parent.getPath(); // the parent is always there, otherwise `shouldJoin` would return `false` const nestedAlias = qb.getAliasForJoinPath(path) || qb.getNextAlias(this.prop?.pivotEntity ?? this.entityName); const field = this.aliased(this.prop.name, options?.alias); const type = this.prop.kind === ReferenceKind.MANY_TO_MANY ? JoinType.pivotJoin : JoinType.leftJoin; qb.join(field, nestedAlias, undefined, type, path); // select the owner as virtual property when joining from 1:1 inverse side, but only if the parent is root entity if ( this.prop.kind === ReferenceKind.ONE_TO_ONE && !parentPath.includes('.') && !qb.state.fields?.includes(field) ) { qb.addSelect(field); } } if (this.payload instanceof QueryBuilder) { return this.payload.toRaw(); } if (this.payload && typeof this.payload === 'object') { const keys = Object.keys(this.payload).filter( key => ARRAY_OPERATORS.includes(key) && Array.isArray(this.payload[key]), ); for (const key of keys) { this.payload[key] = JSON.stringify(this.payload[key]); } } return this.payload; } willAutoJoin(qb, alias, options) { return this.shouldJoin(qb, alias); } shouldJoin(qb, nestedAlias) { if ( !this.parent || !this.prop || (nestedAlias && [QueryType.SELECT, QueryType.COUNT].includes(qb.type ?? QueryType.SELECT)) ) { return false; } switch (this.prop.kind) { case ReferenceKind.ONE_TO_MANY: return true; case ReferenceKind.MANY_TO_MANY: return true; case ReferenceKind.ONE_TO_ONE: return !this.prop.owner; default: return false; // SCALAR, MANY_TO_ONE } } }