• 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.53
/src/query-builder/RelationIdLoader.ts
1
import { RelationMetadata } from "../metadata/RelationMetadata"
2
import { ColumnMetadata } from "../metadata/ColumnMetadata"
3
import { DataSource } from "../data-source/DataSource"
4
import { ObjectLiteral } from "../common/ObjectLiteral"
5
import { SelectQueryBuilder } from "./SelectQueryBuilder"
6
import { DriverUtils } from "../driver/DriverUtils"
1✔
7
import { QueryRunner } from "../query-runner/QueryRunner"
8

9
/**
10
 * Loads relation ids for the given entities.
11
 */
12
export class RelationIdLoader {
1✔
13
    // -------------------------------------------------------------------------
14
    // Constructor
15
    // -------------------------------------------------------------------------
16

17
    constructor(
18
        private connection: DataSource,
42✔
19
        protected queryRunner?: QueryRunner | undefined,
42✔
20
    ) {}
21

22
    // -------------------------------------------------------------------------
23
    // Public Methods
24
    // -------------------------------------------------------------------------
25

26
    /**
27
     * Loads relation ids of the given entity or entities.
28
     */
29
    load(
30
        relation: RelationMetadata,
31
        entityOrEntities: ObjectLiteral | ObjectLiteral[],
32
        relatedEntityOrRelatedEntities?: ObjectLiteral | ObjectLiteral[],
33
    ): Promise<any[]> {
UNCOV
34
        const entities = Array.isArray(entityOrEntities)
×
35
            ? entityOrEntities
36
            : [entityOrEntities]
UNCOV
37
        const relatedEntities = Array.isArray(relatedEntityOrRelatedEntities)
×
38
            ? relatedEntityOrRelatedEntities
39
            : relatedEntityOrRelatedEntities
×
40
            ? [relatedEntityOrRelatedEntities]
41
            : undefined
42

43
        // load relation ids depend of relation type
UNCOV
44
        if (relation.isManyToMany) {
×
UNCOV
45
            return this.loadForManyToMany(relation, entities, relatedEntities)
×
UNCOV
46
        } else if (relation.isManyToOne || relation.isOneToOneOwner) {
×
UNCOV
47
            return this.loadForManyToOneAndOneToOneOwner(
×
48
                relation,
49
                entities,
50
                relatedEntities,
51
            )
52
        } else {
53
            // if (relation.isOneToMany || relation.isOneToOneNotOwner) {
UNCOV
54
            return this.loadForOneToManyAndOneToOneNotOwner(
×
55
                relation,
56
                entities,
57
                relatedEntities,
58
            )
59
        }
60
    }
61

62
    /**
63
     * Loads relation ids of the given entities and groups them into the object with parent and children.
64
     *
65
     * todo: extract this method?
66
     */
67
    async loadManyToManyRelationIdsAndGroup<
68
        E1 extends ObjectLiteral,
69
        E2 extends ObjectLiteral,
70
    >(
71
        relation: RelationMetadata,
72
        entitiesOrEntities: E1 | E1[],
73
        relatedEntityOrEntities?: E2 | E2[],
74
        queryBuilder?: SelectQueryBuilder<any>,
75
    ): Promise<{ entity: E1; related?: E2 | E2[] }[]> {
76
        // console.log("relation:", relation.propertyName);
77
        // console.log("entitiesOrEntities", entitiesOrEntities);
UNCOV
78
        const isMany = relation.isManyToMany || relation.isOneToMany
×
UNCOV
79
        const entities: E1[] = Array.isArray(entitiesOrEntities)
×
80
            ? entitiesOrEntities
81
            : [entitiesOrEntities]
82

UNCOV
83
        if (!relatedEntityOrEntities) {
×
UNCOV
84
            relatedEntityOrEntities = await this.connection.relationLoader.load(
×
85
                relation,
86
                entitiesOrEntities,
87
                this.queryRunner,
88
                queryBuilder,
89
            )
UNCOV
90
            if (!relatedEntityOrEntities.length)
×
91
                return entities.map((entity) => ({
×
92
                    entity: entity,
93
                    related: isMany ? [] : undefined,
×
94
                }))
95
        }
96
        // const relationIds = await this.load(relation, relatedEntityOrEntities!, entitiesOrEntities);
UNCOV
97
        const relationIds = await this.load(
×
98
            relation,
99
            entitiesOrEntities,
100
            relatedEntityOrEntities,
101
        )
102
        // console.log("entities", entities);
103
        // console.log("relatedEntityOrEntities", relatedEntityOrEntities);
104
        // console.log("relationIds", relationIds);
105

UNCOV
106
        const relatedEntities: E2[] = Array.isArray(relatedEntityOrEntities)
×
107
            ? relatedEntityOrEntities
108
            : [relatedEntityOrEntities!]
109

UNCOV
110
        let columns: ColumnMetadata[] = [],
×
UNCOV
111
            inverseColumns: ColumnMetadata[] = []
×
UNCOV
112
        if (relation.isManyToManyOwner) {
×
UNCOV
113
            columns = relation.junctionEntityMetadata!.inverseColumns.map(
×
UNCOV
114
                (column) => column.referencedColumn!,
×
115
            )
UNCOV
116
            inverseColumns = relation.junctionEntityMetadata!.ownerColumns.map(
×
UNCOV
117
                (column) => column.referencedColumn!,
×
118
            )
UNCOV
119
        } else if (relation.isManyToManyNotOwner) {
×
120
            columns = relation.junctionEntityMetadata!.ownerColumns.map(
×
121
                (column) => column.referencedColumn!,
×
122
            )
123
            inverseColumns =
×
124
                relation.junctionEntityMetadata!.inverseColumns.map(
125
                    (column) => column.referencedColumn!,
×
126
                )
UNCOV
127
        } else if (relation.isManyToOne || relation.isOneToOneOwner) {
×
UNCOV
128
            columns = relation.joinColumns.map(
×
UNCOV
129
                (column) => column.referencedColumn!,
×
130
            )
UNCOV
131
            inverseColumns = relation.entityMetadata.primaryColumns
×
UNCOV
132
        } else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
×
UNCOV
133
            columns = relation.inverseRelation!.entityMetadata.primaryColumns
×
UNCOV
134
            inverseColumns = relation.inverseRelation!.joinColumns.map(
×
UNCOV
135
                (column) => column.referencedColumn!,
×
136
            )
137
        } else {
138
        }
139

UNCOV
140
        return entities.map((entity) => {
×
UNCOV
141
            const group: { entity: E1; related?: E2 | E2[] } = {
×
142
                entity: entity,
143
                related: isMany ? [] : undefined,
×
144
            }
145

UNCOV
146
            const entityRelationIds = relationIds.filter((relationId) => {
×
UNCOV
147
                return inverseColumns.every((column) => {
×
UNCOV
148
                    return column.compareEntityValue(
×
149
                        entity,
150
                        relationId[
151
                            column.entityMetadata.name +
152
                                "_" +
153
                                column.propertyAliasName
154
                        ],
155
                    )
156
                })
157
            })
UNCOV
158
            if (!entityRelationIds.length) return group
×
159

UNCOV
160
            relatedEntities.forEach((relatedEntity) => {
×
UNCOV
161
                entityRelationIds.forEach((relationId) => {
×
UNCOV
162
                    const relatedEntityMatched = columns.every((column) => {
×
UNCOV
163
                        return column.compareEntityValue(
×
164
                            relatedEntity,
165
                            relationId[
166
                                DriverUtils.buildAlias(
167
                                    this.connection.driver,
168
                                    undefined,
169
                                    column.entityMetadata.name +
170
                                        "_" +
171
                                        relation.propertyPath.replace(
172
                                            ".",
173
                                            "_",
174
                                        ) +
175
                                        "_" +
176
                                        column.propertyPath.replace(".", "_"),
177
                                )
178
                            ],
179
                        )
180
                    })
UNCOV
181
                    if (relatedEntityMatched) {
×
UNCOV
182
                        if (isMany) {
×
UNCOV
183
                            ;(group.related as E2[]).push(relatedEntity)
×
184
                        } else {
UNCOV
185
                            group.related = relatedEntity
×
186
                        }
187
                    }
188
                })
189
            })
UNCOV
190
            return group
×
191
        })
192
    }
