Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix: fix up selection behavior of @RelationId fields#11999

Open
gioboa wants to merge 4 commits intotypeorm:masterfrom
gioboa:fix/11483
Open

fix: fix up selection behavior of @RelationId fields#11999
gioboa wants to merge 4 commits intotypeorm:masterfrom
gioboa:fix/11483

Conversation

@gioboa
Copy link
Collaborator

@gioboa gioboa commented Feb 17, 2026

Close #11483

Description of change

Pull-Request Checklist

  • Code is up-to-date with the master branch
  • This pull request links relevant issues as Fixes #00000
  • There are new or updated tests validating the change (tests/**.test.ts)
  • Documentation has been updated to reflect this change (docs/docs/**.md)

@qodo-free-for-open-source-projects

User description

Close #11483

Description of change

Pull-Request Checklist

  • Code is up-to-date with the master branch
  • This pull request links relevant issues as Fixes #00000
  • There are new or updated tests validating the change (tests/**.test.ts)
  • Documentation has been updated to reflect this change (docs/docs/**.md)

PR Type

Bug fix, Tests


Description

  • Fix @RelationId fields to respect explicit select statements

  • Add helper function to check if relationId should be loaded

  • Apply selection checks to main alias and joined entities

  • Add comprehensive test suite for relationId select behavior


Diagram Walkthrough

flowchart LR
  A["RelationId Transformer"] -->|"Add shouldLoadRelationId helper"| B["Check selection criteria"]
  B -->|"No selects defined"| C["Load all relationIds"]
  B -->|"Alias selected"| C
  B -->|"Property selected"| C
  B -->|"Not selected"| D["Skip relationId"]
  C -->|"Apply to main alias"| E["Main entity relationIds"]
  C -->|"Apply to joins"| F["Joined entity relationIds"]
Loading

File Walkthrough

Relevant files
Bug fix
RelationIdMetadataToAttributeTransformer.ts
Add selection-aware relationId loading logic                         

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts

  • Add shouldLoadRelationId helper function to determine if a relationId
    property should be loaded based on selection criteria
  • Check if no selects are defined, whole entity is selected, or specific
    property is selected
  • Apply selection check before loading relationIds for main alias
    entities
  • Apply selection check before loading relationIds for joined entities
+44/-0   
Tests
Group.ts
Add Group test entity with RelationId                                       

test/functional/query-builder/relation-id/select-behavior/entity/Group.ts

  • Create test entity Group with @Entity decorator
  • Define id and name columns
  • Add @ManyToMany relationship to User with @JoinTable
  • Add @RelationId field memberIds for the members relationship
+25/-0   
User.ts
Add User test entity                                                                         

test/functional/query-builder/relation-id/select-behavior/entity/User.ts

  • Create test entity User with @Entity decorator
  • Define id and name columns
+10/-0   
relation-id-select-behavior.test.ts
Add relationId select behavior test suite                               

test/functional/query-builder/relation-id/select-behavior/relation-id-select-behavior.test.ts

  • Add test suite for relationId select behavior covering issue @RelationID makes crash on select due huge query built #11483
  • Test that @RelationId is NOT loaded when not explicitly selected
  • Test that @RelationId IS loaded when explicitly selected via
    loadRelationIdAndMap
  • Verify property presence and array contents in loaded relationIds
+103/-0 

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 17, 2026

commit: 420569d

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Feb 17, 2026

PR Code Suggestions ✨

Latest suggestions up to 420569d

CategorySuggestion                                                                                                                                    Impact
Possible issue
Support wildcard entity selections

Add support for wildcard selections like alias.* when determining if a whole
entity is selected to ensure @RelationId fields are loaded correctly.

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts [31-66]

 const shouldLoadRelationId = (
     aliasName: string,
     relationIdPropertyName: string,
 ): boolean => {
     // If no specific selects are defined, we assume everything is selected (default behavior)
     if (this.expressionMap.selects.length === 0) return true
 
     // Check if the whole entity (alias) is selected
-    if (selections.has(aliasName)) return true
+    if (selections.has(aliasName) || selections.has(aliasName + ".*")) return true
 
     // Check if the specific relationId property is selected
     const propertySelection = aliasName + "." + relationIdPropertyName
     if (selections.has(propertySelection)) return true
 
     // Check if all columns are selected
     if (fullSelectionCache.has(aliasName)) {
         return !!fullSelectionCache.get(aliasName)
     }
 
     const alias = this.expressionMap.aliases.find(
         (alias) => alias.name === aliasName,
     )
     if (alias && alias.metadata) {
         const allColumnsSelected = alias.metadata.columns
             .filter((column) => column.isSelect)
             .every((column) =>
                 selections.has(aliasName + "." + column.propertyPath),
             )
         fullSelectionCache.set(aliasName, allColumnsSelected)
         if (allColumnsSelected) return true
     } else {
         fullSelectionCache.set(aliasName, false)
     }
 
     return false
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a missing case (alias.*) for whole-entity selection, improving the robustness of the new shouldLoadRelationId logic.

Low
  • More

Previous suggestions

Suggestions up to commit 6a529e9
CategorySuggestion                                                                                                                                    Impact
Possible issue
Honor wildcard select semantics

Add a check for wildcard selections (e.g., alias.*) to ensure @RelationId fields
are loaded correctly when a full entity is selected via a wildcard.

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts [31-66]

 const shouldLoadRelationId = (
     aliasName: string,
     relationIdPropertyName: string,
 ): boolean => {
     // If no specific selects are defined, we assume everything is selected (default behavior)
     if (this.expressionMap.selects.length === 0) return true
 
     // Check if the whole entity (alias) is selected
     if (selections.has(aliasName)) return true
+
+    // Check if all columns are selected via wildcard
+    if (selections.has(aliasName + ".*")) return true
 
     // Check if the specific relationId property is selected
     const propertySelection = aliasName + "." + relationIdPropertyName
     if (selections.has(propertySelection)) return true
 
     // Check if all columns are selected
     if (fullSelectionCache.has(aliasName)) {
         return !!fullSelectionCache.get(aliasName)
     }
 
     const alias = this.expressionMap.aliases.find(
         (alias) => alias.name === aliasName,
     )
     if (alias && alias.metadata) {
         const allColumnsSelected = alias.metadata.columns
             .filter((column) => column.isSelect)
             .every((column) =>
                 selections.has(aliasName + "." + column.propertyPath),
             )
         fullSelectionCache.set(aliasName, allColumnsSelected)
         if (allColumnsSelected) return true
     } else {
         fullSelectionCache.set(aliasName, false)
     }
 
     return false
 }
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the new logic does not handle wildcard selections (alias.*), a common pattern that should imply loading all properties, including @RelationId fields.

Medium
Normalize select strings for matching

Normalize selection strings by trimming whitespace before adding them to the Set
to prevent matching failures due to formatting differences.

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts [25-27]

 const selections = new Set(
-    this.expressionMap.selects.map((s) => s.selection),
+    this.expressionMap.selects
+        .map((s) => s.selection)
+        .filter((s) => typeof s === "string")
+        .map((s) => s.trim()),
 )
Suggestion importance[1-10]: 4

__

Why: The suggestion improves robustness by trimming whitespace from selection strings, preventing potential mismatches due to formatting variations in user input.

Low
Suggestions up to commit ed07f0b
CategorySuggestion                                                                                                                                    Impact
Possible issue
Avoid duplicate relation-id attributes

Before adding a new RelationIdAttribute for the main alias, check if an
attribute for the same parent alias and relation property already exists in
expressionMap.relationIdAttributes to prevent duplicates.

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts [80-84]

 const attribute = this.metadataToAttribute(
     this.expressionMap.mainAlias!.name,
     relationId,
 )
-this.expressionMap.relationIdAttributes.push(attribute)
 
+const alreadyAdded = this.expressionMap.relationIdAttributes.some(
+    (a) =>
+        a.parentAlias === attribute.parentAlias &&
+        a.relationIdProperty === attribute.relationIdProperty,
+)
+if (!alreadyAdded) this.expressionMap.relationIdAttributes.push(attribute)
+
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly points out a potential issue where duplicate RelationIdAttribute instances could be created, leading to redundant SQL and potential errors. Adding a check to prevent duplicates is a critical improvement for robustness.

Medium
De-duplicate joined relation-ids

Before adding a new RelationIdAttribute for a joined alias, check if an
attribute for the same parent alias and relation property already exists in
expressionMap.relationIdAttributes to prevent duplicates.

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts [103-107]

 const attribute = this.metadataToAttribute(
     join.alias.name,
     relationId,
 )
-this.expressionMap.relationIdAttributes.push(attribute)
 
+const alreadyAdded = this.expressionMap.relationIdAttributes.some(
+    (a) =>
+        a.parentAlias === attribute.parentAlias &&
+        a.relationIdProperty === attribute.relationIdProperty,
+)
+if (!alreadyAdded) this.expressionMap.relationIdAttributes.push(attribute)
+
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly applies the same logic from the previous suggestion to joined entities, which is necessary for consistency and to prevent the same class of bugs (redundant SQL, conflicts) in queries with joins.

Medium
Handle wildcard column selections

Modify the shouldLoadRelationId function to correctly handle wildcard selections
like alias.*, ensuring @RelationId fields are loaded when all columns of an
entity are selected via a wildcard.

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts [31-66]

 const shouldLoadRelationId = (
     aliasName: string,
     relationIdPropertyName: string,
 ): boolean => {
     // If no specific selects are defined, we assume everything is selected (default behavior)
     if (this.expressionMap.selects.length === 0) return true
 
     // Check if the whole entity (alias) is selected
     if (selections.has(aliasName)) return true
+
+    // Check wildcard selection (all columns)
+    if (selections.has(aliasName + ".*")) return true
 
     // Check if the specific relationId property is selected
     const propertySelection = aliasName + "." + relationIdPropertyName
     if (selections.has(propertySelection)) return true
 
     // Check if all columns are selected
     if (fullSelectionCache.has(aliasName)) {
         return !!fullSelectionCache.get(aliasName)
     }
 
     const alias = this.expressionMap.aliases.find(
         (alias) => alias.name === aliasName,
     )
     if (alias && alias.metadata) {
         const allColumnsSelected = alias.metadata.columns
             .filter((column) => column.isSelect)
             .every((column) =>
                 selections.has(aliasName + "." + column.propertyPath),
             )
         fullSelectionCache.set(aliasName, allColumnsSelected)
         if (allColumnsSelected) return true
     } else {
         fullSelectionCache.set(aliasName, false)
     }
 
     return false
 }
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the new shouldLoadRelationId function does not handle wildcard selections (e.g., alias.*), which would cause @RelationId fields to be omitted incorrectly.

Medium
✅ Suggestions up to commit b88e5ad
CategorySuggestion                                                                                                                                    Impact
General
Use select() to test explicit selection
Suggestion Impact:The commit reworked the test setup (removed the initial no-data loadRelationIdAndMap check) and added a new test that uses QueryBuilder.select([...]) with explicit column selection, aiming to validate @RelationId behavior under explicit selections. However, the original test still uses loadRelationIdAndMap and the new select-based test does not select memberIds explicitly as suggested.

code diff:

@@ -63,18 +63,8 @@
         Promise.all(
             connections.map(async (connection) => {
                 const groupRepository = connection.getRepository(Group)
+                const userRepository = connection.getRepository(User)
 
-                // Use loadRelationIdAndMap to explicitly request the RelationId field
-                const foundGroups = await groupRepository
-                    .createQueryBuilder("grp")
-                    .select("grp.id")
-                    .loadRelationIdAndMap("grp.memberIds", "grp.members")
-                    .getMany()
-
-                expect(foundGroups.length).to.be.equal(0) // No data seeded in this test, but structure check is key
-
-                // Seeding data for this specific test to verify values
-                const userRepository = connection.getRepository(User)
                 const users: User[] = []
                 for (let i = 0; i < 5; i++) {
                     users.push(
@@ -85,15 +75,48 @@
                 group.members = users
                 await groupRepository.save(group)
 
-                const reloadedGroups = await groupRepository
+                const foundGroups = await groupRepository
                     .createQueryBuilder("grp")
                     .select("grp.id")
                     .loadRelationIdAndMap("grp.memberIds", "grp.members")
                     .getMany()
 
-                expect(reloadedGroups.length).to.be.equal(1)
-                reloadedGroups.forEach((group) => {
+                expect(foundGroups.length).to.be.equal(1)
+                foundGroups.forEach((group) => {
                     expect(group).to.have.property("id")
+                    expect(group).to.have.property("memberIds")
+                    expect(group.memberIds).to.be.an("array")
+                    expect(group.memberIds).to.have.length(5)
+                })
+            }),
+        ))
+
+    it("should load @RelationId when all columns are explicitly selected", () =>
+        Promise.all(
+            connections.map(async (connection) => {
+                const groupRepository = connection.getRepository(Group)
+                const userRepository = connection.getRepository(User)
+
+                const users: User[] = []
+                for (let i = 0; i < 5; i++) {
+                    users.push(
+                        await userRepository.save({ name: `User ${i + 1}` }),
+                    )
+                }
+                const group = await groupRepository.save({ name: "Group 1" })
+                group.members = users
+                await groupRepository.save(group)
+
+                // Select all columns explicitly
+                const foundGroups = await groupRepository
+                    .createQueryBuilder("grp")
+                    .select(["grp.id", "grp.name"])
+                    .getMany()
+
+                expect(foundGroups).to.have.length(1)
+                foundGroups.forEach((group) => {
+                    expect(group).to.have.property("id")
+                    expect(group).to.have.property("name")
                     expect(group).to.have.property("memberIds")
                     expect(group.memberIds).to.be.an("array")
                     expect(group.memberIds).to.have.length(5)

Modify the test to use select(["grp.id", "grp.memberIds"]) instead of
loadRelationIdAndMap to correctly validate the PR's changes to @RelationId
handling with explicit selections.

test/functional/query-builder/relation-id/select-behavior/relation-id-select-behavior.test.ts [62-102]

 it("should load @RelationId when it IS explicitly selected", () =>
     Promise.all(
         connections.map(async (connection) => {
             const groupRepository = connection.getRepository(Group)
-
-            // Use loadRelationIdAndMap to explicitly request the RelationId field
-            const foundGroups = await groupRepository
-                .createQueryBuilder("grp")
-                .select("grp.id")
-                .loadRelationIdAndMap("grp.memberIds", "grp.members")
-                .getMany()
-
-            expect(foundGroups.length).to.be.equal(0) // No data seeded in this test, but structure check is key
+            const userRepository = connection.getRepository(User)
 
             // Seeding data for this specific test to verify values
-            const userRepository = connection.getRepository(User)
             const users: User[] = []
             for (let i = 0; i < 5; i++) {
                 users.push(
                     await userRepository.save({ name: `User ${i + 1}` }),
                 )
             }
             const group = await groupRepository.save({ name: "Group 1" })
             group.members = users
             await groupRepository.save(group)
 
-            const reloadedGroups = await groupRepository
+            const foundGroups = await groupRepository
                 .createQueryBuilder("grp")
-                .select("grp.id")
-                .loadRelationIdAndMap("grp.memberIds", "grp.members")
+                .select(["grp.id", "grp.memberIds"])
                 .getMany()
 
-            expect(reloadedGroups.length).to.be.equal(1)
-            reloadedGroups.forEach((group) => {
+            expect(foundGroups.length).to.be.equal(1)
+            foundGroups.forEach((group) => {
                 expect(group).to.have.property("id")
                 expect(group).to.have.property("memberIds")
+                expect(Object.keys(group).length).to.be.equal(2)
                 expect(group.memberIds).to.be.an("array")
                 expect(group.memberIds).to.have.length(5)
             })
         }),
     ))
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical flaw in the test logic, where the test uses loadRelationIdAndMap instead of select, failing to validate the actual changes made in the PR.

High
Avoid non-null assertions in callbacks

To improve readability and avoid redundant non-null assertions, assign
this.expressionMap.mainAlias to a local variable after the null check and use
that variable within the forEach callback.

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts [54-72]

 if (this.expressionMap.mainAlias) {
-    this.expressionMap.mainAlias.metadata.relationIds.forEach(
+    const mainAlias = this.expressionMap.mainAlias
+    mainAlias.metadata.relationIds.forEach(
         (relationId) => {
             if (
                 !shouldLoadRelationId(
-                    this.expressionMap.mainAlias!.name,
+                    mainAlias.name,
                     relationId.propertyName,
                 )
             )
                 return
 
             const attribute = this.metadataToAttribute(
-                this.expressionMap.mainAlias!.name,
+                mainAlias.name,
                 relationId,
             )
             this.expressionMap.relationIdAttributes.push(attribute)
         },
     )
 }
Suggestion importance[1-10]: 4

__

Why: The suggestion correctly points out redundant non-null assertions and proposes a cleaner, more readable approach by assigning the checked value to a local variable, which is a good practice.

Low

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Feb 17, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Remediation recommended

1. No docs for @RelationId change 📘 Rule violation ✓ Correctness
Description
The PR changes the selection/loading behavior of @RelationId fields, which is user-facing, but
includes no documentation updates. Users may be surprised by the changed select behavior without
corresponding docs or examples.
Code

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[R57-64]

+                    if (
+                        !shouldLoadRelationId(
+                            this.expressionMap.mainAlias!.name,
+                            relationId.propertyName,
+                        )
+                    )
+                        return
+
Evidence
Compliance requires documentation updates for user-facing behavior changes. The diff adds logic that
conditionally skips loading relation-id attributes based on selects, but no docs changes are
present in the PR diff.

Rule 2: Docs updated for user-facing changes
src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[57-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR changes `@RelationId` selection/loading behavior but does not update documentation for this user-facing behavior.
## Issue Context
New logic conditionally skips creating/loading relation-id attributes depending on query `selects`, which can change what properties appear on returned entities.
## Fix Focus Areas
- src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[57-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Noisy pre-seed empty assertion📘 Rule violation ⛯ Reliability
Description
The new functional test performs an initial query expecting 0 results and includes explanatory
commentary that reads as non-essential noise. This adds AI-like verbosity and unnecessary steps that
can be removed without reducing test coverage.
Code

test/functional/query-builder/relation-id/select-behavior/relation-id-select-behavior.test.ts[R67-76]

+                // Use loadRelationIdAndMap to explicitly request the RelationId field
+                const foundGroups = await groupRepository
+                    .createQueryBuilder("grp")
+                    .select("grp.id")
+                    .loadRelationIdAndMap("grp.memberIds", "grp.members")
+                    .getMany()
+
+                expect(foundGroups.length).to.be.equal(0) // No data seeded in this test, but structure check is key
+
+                // Seeding data for this specific test to verify values
Evidence
The compliance rule requires avoiding AI-generated slop such as extra commentary and
abnormal/unnecessary code. The added test first asserts an empty result set before seeding data,
accompanied by a comment justifying it; this is redundant for the stated goal and can be simplified.

Rule 4: Remove AI-generated noise
test/functional/query-builder/relation-id/select-behavior/relation-id-select-behavior.test.ts[67-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test includes an unnecessary pre-seed query expecting zero results and a verbose explanatory comment, which adds noise without improving coverage.
## Issue Context
The test can validate `@RelationId` explicit selection by seeding data first and then running a single query/assertion pass.
## Fix Focus Areas
- test/functional/query-builder/relation-id/select-behavior/relation-id-select-behavior.test.ts[67-76]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. RelationId selection mismatch🐞 Bug ✓ Correctness
Description
@RelationId loading is now gated on selecting the whole alias (e.g. "grp"), but entity hydration
also supports fully selecting an entity via selecting all its columns individually. This can lead to
fully-hydrated entities unexpectedly missing @RelationId fields depending on how the select list is
written.
Code

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[R25-49]

+        // Helper function to check if a relationId should be loaded
+        const shouldLoadRelationId = (
+            aliasName: string,
+            relationIdPropertyName: string,
+        ): boolean => {
+            // If no specific selects are defined, we assume everything is selected (default behavior)
+            if (this.expressionMap.selects.length === 0) return true
+
+            // Check if the whole entity (alias) is selected
+            if (
+                this.expressionMap.selects.some(
+                    (s) => s.selection === aliasName,
+                )
+            )
+                return true
+
+            // Check if the specific relationId property is selected
+            const propertySelection = aliasName + "." + relationIdPropertyName
+            if (
+                this.expressionMap.selects.some(
+                    (s) => s.selection === propertySelection,
+                )
+            )
+                return true
+
Evidence
The new gating only returns true when the alias itself is selected (or when an
alias.relationIdPropertyName string is selected), otherwise it skips adding the RelationIdAttribute.
But entity transformation treats either selecting the alias OR selecting individual alias.column
paths as sufficient to include column values, so callers can fully select an entity without ever
selecting the alias token—leading to a mismatch where the entity is fully populated but @RelationId
is skipped.

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[25-51]
src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts[472-503]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`shouldLoadRelationId` only considers the alias token (e.g. `grp`) as indicating the entity is selected. However, TypeORM can hydrate a full entity when all of its columns are individually selected (e.g. `select([&amp;amp;amp;quot;grp.id&amp;amp;amp;quot;, &amp;amp;amp;quot;grp.name&amp;amp;amp;quot;])`). With the new gating, these two equivalent ways of fully selecting an entity can yield different outcomes for decorator-based `@RelationId` fields.
### Issue Context
- Entity hydration includes columns when either `alias` OR `alias.column` appears in `expressionMap.selects`.
- The new gating only checks `alias` (and an `alias.relationIdPropertyName` string), so `@RelationId` can be skipped even when the entity is fully populated via explicit column list.
### Fix Focus Areas
- src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[16-95]
- test/functional/query-builder/relation-id/select-behavior/relation-id-select-behavior.test.ts[1-103]
### Implementation notes
- Precompute `const selections = new Set(this.expressionMap.selects.map(s =&amp;amp;amp;gt; s.selection))`.
- For each alias+metadata, compute `isFullySelected`:
- `true` if `selections.has(aliasName)`
- OR if **all** `metadata.columns.filter(c =&amp;amp;amp;gt; c.isSelect)` satisfy `selections.has(`${aliasName}.${c.propertyPath}`)`
- Use `isFullySelected` in `shouldLoadRelationId` (or inline logic) to decide whether to load decorator-based `@RelationId`.
- Add a test that selects all Group columns individually (e.g. `grp.id` and `grp.name`) and asserts `memberIds` is loaded (or, if desired behavior is the opposite, add an explicit test asserting it is not loaded and update the gating/comment accordingly).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

4. Inefficient selection scans🐞 Bug ➹ Performance
Description
The new helper repeatedly linearly scans expressionMap.selects for every relationId across main and
join aliases. This is low impact but avoidable by precomputing a Set of selections once per
transform().
Code

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[R25-51]

+        // Helper function to check if a relationId should be loaded
+        const shouldLoadRelationId = (
+            aliasName: string,
+            relationIdPropertyName: string,
+        ): boolean => {
+            // If no specific selects are defined, we assume everything is selected (default behavior)
+            if (this.expressionMap.selects.length === 0) return true
+
+            // Check if the whole entity (alias) is selected
+            if (
+                this.expressionMap.selects.some(
+                    (s) => s.selection === aliasName,
+                )
+            )
+                return true
+
+            // Check if the specific relationId property is selected
+            const propertySelection = aliasName + "." + relationIdPropertyName
+            if (
+                this.expressionMap.selects.some(
+                    (s) => s.selection === propertySelection,
+                )
+            )
+                return true
+
+            return false
+        }
Evidence
transform() iterates all relationId metadata entries for the main alias and each join alias, calling
shouldLoadRelationId each time. shouldLoadRelationId performs up to two .some() scans over
expressionMap.selects per call, multiplying work by (#relationIds * #selects).

src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[25-51]
src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[53-94]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`shouldLoadRelationId` performs multiple linear scans over `expressionMap.selects` for each relationId on each alias. While likely small in practice, it’s easy to eliminate and simplifies the logic.
### Issue Context
The transformer loops over all `relationIds` for the main alias and joined aliases, calling `shouldLoadRelationId` for each.
### Fix Focus Areas
- src/query-builder/relation-id/RelationIdMetadataToAttributeTransformer.ts[16-95]
### Implementation notes
- Compute once: `const selections = new Set(this.expressionMap.selects.map(s =&amp;amp;amp;gt; s.selection))`.
- Replace `.some(...)` checks with `selections.has(...)`.
- If you implement the ‘effective full selection’ fix from the other finding, you can also cache `isFullySelectedByAliasName` in a `Map&amp;amp;amp;lt;string, boolean&amp;amp;amp;gt;` to avoid recomputing per relationId.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

Persistent review updated to latest commit ed07f0b

@qodo-free-for-open-source-projects

Persistent review updated to latest commit 6a529e9

@qodo-free-for-open-source-projects

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coveralls
Copy link

Coverage Status

coverage: 81.44% (+0.02%) from 81.419%
when pulling 420569d on gioboa:fix/11483
into f47246c on typeorm:master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@RelationID makes crash on select due huge query built

2 participants