* Resolves nested paths like `a.books.title` to their actual field references. * Auto-joins relations as needed and returns `{alias}.{field}`. * For embeddeds: navigates into flattened embeddeds to return the correct field name.
(field: string)
| 3261 | * For embeddeds: navigates into flattened embeddeds to return the correct field name. |
| 3262 | */ |
| 3263 | protected resolveNestedPath(field: string): string | string[] { |
| 3264 | if (typeof field !== 'string' || !field.includes('.')) { |
| 3265 | return field; |
| 3266 | } |
| 3267 | |
| 3268 | const parts = field.split('.'); |
| 3269 | |
| 3270 | // Simple alias.property case - let prepareFields handle it |
| 3271 | if (parts.length === 2 && this.#state.aliases[parts[0]]) { |
| 3272 | return field; |
| 3273 | } |
| 3274 | |
| 3275 | // Start with root alias |
| 3276 | let currentAlias = parts[0]; |
| 3277 | let currentMeta = this.#state.aliases[currentAlias] |
| 3278 | ? this.metadata.get(this.#state.aliases[currentAlias].entityName) |
| 3279 | : this.mainAlias.meta; |
| 3280 | |
| 3281 | // If first part is not an alias, it's a property of the main entity |
| 3282 | if (!this.#state.aliases[currentAlias]) { |
| 3283 | currentAlias = this.mainAlias.aliasName; |
| 3284 | parts.unshift(currentAlias); |
| 3285 | } |
| 3286 | |
| 3287 | // Walk through the path parts (skip the alias) |
| 3288 | for (let i = 1; i < parts.length; i++) { |
| 3289 | const propName = parts[i]; |
| 3290 | const prop = (currentMeta.properties as Dictionary<EntityProperty>)[propName]; |
| 3291 | |
| 3292 | if (!prop) { |
| 3293 | return field; // Unknown property, return as-is for raw SQL support |
| 3294 | } |
| 3295 | |
| 3296 | const isLastPart = i === parts.length - 1; |
| 3297 | |
| 3298 | // Handle embedded properties - navigate into flattened embeddeds |
| 3299 | if (prop.kind === ReferenceKind.EMBEDDED) { |
| 3300 | if (prop.object) { |
| 3301 | return `${currentAlias}.${propName}`; |
| 3302 | } |
| 3303 | |
| 3304 | // Navigate through remaining path to find the leaf property |
| 3305 | const remainingPath = parts.slice(i + 1); |
| 3306 | let embeddedProp: EntityProperty | undefined = prop; |
| 3307 | |
| 3308 | for (const part of remainingPath) { |
| 3309 | embeddedProp = embeddedProp?.embeddedProps?.[part]; |
| 3310 | if (embeddedProp?.object && embeddedProp.fieldNames?.[0]) { |
| 3311 | return `${currentAlias}.${embeddedProp.fieldNames[0]}`; |
| 3312 | } |
| 3313 | } |
| 3314 | |
| 3315 | return `${currentAlias}.${embeddedProp?.fieldNames?.[0] ?? propName}`; |
| 3316 | } |
| 3317 | |
| 3318 | // Handle relations - auto-join if not the last part |
| 3319 | if ( |
| 3320 | prop.kind === ReferenceKind.MANY_TO_ONE || |