193

194
    /**
195
     * Loads relation ids of the given entities and maps them into the given entity property.
196
     async loadManyToManyRelationIdsAndMap(
197
     relation: RelationMetadata,
198
     entityOrEntities: ObjectLiteral|ObjectLiteral[],
199
     mapToEntityOrEntities: ObjectLiteral|ObjectLiteral[],
200
     propertyName: string
201
     ): Promise<void> {
202
        const relationIds = await this.loadManyToManyRelationIds(relation, entityOrEntities, mapToEntityOrEntities);
203
        const mapToEntities = mapToEntityOrEntities instanceof Array ? mapToEntityOrEntities : [mapToEntityOrEntities];
204
        const junctionMetadata = relation.junctionEntityMetadata!;
205
        const mainAlias = junctionMetadata.name;
206
        const columns = relation.isOwning ? junctionMetadata.inverseColumns : junctionMetadata.ownerColumns;
207
        const inverseColumns = relation.isOwning ? junctionMetadata.ownerColumns : junctionMetadata.inverseColumns;
208
        mapToEntities.forEach(mapToEntity => {
209
            mapToEntity[propertyName] = [];
210
            relationIds.forEach(relationId => {
211
                const match = inverseColumns.every(column => {
212
                    return column.referencedColumn!.getEntityValue(mapToEntity) === relationId[mainAlias + "_" + column.propertyName];
213
                });
214
                if (match) {
215
                    if (columns.length === 1) {
216
                        mapToEntity[propertyName].push(relationId[mainAlias + "_" + columns[0].propertyName]);
217
                    } else {
218
                        const value = {};
219
                        columns.forEach(column => {
220
                            column.referencedColumn!.setEntityValue(value, relationId[mainAlias + "_" + column.propertyName]);
221
                        });
222
                        mapToEntity[propertyName].push(value);
223
                    }
224
                }
225
            });
226
        });
227
    }*/
