• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.49
/src/query-builder/relation-count/RelationCountLoader.ts
1
import { ColumnMetadata } from "../../metadata/ColumnMetadata"
2
import { DataSource } from "../../data-source/DataSource"
3
import { RelationCountAttribute } from "./RelationCountAttribute"
4
import { RelationCountLoadResult } from "./RelationCountLoadResult"
5
import { QueryRunner } from "../../query-runner/QueryRunner"
6

7
export class RelationCountLoader {
1✔
8
    // -------------------------------------------------------------------------
9
    // Constructor
10
    // -------------------------------------------------------------------------
11

12
    constructor(
UNCOV
13
        protected connection: DataSource,
×
UNCOV
14
        protected queryRunner: QueryRunner | undefined,
×
UNCOV
15
        protected relationCountAttributes: RelationCountAttribute[],
×
16
    ) {}
17

18
    // -------------------------------------------------------------------------
19
    // Public Methods
20
    // -------------------------------------------------------------------------
21

22
    async load(rawEntities: any[]): Promise<RelationCountLoadResult[]> {
UNCOV
23
        const onlyUnique = (value: any, index: number, self: any) => {
×
UNCOV
24
            return self.indexOf(value) === index
×
25
        }
26

UNCOV
27
        const promises = this.relationCountAttributes.map(
×
28
            async (relationCountAttr) => {
UNCOV
29
                if (relationCountAttr.relation.isOneToMany) {
×
30
                    // example: Post and Category
31
                    // loadRelationCountAndMap("post.categoryCount", "post.categories")
32
                    // we expect it to load array of post ids
33

34
                    // todo(dima): fix issues wit multiple primary keys and remove joinColumns[0]
UNCOV
35
                    const relation = relationCountAttr.relation // "category.posts"
×
UNCOV
36
                    const inverseRelation = relation.inverseRelation! // "post.category"
×
37
                    const referenceColumnName =
UNCOV
38
                        inverseRelation.joinColumns[0].referencedColumn!
×
39
                            .propertyName // post id
40
                    const inverseSideTable =
UNCOV
41
                        relation.inverseEntityMetadata.target // Post
×
42
                    const inverseSideTableName =
UNCOV
43
                        relation.inverseEntityMetadata.tableName // post
×
44
                    const inverseSideTableAlias =
UNCOV
45
                        relationCountAttr.alias || inverseSideTableName // if condition (custom query builder factory) is set then relationIdAttr.alias defined
×
UNCOV
46
                    const inverseSidePropertyName = inverseRelation.propertyName // "category" from "post.category"
×
47

UNCOV
48
                    let referenceColumnValues = rawEntities
×
49
                        .map(
50
                            (rawEntity) =>
UNCOV
51
                                rawEntity[
×
52
                                    relationCountAttr.parentAlias +
53
                                        "_" +
54
                                        referenceColumnName
55
                                ],
56
                        )
UNCOV
57
                        .filter((value) => !!value)
×
UNCOV
58
                    referenceColumnValues =
×
59
                        referenceColumnValues.filter(onlyUnique)
60

61
                    // ensure we won't perform redundant queries for joined data which was not found in selection
62
                    // example: if post.category was not found in db then no need to execute query for category.imageIds
UNCOV
63
                    if (referenceColumnValues.length === 0)
×
64
                        return {
×
65
                            relationCountAttribute: relationCountAttr,
66
                            results: [],
67
                        }
68

69
                    // generate query:
70
                    // SELECT category.post as parentId, COUNT(*) AS cnt FROM category category WHERE category.post IN (1, 2) GROUP BY category.post
UNCOV
71
                    const qb = this.connection.createQueryBuilder(
×
72
                        this.queryRunner,
73
                    )
UNCOV
74
                    qb.select(
×
75
                        inverseSideTableAlias + "." + inverseSidePropertyName,
76
                        "parentId",
77
                    )
78
                        .addSelect("COUNT(*)", "cnt")
79
                        .from(inverseSideTable, inverseSideTableAlias)
80
                        .where(
81
                            inverseSideTableAlias +
82
                                "." +
83
                                inverseSidePropertyName +
84
                                " IN (:...ids)",
85
                        )
86
                        .addGroupBy(
87
                            inverseSideTableAlias +
88
                                "." +
89
                                inverseSidePropertyName,
90
                        )
91
                        .setParameter("ids", referenceColumnValues)
92

93
                    // apply condition (custom query builder factory)
UNCOV
94
                    if (relationCountAttr.queryBuilderFactory)
×
UNCOV
95
                        relationCountAttr.queryBuilderFactory(qb)
×
96

UNCOV
97
                    return {
×
98
                        relationCountAttribute: relationCountAttr,
99
                        results: await qb.getRawMany(),
100
                    }
101
                } else {
102
                    // example: Post and Category
103
                    // owner side: loadRelationIdAndMap("post.categoryIds", "post.categories")
104
                    // inverse side: loadRelationIdAndMap("category.postIds", "category.posts")
105
                    // we expect it to load array of post ids
106

107
                    let joinTableColumnName: string
108
                    let inverseJoinColumnName: string
109
                    let firstJunctionColumn: ColumnMetadata
110
                    let secondJunctionColumn: ColumnMetadata
111

UNCOV
112
                    if (relationCountAttr.relation.isOwning) {
×
113
                        // todo fix joinColumns[0] and inverseJoinColumns[0].
UNCOV
114
                        joinTableColumnName =
×
115
                            relationCountAttr.relation.joinColumns[0]
116
                                .referencedColumn!.databaseName
UNCOV
117
                        inverseJoinColumnName =
×
118
                            relationCountAttr.relation.inverseJoinColumns[0]
119
                                .referencedColumn!.databaseName
UNCOV
120
                        firstJunctionColumn =
×
121
                            relationCountAttr.relation.junctionEntityMetadata!
122
                                .columns[0]
UNCOV
123
                        secondJunctionColumn =
×
124
                            relationCountAttr.relation.junctionEntityMetadata!
125
                                .columns[1]
126
                    } else {
UNCOV
127
                        joinTableColumnName =
×
128
                            relationCountAttr.relation.inverseRelation!
129
                                .inverseJoinColumns[0].referencedColumn!
130
                                .databaseName
UNCOV
131
                        inverseJoinColumnName =
×
132
                            relationCountAttr.relation.inverseRelation!
133
                                .joinColumns[0].referencedColumn!.databaseName
UNCOV
134
                        firstJunctionColumn =
×
135
                            relationCountAttr.relation.junctionEntityMetadata!
136
                                .columns[1]
UNCOV
137
                        secondJunctionColumn =
×
138
                            relationCountAttr.relation.junctionEntityMetadata!
139
                                .columns[0]
140
                    }
141

UNCOV
142
                    let referenceColumnValues = rawEntities
×
143
                        .map(
144
                            (rawEntity) =>
UNCOV
145
                                rawEntity[
×
146
                                    relationCountAttr.parentAlias +
147
                                        "_" +
148
                                        joinTableColumnName
149
                                ],
150
                        )
UNCOV
151
                        .filter((value) => !!value)
×
UNCOV
152
                    referenceColumnValues =
×
153
                        referenceColumnValues.filter(onlyUnique)
154

155
                    // ensure we won't perform redundant queries for joined data which was not found in selection
156
                    // example: if post.category was not found in db then no need to execute query for category.imageIds
UNCOV
157
                    if (referenceColumnValues.length === 0)
×
158
                        return {
×
159
                            relationCountAttribute: relationCountAttr,
160
                            results: [],
161
                        }
162

UNCOV
163
                    const junctionAlias = relationCountAttr.junctionAlias
×
164
                    const inverseSideTableName =
UNCOV
165
                        relationCountAttr.joinInverseSideMetadata.tableName
×
166
                    const inverseSideTableAlias =
UNCOV
167
                        relationCountAttr.alias || inverseSideTableName
×
168
                    const junctionTableName =
UNCOV
169
                        relationCountAttr.relation.junctionEntityMetadata!
×
170
                            .tableName
171

172
                    const condition =
UNCOV
173
                        junctionAlias +
×
174
                        "." +
175
                        firstJunctionColumn.propertyName +
176
                        " IN (" +
177
                        referenceColumnValues.map((vals) =>
UNCOV
178
                            isNaN(vals) ? "'" + vals + "'" : vals,
×
179
                        ) +
180
                        ")" +
181
                        " AND " +
182
                        junctionAlias +
183
                        "." +
184
                        secondJunctionColumn.propertyName +
185
                        " = " +
186
                        inverseSideTableAlias +
187
                        "." +
188
                        inverseJoinColumnName
189

UNCOV
190
                    const qb = this.connection.createQueryBuilder(
×
191
                        this.queryRunner,
192
                    )
UNCOV
193
                    qb.select(
×
194
                        junctionAlias + "." + firstJunctionColumn.propertyName,
195
                        "parentId",
196
                    )
197
                        .addSelect(
198
                            "COUNT(" +
199
                                qb.escape(inverseSideTableAlias) +
200
                                "." +
201
                                qb.escape(inverseJoinColumnName) +
202
                                ")",
203
                            "cnt",
204
                        )
205
                        .from(inverseSideTableName, inverseSideTableAlias)
206
                        .innerJoin(junctionTableName, junctionAlias, condition)
207
                        .addGroupBy(
208
                            junctionAlias +
209
                                "." +
210
                                firstJunctionColumn.propertyName,
211
                        )
212

213
                    // apply condition (custom query builder factory)
UNCOV
214
                    if (relationCountAttr.queryBuilderFactory)
×
UNCOV
215
                        relationCountAttr.queryBuilderFactory(qb)
×
216

UNCOV
217
                    return {
×
218
                        relationCountAttribute: relationCountAttr,
219
                        results: await qb.getRawMany(),
220
                    }
221
                }
222
            },
223
        )
224

UNCOV
225
        return Promise.all(promises)
×
226
    }
227
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc