(
qb: AnyQueryBuilder<T>,
meta: EntityMetadata<T>,
populateOrderBy: QueryOrderMap<T>[],
parentPath: string,
explicit: boolean,
parentAlias = qb.alias,
)
| 3220 | } |
| 3221 | |
| 3222 | protected buildPopulateOrderBy<T extends object>( |
| 3223 | qb: AnyQueryBuilder<T>, |
| 3224 | meta: EntityMetadata<T>, |
| 3225 | populateOrderBy: QueryOrderMap<T>[], |
| 3226 | parentPath: string, |
| 3227 | explicit: boolean, |
| 3228 | parentAlias = qb.alias, |
| 3229 | ): QueryOrderMap<T>[] { |
| 3230 | const orderBy: QueryOrderMap<T>[] = []; |
| 3231 | |
| 3232 | for (let i = 0; i < populateOrderBy.length; i++) { |
| 3233 | const orderHint = populateOrderBy[i]; |
| 3234 | |
| 3235 | for (const field of Utils.getObjectQueryKeys(orderHint)) { |
| 3236 | const childOrder = orderHint[field as keyof typeof orderHint]; |
| 3237 | |
| 3238 | if (RawQueryFragment.isKnownFragmentSymbol(field)) { |
| 3239 | const { sql, params } = RawQueryFragment.getKnownFragment(field)!; |
| 3240 | const key = raw(sql.replace(new RegExp(ALIAS_REPLACEMENT_RE, 'g'), parentAlias), params); |
| 3241 | orderBy.push({ [key]: childOrder } as QueryOrderMap<T>); |
| 3242 | continue; |
| 3243 | } |
| 3244 | |
| 3245 | const prop = meta.properties[field]; |
| 3246 | |
| 3247 | if (!prop) { |
| 3248 | throw new Error(`Trying to order by not existing property ${meta.className}.${field}`); |
| 3249 | } |
| 3250 | |
| 3251 | let path = parentPath; |
| 3252 | const meta2 = prop.targetMeta!; |
| 3253 | if ( |
| 3254 | prop.kind !== ReferenceKind.SCALAR && |
| 3255 | (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || |
| 3256 | !prop.owner || |
| 3257 | Utils.isPlainObject(childOrder)) |
| 3258 | ) { |
| 3259 | path += `.${field}`; |
| 3260 | } |
| 3261 | |
| 3262 | if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') { |
| 3263 | path += '[pivot]'; |
| 3264 | } |
| 3265 | |
| 3266 | const join = qb.getJoinForPath(path, { matchPopulateJoins: true }); |
| 3267 | const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true }) ?? parentAlias; |
| 3268 | |
| 3269 | if (!join) { |
| 3270 | // an owner to-one relation that is not joined (e.g. ordering a populated collection by such |
| 3271 | // a relation's primary key) can be ordered by its local FK columns, matching the `select-in` |
| 3272 | // and `balanced` strategies instead of silently dropping the clause |
| 3273 | if ( |
| 3274 | prop.owner && |
| 3275 | [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && |
| 3276 | Utils.isPlainObject(childOrder) |
| 3277 | ) { |
| 3278 | for (const childField of Utils.getObjectQueryKeys(childOrder as Dictionary)) { |
| 3279 | const idx = prop.referencedPKs.indexOf(childField as EntityKey); |
nothing calls this directly
no test coverage detected