228

229
    // -------------------------------------------------------------------------
230
    // Protected Methods
231
    // -------------------------------------------------------------------------
232

233
    /**
234
     * Loads relation ids for the many-to-many relation.
235
     */
236
    protected loadForManyToMany(
237
        relation: RelationMetadata,
238
        entities: ObjectLiteral[],
239
        relatedEntities?: ObjectLiteral[],
240
    ) {
UNCOV
241
        const junctionMetadata = relation.junctionEntityMetadata!
×
UNCOV
242
        const mainAlias = junctionMetadata.name
×
UNCOV
243
        const columns = relation.isOwning
×
244
            ? junctionMetadata.ownerColumns
245
            : junctionMetadata.inverseColumns
UNCOV
246
        const inverseColumns = relation.isOwning
×
247
            ? junctionMetadata.inverseColumns
248
            : junctionMetadata.ownerColumns
UNCOV
249
        const qb = this.connection.createQueryBuilder(this.queryRunner)
×
250

251
        // select all columns from junction table
UNCOV
252
        columns.forEach((column) => {
×
UNCOV
253
            const columnName = DriverUtils.buildAlias(
×
254
                this.connection.driver,
255
                undefined,
256
                column.referencedColumn!.entityMetadata.name +
257
                    "_" +
258
                    column.referencedColumn!.propertyPath.replace(".", "_"),
259
            )
UNCOV
260
            qb.addSelect(mainAlias + "." + column.propertyPath, columnName)
×
261
        })
UNCOV
262
        inverseColumns.forEach((column) => {
×
UNCOV
263
            const columnName = DriverUtils.buildAlias(
×
264
                this.connection.driver,
265
                undefined,
266
                column.referencedColumn!.entityMetadata.name +
267
                    "_" +
268
                    relation.propertyPath.replace(".", "_") +
269
                    "_" +
270
                    column.referencedColumn!.propertyPath.replace(".", "_"),
271
            )
UNCOV
272
            qb.addSelect(mainAlias + "." + column.propertyPath, columnName)
×
273
        })
274

275
        // add conditions for the given entities
UNCOV
276
        let condition1 = ""
×
UNCOV
277
        if (columns.length === 1) {
×
UNCOV
278
            const values = entities.map((entity) =>
×
UNCOV
279
                columns[0].referencedColumn!.getEntityValue(entity),
×
280
            )
UNCOV
281
            const areAllNumbers = values.every(
×
UNCOV
282
                (value) => typeof value === "number",
×
283
            )
284

UNCOV
285
            if (areAllNumbers) {
×
UNCOV
286
                condition1 = `${mainAlias}.${
×
287
                    columns[0].propertyPath
288
                } IN (${values.join(", ")})`
289
            } else {
290
                qb.setParameter("values1", values)
×
291
                condition1 =
×
292
                    mainAlias +
293
                    "." +
294
                    columns[0].propertyPath +
295
                    " IN (:...values1)" // todo: use ANY for postgres
296
            }
297
        } else {
298
            condition1 =
×
299
                "(" +
300
                entities
301
                    .map((entity, entityIndex) => {
302
                        return columns
×
303
                            .map((column) => {
304
                                const paramName =
305
                                    "entity1_" +
×
306
                                    entityIndex +
307
                                    "_" +
308
                                    column.propertyName
309
                                qb.setParameter(
×
310
                                    paramName,
311
                                    column.referencedColumn!.getEntityValue(
312
                                        entity,
313
                                    ),
314
                                )
315
                                return (
×
316
                                    mainAlias +
317
                                    "." +
318
                                    column.propertyPath +
319
                                    " = :" +
320
                                    paramName
321
                                )
322
                            })
323
                            .join(" AND ")
324
                    })
325
                    .map((condition) => "(" + condition + ")")
×
326
                    .join(" OR ") +
327
                ")"
328
        }
329

330
        // add conditions for the given inverse entities
UNCOV
331
        let condition2 = ""
×
UNCOV
332
        if (relatedEntities) {
×
UNCOV
333
            if (inverseColumns.length === 1) {
×
UNCOV
334
                const values = relatedEntities.map((entity) =>
×
UNCOV
335
                    inverseColumns[0].referencedColumn!.getEntityValue(entity),
×
336
                )
UNCOV
337
                const areAllNumbers = values.every(
×
UNCOV
338
                    (value) => typeof value === "number",
×
339
                )
340

UNCOV
341
                if (areAllNumbers) {
×
UNCOV
342
                    condition2 = `${mainAlias}.${
×
343
                        inverseColumns[0].propertyPath
344
                    } IN (${values.join(", ")})`
345
                } else {
346
                    qb.setParameter("values2", values)
×
347
                    condition2 =
×
348
                        mainAlias +
349
                        "." +
350
                        inverseColumns[0].propertyPath +
351
                        " IN (:...values2)" // todo: use ANY for postgres
352
                }
353
            } else {
354
                condition2 =
×
355
                    "(" +
356
                    relatedEntities
357
                        .map((entity, entityIndex) => {
358
                            return inverseColumns
×
359
                                .map((column) => {
360
                                    const paramName =
361
                                        "entity2_" +
×
362
                                        entityIndex +
363
                                        "_" +
364
                                        column.propertyName
365
                                    qb.setParameter(
×
366
                                        paramName,
367
                                        column.referencedColumn!.getEntityValue(
368
                                            entity,
369
                                        ),
370
                                    )
371
                                    return (
×
372
                                        mainAlias +
373
                                        "." +
374
                                        column.propertyPath +
375
                                        " = :" +
376
                                        paramName
377
                                    )
378
                                })
379
                                .join(" AND ")
380
                        })
381
                        .map((condition) => "(" + condition + ")")
×
382
                        .join(" OR ") +
383
                    ")"
384
            }
385
        }
386

387
        // qb.from(junctionMetadata.target, mainAlias)
388
        //     .where(condition1 + (condition2 ? " AND " + condition2 : ""));
389
        //
390
        // // execute query
391
        // const { values1, values2 } = qb.getParameters();
392
        // console.log(`I can do it`, { values1, values2 });
393
        // if (inverseColumns.length === 1 &&
394
        //     columns.length === 1 &&
395
        //     this.connection.driver instanceof SqliteDriver &&
396
        //     (values1.length + values2.length) > 500 &&
397
        //     values1.length === values2.length) {
398
        //     console.log(`I can do it`);
399
        //     return qb.getRawMany();
400
        //
401
        // } else {
402
        //     return qb.getRawMany();
403
        // }
404

405
        // execute query
UNCOV
406
        const condition = [condition1, condition2]
×
UNCOV
407
            .filter((v) => v.length > 0)
×
408
            .join(" AND ")
UNCOV
409
        return qb
×
410
            .from(junctionMetadata.target, mainAlias)
411
            .where(condition)
412
            .getRawMany()
413
    }
414

415
    /**
416
     * Loads relation ids for the many-to-one and one-to-one owner relations.
417
     */
418
    protected loadForManyToOneAndOneToOneOwner(
419
        relation: RelationMetadata,
420
        entities: ObjectLiteral[],
421
        relatedEntities?: ObjectLiteral[],
422
    ) {
UNCOV
423
        const mainAlias = relation.entityMetadata.targetName
×
424

425
        // console.log("entitiesx", entities);
426
        // console.log("relatedEntitiesx", relatedEntities);
UNCOV
427
        const hasAllJoinColumnsInEntity = relation.joinColumns.every(
×
428
            (joinColumn) => {
UNCOV
429
                return !!relation.entityMetadata.nonVirtualColumns.find(
×
UNCOV
430
                    (column) => column === joinColumn,
×
431
                )
432
            },
433
        )
UNCOV
434
        if (relatedEntities && hasAllJoinColumnsInEntity) {
×
UNCOV
435
            const relationIdMaps: ObjectLiteral[] = []
×
UNCOV
436
            entities.forEach((entity) => {
×
UNCOV
437
                const relationIdMap: ObjectLiteral = {}
×
UNCOV
438
                relation.entityMetadata.primaryColumns.forEach(
×
439
                    (primaryColumn) => {
440
                        const key =
UNCOV
441
                            primaryColumn.entityMetadata.name +
×
442
                            "_" +
443
                            primaryColumn.propertyPath.replace(".", "_")
UNCOV
444
                        relationIdMap[key] =
×
445
                            primaryColumn.getEntityValue(entity)
446
                    },
447
                )
448

UNCOV
449
                relatedEntities.forEach((relatedEntity) => {
×
UNCOV
450
                    relation.joinColumns.forEach((joinColumn) => {
×
451
                        const entityColumnValue =
UNCOV
452
                            joinColumn.getEntityValue(entity)
×
453
                        const relatedEntityColumnValue =
UNCOV
454
                            joinColumn.referencedColumn!.getEntityValue(
×
455
                                relatedEntity,
456
                            )
UNCOV
457
                        if (
×
458
                            entityColumnValue === undefined ||
×
459
                            relatedEntityColumnValue === undefined
460
                        )
UNCOV
461
                            return
×
462

463
                        if (entityColumnValue === relatedEntityColumnValue) {
×
464
                            const key =
465
                                joinColumn.referencedColumn!.entityMetadata
×
466
                                    .name +
467
                                "_" +
468
                                relation.propertyPath.replace(".", "_") +
469
                                "_" +
470
                                joinColumn.referencedColumn!.propertyPath.replace(
471
                                    ".",
472
                                    "_",
473
                                )
474
                            relationIdMap[key] = relatedEntityColumnValue
×
475
                        }
476
                    })
477
                })
UNCOV
478
                if (
×
479
                    Object.keys(relationIdMap).length ===
480
                    relation.entityMetadata.primaryColumns.length +
481
                        relation.joinColumns.length
482
                ) {
483
                    relationIdMaps.push(relationIdMap)
×
484
                }
485
            })
486
            // console.log("relationIdMap", relationIdMaps);
487
            // console.log("entities.length", entities.length);
UNCOV
488
            if (relationIdMaps.length === entities.length)
×
489
                return Promise.resolve(relationIdMaps)
×
490
        }
491

492
        // select all columns we need
UNCOV
493
        const qb = this.connection.createQueryBuilder(this.queryRunner)
×
UNCOV
494
        relation.entityMetadata.primaryColumns.forEach((primaryColumn) => {
×
UNCOV
495
            const columnName = DriverUtils.buildAlias(
×
496
                this.connection.driver,
497
                undefined,
498
                primaryColumn.entityMetadata.name +
499
                    "_" +
500
                    primaryColumn.propertyPath.replace(".", "_"),
501
            )
UNCOV
502
            qb.addSelect(
×
503
                mainAlias + "." + primaryColumn.propertyPath,
504
                columnName,
505
            )
506
        })
UNCOV
507
        relation.joinColumns.forEach((column) => {
×
UNCOV
508
            const columnName = DriverUtils.buildAlias(
×
509
                this.connection.driver,
510
                undefined,
511
                column.referencedColumn!.entityMetadata.name +
512
                    "_" +
513
                    relation.propertyPath.replace(".", "_") +
514
                    "_" +
515
                    column.referencedColumn!.propertyPath.replace(".", "_"),
516
            )
UNCOV
517
            qb.addSelect(mainAlias + "." + column.propertyPath, columnName)
×
518
        })
519

520
        // add condition for entities
UNCOV
521
        let condition: string = ""
×
UNCOV
522
        if (relation.entityMetadata.primaryColumns.length === 1) {
×
UNCOV
523
            const values = entities.map((entity) =>
×
UNCOV
524
                relation.entityMetadata.primaryColumns[0].getEntityValue(
×
525
                    entity,
526
                ),
527
            )
UNCOV
528
            const areAllNumbers = values.every(
×
UNCOV
529
                (value) => typeof value === "number",
×
530
            )
531

UNCOV
532
            if (areAllNumbers) {
×
UNCOV
533
                condition = `${mainAlias}.${
×
534
                    relation.entityMetadata.primaryColumns[0].propertyPath
535
                } IN (${values.join(", ")})`
536
            } else {
537
                qb.setParameter("values", values)
×
538
                condition =
×
539
                    mainAlias +
540
                    "." +
541
                    relation.entityMetadata.primaryColumns[0].propertyPath +
542
                    " IN (:...values)" // todo: use ANY for postgres
543
            }
544
        } else {
545
            condition = entities
×
546
                .map((entity, entityIndex) => {
547
                    return relation.entityMetadata.primaryColumns
×
548
                        .map((column, columnIndex) => {
549
                            const paramName =
550
                                "entity" + entityIndex + "_" + columnIndex
×
551
                            qb.setParameter(
×
552
                                paramName,
553
                                column.getEntityValue(entity),
554
                            )
555
                            return (
×
556
                                mainAlias +
557
                                "." +
558
                                column.propertyPath +
559
                                " = :" +
560
                                paramName
561
                            )
562
                        })
563
                        .join(" AND ")
564
                })
565
                .map((condition) => "(" + condition + ")")
×
566
                .join(" OR ")
567
        }
568

569
        // execute query
UNCOV
570
        return qb
×
571
            .from(relation.entityMetadata.target, mainAlias)
572
            .where(condition)
573
            .getRawMany()
574
    }
575

576
    /**
577
     * Loads relation ids for the one-to-many and one-to-one not owner relations.
578
     */
579
    protected loadForOneToManyAndOneToOneNotOwner(
580
        relation: RelationMetadata,
581
        entities: ObjectLiteral[],
582
        relatedEntities?: ObjectLiteral[],
583
    ) {
UNCOV
584
        const originalRelation = relation
×
UNCOV
585
        relation = relation.inverseRelation!
×
586

UNCOV
587
        if (
×
588
            relation.entityMetadata.primaryColumns.length ===
589
            relation.joinColumns.length
590
        ) {
591
            const sameReferencedColumns =
UNCOV
592
                relation.entityMetadata.primaryColumns.every((column) => {
×
UNCOV
593
                    return relation.joinColumns.indexOf(column) !== -1
×
594
                })
UNCOV
595
            if (sameReferencedColumns) {
×
596
                return Promise.resolve(
×
597
                    entities.map((entity) => {
598
                        const result: ObjectLiteral = {}
×
599
                        relation.joinColumns.forEach(function (joinColumn) {
×
600
                            const value =
601
                                joinColumn.referencedColumn!.getEntityValue(
×
602
                                    entity,
603
                                )
604
                            const joinColumnName =
605
                                joinColumn.referencedColumn!.entityMetadata
×
606
                                    .name +
607
                                "_" +
608
                                joinColumn.referencedColumn!.propertyPath.replace(
609
                                    ".",
610
                                    "_",
611
                                )
612
                            const primaryColumnName =
613
                                joinColumn.entityMetadata.name +
×
614
                                "_" +
615
                                originalRelation.propertyPath.replace(
616
                                    ".",
617
                                    "_",
618
                                ) +
619
                                "_" +
620
                                joinColumn.propertyPath.replace(".", "_")
621
                            result[joinColumnName] = value
×
622
                            result[primaryColumnName] = value
×
623
                        })
624
                        return result
×
625
                    }),
626
                )
627
            }
628
        }
629

UNCOV
630
        const mainAlias = relation.entityMetadata.targetName
×
631

632
        // select all columns we need
UNCOV
633
        const qb = this.connection.createQueryBuilder(this.queryRunner)
×
UNCOV
634
        relation.entityMetadata.primaryColumns.forEach((primaryColumn) => {
×
UNCOV
635
            const columnName = DriverUtils.buildAlias(
×
636
                this.connection.driver,
637
                undefined,
638
                primaryColumn.entityMetadata.name +
639
                    "_" +
640
                    originalRelation.propertyPath.replace(".", "_") +
641
                    "_" +
642
                    primaryColumn.propertyPath.replace(".", "_"),
643
            )
UNCOV
644
            qb.addSelect(
×
645
                mainAlias + "." + primaryColumn.propertyPath,
646
                columnName,
647
            )
648
        })
UNCOV
649
        relation.joinColumns.forEach((column) => {
×
UNCOV
650
            const columnName = DriverUtils.buildAlias(
×
651
                this.connection.driver,
652
                undefined,
653
                column.referencedColumn!.entityMetadata.name +
654
                    "_" +
655
                    column.referencedColumn!.propertyPath.replace(".", "_"),
656
            )
UNCOV
657
            qb.addSelect(mainAlias + "." + column.propertyPath, columnName)
×
658
        })
659

660
        // add condition for entities
UNCOV
661
        let condition: string = ""
×
UNCOV
662
        if (relation.joinColumns.length === 1) {
×
UNCOV
663
            const values = entities.map((entity) =>
×
UNCOV
664
                relation.joinColumns[0].referencedColumn!.getEntityValue(
×
665
                    entity,
666
                ),
667
            )
UNCOV
668
            const areAllNumbers = values.every(
×
UNCOV
669
                (value) => typeof value === "number",
×
670
            )
671

UNCOV
672
            if (areAllNumbers) {
×
UNCOV
673
                condition = `${mainAlias}.${
×
674
                    relation.joinColumns[0].propertyPath
675
                } IN (${values.join(", ")})`
676
            } else {
677
                qb.setParameter("values", values)
×
678
                condition =
×
679
                    mainAlias +
680
                    "." +
681
                    relation.joinColumns[0].propertyPath +
682
                    " IN (:...values)" // todo: use ANY for postgres
683
            }
684
        } else {
685
            condition = entities
×
686
                .map((entity, entityIndex) => {
687
                    return relation.joinColumns
×
688
                        .map((joinColumn, joinColumnIndex) => {
689
                            const paramName =
690
                                "entity" + entityIndex + "_" + joinColumnIndex
×
691
                            qb.setParameter(
×
692
                                paramName,
693
                                joinColumn.referencedColumn!.getEntityValue(
694
                                    entity,
695
                                ),
696
                            )
697
                            return (
×
698
                                mainAlias +
699
                                "." +
700
                                joinColumn.propertyPath +
701
                                " = :" +
702
                                paramName
703
                            )
704
                        })
705
                        .join(" AND ")
706
                })
707
                .map((condition) => "(" + condition + ")")
×
708
                .join(" OR ")
709
        }
710

711
        // execute query
UNCOV
712
        return qb
×
713
            .from(relation.entityMetadata.target, mainAlias)
714
            .where(condition)
715
            .getRawMany()
716
    }
717
}
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